Getting Raycasts to Only Apply to Closest Fixture

Topics: User Forum
Feb 22, 2011 at 8:26 PM
Edited Feb 24, 2011 at 3:15 AM

Hi there,

I'm using Farseer to develop a 2D shooter, and I'm currently using raycasts to perform bullet collision checks.  However, I'm having some trouble getting the "bullets" to only affect the first object hit, making it appear that shots are tunneling through objects to hit additional targets.


The FireBullet method is pretty simple.  It gets a penetration value from an array of ints, and fires that many raycasts into the world.  A successful raycast adds the fixture it hit to a Dictionary of _fixturesHit.

The bullet ray test first checks if the fixture hit is one that is supposed to be shootable (in Category.Cat11, and not already part of _fixturesHit).  If so, it should add the fixture to _fixturesHit and end the raycast at that point.  Otherwise, it should do nothing with that fixture and continue the cast to try to hit a shootable fixture.

I've tried a bunch of combinations of return values for hit/unshootable: 0/-1, fraction/1, fraction/-1, 0/1, etc.   However, none have resulted in a successful behavior of bullets consistently colliding with the first fixture they should encounter, and ONLY that first fixture.  I am especially confused as to the difference between -1 (filter the fixture) and 1 (don't clip and continue... which presumably will not hit the same fixture again).  However, as this is the return value of "unshootable" objects, which I am currently not testing, I can't see how that distinction would be the issue.


I have included the pertinent code below.  Any additional info on how raycasts function and what return values I should be using would be greatly appreciated.  Thanks!



       internal static RayCastCallback _bulletHitCallback = new RayCastCallback(bulletHitRayTest);


       public static void FireBullet(Vector2 origin, Vector2 _aimVector, Weapon _weapon) {
            int penetration = _gunPenetrations[(int)_weapon];
            for (int i = 0; i <= penetration; i++)
                //Fire a shot, adding all hit targets to _fixturesHit
      , origin, origin + (_aimVector * _bulletRange));
            if (_fixturesHit.Count != 0)
                foreach (Fixture fixture in _fixturesHit.Keys)
           //Apply damage and forces
            _fixturesHit.Clear();  //Reset the list of _fixturesHit to after processing the shot.


        public static float bulletHitRayTest(Fixture fixture, Vector2 point, Vector2 normal, float fraction)
            //Check if fixture is something that can be shot. (Cat11)
            if (fixture != null) 
                if (!_fixturesHit.ContainsKey(fixture) && (fixture.CollisionFilter.CollisionCategories & Category.Cat11) == Category.Cat11)
                    _fixturesHit.Add(fixture, point);
                    return fraction;  //Terminate ray
            return -1;  //Filter fixture
Feb 23, 2011 at 9:28 AM

I have to admit I never quite got what returning 1 actually does either... (genbox halp? :)

But have you already looked at the raycasting sample in our testbed. It demonstrates the different settings and their application.

I think you need to return fraction instead of 0 where you want to terminate your ray.

Feb 23, 2011 at 3:01 PM

According to this post by genbox:

The fraction works like this:

return -1: ignore this fixture and continue
return 0: terminate the ray cast
return fraction: clip the ray to this point
return 1: don't clip the ray and continue

Feb 24, 2011 at 3:14 AM

Thanks for the suggestions.  I've set the "hit" behavior to return the fraction, which works relatively well.  However, after checking with a healthy load of debug print statements, I have worked out that it will still frequently process 3 or more fixtures within the same ray test.  Does anyone have an idea as to why this could be?  Genbox?

Feb 27, 2011 at 9:10 AM

Sorry to shamelessly bump my own post, but does anyone have anything to suggest regarding having my rays consistently only affect the closest fixture?

Mar 10, 2011 at 5:00 PM

I'm having the same issue here, very strange as in the testbed seems to work ok.

Mar 11, 2011 at 7:22 AM

OK, I solved it, but makes me sad how I did it, because I'm not clipping the ray, but instead I'm checking the whole ray.

Vector2 targetPosition;
float minFrac = float.MaxValue;

World.RayCast((f, p, n, fr) =>
	if (!ShouldBeIgnored(f))
		if (fr < minFrac)
			minFrac = fr;
			targetPosition = p;
		return 1.0f;
		return -1.0f;
}, point1, point2);