- Home /
False-positive Collision between Eye-Ray and Capsule Collider
I have a key on the origin, which can be pointed towards 12 clock points on the XZ plane, or straight upwards:

The key has a capsule collider, so I can check when the user clicks on it:
void Update( )
{
if( Input.GetMouseButtonDown( 0 ) )
tryTouchPoint( Input.mousePosition );
}
void tryTouchPoint( Vector3 p )
{
Ray ray = Camera.main.ScreenPointToRay( p );
RaycastHit hitResult;
if( key.collider.Raycast( ray, out hitResult, 1000.0f ) )
{
Debug.DrawLine (ray.origin, hitResult.point);
Debug.Break();
:
}
It does correctly register on-target clicks.
However, it is possible to create a situation where it also registers false positives: a ray that clearly misses the capsule can trigger a collision.
It's easiest to show it in a video: http://screencast.com/t/QNMQodecGnV0
EDIT: Complete key rotation code here:
// http://forum.unity3d.com/threads/54720-Gold-texture-on-iphone
using UnityEngine;
using System.Collections;
public class KeyScript : MonoBehaviour
{
public int currKey;
float tKeyChange;
float xRot = 0f, yRot = 0f, zRot = 0f;
enum Status { Idle, Spinning, Transitioning };
Status m_status = Status.Idle;
// better than doing FindObjectOfType(typeof(X)) from client
static public KeyScript singleInstance;
void Awake( ) {
singleInstance = this;
newQuat = Quaternion.identity;
m_status = Status.Idle;
currKey = -1;
}
public bool isSpinning { get { return m_status == Status.Spinning; } }
// public void Toggle( ) {
// if( m_status == Status.Spinning )
// m_status = Status.Idle;
// else
// m_status = Status.Spinning;
// }
public void Spin( )
{
m_status = Status.Spinning;
}
Quaternion oldQuat, newQuat;
// Update is called once per frame
void Update ()
{
// float scale = Wheel.btnScale / 9.0f;
//
// transform.localScale = new Vector3( scale, scale, scale );
switch( m_status )
{
case Status.Idle:
break;
case Status.Spinning:
float rotSpeed = 60f;
xRot += Time.deltaTime * rotSpeed;
while( xRot > 360f )
xRot -= 360f;
transform.rotation = ConstructRot( xRot, yRot, zRot );
break;
case Status.Transitioning:
float t = Mathf.InverseLerp( tKeyChange, tKeyChange + 0.3f, Time.time );
Quaternion foo = Quaternion.Slerp( oldQuat, newQuat, t );
if( t == 1f )
m_status = Status.Idle;
transform.rotation = foo;
break;
}
}
public void SetKey( int k )
{
m_status = Status.Transitioning;
tKeyChange = Time.time;
currKey = k;
if( k == -1 )
{
xRot = 0f;
yRot = 0f;
zRot = 90f;
}
else{
xRot = 0f;
yRot = 360f * (float)(k-0f) / 12f;
zRot = 0f;
}
oldQuat = transform.rotation;
newQuat = ConstructRot( xRot, yRot, zRot );
}
Quaternion ConstructRot( float xRot, float yRot, float zRot )
{
Quaternion firstXRot = Quaternion.AngleAxis( xRot, Vector3.forward );
Quaternion nowYRot = Quaternion.AngleAxis( yRot, Vector3.up );
Quaternion finallyZRot = Quaternion.AngleAxis( zRot, Vector3.right );
return finallyZRot * ( nowYRot * firstXRot );
}
}
EDIT: Main code:
void Update( )
{
float secs_per_frame = Time.deltaTime;
float fps = 1f / secs_per_frame;
fps_smooth = Mathf.Lerp( fps_smooth, fps, 0.1f );
CheckForUserInput( );
}
void CheckForUserInput( )
{
// mouse L button
if( Input.GetMouseButtonDown( 0 ) )
tryTouchPoint( Input.mousePosition );
}
void OnGUI( )
{
if( GUILayout.Button( "BUG" ) )
fail();
}
void fail()
{
RaycastHit hitResult;
key.gameObject.transform.rotation = new Quaternion(0.7071068f,0f,0f,0.7071068f);
Ray ray = new Ray(
new Vector3(0.03398795f, 9.7f, -0.05699179f) ,
new Vector3(0.1106194f, -0.9764f, -0.1854893f)
);
if( key.collider.Raycast( ray, out hitResult, 1000.0f ) )
Debug.Log( "Got teh bug!" );
else
Debug.Log( " no collision wtf " );
}
void tryTouchPoint( Vector3 p )
{
Ray ray = Camera.main.ScreenPointToRay( p );
Debug.DrawLine( ray.origin, ray.origin + (ray.direction * 1024f) );
RaycastHit hitResult;
if( key.collider.Raycast( ray, out hitResult, 1000.0f ) )
{
//if( ! key.isSpinning )
Quaternion Q = key.gameObject.transform.rotation;
Vector3 O = ray.origin, D = ray.direction;
string LogString = string.Format( "Quaternion: ({0}f, {1}f, {2}f, {3}f), Ray Origin: ({4}f, {5}f, {6}f), Ray Direction: ({7}f, {8}f, {9}f)",
Q.x, Q.y, Q.z, Q.w,
O.x, O.y, O.z,
D.x, D.y, D.z
);
Debug.Log( LogString );
if( key.isSpinning ) SetKey( -1 );
else key.Spin( );
return;
}
float dWinner = 1234567;
int winner = 0;
for( int i=0; i < allGems.Length; i++ )
{
GlowGem G = allGems[ i ];
float dist = DistBetween( ray, G.transform.position );
if( dist < dWinner )
{
dWinner = dist;
winner = i;
}
}
if( dWinner < 1f )
{
if( key.isSpinning ) {
SetKey( winner );
}
else {
allGems[ winner ].UserHit( );
if( noteHit_User_Event != null )
noteHit_User_Event( winner );
}
}
}
void SetKey( int k )
{
key.SetKey( k );
//float [] s = new float[] { 10,3,7,3,7,7,3,7,3,7,3,7 };
for( int deg=0; deg < 12; deg++ ) { // scale degree; 0 is tonic etc
//float size = ( k == -1 ) ? 0.8f : new float[] { 10,3,7,3,7,7,3,7,3,7,3,7 }[ deg ] / 10f;
int toneme = ( k == -1 ) ? deg : ( k + deg ) % 12; // offset from keynote
allGems[ toneme ].SetDegree ( ( k == -1 ) ? 2 : deg );
}
if( k > -1 )
allGems[ k ].UserHit( );
}
[1]: /storage/temp/22972-screen+shot+2014-03-02+at+16.55.35.png
$$anonymous$$oved to: http://forum.unity3d.com/threads/232061-Quaternion-Capsule-Ray-Collision-bug!
Answer by Owen-Reynolds · Mar 02, 2014 at 06:50 PM
The coordinate systems seem fine: screenPointToRay gives a ray in world space, and ray casts work in world space. Collider.raycast, I think, is just an faster/alternate way of doing a standard raycast on just one object, w/o having to use layerMasks. I'm pretty sure it's still in world space (not Object space AKA local coordinate system.) And, if the math was wrong, it would probably be really wrong.
I'm wondering if it isn't an out-of-order problem. Like, each frame your code puts the key on it's side, then does the raycast to hit the tip, then snaps it upwards. More testing might find a pattern. Are the false hits always in a certain area and consistant? Sporadic?
I just managed to get a reproducible fail; I've added a screencast to my post.
I want to say it's just a coding problem somewhere. That the key really does "snap" sometimes, fast enough it's not noticeable, and the glitch is only during this snap, when the key is incorrectly sideways. But that would be only for a frame or two.
Like, oldQuat doesn't seem to always be current? $$anonymous$$aybe always compare the angle between old and new rotation each frame and look for jumps? EulerAngles are junk to work with, but I'd rather print them for a debug then Q.x/y/z (since you can enter✓ euler angles.)
$$anonymous$$aybe have the game pause on each raycast (and resume when you press x?)
It could be a raycast thing, but I get many more misses when it should hit, than hits when it should miss. Just in case, you might convert to Physics raycast and check for the $$anonymous$$ey (you should get the exact same, glitchy, results.)
Your answer
Follow this Question
Related Questions
Collision Detection in Capsule Collider 0 Answers
Raycast doesn't collide as it should 1 Answer
Scene Audio Script Error 2 Answers
physics rolling error( included sample and simple demo) 0 Answers
Using GetComponent Within OnTriggerEnter 0 Answers