I’ve tried a few different methods gleaned from the internet about how to set up raycasts, but no matter what I do I just can’t get the thing to register any kind of connection. What’s going on here? The world is set up correctly (as in, things appear and collide and whatnot), but the raycast never seems to detect anything and I try three different methods (two slightly different). For completeness, I am going to include my testcase and the affiliated classes (some of you will probably recognize setups from Ray Wenderlich):
ccLayer header
//B2dtest.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#import "MyContactListener.h"
#define PTM_RATIO 16
@interface B2dTest : CCLayer {
b2World* world; // strong ref
GLESDebugDraw *m_debugDraw;
MyContactListener *_contactListener;
b2Body* groundBody;
b2Body* wallBody;
DataModel *m;
float rayAngle;
b2Vec2 p1;
b2Vec2 p2;
b2Vec2 intersectionPoint;
}
+(CCScene *) scene;
-(void)initPhysics;
@end
ccLayer class (offending code most likely in update)
//B2dtest.mm
#import "B2dTest.h"
#import "RaysCastCallback.h"
#import "OtherCast.h"
enum {kTagParentNode = 1,};
@implementation B2dTest
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
B2dTest *layer = [B2dTest node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init])) {
[self initPhysics];
[self addBarrier];
[self scheduleUpdate];
}
return self;
}
-(void) initPhysics
{
CGSize s = [[CCDirector sharedDirector] winSize];
b2Vec2 gravity;
gravity.Set(0.0f, -21.0f); //was ten
world = new b2World(gravity);
_contactListener = new MyContactListener();
world->SetContactListener(_contactListener);
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
m_debugDraw->SetFlags(flags);
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
groundBody = world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
groundBox.Set(b2Vec2(0,s.height/PTM_RATIO/3), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO/3));
b2FixtureDef fixtureDef;
fixtureDef.filter.categoryBits = 0x0004;
fixtureDef.filter.maskBits = 0x0002;
fixtureDef.shape = &groundBox;
fixtureDef.density = 0;
fixtureDef.restitution = 0.0f;
groundBody->CreateFixture(&fixtureDef);
}
-(void)addBarrier{
b2BodyDef bodyDef;
bodyDef.type = b2_staticBody;
wallBody = world->CreateBody(&bodyDef);
for (int i = 0; i < 2;i++){
b2PolygonShape platform;
platform.SetAsBox(2, .25, b2Vec2(12+ (i * 5), 9 + i),0);
b2FixtureDef fixtureDef;
fixtureDef.shape = &platform;
wallBody->CreateFixture(&fixtureDef);
}
}
-(void) draw{
ccDrawLine(ccp(p1.x,p1.y),
ccp(p2.x,p2.y));
[super draw];
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
-(void) update: (ccTime) dt{
int32 velocityIterations = 4;
int32 positionIterations = 1;
world->Step(dt, velocityIterations, positionIterations);
rayAngle = rayAngle + (2 * (M_PI / 180));
float rayLength = 200;
p1 = b2Vec2( 240, 240 ); //center of scene
p2 = p1 + rayLength * b2Vec2( sinf(rayAngle), cosf(rayAngle) );
RaysCastCallback callback; //FIRST RAYCAST METHOD
world->RayCast(&callback, p1, p2);
if (callback.m_fixture){
CCLOG(@"OH YESAAH!");
p2 = b2Vec2(callback.m_point.x * PTM_RATIO, callback.m_point.y * PTM_RATIO);
}
OtherCast allback; //SECONDRAYCASTMETHOD
world->RayCast(&allback, p1, p2);
if (allback.m_hit){
CCLOG(@"OH YESAAH! AD");
// p2 = b2Vec2(callback.m_point.x * PTM_RATIO, callback.m_point.y * PTM_RATIO);
}
b2RayCastInput input; //THIRDRAYCASTMETHOD
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
float closestFraction = 1;
b2Vec2 intersectionNormal(0,0);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if ( ! f->RayCast( &output, input, 0 ) )
continue;
if ( output.fraction < closestFraction ) {
CCLOG(@"woo");
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
}
@end
RaysCastCallback.h
#ifndef Shoot_N_Run_RaysCastCallback_h
#define Shoot_N_Run_RaysCastCallback_h
#endif
#import "Box2D.h"
class RaysCastCallback : public b2RayCastCallback{
public:
RaysCastCallback() : m_fixture(NULL) {
}
float32 ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) {
m_fixture = fixture;
m_point = point;
m_normal = normal;
m_fraction = fraction;
return fraction;
}
b2Fixture* m_fixture;
b2Vec2 m_point;
b2Vec2 m_normal;
float32 m_fraction;
};
OtherCast.h
#import "Box2D.h"
class OtherCast : public b2RayCastCallback{
public:
OtherCast()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return fraction;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
Check if ReportFixture methods are called. If they are, then the ray simply didn’t hit anything. This might be due to scale of values, perhaps the ray is too short or perhaps the objects are too far away (PTM_RATIO not applied?). But also the shapes of the bodies could be too tiny, or perhaps they don’t even have a shape. Perhaps the ray is aiming in the opposite direction or at a 90° angle, it’s easy to forget or incorrectly convert degrees to radians and vice versa. None of these issues are uncommon.
It helps to set up a test case where you manually put two objects at known positions, then cast a ray so that the ray is guaranteed to intersect. Take out pen & paper and trace the positions and the ray, see if the values of variables during a debug run match. This should help to locate any issues due to incorrect vectors, angles and what not.