Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Air hockey game - Player bat goes through puck if moved too fast

Tags:

c#

unity3d

Im currently developing an Air hockey game in Unity3d. The issue I'm having is that when the player attempts to hit the puck too quickly, the player ends up going through the puck and therefore there is no collision. The game works perfectly as expected if the player stays still and the puck hits the player or if the player hits the puck at a slow pace.

The player has a rigidbody using continuous collision detection using a capsule collider. The puck also has rigidbody with continuous dynamic collision detection and a mesh collider with convex.

I tried setting the fixed timestep to 0.01 but that didn't have an effect. Here is the script for the player movement:

void ObjectFollowCursor()
{
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    Vector3 point = ray.origin + (ray.direction * distance);

    Vector3 temp = point;
    temp.y = 0.2f; // limits player on y axis

    cursorObject.position = temp;
}

and here is the code for the puck when it collides with the player:

// If puck hits player
if(collision.gameObject.tag == "Player")
{
    Vector3 forceVec = this.GetComponent<Rigidbody>().velocity.normalized * hitForce;
    rb.AddForce(forceVec, ForceMode.Impulse);
    Debug.Log ("Player Hit");
}

Any help would be much appreciated. Thanks.

like image 475
Gazz Avatar asked Nov 20 '15 15:11

Gazz


People also ask

How do air hockey pucks float?

The lift generated to levitate the puck is achieved by creating tiny holes all over the playfield in a grid that blows air at high pressure from underneath. The air is then forced through these holes and exits with a high velocity countering the weight of the puck making it float on a layer of thin air.

When playing air hockey can you stop the puck?

Other rules about Air Hockey The most broken rule is the one about stopping the puck. When the puck is struck, and the defense loses their mallet, they can't stop the puck with their hands or body. When this shot situation occurs, prior to or simultaneously losing a mallet, the score counts.

What are the things in air hockey called?

Air hockey is a Pong-like tabletop sport where two opposing players try to score goals against each other on a low-friction table using two hand-held discs ("mallets") and a lightweight plastic puck.


2 Answers

The problem you are having its called "tunneling".

This happens because your object is moving at a high speed and in that specific frame the collision is not detected. In frame n the ball is just in front of the bat, but when frame n+1 is calculated the ball has moved behind the bat, thus "missing" the collision completely.

It is a common problem but there are solutions.

I recommend you study this script and try to implement on your game.

This is not my code: SOURCE: http://wiki.unity3d.com/index.php?title=DontGoThroughThings

using UnityEngine;
using System.Collections;

public class DontGoThroughThings : MonoBehaviour
{
       // Careful when setting this to true - it might cause double
       // events to be fired - but it won't pass through the trigger
       public bool sendTriggerMessage = false;  

    public LayerMask layerMask = -1; //make sure we aren't in this layer 
    public float skinWidth = 0.1f; //probably doesn't need to be changed 

    private float minimumExtent; 
    private float partialExtent; 
    private float sqrMinimumExtent; 
    private Vector3 previousPosition; 
    private Rigidbody myRigidbody;
    private Collider myCollider;

    //initialize values 
    void Start() 
    { 
       myRigidbody = GetComponent<Rigidbody>();
       myCollider = GetComponent<Collider>();
       previousPosition = myRigidbody.position; 
       minimumExtent = Mathf.Min(Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z); 
       partialExtent = minimumExtent * (1.0f - skinWidth); 
       sqrMinimumExtent = minimumExtent * minimumExtent; 
    } 

    void FixedUpdate() 
    { 
       //have we moved more than our minimum extent? 
       Vector3 movementThisStep = myRigidbody.position - previousPosition; 
       float movementSqrMagnitude = movementThisStep.sqrMagnitude;

       if (movementSqrMagnitude > sqrMinimumExtent) 
        { 
          float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
          RaycastHit hitInfo; 

          //check for obstructions we might have missed 
          if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movementMagnitude, layerMask.value))
              {
                 if (!hitInfo.collider)
                     return;

                 if (hitInfo.collider.isTrigger) 
                     hitInfo.collider.SendMessage("OnTriggerEnter", myCollider);

                 if (!hitInfo.collider.isTrigger)
                     myRigidbody.position = hitInfo.point - (movementThisStep / movementMagnitude) * partialExtent; 

              }
       } 

       previousPosition = myRigidbody.position; 
    }
}
like image 116
Jorge Santos Avatar answered Oct 05 '22 23:10

Jorge Santos


You were right to try continuous collision detection (CCD). There are some constraints (especially in this case where you want to use CCD with two moving objects rather than one moving object and one static object), but it is designed for this kind of scenario. The Rigidbody documentation goes into these constraints:

Set the collision detection mode to Continuous to prevent the rigidbody from passing through any static (ie, non-rigidbody) MeshColliders. Set it to Continuous Dynamic to also prevent the rigidbody from passing through any other supported rigidbodies with collision detection mode set to Continuous or Continuous Dynamic. Continuous collision detection is supported for Box-, Sphere- and CapsuleColliders.

To sum up, both puck and paddle need to be set to Continuous Dynamic, and both need to be Box-, Sphere-, or Capsule Colliders. If you can make these constraints work for your game you should be able to get continuous collision detection without writing it yourself.


A note about Unity's CCD that bears repeating:

Note that continuous collision detection is intended as a safety net to catch collisions in cases where objects would otherwise pass through each other, but will not deliver physically accurate collision results, so you might still consider decreasing the fixed Time step value in the TimeManager inspector to make the simulation more precise, if you run into problems with fast moving objects.

But since you are manually specifying the collision reaction, that might not be an issue.

like image 44
31eee384 Avatar answered Oct 05 '22 23:10

31eee384