i have a problem with revolutejoint. When i make a line with litlle boxes and revoluteJoint i noticed a strange behavior of my first box. it separates from rest of the boxes.
You can see it here:
Youtube
You can compile it, and you'll see what im talking about…
HelloWorldScene.h
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
// HelloWorld Layer
#interface HelloWorld : CCLayer
{
b2World* world;
GLESDebugDraw *m_debugDraw;
}
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
-(void) Test;
#end
HelloWorldScene.mm
// Import the interfaces
#import "HelloWorldScene.h"
//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
// enums that will be used as tags
enum {
kTagTileMap = 1,
kTagBatchNode = 1,
kTagAnimation1 = 1,
};
// HelloWorld implementation
#implementation HelloWorld
+(id) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorld *layer = [HelloWorld node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// initialize your instance here
-(id) init
{
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
// enable accelerometer
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
CCLOG(#"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = true;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2DebugDraw::e_shapeBit;
flags += b2DebugDraw::e_jointBit;
// flags += b2DebugDraw::e_aabbBit;
// flags += b2DebugDraw::e_pairBit;
// flags += b2DebugDraw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2PolygonShape groundBox;
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
// left
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);
// right
groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
//Set up sprite
[self Test];
[self schedule: #selector(tick:)];
}
return self;
}
- (void) Test {
// Circle
b2Body *circle1;
b2BodyDef bd1;
bd1.position.Set(45.0f/PTM_RATIO, 180.0f/PTM_RATIO);
bd1.type = b2_kinematicBody;
bd1.fixedRotation = false;
bd1.allowSleep = false;
circle1 = world->CreateBody(&bd1);
b2CircleShape shapecircle1;
shapecircle1.m_radius = 0.5f;
b2FixtureDef fdcircle1;
fdcircle1.shape = &shapecircle1;
fdcircle1.density = 2.0f;
fdcircle1.friction = 2.0f;
circle1->CreateFixture(&fdcircle1);
// Boxes
b2PolygonShape shape;
shape.SetAsBox(6.0f/PTM_RATIO, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 9.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(15.0f/PTM_RATIO, y);
b2Body* prevBody = world->CreateBody(&bd);
prevBody->CreateFixture(&fd);
b2Vec2 anchor(float32(0), y);
for (int32 i = 1; i < 8; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set((15.0f + (i*10))/PTM_RATIO, y);
b2Body* body = world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i*10)/PTM_RATIO, y);
jd.Initialize(prevBody, body, anchor);
world->CreateJoint(&jd);
prevBody = body;
}
}
-(void) draw
{
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_VERTEX_ARRAY,
// Unneeded states: GL_TEXTURE_2D, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
world->DrawDebugData();
// restore default GL states
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
-(void) tick: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
delete world;
world = NULL;
delete m_debugDraw;
// don't forget to call "super dealloc"
[super dealloc];
}
#end
Any idea? Thanks in advance
The problem in your case is that your first box makes a 360 degree rotation around the anchor. This can happen to first and last box. I think you need to restrict the 360 degree rotation for your join. You have do define that maximum angle that your box can achieve. You can do it by using motors in your joints. Following is some code from Box2D manual to create motors for your joints,
b2RevoluteJointDef jointDef;
jointDef.Initialize(body1, body2, myBody1->GetWorldCenter());
jointDef.lowerAngle = -0.5f * b2_pi; // -90 degrees
jointDef.upperAngle = 0.25f * b2_pi; // 45 degrees
jointDef.enableLimit = true;
jointDef.maxMotorTorque = 10.0f;
jointDef.motorSpeed = 0.0f;
jointDef.enableMotor = true;
I hope it helps.
Related
I'm having an issue with the HelloWorldLayer class' update: method not being called when building and running the applications on my iPad.
Not really sure what the issue is as init: and accelerometer: didAccelerate: are called as expected.
Here is the implementation of HelloWorldLayer:
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
const float MaxPlayerAccel = 400.0f;
const float MaxPlayerSpeed = 200.0f;
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
{
CGSize _winSize;
CCSprite *_playerSprite;
UIAccelerationValue _accelerometerX;
UIAccelerationValue _accelerometerY;
float _playerAccelX;
float _playerAccelY;
float _playerSpeedX;
float _playerSpeedY;
}
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
- (id)init
{
if (self = [super initWithColor:ccc4(94, 63, 107, 255)])
{
_winSize = [CCDirector sharedDirector].winSize;
_playerSprite = [CCSprite spriteWithFile:#"Player-hd.png"];
_playerSprite.position = ccp(_winSize.width - 50.0f, 50.0f);
[self addChild:_playerSprite];
self.accelerometerEnabled = YES;
NSLog(#"init: method executed");
}
return self;
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
const double FilteringFactor = 0.75;
_accelerometerX = acceleration.x * FilteringFactor + _accelerometerX * (1.0 - FilteringFactor);
_accelerometerY = acceleration.y * FilteringFactor + _accelerometerY * (1.0 - FilteringFactor);
if (_accelerometerY > 0.05)
{
_playerAccelX = -MaxPlayerAccel;
}
else if (_accelerometerY < 0.05)
{
_playerAccelX = MaxPlayerAccel;
}
if (_playerAccelX < -0.05)
{
_playerAccelY = -MaxPlayerAccel;
}
else if (_playerAccelX > -0.05)
{
_playerAccelY = MaxPlayerAccel;
}
}
- (void)update:(ccTime)dt
{
NSLog(#"update: method is being called");
// 1
_playerSpeedX += _playerAccelX * dt;
_playerSpeedY += _playerAccelY * dt;
// 2
_playerSpeedX = fmaxf(fminf(_playerSpeedX, MaxPlayerSpeed), - MaxPlayerSpeed);
_playerSpeedY = fmaxf(fminf(_playerSpeedY, MaxPlayerSpeed), - MaxPlayerSpeed);
// 3
float newX = _playerSprite.position.x + _playerSpeedX * dt;
float newY = _playerSprite.position.y + _playerSpeedY * dt;
// 4
newX = MIN(_winSize.width, MAX(newX, 0));
newY = MIN(_winSize.height, MAX(newY, 0));
_playerSprite.position = ccp(newX, newY);
}
#end
You need to schedule the update in your init method:
[self scheduleUpdate];
Edit: Also, happened to see another possible issue with your code - you're loading the sprite image as image-hd.png. In Cocos2D, you only need to name the file without the -hd prefix: image.png, and it will automatically select the HD version if the screen is retina.
I'm using cocos2d-x 2.0.3 with Box2d 2.2.1 and has first example I've introduced a dynamic body (a boat defined with PhysicsEditor) in a frame. Applying a gravity the behaviour expected is that the boat collide with the frame but instead it go through.
Here is the definition of world and bodies:
// create world
b2Vec2 gravity;
gravity.Set(0.1f, -0.0f);
bool doSleep = true;
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
m_debugDraw = new GLESDebugDraw( PTM_RATIO * CCDirector::sharedDirector()->getContentScaleFactor());
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
flags += b2Draw::e_jointBit;
flags += b2Draw::e_aabbBit;
flags += b2Draw::e_pairBit;
flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// for the screenBorder body we'll need these values
CCSize screenSize = size;
float widthInMeters = screenSize.width / PTM_RATIO;
float heightInMeters = screenSize.height / PTM_RATIO;
b2Vec2 lowerLeftCorner = b2Vec2(- widthInMeters / 2, 0.0);
b2Vec2 lowerRightCorner = b2Vec2(widthInMeters / 2, 0.0);
b2Vec2 upperLeftCorner = b2Vec2(- widthInMeters / 2, heightInMeters);
b2Vec2 upperRightCorner = b2Vec2(widthInMeters/2, heightInMeters);
// Define the static container body, which will provide the collisions at screen borders.
b2BodyDef screenBorderDef;
screenBorderDef.position.Set(0, 0);
screenBorderDef.type = b2_staticBody;
b2Body* screenBorderBody = world->CreateBody(&screenBorderDef);
b2EdgeShape screenBorderShape;
// Create fixtures for the four borders (the border shape is re-used)
screenBorderShape.Set(lowerLeftCorner, lowerRightCorner);
screenBorderBody->CreateFixture(&screenBorderShape, 0);
screenBorderShape.Set(lowerRightCorner, upperRightCorner);
screenBorderBody->CreateFixture(&screenBorderShape, 0);
screenBorderShape.Set(upperRightCorner, upperLeftCorner);
screenBorderBody->CreateFixture(&screenBorderShape, 0);
screenBorderShape.Set(upperLeftCorner, lowerLeftCorner);
screenBorderBody->CreateFixture(&screenBorderShape, 0);
// add sail boat sprite
CCSprite *sailBoatSprite = CCSprite::create("SailBoat.png");
addChild(sailBoatSprite, -1, kSailingBoat);
sailBoatSprite->setAnchorPoint(ccp(0.5, 0.5));
sailBoatSprite->setPosition(ccp(0.0, 128.0));
// add sail boat body
GB2ShapeCache *shapeCache = GB2ShapeCache::sharedGB2ShapeCache();
shapeCache->addShapesWithFile("SailBoat.plist");
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0/PTM_RATIO, 128.0/PTM_RATIO);
bodyDef.userData = sailBoatSprite;
b2Body *body = world->CreateBody(&bodyDef);
shapeCache->addFixturesToBody(body, "SailBoat");
sailBoatSprite->setAnchorPoint(GB2ShapeCache::sharedGB2ShapeCache()->anchorPointForShape("SailBoat"));
this->schedule(schedule_selector(SailingFieldNode::tick));
Here is the updates method:
void SailingFieldNode::tick(float dt)
{
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(0.01, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor->setPosition(ccp( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO));
myActor->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
}
void SailingFieldNode::draw()
{
CCLayer::draw();
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position);
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
CHECK_GL_ERROR_DEBUG();
}
As you can see in the following screenshot the boat is in the frame then there should be the collision:
Any help?
Thanks in advance.
Jean
Problem solved, the issue was that in PhysicsEditor I haven't set the dynamic body in a category (with the flag). In my code I don't use them yet but probably you must set it in one category to have collisions (I've set my body in bit_0).
Jean
I keep getting the same crash report "'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt'".
This is getting pretty annoying now, i only wanted to make the Backgound scrolling infinitely.
Here is my code:
-(id) init {
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
/*CCSprite *Player = [CCSprite spriteWithFile:#"Icon.png"];
Player.position = ccp(screenSize.width/2 -110, screenSize.height/2);
Player.scale = 0.7;
[self addChild:Player];
[self scheduleUpdate];*/
CCLOG(#"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
bool doSleep = true;
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
/*m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2DebugDraw::e_shapeBit;
//flags += b2DebugDraw::e_jointBit;
//flags += b2DebugDraw::e_aabbBit;
//flags += b2DebugDraw::e_pairBit;
//flags += b2DebugDraw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);*/
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2PolygonShape groundBox;
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
//Set up sprite
BG1 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG1.anchorPoint = ccp(0,0);
BG1.position = ccp(0, 0);
BG1.scale = 0.5;
[self addChild:BG1];
BG2 = [CCSprite spriteWithFile:#"SkyAndClouds.png"];
BG2.anchorPoint = ccp(0,0);
BG2.position = ccp([BG1 boundingBox].size.width-1,0);
[self addChild:BG2];
[self schedule: #selector(AnimatedBG:) interval:0];
CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:#"Stick.png" capacity:150];
[self addChild:batch z:0 tag:kTagBatchNode];
[self addNewSpriteWithCoords:ccp(screenSize.width/2-110, screenSize.height/2)];
[self addZombieWithCoords:ccp(screenSize.width/2+240, screenSize.height/2)];
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Stick, you Better Run!" fontName:#"Helvetica" fontSize:16];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( screenSize.width/2, screenSize.height-17);
[self schedule: #selector(tick:)];
}
return self;
}
-(void)AnimatedBG {
BG1.position = ccp(BG1.position.x-1,BG1.position.y);
}
Your AnimatedBG method should be defined as:
-(void) AnimatedBG:(ccTime)dt {
}
You should also make sure you have a definition for your scheduled tick: method:
-(void) tick:(ccTime)dt {
}
I've been straggling with this problem for a few days now and I'm desperate for your help. As I've been following Ray Wenderlich's tutorials and I still get EXC_BAD_ACCESS error when I'm trying to getUserData from b2Body inside a ContactListener
so WHContactListener.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
class WHContactListener : public b2ContactListener
{
private:
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
};
WHContactListener.mm
#import "WHContactListener.h"
#import "cocos2d.h"
#import "ExSprite.h"
void WHContactListener::BeginContact(b2Contact *contact)
{
b2Body *bodyA = contact->GetFixtureA()->GetBody();
b2Body *bodyB = contact->GetFixtureB()->GetBody();
/*The problem occurs here, while it returns a non-null value it just crashes when I implement any method here, such as NSLog */
ExSprite *spriteB = (ExSprite*)bodyB->GetUserData();
NSLog(#"output %#", spriteB);
}
void WHContactListener::EndContact(b2Contact *contact)
{
}
Sprite class ExSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#interface ExSprite : CCSprite {
BOOL _exploded;
b2Body *_explBody;
}
#property (nonatomic, readwrite) BOOL exploded;
#property (nonatomic, assign) b2Body *explBody;
-(void)setPhysicsBody:(b2Body*)body;
#end
ExSprite.mm
#import "ExSprite.h"
#import "HelloWorldLayer.h"
#pragma mark - ExSprite
#implementation ExSprite
#synthesize exploded = _exploded;
#synthesize explBody = _explBody;
-(void)setPhysicsBody:(b2Body *)body
{
_explBody = body;
_explBody->SetUserData(self);
}
// this method will only get called if the sprite is batched.
// return YES if the physics values (angles, position ) changed
// If you return NO, then nodeToParentTransform won't be called.
-(BOOL) dirty
{
return YES;
}
// returns the transform matrix according the Chipmunk Body values
-(CGAffineTransform) nodeToParentTransform
{
b2Vec2 pos = _explBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPoints_.x;
y += anchorPointInPoints_.y;
}
// Make matrix
float radians = _explBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
-(void) dealloc
{
//
[super dealloc];
}
#end
Finally the layer that I'm using HelloWorld.h
#import <GameKit/GameKit.h>
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#import "WHContactListener.h"
//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
CCTexture2D *spriteTexture_; // weak ref
b2World* world; // strong ref
GLESDebugDraw *m_debugDraw; // strong ref
WHContactListener* contactListener;
CCRenderTexture *target;
CCSprite *brush;
b2Body *groundBody;
NSMutableArray *shapeVert;
}
#property (nonatomic, readwrite)b2Body *groundBody;
#property (nonatomic, assign)NSMutableArray *shapeVert;
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
and HelloWorld.mm
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#import "PhysicsSprite.h"
#import "ExSprite.h"
enum {
kTagParentNode = 1,
};
#pragma mark - HelloWorldLayer
#interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
#end
#implementation HelloWorldLayer
#synthesize groundBody = groundBody_;
#synthesize shapeVert = shapeVert_;
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init])) {
// enable events
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize s = [CCDirector sharedDirector].winSize;
// init physics
[self initPhysics];
target = [CCRenderTexture renderTextureWithWidth:s.width height:s.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[target retain];
[target setPosition:ccp(s.width/2, s.height/2)];
[self addChild:target z:0];
brush = [CCSprite spriteWithFile:#"bird.png"];
[brush retain];
[self scheduleUpdate];
}
return self;
}
-(void) dealloc
{
delete world;
world = NULL;
delete contactListener;
contactListener = NULL;
delete m_debugDraw;
m_debugDraw = NULL;
[super dealloc];
}
-(void) initPhysics
{
CGSize s = [[CCDirector sharedDirector] winSize];
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);
// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
contactListener = new WHContactListener();
world->SetContactListener(contactListener);
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
// flags += b2Draw::e_jointBit;
// flags += b2Draw::e_aabbBit;
// flags += b2Draw::e_pairBit;
// flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(s.width/2/PTM_RATIO, s.height/2/PTM_RATIO); // bottom-left corner
//SET USERDATA
//groundBodyDef.userData = groundBody;
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2EdgeShape groundBox;
float32 x1 = 2*cosf(0.0f * b2_pi/180);
float32 y1 = 2*sinf(0.0f * b2_pi/180);
for(int32 i=1; i<=18; i++){
float32 x2 = 2*cosf((i*20) * b2_pi / 180);
float32 y2 = 2*sinf((i*20) * b2_pi / 180);
groundBox.Set(b2Vec2(x1,y1), b2Vec2(x2,y2));
groundBody->CreateFixture(&groundBox, 0);
x1 = x2;
y1 = y2;
}
}
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
//CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
CCNode *parent = [self getChildByTag:kTagParentNode];
//We have a 64x64 sprite sheet with 4 different 32x32 images. The following code is
//just randomly picking one of the images
int idx = (CCRANDOM_0_1() > .5 ? 0:1);
int idy = (CCRANDOM_0_1() > .5 ? 0:1);
//PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)];
ExSprite *sprite = [ExSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32*idx, 32*idy, 32, 32)];
[parent addChild:sprite];
sprite.position = ccp( p.x, p.y);
// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
//USERDATA set
//bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.restitution = 0.0f;
//fixtureDef.density = rand() * 20.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
[sprite setPhysicsBody:body];
//NSLog(#"here %#", body->GetUserData());
}
-(void) update: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addNewSpriteAtPosition: location];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
/*UITouch *touch = (UITouch*)touches.anyObject;
CGPoint start = [touch locationInView:[touch view]];
start = [[CCDirector sharedDirector] convertToGL:(start)];
CGPoint end = [touch previousLocationInView:[touch view]];
end = [[CCDirector sharedDirector] convertToGL:end];
[target begin];
float distance = ccpDistance(start, end);
for(int i =0; i < distance; i++){
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx*delta), start.y + (dify * delta))];
[brush visit];
}
[target end];
*/
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
Any help will be appreciated on the matter.
I've found the problem. It seems that I've left CCNode undeclared, and when I attach body with the UserData to the unavailable CCNode, it then crashes when I retrieve it in the contactlistener class
Ball is not getting bounced what am I doing wrong ?
here is the class
#include "GameLayer.h"
USING_NS_CC;
GameLayer::GameLayer(){
mWorld = NULL;
mBody=NULL;
mBall=NULL;
}
GameLayer::~GameLayer(){
delete mWorld;
mBody=NULL;
delete mBall;
mBall=NULL;
}
bool GameLayer::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
// Create sprite and add it to the layer
mBall = CCSprite::spriteWithFile("Ball.jpg",CCRectMake(0,0,52,52));
mBall->setPosition(ccp(100,100));
addChild(mBall);
// Create a world
b2Vec2 gravity;
gravity.Set(0.0f,-10.0f);
mWorld = new b2World(gravity);
// Do we want to let bodies sleep?
// mWorld->SetAllowSleeping(true);
// mWorld->SetContinuousPhysics(true);
//Definition of the body
// body definition to specify initial properties of the body such as position or velocity.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
// use the world object to create a body object by specifying the body definition.
b2Body *groundBody = mWorld->CreateBody(&groundBodyDef);
//Create the shape as needed (for ground all the sides)
b2EdgeShape groundEdge;
groundEdge.Set(b2Vec2_zero,b2Vec2(winSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundEdge,0);
groundEdge.Set(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundEdge,0);
groundEdge.Set(b2Vec2(0, winSize.height/PTM_RATIO),
b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundEdge,0);
groundEdge.Set(b2Vec2(winSize.width/PTM_RATIO,
winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&groundEdge,0);
// Create the another body
//1. body defn
//2. create body from game world
// 3. define shape and create fixtures
b2BodyDef ballBodyDef;
ballBodyDef.position.Set(100/PTM_RATIO,100/PTM_RATIO);
// applying sprite as userdata to the bodydef
ballBodyDef.userData = mBall;
mBody = mWorld->CreateBody(&ballBodyDef);
// created different shapes as you want
b2CircleShape circle;
circle.m_radius = 26.0/PTM_RATIO;
//create the fixturedef
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density=1.0f;
ballShapeDef.friction=0.2f;
ballShapeDef.restitution=0.8f;
mBody->CreateFixture(&ballShapeDef);
setAccelerometerEnabled(true);
scheduleUpdate();
return true;
}
void GameLayer::update(float dt){
mWorld->Step(dt,10,10);
for(b2Body *b = mWorld->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *ballData = (CCSprite *)b->GetUserData();
ballData->setPosition(ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO));
ballData->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
}
void GameLayer::didAccelerate(CCAcceleration* pAccelerationValue)
{
b2Vec2 gravity(-pAccelerationValue->y * 15,pAccelerationValue->x *15);
mWorld->SetGravity(gravity);
}
b2BodyDef has b2_staticBody type by default. Try to set
ballBodyDef.type = b2_dynamicBody;