Greetings All, I have a game in progress with a ball under physics control that can get to moving so fast that it will sometimes fly through objects. I have increased the the number of times per second physics is calculated and it resolves this issue, but SIGNIFICANTLY increases CPU Load. Someone had mentioned in passing that it could be possible to implement a system where the fast moving ball would use raycasting so as to avoid these "pass throughs". Any ideas as to how this could be done? Any help would be greatly appreciated. Thanks!
Here's the solution I'm using. It's more general and more efficient than what the other threads discuss. Improvements include: • works for any type of Collider • tests where we've been rather than where we might be going • LayerMask for ignoring the object itself, or any other layer • compares squared magnitudes which is faster, and more appropriate for FixedUpdate() There's a variable called skinWidth, which allows the definition of a skin width, which is expressed as a fraction of the minimum extent. This defines how far into the obstruction the script will place the object, and should be a value between 0 and 1. A value of 1 means the object will have its origin exactly at the ray hit. A value of 0.2 or so will result in the following: A value of 0 means the object will be placed with its origin a distance of minimumExtent away from the hit point. There is the possibility of missing the collision if you use a value of zero. Code (csharp): #pragma strict var layerMask : LayerMask; //make sure we aren't in this layer var skinWidth : float = 0.1; //probably doesn't need to be changed private var minimumExtent : float; private var partialExtent : float; private var sqrMinimumExtent : float; private var previousPosition : Vector3; private var myRigidbody : Rigidbody; //initialize values function Awake() { myRigidbody = rigidbody; previousPosition = myRigidbody.position; minimumExtent = Mathf.Min(Mathf.Min(collider.bounds.extents.x, collider.bounds.extents.y), collider.bounds.extents.z); partialExtent = minimumExtent*(1.0 - skinWidth); sqrMinimumExtent = minimumExtent*minimumExtent; } function FixedUpdate() { //have we moved more than our minimum extent? if ((previousPosition - myRigidbody.position).sqrMagnitude > sqrMinimumExtent) { var movementThisStep : Vector3 = myRigidbody.position - previousPosition; var movementMagnitude : float = movementThisStep.magnitude; var hitInfo : RaycastHit; //check for obstructions we might have missed if (Physics.Raycast(previousPosition, movementThisStep, hitInfo, movementMagnitude, layerMask.value)) myRigidbody.position = hitInfo.point - (movementThisStep/movementMagnitude)*partialExtent; } previousPosition = myRigidbody.position; }
Thanks so much for the help everybody. I'm trying to get the last script working on my object but keep getting the error- 'bounds' is not a member of 'System.Object'. While the debugger points to the line- minimumExtent = Mathf.Min(Mathf.Min(collider.bounds.extents.x, collider.bounds.extents.y), collider.bounds.extents.z); Any idea what is going on? My ball has a sphere collider and rigidbody attached. I'm sure it's something simple I'm missing. Thanks again for all your help.
That did the trick! I'm still getting some weirdness, but this collision method may well save a game I had previously abandoned. Thanks again to everyone who helped.
This is a great piece of code. I'm using it now in my program to keep a hockey puck from going through the boards at high speed. There is just one issue - the way it works is that we check to see if we already passed through any colliders. It's checking the previous position in the last frame of action. And then backs the projectile up so that the physics engine can do the work in the next frame. (I think) It's brilliant, but we do go through a mesh for one frame. Anyway to fix that? I was thinking of making a GO of the puck that is just of the image and make that GO a child of the puck GO. And then dynamically correct the position of the image GO in a script so that it doesn't "look" like it went through the mesh for even one frame (although it would freeze for a frame). I'm having no success. Any thoughts how to make this work? Is there a better way to approach this problem? Thanks for any help.
There's no LateFixedUpdate(), so you'll have to predict where the object will be next physics frame in order to catch it before it goes through anything. You can pretty easily change the code to cast a ray in the direction of rigidbody.velocity.
OK - that's what I am doing. I made another GO as a child of the projectile GO and assigned it the visual mesh. I also exposed the child GO in the script and attached the child GO in the inspector. I cast a new raycast using velocity to see if we will hit anything in the next time step and if we do move the child GO that contains the mesh so that it "looks" like it does not go through the object we hit for one frame. I also factored in the collider.bounds.extents so that the projectile (hockey puck) doesn't go halfway into the mesh that it collides with. So two raycasts are used - one that cast back to see if we passed through anything so we can manually back up and let the physics engine do the work, and another ray that casts forward to fix the visual image. Here's the additional code I added to the original code. I'm not the most experienced programmer so forgive my slopiness. (I'm working on an ice hockey game so that's why my code makes references to a puck!) Code (csharp): var puckImage : Transform; // puckImage is a child GO of the puck - contains the image mesh only - attach via the inspector var layerMask : LayerMask; //make sure we aren't in this layer var skinWidth : float = 0.0001; //probably doesn't need to be changed private var minimumExtent : float; private var partialExtent : float; private var sqrMinimumExtent : float; private var previousPosition : Vector3; private var myRigidbody : Rigidbody; //initialize values function Awake() { myRigidbody = rigidbody; previousPosition = myRigidbody.position; } function FixedUpdate() { minimumExtent = Mathf.Min(Mathf.Min(collider.bounds.extents.x, collider.bounds.extents.y), collider.bounds.extents.z); partialExtent = minimumExtent*(1.0 - skinWidth); sqrMinimumExtent = minimumExtent*minimumExtent; puckImage.localPosition = Vector3.zero; // reset child GO // WILL we move more than our minimum extent in the next time step? if (myRigidbody.velocity.sqrMagnitude*Time.deltaTime > sqrMinimumExtent) { var movementNextStep : Vector3 = myRigidbody.velocity*Time.deltaTime; var nextStepHitInfo : RaycastHit; // check for obstructions we might hit if (Physics.Raycast(myRigidbody.position, movementNextStep, nextStepHitInfo, movementNextStep.magnitude, layerMask.value)) { puckImage.position = nextStepHitInfo.point - myRigidbody.velocity*Time.deltaTime; // need to subtract the velocity since it is a child of a rigidbody! // let's adjust the puckImage position by the extents in the direction of travel!!! if (myRigidbody.position.x < nextStepHitInfo.point.x) puckImage.position.x -= collider.bounds.extents.x; else puckImage.position.x += collider.bounds.extents.x; if (myRigidbody.position.y < nextStepHitInfo.point.y) puckImage.position.y -= collider.bounds.extents.y; else puckImage.position.y += collider.bounds.extents.y; if (myRigidbody.position.z < nextStepHitInfo.point.z) puckImage.position.z -= collider.bounds.extents.z; else puckImage.position.z += collider.bounds.extents.z; } } //have we moved more than our minimum extent? if ((previousPosition - myRigidbody.position).sqrMagnitude > sqrMinimumExtent) { var movementThisStep : Vector3 = myRigidbody.position - previousPosition; var thisStepHitInfo : RaycastHit; //check for obstructions we might have missed if (Physics.Raycast(previousPosition, movementThisStep, thisStepHitInfo, movementThisStep.magnitude, layerMask.value)) { myRigidbody.position = thisStepHitInfo.point - (movementThisStep/movementThisStep.magnitude)*partialExtent; puckImage.localPosition = Vector3.zero; // this is redundant, but for complicated collisions, may be needed } } previousPosition = myRigidbody.position; }
Just a tip: if you want to keep your #pragma strict, you can probably do something like that: Code (csharp): var sphereCollider : SphereCollider = collider; minimumExtent = Mathf.Min(Mathf.Min(sphereCollider.bounds.extents.x, sphereCollider.bounds.extents.y), sphereCollider.bounds.extents.z); This way the compiler should find bounds without problem.