Ray Cast do not intercept Concave objects - unity3d

I am making a game with Unity, the project model is 2D.
What I have to do is a wheel divided into segments, each segment is an individual object, this wheel turns on itself with a certain speed, here is an imamgine for better understanding:
I have a "Selector", i.e. something to select a slice, so I made a temporary sprite, i.e. the red triangle, and a script to generate a Ray Cast to locate the selected slice, some images to better understand:
So far so good, my problem lies in the fact that the mesh of the slices is also the mesh of the collider, which is not convex but concave, so from what I've read on the Internet Unity does not allow to intercept objects with concave mesh by the Ray Cast due to calculation problems, so I can not intercept the slices, the only way to hit them is to tick the parameter "Convex"
of the Collider component, but I create a collider with a square shape, and so the selection precision is lacking, here are some pictures to better understand:
So I looked on the internet for a solution and found that the solution was to split the collider into several smaller but convex colliders, so I tried this, i.e. for each pair, i.e. 2 triangles, I created a collider that had a mesh made from the two triangles in question, so I got this:
But it is still not intercepted by RayCast, unless I tick the "Convex" parameter of the collider component, but even then a collider with a square shape is created.
Finally, here are some parts of the code within the post:
Code for generating ray cast:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Selector : MonoBehaviour
{
public GameObject objPosRef;
void Update()
{
if (Input.GetMouseButtonUp(1))
{
RaycastHit hit;
Debug.DrawRay(objPosRef.transform.position, new Vector3(0, 0.5f, 0), Color.green, 0.5f);
if (Physics.Raycast(objPosRef.transform.position, new Vector3(0, 0.5f, 0), out hit))
{
Debug.Log("Colpito: " + hit.collider.name);
}
}
}
}
Code to update the segment mesh:
...
private void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertex;
mesh.triangles = triangles;
createColldiers();
mesh.RecalculateNormals();
mesh.RecalculateBounds();
GetComponent<MeshRenderer>().material = new Material(material);
GetComponent<MeshRenderer>().material.color = color;
}
Code for creating segment colliders:
private void createColldiers()
{
int numColliders = (numVertex * 2 - 2) / 2;
for (int i = 0; i < numColliders; i++)
{
MeshCollider collider = gameObject.AddComponent<MeshCollider>();
Mesh mesh = new Mesh();
int[] tr = new int[6];
int k = 6 * i;
for (int j = 0; j < 6; j++)
{
tr[j] = triangles[k];
k++;
}
mesh.vertices = vertex;
mesh.triangles = tr;
collider.sharedMesh = mesh;
}
}
In summary I create a new mesh, the vertices of this mesh are identical to those of the segment mesh, although the new mesh does not need all the vertices, the triangles of the new mesh are only 2, and are taken in pairs from the triangle array of the segment mesh.
Sorry if my post is full of pictures, I hope I have been able to give you the best possible understanding of my problem, most likely the probelma will be something popping, thank you in advance for your help.
p.s: very few people say that raycasts can safely intercept objects with a concave collider, thus going against the grain of all other claims to the contrary, who is right?

Related

How do I make my 2D character's legs snap to non flat terrain?

