- Home /
CapsuleCast not registering....
So i'm trying to build a character controller using no rigidbody, capsule collider or the default character controller. Instead i'm trying to catch collision events with a few CapsuleCasts.
I can jump, run around and not fall trough my terrain; which is all great. However i'm trying to run up an incline plane, the capsule cast does not register a hit until the center of the capsule collides with the plane.
I was going to post some code snippets, but a zipped up version of my project might be more helpful. http://defalcator.com/Raycast.zip (Ignore the filename)
Can anyone help me out as to why the collision isn't registered on time? I want to catch the collision when my player first steps on the slope so i can correct it and implement ground following...
Thanks ~Gabe
Answer by guavaman · Oct 06, 2012 at 08:45 PM
I think your problem might have to do with planar/non-closed collision meshes (floor). Testing against a closed cube gives very different results from a non-closed mesh. I don't fully understand it, but there are many cases I've run into where the capsule will not register a collision with a mesh when that mesh already intersects the volume of the starting capsule. I find that when the starting capsule doesn't instersect a mesh, it will always collide with it during the sweep, but if it does intersect it, it may not collide depending on the angles of the collider polygon and the sweep angle. (Sorry I don't have a definitive answer.)
One possible solution is to do a Physics.CheckCapsule before you do the cast to see if you're colliding with anything in the volume of the starting capsule. If you're not, then go ahead and cast forward.
Answer by Kristian · Sep 02, 2011 at 09:52 AM
We've been having the same problem for a while now and finally found the solution. You must add the radius of the capsule to the sweep distance, since unity checks from the middle of your capsule to the end of a line, not the middle of the end capsule as I thought as well.
I hope this helped, as I know the frustration of not having reliable physics checks.
-Kristian BD
from me own experience, this seems not to be the case :x
I also don't agree with this. Check your Project Settings -> Physics $$anonymous$$ penetration for penalty values. This makes it so the collision has to be a bit into the capsule before it will register. If this value is too high compared to the size of your capsule, you may well only see a collision in the center of your capsule. Double this value for two colliders -- if the value is 0.1, each collider has to reach a depth of 0.1 inside it before it will register for a total depth of 0.2.
Also, not that collisions with CharacterControllers are a little wonky. There seems to be some built-in (non customizable) depth penetration settings on them that makes these kinds of physics checks unreliable.
I thought I found something that had to do with the center like $$anonymous$$ristian said, but it turns out it's not always the center. I think the confusion has something more to do with the direction of the cast. The back half (half facing away from the cast) of a capsule won't collide with anything its intersecting most of the time. I suppose this makes sense because the cast is in the opposite direction and the face you're colliding with would now be backwards from the cast direction. In other words, if your capsule is intersecting something, it's not going to detect collisions backwards because it's not back-casting.
Ultimately, I'm thinking the confusion is really caused because we expect CapsuleCast to do something its not designed to. Capsule casting does not just project a solid volume from start to finish and detect any collisions in any direction in its swept volume. Ins$$anonymous$$d, it's like casting a ray. It only hits if the collider it intersects has a face with normal toward the ray origin. Use Physics.CheckCapsule if you want a volume test.
Play around with this little test script if you want to see what I'm saying. Put this on a game object and make a cube for it to collide with and test it out:
using UnityEngine; using System.Collections;
[ExecuteInEdit$$anonymous$$ode] public class Capcasttest : $$anonymous$$onoBehaviour { public enum $$anonymous$$ode { CapsuleCast = 0, CheckCapsule = 1 };
public $$anonymous$$ode mode = $$anonymous$$ode.CapsuleCast;
public Vector3 height = new Vector3(0, 2.0f, 0);
public float radius = 1.0f;
public float dist = 3.0f;
private Vector3 vOffset;
private Vector3 dir;
private bool hit;
void Awake() {
vOffset = new Vector3(0, radius, 0);
}
void Update () { dir = transform.forward; Vector3 botSpherePos = transform.position + vOffset; Vector3 topSpherePos = botSpherePos + height;
if(mode == $$anonymous$$ode.CapsuleCast) {
hit = Physics.CapsuleCast(botSpherePos, topSpherePos, radius, dir, dist);
// Draw lines
Color lineColor = Color.white;
if(hit) lineColor = Color.red;
Debug.DrawLine(botSpherePos, botSpherePos + (dir * dist), lineColor);
Debug.DrawLine(topSpherePos, topSpherePos + (dir * dist), lineColor);
} else
hit = Physics.CheckCapsule(botSpherePos, topSpherePos, radius);
}
void OnDrawGizmos() {
Vector3 botSpherePos = transform.position + vOffset;
Vector3 topSpherePos = botSpherePos + height;
// Draw spheres
if(hit) Gizmos.color = Color.red;
Gizmos.DrawSphere(botSpherePos, radius); // draw origin top sphere
Gizmos.DrawSphere(topSpherePos, radius); // draw origin bottom sphere
if(mode == $$anonymous$$ode.CapsuleCast) {
Gizmos.DrawWireSphere(botSpherePos + (dir * dist), radius); // draw swept bottom sphere
Gizmos.DrawWireSphere(topSpherePos + (dir * dist), radius); // draw swept top sphere
}
}
}
Answer by Matt-Downey · Jun 14, 2012 at 10:39 PM
Check the y-coordinate of P1 and P2, if P1 is below P2 (lower altitude) Physics.CapsuleCast WILL work. If not, everything will go to hell.
http://forum.unity3d.com/threads/130169-Why-CaspsuleCast-is-buggy
I'd also like to keep a remnant of my old answer.
This is a very powerful bit of code (which I dub IterationArray):
var hit : RaycastHit;
var hit2 : RaycastHit;
if(Physics.Raycast(pos - Vector3.up*0.5,dir,hit,Mathf.Infinity,(1 << 0))
&& Physics.Raycast(pos - Vector3.up*0.5,-hit.normal,hit2,Mathf.Infinity, (1 << 0))
&& (hit.distance < radius + dist || hit2.distance < radius + dist))//find the normal below us and find how close it is
{
if(hit.distance > hit2.distance)
{
hit.distance = hit2.distance - radius;
}
else hit.distance -= radius;
}
Basically, what it's saying is: find the nearest normal below you, then do a raycast along the opposite direction of that normal and find the closest distance. If either of those distances are within a suitable distance, then use that normal and DON'T spherecast because it will just waste CPU and give you a slightly less accurate normal. Furthermore, CapsuleCast/SphereCast are unreliable in that they will not return anything if they start "sweeping" from inside a mesh.
Usually, my collision codes look something like this
if(Physics.Check Capsule) //if there is something occupying the space, then do stuff, otherwise don't bother
{
if(IterationArray) //check if we are on a flat plane first
else if(Physics.SphereCast) //check for a contour or corner of a mesh
}