- Home /
Cheapest way to catch collisions on very fast moving objects...
Hey guys,
For our current project we need to catch collisions on objects that can be moving very quickly in opposite directions.
We're using non-kinimatice rigidbodies on both objects, one with a capsule collider and the the other with a box collider...
One object moves via translation, the other via velocity.
Most of the time this works fine, but It seems that sometimes an object can go straight through the other without colliding properly when both are at high speed.
Any suggestions on the cheapest way of solving this, is the only option to use continous dynamic collision detection on one of the rigidbodies?
Thanks in advance guys :)
Answer by cregox · Mar 29, 2011 at 07:40 PM
Maybe you find some enlightenment on the awesome DontGoThroughThings Daniel's script. From my brief experience, the new physics improvement on Unity 3, collisionDetectionMode, still doesn't work as good as this raycasting implementation. For sure, neither is 100%, but this was pretty close on my case:
This script uses raycasting to avoid the physics engine letting fast-moving objects go through other objects (particularly meshes).
So it solves the issue of "objects moving very quickly" in the very practical instance of crossing walls that it never should be able to. You could extrapolate this script into one able to detect another object moving really fast.
The problem exists even if you use continuous dynamic collision detection because fast moving objects can move so fast that they are too far apart from itself from one frame to the next immediate frame. It's like they teleported and no collision detection would ever be triggered because no collision existed, from each frame perspective, and thus from all calculations processed.
The real issue comes when the wall is moving too :P Am currently trying to solve it
Hi, the link to the script is dead. Does anyone have a copy still, that can be added into the answer?
Here is a new link to it. http://wiki.unity3d.com/index.php?title=DontGoThroughThings
And here's the C# code.
// Script from $$anonymous$$ Brauer, Adrian
// http://wiki.unity3d.com/index.php?title=DontGoThroughThings
using UnityEngine;
public class DontGoThroughThings : $$anonymous$$onoBehaviour
{
// Careful when setting this to true - it might cause double
// events to be fired - but it won't pass through the trigger
public bool sendTrigger$$anonymous$$essage = false;
public Layer$$anonymous$$ask layer$$anonymous$$ask = -1; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float $$anonymous$$imumExtent;
private float partialExtent;
private float sqr$$anonymous$$inimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Collider myCollider;
//initialize values
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
myCollider = GetComponent<Collider>();
previousPosition = myRigidbody.position;
$$anonymous$$imumExtent = $$anonymous$$athf.$$anonymous$$in($$anonymous$$athf.$$anonymous$$in(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z);
partialExtent = $$anonymous$$imumExtent * (1.0f - skinWidth);
sqr$$anonymous$$inimumExtent = $$anonymous$$imumExtent * $$anonymous$$imumExtent;
}
void FixedUpdate()
{
//have we moved more than our $$anonymous$$imum extent?
Vector3 movementThisStep = myRigidbody.position - previousPosition;
float movementSqr$$anonymous$$agnitude = movementThisStep.sqr$$anonymous$$agnitude;
if (movementSqr$$anonymous$$agnitude > sqr$$anonymous$$inimumExtent)
{
float movement$$anonymous$$agnitude = $$anonymous$$athf.Sqrt(movementSqr$$anonymous$$agnitude);
RaycastHit hitInfo;
//check for obstructions we might have missed
if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movement$$anonymous$$agnitude, layer$$anonymous$$ask.value))
{
if (!hitInfo.collider)
return;
if (hitInfo.collider.isTrigger)
hitInfo.collider.Send$$anonymous$$essage("OnTriggerEnter", myCollider);
if (!hitInfo.collider.isTrigger)
myRigidbody.position = hitInfo.point - (movementThisStep / movement$$anonymous$$agnitude) * partialExtent;
}
}
previousPosition = myRigidbody.position;
}
}
Answer by Sun-Pengfei · Nov 11, 2015 at 02:51 PM
I made an arrow with a circle collider2D(following discussion are all in 2D) at the top, and had encountered this typical issue because the circle is so small that with a fast flying speed, the arrow will fly through the edge collider without trigger any collision.
I simply solved this by changing the circle collider with a box collider, which is long enough to cope with the speed. This solved my problem well, it's simple, and have no other overhead.
However you should keep in mind that whether this solution works is due to the length of the new collider (to be more precisely the length in the flying direction) and the speed. Higher speed requires longer length. Better test many times to decide the safe speed range in your specific case.
This is the easiest way. I'm working on a Pong game and I sorted my "Walls" problems by making bigger box colliders. If my Ball speed is really huge at some point it will pass through but I can limit this max speed. I was almost redoing everything but it saved me for now :D
This is not the best solution at all for several reasons. First of all the question was not about 2d but about 3d. Hints are terms like rigidbody ins$$anonymous$$d of rigidbody2d, also "capsule collider" and "box collider". So this answer is actually off-topic as it is worded.
Of course a similar solution could be used in 3d, but the solution is actually quite bad, When you make your "bullet" collider too long it might even stick out at the back of your player when fired and could hit something behind your player. If you offset the bullet spawn to the front the player might be able to fire through walls when standing close to the wall. This approach introduces much more edge case problems. Using a raycast / spherecast is much more reliable. That's why the accepted answer is the better answer.
totally agree. $$anonymous$$y answer is just a convenient hack for simple scenarios.
Answer by Umbata · Nov 30, 2015 at 03:10 PM
Go to "Edit > Project Settings > Time" and reduce the 'Fixed Timestep'
That must be the least efficient way of doing this. You will increase the amount of ALL physics calculations that are done per second on any and all objects. This is a massive strain on your CPU.
Do you know any way to increase amount of physics calculations for exact rigidbody?
Answer by WarpZone · Jan 19 at 07:24 PM
An unorthodox solution, for linear bullet trajectories only: Resize your bullets' hit boxes. I used this for player shots and it worked great. That was quite a few versions of Unity ago, though, so take it with a grain of salt.
The downside is if a bullet overlaps multiple enemies, there's no guarantee which one will take the hit, the same as if the enemies were all stacked up in an overlapping pile when the bullet hit. If mandatory bullet overpenetration is not a deliberate feature of your game, you'll need to manually sort through all collided enemies and then pick the one furthest from the visible bullet's current location (I.E., closest to the shooter, assuming the shooter hasn't moved since firing.) But the upside is your bullets will have exactly the same physics performance cost as if you hadn't resized the hitboxes.
To implement: Assuming "length" is the size of the collision box parallel to the direction of the bullet, multiply the default length of your collider primitive by bullet's speed, and use that value to set boxCollider.size. Then set boxCollider.center to an offset that puts the front of the collider where the bullet visually is, and the back of the invisible collider sticking out behind the bullet. If the bullet travels half the screen in one update, where the bullet is now and everything the bullet passed through during the physics step will all be hit.
You want change boxCollider.size infrequently, not every update. A good time to do it is when the player collects a powerup that changes how fast their shots travel, or when they switch weapons. If the player only changes their weapons between levels, such as in a shop or upgrade menu, then you can cycle through all your pooled objects and set them then, while the player is looking at the shop UI.
Worst-case scenario, if there are frequent pickups in-game that change the speed of all the player's bullets, or bullet speed is randomized upon firing, or the player switches weapons frequently during play, then you'll want to scale each bullet as it is fired.
If the speed of your shots changes over time after they leave the barrel, then this answer is probably not a good solution for your use case. Likewise, if the shot curves, arcs or homes in any way, this is not a good solution. Linear bullet paths only! fortunately, most "spray and pray" weapons tend to fire dumb bullets anyway. Your spread guns, your gattling guns. Use the other solutions suggested here for things like homing missiles that probably fire one at a time anyway because they're more likely to hit.
Your answer
Follow this Question
Related Questions
Fast moving objects 2D game 2 Answers
rigidbody - kinematic performance 0 Answers
Physics calculates collisions between disabled layers 0 Answers
colliders performance 1 Answer
Multiple Colliders Performance 2 Answers