Reputation: 9065
I want to restrict a moving object on a plane( the plane may be or may not be a square) here is the prototype:
I attached a mesh collider to the plane and hope to use that to restrict the movement of the sphere the sphere will move randomly, the idea is that when the ball moved to the edge the velocity will be reflected so it will not fall off the plane with this:
public class BallScript : MonoBehaviour {
// Use this for initialization
void Start () {
GetComponent<Rigidbody>().velocity = new Vector3(0.1f,0,0.1f);
}
private void OnCollisionExit(Collision collision)
{
GetComponent<Rigidbody>().velocity = -GetComponent<Rigidbody>().velocity;
}
}
but the ball kept fall at the edge,
I can't make edge colliders cause the shape of the plane will not be decided until runtime.
Is there a way to do that?
Upvotes: 0
Views: 3049
Reputation: 1648
So here's just an Idea as the OP agreed to put this as an answer
public Layer[] layerPriorities =
{
Layer.Enemy,
Layer.Walkable
};
[SerializeField] float distanceToBackground = 100f;
public delegate void OnLayerChange(Layer newLayer); // declare new delegate type
//lets use event for the protection of the layerchanges
public event OnLayerChange onlayerChange; // instantiate a observer set
//Look for and return priority layer hit
foreach (Layer layer in layerPriorities)
{
var hit = RaycastForLayer(layer);
if (hit.HasValue)
{
raycastHit = hit.Value;
if(layerHit != layer){ //if layer has changed
layerHit = layer;
onlayerChange(layer); //call the delegate
}
layerHit = layer;
return;
}
}
// otherwise return background hit
raycastHit.distance = distanceToBackground;
layerHit = Layer.RaycastEndStop;
}
//? is a nullable parameter
RaycastHit? RaycastForLayer(Layer layer)
{
/*(Use a bitshift) <<*/
int layerMask = 1 << (int)layer; // lets do masking formation
Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit; //used as an out parameter
bool hasHit = Physics.Raycast(ray, out hit, distanceToBackground, layerMask);
if (hasHit)
{
return hit;
}
return null;
}
and create an enum for that
public enum Layer
{
Walkable = 8,
RaycastEndStop = -1 //lowest priority
}
As the Layer.Enemy,
You could set it to NotWalkable
something like well it depends you.
So the small missing part here is just calculate the distance of the ball from the edge of the plane instead of clicking it . That's what I am telling you about the SqrMagnitude
Upvotes: 1
Reputation: 125305
The OnCollisionExit
function is called when the object is no longer colliding with the other object. In, your case, it will be called not when the ball is on the edge of the plane but when the ball is no longer touching the plane.
The safest way to do this is to use a BoxCollider. I suggest you just use Unity's Cube primitive which comes with a BoxCollider. The mesh on it will be useful and should act as a visual guide when resizing the BoxCollider.
1.Go to GameObject ---> 3D Object ---> Cube and create new Cube Objects. Looking at the shape of your plane, you will need 6 BoxColliders so this means that you need 6 cubes.
2.Resize, move and rotate each one until you they cover each side of the plane.
3.Create new layer and call it "Boundary". Select each cube and set the layer to "Boundary".
4.Got to Edit ---> Project Settings ---> Physics and use the Layer Collision Matrix to make sure that "Boundary" cannot collide with "Boundary".
5.Disable or remove MeshRenderer
of each cube and that's that.
The code in your question is unnecessary but if you still want it to reflect then use OnCollisionEnter
and check when the ball hits the wall.
Attach to the ball:
Rigidbody rb;
public float force = 50;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void OnCollisionEnter(Collision col)
{
if (col.collider.CompareTag("Boundary"))
rb.AddForce(col.contacts[0].normal * force, ForceMode.Impulse);
}
Upvotes: 2