armnotstrong
armnotstrong

Reputation: 9065

unity restrict moving object on a plane

I want to restrict a moving object on a plane( the plane may be or may not be a square) here is the prototype:

enter image description here

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

Answers (2)

Ginxxx
Ginxxx

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

Programmer
Programmer

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

Related Questions