I'm new to cocos2d and I made my way through the 'Learning cocos2d' book.
Like in the Book I implemented a scrollingLayer for scrolling, when my gamecharacter moves.
My problem is the following: I'm using a normal Sprite to do the scrolling layer rendering who can I check if my character is touching the scorlling layer?
fyi: It's a fish and he needs to swim through a hole and there are walls above und underneath it and he should not be able to get in front or behind the rocks.
I've searched the internet and this forum, but was not be able to find any suitable solutions.
Is 'Tiled' the kay for happiness, I read about collisions implemented but can't make it out how they are implemented using tmx's
Can someone assist me with that?
For simplier, but powerful collision detection you most likely need physics engine.
I recommend SpaceManager extension for Chipmunk engine.
In Tiled you can add object layer to tmx file, where you can line out all obstacles.
That's how I parse this information in my game scene:
in init:
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile:#"TileMap.tmx"];
...
smgr = [[SpaceManagerCocos2d alloc] init];
smgr.constantDt = 1/60.0;
smgr.gravity=ccp(0, 0);
[smgr addWindowContainmentWithFriction:0.0 elasticity:0.0 size:CGSizeMake(1000, 1000) inset:cpvzero radius:1.0f]; //borders of your world, can be set from tmx size
player = [smgr addCircleAt:ccp(x,y) mass:6 radius:15];
and somewhere:
- (void) drawWalls {
//searching for object layer called "Collisions"
CCTMXObjectGroup *objects = [_tileMap objectGroupNamed:#"Walls"];
NSMutableDictionary * objPoint;
int x ;
int y ;
int w ;
int h ;
for (objPoint in [objects objects]) {
x = [[objPoint valueForKey:#"x"] intValue];
y = [[objPoint valueForKey:#"y"] intValue];
w = [[objPoint valueForKey:#"width"] intValue];
h = [[objPoint valueForKey:#"height"] intValue];
GameController *controller = [GameController sharedController];
if(controller.retina==1)
{
x=x/2;
y=y/2;
w=w/2;
h=h/2;
}
cpShape *staticShape = [smgr addRectAt:ccp(x+w/2,y+h/2) mass:STATIC_MASS width:w height:h rotation:0];
}
}
Then, you can apply force at "player" object and update your sprite position in "update" method:
playerSprite.position=ccp(player->body->p.x, player->body->p.y);
Of course you can use some other algorithm to detect collision, but this is just an example of storing and retrieving this information from tmx.
Related
i have a tower and a monster. The tower has a SkShapeNode around it, acting as its range. And i want to check if the monster is still inside the range of the tower( i.e inside the skshape node) every frame.
i have this method which i think checks if there is anything still inside the towers' range:
-(void)update
{
[self.currentScene.physicsWorld enumerateBodiesInRect:self.towerRangeNode.frame usingBlock:^(SKPhysicsBody *body, BOOL *stop) {
if (body == nil) {
[self lostSightOfMonster];
}
}];
}
however, the [self lostSightOfMonster] method never runs.
for additional understanding, i call this method in the update method of the scene:
[self enumerateChildNodesWithName:#"Tower" usingBlock:^(SKNode *node, BOOL *stop) {
Tower *tower = (Tower *) node;
[tower update];
if (tower.hasChosenEnemy) {
[tower updateRotation];
}
}];
}
i suspect its something to do with the enumerating bodies in rect method, however i am not sure.
thanks in advance
I would suggest a different approach. Rather than checking if it is in contact with the sprite I would check its distance to the origin of the tower using Pythagorean theorem. I think the code would be more simple and there would be the added bonus of not having the range sprite to be visible at all times.
here is some concept code to show what I mean:
double distance;
distance = pow(enemySprite.position.x-towerSprite.position.x, 2);
distance += pow(enemySprite.position.y-towerSprite.position.y, 2);
distance = sqrt(distance);
if (distance < towerRange) {
//shoot at ememy
}
You could hypothetically put this in a for loop to check for multiple enemies as well.
Use this code to check for frame intersection of 2 nodes:
if(CGRectIntersectsRect(towerNode.frame, enemyNode.frame))
{
// do your thing
}
If you have more than one tower and one enemy you would use this code:
for(SKNode *towerNode in towerNodeArray)
{
for(SKNode *enemyNode in enemyNodeArray)
{
if(CGRectIntersectsRect(towerNode.frame, enemyNode.frame))
{
// do your thing
}
}
}
The above assumes you are using the physics body of the tower to check for range. Alternately you can also use some simple x and y coordinate checking like this:
if(((towerNode.position.y <= enemyNode.position.y+100) && (towerNode.position.y >= enemyNode.position.y-100)) &&
((towerNode.position.x <= enemyNode.position.x+100) && (towerNode.position.x > enemyNode.position.x+100)))
{
// do your thing
}
My testing environment:
Xcode 4.6, New cocos2d-x+box2d project
cocos2d-2.1beta3-x-2.1.1
PhysicsEditor 1.0.10
I modified a bit in HelloWorldScene of PhysicsEditor cocos2dx demo to make it simpler, Here are some of my code:
initPhysics
gravity.Set(0.0f, 0.0f);
So that the sprite will not move.
Replace source code inside ccTouchesEnded to:
CCTouch* pTouch = (CCTouch *)touches->anyObject();
CCPoint location = pTouch->locationInView(pTouch->view());
CCPoint convLoc = CCDirector::sharedDirector()->convertToGL(location);
b2Vec2 v = b2Vec2(convLoc.x/PTM_RATIO, convLoc.y/PTM_RATIO);
for (b2Body *b = world->GetBodyList(); b; b = b->GetNext())
{
b2Fixture *f = b->GetFixtureList(); // get the first fixture
CCSprite *sprite =(CCSprite *) b->GetUserData();
if(sprite != NULL)
{
if(f -> TestPoint(v))
{
CCLog("You touched a body %d",sprite->getTag());
}
}
}
The problem is that TestPoint only return true in a very small area (not for whole shape area).
Here is the screenshot:
Can anybody suggest how I debug this problem? Thanks
Updated: showing the generated data from PhysicsEditor
The problem is that you only probe for the first fixture. But complex bodies are made from several. The reason is that
Box2d only allows 8 vertexes per fixture
Box2d can only work with convex shapes
This is why complex shapes are decomposed into a list of polygons.
Iterate over the fixture list instead.
b2Fixture *f = body->GetFixtureList();
while(f)
{
if(f -> TestPoint(v))
{
CCLog("You touched a body %d",sprite->getTag());
}
f = f->GetNext();
}
I'm working on a game in Cocos2d with Box2D. Currently I have a system set up where there are some boundaries. They are for a ball. I want meteors to come in from off the screen and smack the ball.
The boundaries are defined in my init method as follows:
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.type = b2_staticBody;
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);
//Collision filter stuff here. Not working.
b2Filter filter;
filter.groupIndex = -2;
groundBody->GetFixtureList()[0].SetFilterData(filter);
My ball is defined as follows:
//Physics object.
b2BodyDef ballBodyDef;
ballBodyDef.position.Set(ball.position.x/PTM_RATIO, ball.position.y/PTM_RATIO);
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.userData = ball;
ballBodyDef.bullet = true;
ballBody = world->CreateBody(&ballBodyDef);
b2CircleShape ballShape;
ballShape.m_radius = 10.0/PTM_RATIO;
b2FixtureDef ballFix;
ballFix.restitution = 1.1f;
ballFix.density = 0.0f;
ballFix.shape = &ballShape;
ballBody->CreateFixture(&ballFix);
And the meteors are made using two methods, the shorter method places the meteors. The longer one actually creates them using the points given from the shorter one:
-(void)createMeteorAtPoint:(CGPoint)point {
//Create particle sprite representation.
CCParticleMeteor *meteor = [[CCParticleMeteor alloc] initWithTotalParticles:200];
meteor.gravity = ccp(-200,0);
meteor.life = 0.5;
meteor.lifeVar = 0.25;
meteor.position = point;
meteor.tag = 2;
meteor.startSize = 5.0;
meteor.startSizeVar = 3.0;
[self addChild:meteor];
//Make meteor physics body.
b2BodyDef meteorBodyDef;
meteorBodyDef.position.Set(point.x/PTM_RATIO, point.y/PTM_RATIO);
meteorBodyDef.type = b2_dynamicBody;
meteorBodyDef.userData = meteor;
b2Body *meteorBody = world->CreateBody(&meteorBodyDef);
meteorBody->SetBullet(true);
b2CircleShape meteorShape;
meteorShape.m_radius = 7.5/PTM_RATIO;
b2FixtureDef meteorFix;
meteorFix.shape = &meteorShape;
meteorFix.density = 1;
//Give it the same negative group index of the boundaries to prevent collision.
//Not working.
meteorFix.filter.groupIndex = -2;
meteorBody->CreateFixture(&meteorFix);
//Give it a motion.
b2Vec2 F;
F.Set(CCRANDOM_0_1()*2, 0);
meteorBody->SetLinearVelocity(F);
}
//Create a bunch of meteors that will sweep from left to right.
-(void)createMeteors {
for (int i = 0; i < 8*40; i += 20) {
[self createMeteorAtPoint:ccp(-40.0f, i + 2)];
}
NSLog(#"HERE");
}
I can see physics objects pile up on the side of the screen because I have debug flags set up. But one: They are not getting past the boundary despite being the same negative group index. And two: the ball decides it doesn't like the right wall, and goes through it.
If you believe I am missing something crucial please tell me.
Only one of the boundary fixtures is being given the group index of -2 (the bottom). You can loop over all fixtures of a body like this:
for (b2Fixture* f = body->GetFixtureList(); f; f = f->GetNext())
{
//do something with the fixture 'f'
f->SetFilterData(...);
}
I'm not sure why the ball would be ignoring any walls though (unless you are moving it by creating a mouse joint between the ball and the ground body that the walls belong to, and the joint is set to not collide connected bodies... but in that case the ball would ignore all walls, and only while being moved).
I dont know how to use this metnod in my application
void MyContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) {
b2WorldManifold worldManifold;
contact->GetWorldManifold(&worldManifold);
b2PointState state1[2], state2[2];
b2GetPointStates(state1, state2, oldManifold, contact->GetManifold());
//NSLog(#"Presolving");
if (state2[0] == b2_addState)
{
const b2Body* bodyA = contact->GetFixtureA()->GetBody();
const b2Body* bodyB = contact->GetFixtureB()->GetBody();
b2Vec2 point = worldManifold.points[0];
b2Vec2 vA = bodyA->GetLinearVelocityFromWorldPoint(point);
b2Vec2 vB = bodyB->GetLinearVelocityFromWorldPoint(point);
b2Vec2 rV = vB - vA;
float32 approachVelocity = b2Dot(rV, worldManifold.normal);
if (-1.5f < approachVelocity && approachVelocity < 0.0f)
{
//MyPlayCollisionSound();
NSLog(#"Not Playing Sound");
}
else
{
NSLog(#"playing the sound");
}
}
}
How can I use this code in my HelloWorldLayer.mm Please help me...
I have a problem here, I have a scene where some bodies falls and hit a static body, edges alike, I implemented the b2ContactListener, and in my tick method Im checking for contacts and play the sound, the problem with this approach is that when a body is constantly in contact with the static body, the sound plays indefinitely overlaying the previous one, so at the end I have huge noise..
What can I do to avoid this situation?
Please help me thanks......
You just used following method into your update method and declare its object before schedule update method call.
Like in HelloWorldLayer.h
MyContactListener *contactListener;
And in HelloworldLayer.mm
Before
[self scheduleupdate];
contactListener=new MyContactListener();
world->setContactListener(contactListener);
Than this type of error not occur.
When two bodies collide, the b2ContactListener methods are called in the following sequence:
BeginContact
PreSolve
PostSolve
PreSolve
PostSolve
...etc
EndContact
So if you want to detect collision between the bodies once for each collision, use BeginContact or EndContact instead. These methods only take a single b2Contact parameter though so you might need to do away with using the oldManifold value in your calculation.
I was wondering if anyone could help me with my program,
I have randomised my sprites into a specific set of co-ordinates.
I want one of the sprites that is at that specific co-ordinate, to be able to make them do something when they are at this random co-ordinate. The problem i am having is that i have to make a long list of if statements saying if this sprite is here do this if another sprite is here do the exact same thing.
if (red1.position.y>=0 && red1.position.y<=63) {
id r1animation = [CCMoveTo actionWithDuration:0.2 position:ccp(red1.position.x,33)];
[red1 runAction:r1animation];
}
if (red2.position.y>=0 && red2.position.y<=63) {
id r2animation = [CCMoveTo actionWithDuration:0.2 position:ccp(red2.position.x,33)];
[red2 runAction:r2animation];
}
i want to be able to say if any of the sprites are at that exact co-ordinate then move them to a point, in a short amount of code as possible. so basically grouping the sprites or something i'm not sure.
Thanks
i want to be able to say if any of the sprites are at that exact co-ordinate then move them to a point
Firstly, specify the 'hotspot' programatically:
CGPoint hotspot = ccp(32,32); // convenience macro,
//creates a CGPoint with x = 32, y = 32
You should store a reference to all your sprites in an array when you create them (you can use cocos2d's 'tagging' also, but I usually like to use an array for simplicity)
-(void)init {
//.. misc
// creating sprite returns a reference so keep it in an array
CCSprite* curSprite = [CCSprite spriteWithFile: //...etc]
[self.spriteArray addObject: curSprite];
// add all sprite references to your array
}
Now you can iterate over this array to see if any of the sprite's frames overlap the hotspot:
-(BOOL) checkAllSpritesForCollision
{
for (CCSprite *sp in self.spriteArray)
{
CGRect spriteRect = sp.frame;
if (CGRectContainsPoint(spriteRect,hotspot))
{
// run your action on sp...
}
}
// you might like to return YES if a collision happened?
}
This is a brute force method of checking whether every sprites frame contains a given point. There are many ways to skin this cat of course, but hopefully this will set you on a better path.
What you can do is to calculate the distance:
float pointX = thePoint.position.x;
float pointY = thePoint.position.y;
float pointDeltax = sprite.position.x-pointX;
float pointDeltay = sprite.position.y-pointY;
float pointDist = sqrt(pointDeltax*pointDeltax+pointDeltay*pointDeltay);
But maybe davbryns solution suits your purpose better.