I am creating a 2D platformer type game, and it has non flat terrain. How do I make it so that my character's legs always smoothly transition from a flat surface to a slope surface?
For reference you can check out [Alto's Adventure] (http://altosadventure.com/) (mobile). In that, the skateboard snaps to the curvy terrain.
Feel free to ask for more details.
To make such a possibility, you have to send a ray to the ground and then set the direction of up transform according to the Normal vector point of impact. The following code solves the basic problem.
public LayerMask groundLayer;
public float maxRayLength = 3;
public float offset = .5f; // set it to half of your character height
public void Update()
{
var ground = Physics2D.Raycast(transform.position, -transform.up, maxRayLength, groundLayer.value);
if (ground)
{
transform.up = ground.normal;
transform.position = new Vector3(ground.point.x, ground.point.y) + transform.up*offset;
}
}
In addition to the script, you must specify a suitable 2D collider as well as its layer. For layers, make sure that both the script inspector and the layer are set to the hypothetical ground layer.
Example Result:

In unity, how do you find voxel information at a given worldspace position?

I am trying to have a gameobject in unity react with sound if another object is inside it. I want the gameobject to use the entering objects location to then see what voxel is closest and then play audio based on the voxel intensity/colour. Does anyone have any ideas? I am working with a dataset that is 512x256x512 voxels. I want it to work if the object is resized as well. Any help is much appreciated :).
The dataset I'm working with is a 3d .mhd medical scan of a body. Here is how the texture is added to the renderer on start:
for (int k = 0; k < NumberOfFrames; k++) {
string fname_ = "T" + k.ToString("D2");
Color[] colors = LoadData(Path.Combine (imageDir, fname_+".raw"));
_volumeBuffer.Add (new Texture3D (dim [0], dim [1], dim [2], TextureFormat.RGBAHalf, mipmap));
_volumeBuffer[k].SetPixels(colors);
_volumeBuffer [k].Apply ();
}
GetComponent<Renderer>().material.SetTexture("_Data", _volumeBuffer[0]);
The size of the object is defined by using the mdh header files spacing as well as voxel dimensions:
transform.localScale = new Vector3(mhdheader.spacing[0] * volScale, mhdheader.spacing[1] * volScale * dim[1] / dim[0], mhdheader.spacing[2] * volScale * dim[2] / dim[0]);
I have tried making my own function to get the index from the world by offsetting it to the beginning of the render mesh (not sure if this is right). Then, scaling it by the local scale. Then, multiplying by the amount of voxels in each dimension. However, I am not sure if my logic is right whatsoever... Here is the code I tried:
public Vector3Int GetIndexFromWorld(Vector3 worldPos)
{
Vector3 startOfTex = gameObject.GetComponent<Renderer>().bounds.min;
Vector3 localPos = transform.InverseTransformPoint(worldPos);
Vector3 localScale = gameObject.transform.localScale;
Vector3 OffsetPos = localPos - startOfTex;
Vector3 VoxelPosFloat = new Vector3(OffsetPos[0] / localScale[0], OffsetPos[1] / localScale[1], OffsetPos[2] / localScale[2]);
VoxelPosFloat = Vector3.Scale(VoxelPosFloat, new Vector3(voxelDims[0], voxelDims[1], voxelDims[2]));
Vector3Int voxelPos = Vector3Int.FloorToInt(VoxelPosFloat);
return voxelPos;
}
You can try setting up a large amount of box colliders and the OnTriggerEnter() function running on each. But a much better solution is to sort your array of voxels and then use simple math to clamp the moving objects position vector to ints and do some maths to map the vector to an index in the array. For example the vector (0,0,0) could map to voxels[0]. Then just fetch that voxels properties as you like. For a voxel application this would be a much needed faster calculation than colliders.
I figured it out I think. If anyone sees any flaw in my coding, please let me know :).
public Vector3Int GetIndexFromWorld(Vector3 worldPos)
{
Vector3 deltaBounds = rend.bounds.max - rend.bounds.min;
Vector3 OffsetPos = worldPos - rend.bounds.min;
Vector3 normPos = new Vector3(OffsetPos[0] / deltaBounds[0], OffsetPos[1] / deltaBounds[1], OffsetPos[2] / deltaBounds[2]);
Vector3 voxelPositions = new Vector3(normPos[0] * voxelDims[0], normPos[1] * voxelDims[1], normPos[2] * voxelDims[2]);
Vector3Int voxelPos = Vector3Int.FloorToInt(voxelPositions);
return voxelPos;
}

Drawing 3D polygon on unity 3d

I am trying out unity for a project that i am on.
I am attempting to draw 3D polygon from a set of coordinate that I have.
So what i am doing now is to build a row of cube btw the two points. I plan to build these points into either a solid shape or just "walls" to form a room.
However, it doesn't seem to work as expected. Please advise.
drawCube( Vector3(10,0,14),Vector3(70,0,14));
drawCube( Vector3(90,0,14),Vector3(60,87,45));
function drawCube(v1,v2) {
pA = v1;
pB = v2;
var plane : GameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
var between:Vector3 = pB - pA;
var distance:float = between.magnitude;
plane.transform.localScale.x = distance;
plane.transform.localScale.y=10;
plane.transform.position = pA + (between / 2.0);
plane.transform.LookAt(pB);
}
updated: I have also tried using a mesh but all i got was the below image. What am i doing wrong?
I am trying to achieve something like this
You could make primitives and manipulate them but that would limit you very much if you needed to scale or change your requirements in the future. I would recommend using a procedural mesh to create the geometry you need as you need it. The basics aren't too hard, it's just a matter of constructing the Mesh object from it's base components given some vertices. Here's an example of constructing a 3d quadrilateral:
using UnityEngine;
using System.Collections.Generic;
public class SquareMaker : MonoBehaviour {
public List<Vector3> points;
void Start()
{
GameObject threeDSquare = new GameObject("3DSquare");
threeDSquare.AddComponent<MeshRenderer>();
threeDSquare.AddComponent<MeshFilter>();
threeDSquare.GetComponent<MeshFilter>().mesh = CreateMesh(points);
}
private Mesh CreateMesh(List<Vector3> points)
{
List<int> tris = new List<int>(); // Every 3 ints represents a triangle
List<Vector2> uvs = new List<Vector2>(); // Vertex position in 0-1 UV space
/* 4 points in the list for the square made of two triangles:
0 *--* 1
| /|
|/ |
3 *--* 2
*/
tris.Add(1);
tris.Add(2);
tris.Add(3);
tris.Add(3);
tris.Add(0);
tris.Add(1);
// uvs determine vert (point) coordinates in uv space
uvs.Add(new Vector2(0f, 1f));
uvs.Add(new Vector2(1f, 1f));
uvs.Add(new Vector2(1f, 0f));
uvs.Add(new Vector2(0f, 0f));
Mesh mesh = new Mesh();
mesh.vertices = points.ToArray();
mesh.uv = uvs.ToArray();
mesh.triangles = tris.ToArray();
mesh.RecalculateNormals();
return mesh;
}
}

