Thanks for your time reading this!
I used Cocos2d and Box2d in my project. The logic of the project is pretty simple. Player just shoots at enemies.If the bullet hits an enemy, the bullet as well as the enemy will be destroyed.If any enemy walks through the screen without getting shot, then the game is over. Part of code is as follows:
- (void)birdDone:(ZLBird*) birdToDelete {
CCSprite *sprite = (CCSprite *)birdToDelete;
b2Body *spriteBody = NULL;
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *curSprite = (__bridge CCSprite *)b->GetUserData();
if (sprite == curSprite) {
spriteBody = b;
break;
}
}
}
if (spriteBody != NULL) {
world->DestroyBody(spriteBody);
}
[sprite removeFromParentAndCleanup:YES];
sprite = NULL;
}
-(void) update:(ccTime)delta{
SOME CODE HERE
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
CCSprite *myActor = (__bridge CCSprite*)b->GetUserData();
if (b->GetUserData() != NULL)
{
//Synchronize the AtlasSprites position and rotation with the corresponding body
myActor.position = (CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO));
myActor.rotation = (-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
// goes through all the rocks
for (ZLRock *rockToAct in rocksArr){
[rockToAct incrementTime];
if ([rockToAct getExistedTime] >= 400)
{
// Remove the rock several seconds after it has been shot
[self rockDone:rockToAct];
}
}
// goes through all the elements in BirdsArr
for (ZLBird *birdToAct in birdsArr){
[birdToAct incrementTime];
if ([birdToAct getDeadTime] >= 400 || birdToAct.position.x > WIDTH_WINDOW+20)
{
// Remove the bird several seconds after it has been dead
[self birdDone:birdToAct];
}
// JUST FOR TEST
if (birdToAct.position.x > WIDTH_WINDOW && !birdToAct.isDead)
NSLog(#"YOU LOSSSSSSSS!");
}
When I run this project on my iphone, somehow it gets really slow. I checked CPU usage on Instruments, the CPU usage is like 70%.
Why is that? I guess there's no memory leak since I enabled ARC. Is it because I go through several arrays in each frame, and that really makes the device slow down?
Thanks for your help!
Just found out the problem!
I didn't remove those objects from array when I remove them from their parent. So the array size keep growing, and finally cause crush.
Thanks for the answers!
Related
The only way I can think to do it is to check velocities for all physics bodies during every collisions.
- (BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair piece:(CCNode *)pieceA piece:(CCNode *)pieceB{
float x = 0;
float y = 0;
for (int i = 0; i < [[_physicsWorld children] count]; i++) {
x = x + [[[_physicsWorld children][i] physicsBody] velocity].x;
y = y + [[[_physicsWorld children][i] physicsBody] velocity].y;
}
if ( x == 0 && y == 0 ) {
NSLog(#"stopped");
}
return YES;
}
This logs “stopped” multiple times when the scene first loads, then doesn’t log “stopped” again, even after physics bodies have clearly started moving and colliding and then come to a stop.
Ideally I'd like a delegate method that would notify me when all physics bodies have stopped moving, but I can't seem to find one.
FYI: I'm using the standard Chipmunk physics engine that's baked into Cocos2d V3.0
Chipmunk has a internal mechanism, which can, if activated, automatically deactivate physics bodies. My approach (I am using cocos2dx 3.11.1 and not -obj version with chipmunk 7.0.1) is:
activate the chipmunk idle mechanism (0.5 second - meaning, if an object is not moving for longer than 0.5 second it will be deactivated):
cpSpaceSetSleepTimeThreshold(space, 0.5f);
You do not need to use
cpSpaceSetIdleSpeedThreshold(space, <speed>);
because chipmunk calculates the threshold speed for you (according the gravitation used).
use this code for determination if all objects are not moving (static and kinetic bodies never sleep):
bool isAnyPhysicsBodyMoving(){
int i = 0; bool isMoving = false;
const Vector<PhysicsBody*>& bodies = getPhysicsWorld()->getAllBodies();
while( i < bodies.size() && !isMoving){
PhysicsBody *body = bodies.at(i);
isMoving = cpBodyGetType(body->getCPBody()) == CP_BODY_TYPE_DYNAMIC
&& !body->isResting();
i++;
}
return isMoving;
}
use static (and not kinetic) body for walls, in order to let objects sleep:
// wall
Size visibleSize = Director::getInstance()->getWinSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
float border = 10.0f;
Size wallBodySize = Size(visibleSize.width+2*border, visibleSize.height+2*border);
PhysicsBody *wallBody = PhysicsBody::createEdgeBox(wallBodySize, PhysicsMaterial(1.0f, 1.0f, 0.5f), border);
Node* wall = Node::create();
wall->addComponent(wallBody);
wall->setAnchorPoint(Vec2(0.5f, 0.5f));
wall->setPosition(Point(visibleSize.width/2+origin.x, visibleSize.height/2+origin.y));
cpVect tt;
tt.x = wall->getPosition().x; tt.y = wall->getPosition().y;
//set position manually and BEFORE adding the object into the space
cpBodySetPosition(wallBody->getCPBody(), tt);
cpBodySetType(wallBody->getCPBody(), CP_BODY_TYPE_STATIC);
addChild(wall);
Any dynamic body connected to a kinetic body (for example laying on) will never sleep.
test it with DEBUG activated
getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
the boxes (their content) must become grey (=sleeping) and not red (=active):
In order to let it work, I have:
added an access method (to get cpSpace) in CCPhysicsWorld.h:
inline cpSpace* getSpace() const { return _cpSpace; }
Fix call of
cpBodySetTorque(body, 0.0f);`
in CCPhysicsBody.cpp to
if (body->t != 0.0f){
cpBodySetTorque(body, 0.0f);
}
Fix call of
cpBodySetPosition(_cpBody, tt);`
in CCPhysicsBody.cpp to
if (!cpveql(tt, cpBodyGetPosition(_cpBody))){
cpBodySetPosition(_cpBody, tt);
}
Steps 2. and 3. are necessary to avoid setting of the same physics body properties, which wake up a sleeping body.
The advantage of this approach is, that the chipmunk does not make any calculations for such physical bodies - saving CPU and battery.
I found something that works.
tl;dr
The basic idea is to keep track of the positions of the sprites myself, and then periodically check them to see if any of them have moved since they were last checked.
Longer version
I created a subclass of CCNode with the class name Piece.
These are my objects that are added to the physics world.
#implementation Piece {
float _previousX;
float _previousY;
}
-(void)updatePreviousScreenXandY{
_previousX = self.position.x;
_previousY = self.position.y;
}
-(BOOL)hasntMoved{
float currentX = self.position.x;
float currentY = self.position.y;
if ( currentX == _previousX && currentY == _previousY ) {
return TRUE;
}else{
return FALSE;
}
}
This is in my CCNode that acts as the game scene
-(void)doStuffAfterPiecesStopMoving:(NSTimer*)timer{
BOOL noPiecesHaveMoved = TRUE;
for (int i = 0; i < [[_physicsWorld children] count]; i++) {
if ( [[_physicsWorld children][i] hasntMoved] == FALSE ) {
noPiecesHaveMoved = FALSE;
break;
}
}
if ( noPiecesHaveMoved ) {
[timer invalidate];
NSLog(“Pieces have stopped moving”);
}else{
NSLog(“Pieces are still moving”);
[self updateAllPreviousPiecePositions];
}
}
-(void)updateAllPreviousPiecePositions{
for (int i=0; i < [[_physicsWorld children] count]; i++) {
Piece *piece = (Piece*)[_physicsWorld children][i];
[piece updatePreviousScreenXandY];
}
}
All I have to do is
[NSTimer scheduledTimerWithTimeInterval:TIME_BETWEEN_CHECKS
target:_gamePlay
selector:#selector(doStuffAfterPiecesStopMoving:)
userInfo:nil
repeats:YES];
and it’ll run whatever code I want after all Piece nodes have stopped moving.
The key to getting it to work well is to get the values for the Chipmunk space’s sleepTimeThreshold and the timer above’s time as low as possible.
My experimenting suggests the following settings work okay, but anything lower will cause problems (i.e. collisions not taking place properly):
sleepTimeThreshold = 0.15
my timer = 0.05
If anyone has a different/better solution or improvements to the above code, please post.
I'm writing a 2D ball game with sprite kit on iOS 7 and currently struggling on one physic simulation.
To explain the expected behavior: if a ball is dropped into a tea cup, it will circle around, loosing speed and finally stand still in the center of the cup.
I've tried to archive this with gravity, but gravity in sprite kit only applies to vertical X and Y axis, not Z-axis. I also tried to use level gravity by switching gravity values with small physic bodies on beginContact depending on the current ball position in the tea cup. But some contacts are dropped and the result is far away to look realistic.
I think I need to solve this in the update: method, but I have no idea which way to go.
Any advice greatly welcome and I need to mention that I'm not an expert on math, please explain your path to go. :-)
Since there's no built-in support for this kind of behavior in SpriteKit, rather than trying to hack existing functions to get what you want, you're probably better off integrating some published 2D physics formulas in your x,y 2D world. I would think that something like simulating magnetic or a homing behavior might be right for this.
A simple example would be something like (in the scene's -update: method):
CGFloat strength = 0.5; //(some scaling value)
CGPoint ballLocation = ball.position;
CGPoint cupLocation = cup.position;
[ball.physicsBody applyForce:CGVectorMake((cupLocation.x - ballLocation.x) * strength,
(cupLocation.y - ballLocation.y) * strength)];
following Joshd great idea, I have created an NSArray with like explained in my comment above. Hope this snippets does help some others...
The result could be found on youtube: http://youtu.be/Uephg94UH30
Sorry for the bad Airplay frame rate, it runs perfectly smooth on my iPad
The -update: functions does the work but only triggered if _meditationIsActive. This bool is set in -didBeginContact: when any ball gets in contact with a hole.
if (_lastCheck > 0.005)
{
if (_meditationIsActive)
{
CGFloat strength = 0.1; //(some scaling value)
CGPoint ballLocation;
CGPoint holeLocation;
for (MeditationHole * holeObj in _meditationHoles)
{
if (holeObj.connectedMeditationBall != nil)
{
ballLocation = holeObj.connectedMeditationBall.position;
holeLocation = holeObj.position;
[holeObj.connectedMeditationBall.physicsBody applyForce:CGVectorMake(
(holeLocation.x - ballLocation.x) * strength, (holeLocation.y - ballLocation.y) * strength)];
}
}
_meditationIsActive = [self doesMeditationApplies];
}
_lastCheck = 0;
}
At the end I'm checking if there is a valid ball out of the array in contact with a hole to avoid checking during every update. This is done with the following function where position check +/- 48 detects a ball close to a hole and +/-1 ball stands still
- (bool)doesMeditationApplies
{
bool isInArea = NO;
int perfectMatchCount = 0;
for (MeditationHole * holeObj in _meditationHoles)
{
if (holeObj)
{
if (holeObj.connectedMeditationBall != nil)
{
MeditationBall * ballObj = holeObj.connectedMeditationBall;
if ((ballObj.position.x >= holeObj.position.x - 48) &&
(ballObj.position.x <= holeObj.position.x + 48) &&
(ballObj.position.y >= holeObj.position.y - 48) &&
(ballObj.position.y <= holeObj.position.y + 48))
{
isInArea = YES;
}
else
{
holeObj.connectedMeditationBall = nil;
}
if ((ballObj.position.x >= holeObj.position.x - 1) &&
(ballObj.position.x <= holeObj.position.x + 1) &&
(ballObj.position.y >= holeObj.position.y - 1) &&
(ballObj.position.y <= holeObj.position.y + 1))
{
perfectMatchCount++;
isInArea = YES;
}
}
}
}
if (perfectMatchCount == _oxydStonesMax)
{
if (_sound)
{
self.pauseMusicPlaybackBlock(YES);
NSLog(#"PlaySound Meditation");
[OxydScene PlaySystemSound:#"Win2"];
}
isInArea = NO;
[self showPauseScreenWithWin:YES andPauseOnly:NO];
}
return isInArea;
}
I'm a beginner in the Cocos2D world, and even more in Box2D's. I'm developing an iOS game with these two engines, and what I want, is to have a guy (hero) to pick up coins in a scrolling map.
Well, the problem is the "Contact Listener". I can't seem to understand what it really does, and how I can use it to make the difference between my coins and other bodies (that are all static bodies) around the map.
I set all the coins .isSensor = true to disable collisions. I'm using MyContactListener from raywenderlich.com tutorial.
So, the main question is: How to know what type of body is in collision with the hero's body through the contact listener?
(i've read many tutorials and questions on here and I can't seem to know where to begin)
Thanks!
Edit:
Si, I finally got it to work with sprite.tag, but I can't destroy the bodies and the textures. Sometimes when using [textureB removeFromParentAndCleanup:YES]; I get EXC_BAD_ACCESS.
And as I understood, I can't destroy the body since I'm actually using it.
But when trying to use body->SetUserData(self); in my Hero class
#interface Hero : CCNode {
CCSprite * texture;
CGPoint position;
b2Body* body;
TypeCase type;
}
I get EXC_BAD_ACCESS in my update method:
for(b2Body *b = world_->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
if ([b->GetUserData() isKindOfClass:[Hero class]])
{
Hero* hero = (Hero*)b->GetUserData();
CCSprite *heroSprite = hero.texture;
heroSprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
}
else if ([b->GetUserData() isKindOfClass:[Tile class]])
{
Tile* tile = (Tile*)b->GetUserData();
CCSprite *tileSprite = tile.texture;
tileSprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
}
}
}
I get EXC_BAD_ACCESS when calling isKindOfClass.
Hero userData
b2BodyDef heroBodyDef;
heroBodyDef.fixedRotation = true;
heroBodyDef.type = b2_dynamicBody;
heroBodyDef.position.Set(position.x/PTM_RATIO, position.y/PTM_RATIO);
body = world_->CreateBody(&heroBodyDef);
body->SetUserData(self);
Tile userData
b2BodyDef blockBodyDef;
blockBodyDef.type = b2_staticBody;
blockBodyDef.position.Set(position.x/PTM_RATIO, position.y/PTM_RATIO);
blockBodyDef.userData = self;
b2Body *body = world ->CreateBody(&blockBodyDef);
Don't use physics objects for coins.. this will affect on fps :)
create CCSprite for coins and detect collisions with coin sprite and body->UserData;
Add your coins in CCArray and write something like this in your update method:
CCSprite *sprite = (CCSprite*)yourObject.body->GetUserData();
Coins *coinObject;
CCARRAY_FOREACH(coinsArray, coinObject)
{
if (CGRectIntersectsRect([sprite boundingBox], [coinObject boundingBox]))
{
// collision detected;
}
}
if you want to use physics body for your coins, you need to create typedef for yourObject states, and in your contact listener void ContactListener::EndContact(b2Contact* contact) method change yourObject currentState. currentState is yourObject's property.
Do something like this in void ContactListener::EndContact(b2Contact* contact)
yourObject.currentState = isCollideOnCoin;
and after this in your update add this line:
if (yourObject.currentState == isCollideOnCoin)
{
// collision detected;
}
You can create a custom class (say BodyType) which indicates bodies type (in your case player/coin)
then you can use the user data property on your created bodies :
playerBody->SetUserData(new BodyType(1));
coinBody->SetUserData(new BodyType(2));
In the contact listener there is a method named beginContact which has an argument of type b2Contact* named contact.
Use this argument to get the two bodies :
b2Body* bodyA = contact->GetFixtureA()->GetBody();
b2Body* bodyB = contact->GetFixtureB()->GetBody();
then you can know if your player hit a coin by checking :
if ((bodyA->GetUserData().getType() == 1 && bodyB->GetUserData().getType() == 2) ||
(bodyA->GetUserData().getType() == 2 && bodyB->GetUserData().getType() == 1)) {
// Mark collision or save coin body in a member of your contact listener for removing it later
}
Of course make sure the user data is not null as all bodies will be passed to the contact listener and not all might have the user data member set.
EDIT
Regarding the BAD_ACCESS do the following :
if (b->GetUserData() != NULL) {
id myUserData = (id)b->GetUserData();
if ([myUserData isKindOfClass:[Hero class]])
{
Hero *hero = (Hero *)myUserData;
// Do your stuff
}
}
I'm trying to make some moving tiles from a Tiled map editor tmx file.
I have the moving tiles in their own layer, and I just want to simply have them move up, and then when they reach a certain y, move back down, and etc.
I have been looking around for a bit on a clear way of accomplishing this, but my efforts have been unsuccessful.
I tried using some of the methods here.
I'm still really new to cocos2d development in general, so I wold appreciate any insight on this. Thank you very much for your time. If you have any questions, please ask! :)
Also if it helps, the tiles I'm trying to move are in a big T shape.
FINAL UPDATE:
(Removed more irrelevant code so anyone in the future can easily find my solution (the full answer is below), you can find where I got my layer iterate method at the link above).
Okay, so I have finally got it working close to how I want.. I don't think this is exactly the most ideal way of doing it, but this is what I've got.
Note: In order for this to work for you, you have to run your app out of debug mode or it will lag/make the player fall through the ground (at least it did for me..).
I have an update function that calls certain functions every frame. (Checking collisions, moving platforms, etc).
That update function calls my move platforms function..
like this:
[self movePlatforms:0.1];
this is my movePlatforms function..
-(void)movePlatforms: (ccTime) dt{
if(goingDown){
moveCount++;
}else{
moveCount--;
}
CGSize s = [movingTiles layerSize];
for( int x=0; x<s.width;x++) {
for( int y=0; y< s.height; y++ ) {
CCSprite *tile = [movingTiles tileAt:ccp(x,y)];
if(goingDown){
CGPoint newPosition = ccp(tile.position.x, tile.position.y - 1);
tile.position = newPosition;
if(moveCount >= 100){
goingDown = false;
}
}else{
CGPoint newPosition = ccp(tile.position.x, tile.position.y + 1);
tile.position = newPosition;
if(moveCount <= 0){
goingDown = true;
}
}
}
}
}
So basically, I created a int moveCount and a BOOL goingDown to keep track of how many times my movePlatform function has been called. So after 100 calls, it switches direction.
(This works fine for me, you might need something else like a collision detecter if that is the case use this).
if (CGRectIntersectsRect([someSprite boundingBox], [someSprite boundingBox])) {
//Do something
}
Hopefully this works for someone in the future, I know this was quite the headache for me, and it probably isn't even done correctly or there is a much better way to do it, but if this helps you, that is awesome!
Creating and removing tiles will effect your performance.
Instead of it, try to move the tile changing their position:
CCSprite *tile = [movingTiles tileAt:ccp(92,platformY)];
[movingTiles removeTileAt:ccp(92,platformY)];
CGPoint newTilePosition = tile.position;
if (goingDown){
newTilePosition.y ++;
if(newTilePosition.y >= 20){
goingDown = false;
}
}else{
newTilePosition.y --;
if(newTilePosition.y <= 10){
goingDown = true;
}
}
tile.position = newTilePosition;
Here is the (kind of) step by step of how I got my moving tiles working, this is only related to the moving tiles, and nothing else.
Note: You will need to run this as a release (not debug) in order to get everything running smoothly, and not having your character fall through the ground.
In the interface I created these variables:
#interface HelloWorldLayer(){
CCTMXTiledMap *map;
BOOL goingDown;
int moveCount;
}
The CCTMXTiledMap is the instance of my map.
The BOOL and int are two variables I use to keep track of my moving tiles.
-(id) init {
if( (self=[super init]) ) {
// add our map
map = [[CCTMXTiledMap alloc] initWithTMXFile:#"level1-1.tmx"];
map.position = ccp(0,0);
[self addChild:map];
//add our moving platforms layer
movingTiles = [map layerNamed:#"moving_platforms"];
//set the variables I use to keep track of the moving platforms
goingDown = true;
moveCount = 0;
//schedule my update method
[self schedule:#selector(update:)];
}
return self;
}
After the init method, I then create my move platforms method:
-(void)movePlatforms: (ccTime) dt{
if(goingDown){
moveCount++;
}else{
moveCount--;
}
CGSize s = [movingTiles layerSize];
for( int x=0; x<s.width;x++) {
for( int y=0; y< s.height; y++ ) {
CCSprite *tile = [movingTiles tileAt:ccp(x,y)];
if(goingDown){
CGPoint newPosition = ccp(tile.position.x, tile.position.y - 1);
tile.position = newPosition;
if(moveCount >= 100){
goingDown = false;
}
}else{
CGPoint newPosition = ccp(tile.position.x, tile.position.y + 1);
tile.position = newPosition;
if(moveCount <= 0){
goingDown = true;
}
}
}
}
}
So this is where the magic happens, I use methods I got from here, and the gentleman Mauricio Tollin told me I could update a tile position rather than destroy and recreate them.
So I iterate through every tile in my moving platforms layer, and tell them to go down 1 every call, until moveCount >= 100, then it says goingDown is now false, and it switches its direction. From there it just goes back and forth, counting to 100, and then back down.
If you want it to move longer, just increase 100 to 200 or whatever you want. (Or you can use a check to detect collision, and when it collides with a specified sprite, you can have it change then. If that is more of what you want, use this).
if (CGRectIntersectsRect([someSprite boundingBox], [someSprite boundingBox])) {
//Do something
}
After all of that, I create my update method:
-(void)update:(ccTime)dt{
[self movePlatforms:0.1];
}
In the init method it schedules the update method to be called every frame, and then the update method will run the movePlatforms method (or any other function that needs to be checked frequently, such as hazard detection, etc).
You can also make the platforms move slower by changing the time passed into movePlatforms, or you can schedule a slower update interval in the init method.
I hope this helps someone out in the future, I just wanted to create this answer with a more in depth process of how I got this working, since my question post was really unorganized and heavily edited while I was learning.
I am using Cocos2d to create animations in an iPad app.
In the app, the user can create a sequence of animations and choose how many times that sequence will repeat. For unrelated reasons I chose to split up each action in to many small actions- i.e. if the user chooses to move 100pts she will instead move 2pts 100 times. This can lead to very large CCSequence arrays.
I have found that creating a sequence of about 16k CCActions causes the app to crash. Is this the expected behavior? Does anyone know of a workaround to achieve this effect or do I need to end the splitting up of movements?
Thanks!
Edit: Here's a screenshot depicting the effect I want to achieve and the reason I am splitting up my animation.
typedef enum Actor_STATE{
kState_MovingLeft = 14001,
kState_MovingRight
}ActorState;
#interface HorseRider : CCSprite
{
ActorState mState;
float mMoveSpeed;
}
#property(nonatomic, assign) ActorState state;
//implementation of HorseRider
-(void)updateRider
{
CGPoint pos = self.position;
if(self.state == kState_MovingLeft)
{
pos.x -= mMoveSpeed;
if(pos.x>screenWidth)
{
state = kState_MovingRight;
continue;
}
}
else
{
pos.x += mMoveSpeed;
if(pos.x>0.0f)
{
state = kState_MovingRight;
continue;
}
[self createTrackDots];
}
self.position = pos;
}
-(void) createTrackDots
{
if(ABS(lastDots.x - self.position.x) > MIN_DOT_DISTANCE)
{
[self createDotsAtPos: self.position];
}
}
//Game init
-(id)init
{
…
hero = [HorseRider spriteWithSpriteFrameName:FRAME_NAME];
hero.state = kState_MovingLeft;
….
[self schedule: #selector(tick:)];
}
-(void) tick: (ccTime) dt
{
if(self.isGamePaused || self.isGameOver )
{
[hero updateRider];
}
}