How to limit where a sprite can move in Unity?

I am fairly new to Unity so please bear with me I have tried looking for the answer everywhere, but have had no luck.
Basically I am using onMouseDrag to move a sprite around the background for a classroom (1366x768) that has a table. However, I want to limit where the sprite can go so that it does not end up off screen or off of the table on my background.
My sprite has the 2d box collider and rigidbody components attached (gravity is set to zero and it is at a fixed angle). I thought that by placing four 2d box colliders around the area I want to keep the sprite in it would be enough to contain it but the sprite simply goes straight through them.
I also read up about using Mathf.Clamp to restrict the area but I do not really understand how to use it from the examples I have seen.
Below is my code for moving the sprite:
using UnityEngine;
using System.Collections;
public class MovementScript : MonoBehaviour {
float x;
float y;
void Update() {
x = Input.mousePosition.x;
y = Input.mousePosition.y;
}
public void OnMouseDrag() {
transform.position = Camera.main.ScreenToWorldPoint (new Vector3 (x, y, 1.0f));
}
}
Any help would be greatly appreciated!
Moving an object using its transform is not the same as moving it.
When you use the transform, the object doesn't "move", it teleports every Update by a small amount. Unfortunately, the Rigidbody can't detect this change in position as movement and thus does not react with any colliders. Regardless, that's probably the more complicated way to do this. Using Clamp is definitely easier.
Clamp is a pretty straightforward function. it takes three args: a value, a min, and a max. If the value is less than min or greater than max, it returns that boundary. Otherwise, it returns the value itself.
For instance,
Mathf.Clamp(5, 1, 3); //returns 3
Mathf.Clamp(2, 1, 3); //returns 2
Mathf.Clamp(-2, 1, 3); //returns 1
This is simply a convenience function for something like this:
if(val > max) {
return max;
} else if(val < min) {
return min;
} else {
return val;
}
So using Clamp, you can restrict the values of your x and y coordinates:
//to avoid confusion, I'm referring to your x and y variables
//as inputX and inputY. they represent the mouse position.
public void OnMouseDrag() {
Vector3 pos = Vector3.zero;
pos.x = Mathf.Clamp(inputX, minX, maxX);
pos.y = Mathf.Clamp(inputY, minY, maxY);
pos.z = 1.0;
transform.position = Camera.main.ScreenToWorldPoint (pos);
}
Mathf.Clamp will keep the X and Y coordinates of your Transform within the range (minX, maxX) and (minY, maxY) respectively. You can create these variables as inspector variables so you can change them on the fly.

2D bouncing formula doesn't work properly

I am new to unity, and i am trying to create a bouncing ball, so i've did many researches about bouncing realted physics and i found a formula :
Formula:
-2*(V dot N)*N + V
Where V is the velocity vector and N is the normal of the surface on which the ball will bounce
Here is my script :
using UnityEngine;
using System.Collections;
public class BallPhysics : MonoBehaviour {
void Start () {
rigidbody2D.velocity =new Vector2 (-1,-3);
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter2D(Collider2D col) {
if (col.gameObject.name == "Pong") {
tBounce ();
}
}
void tBounce(){
RaycastHit2D hit = Physics2D.Raycast (new Vector2 (transform.position.x,transform.position.y), new Vector2(-1f,-1f));
Vector2 n = hit.normal;
Vector2 v = rigidbody2D.velocity;
Vector2 R = -2 * (Vector2.Dot (v, n)) * n + v;
rigidbody2D.velocity = R;
}
}
I am giving the ball a velocity vector in the start function, i am using OnTriggerEnter2D for collision handling and raycast2D to get the normal of a surface.
The problem is that the script doesn't reflect the velocity vector called R, i think the probleme is in the normal vector.
For example let's say V is a Vector2(-1,-1) so basically R should be (-1,1), but it's not. R is (3,1) !
i've successfuly been able to make a ball bouncing on Horizontal/vertical surface by reversing the ball velocity but this won't work properly with arbitary angles,that's why i am using this formula.
So what's the problem ?
Your raycast is likely hitting the wrong collider. You start it at transform.position. That is the center point of your ball object.
Imagine a ray coming out of a circle's center. What's the first line it hits? It hits the circle itself first. So your Raycast reports the surface normal of the ball, not the pong paddle.
There are many ways to get around this. You can apply a Physics Layer that ignores raycasts. I think there is one pre-defined like that. You can create custom ones as well and supply a layer mask to your raycast.
There are other ways to solve it like originating the raycast from somewhere else, but since you're doing this as the objects collide, a layer mask is probably the simplest.