SimplyKiwi
SimplyKiwi

Reputation: 12444

CCSprite positioning issue on top of other CCSprite as it animates up screen?

In my app I have a character that has feet and I want to make it so that in my app it looks like it is standing on top of another CCSprite as it travels UP the screen (an animation). Everything is working fine EXCEPT there is a little positioning issue that I just cannot figure out! Let me explain the issue a bit more, when the character is on top of the CCSprite, it seems as if the character moves up/down about 10 points in very fast intervals.

Does anyone have any idea why this happening?

Thanks!

FINAL EDIT: I would just like to thank you again for helping me through all of this. First off, I have deleted all my other edits since the post was getting too long and if you need to reference the older edits for any reason just look through my edit history!

So after about an hour of testing, I have narrowed it down to one issue which you have mentioned earlier, the if statements checking if the character and the floor piece are colliding are NOT getting called each iteration in the game loop when they SHOULD be (as far as they look in the UI).

My cocosGameLoop log is outside and before all of the collision detecting code but still in the game loop.f

I have also noticed a pattern in my NSLogs as you can see below:

2012-05-27 17:00:54.791 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.811 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.825 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.841 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.858 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.874 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.891 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.908 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.924 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.928 App[2769:707] collisiontwo
2012-05-27 17:00:54.929 App[2769:707] two
2012-05-27 17:00:54.941 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.944 App[2769:707] collisiontwo
2012-05-27 17:00:54.945 App[2769:707] two
2012-05-27 17:00:54.958 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.974 App[2769:707] cocosGameLoop
2012-05-27 17:00:54.991 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.008 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.025 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.043 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.058 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.076 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.078 App[2769:707] collisiontwo
2012-05-27 17:00:55.078 App[2769:707] two
2012-05-27 17:00:55.091 App[2769:707] cocosGameLoop
2012-05-27 17:00:55.094 App[2769:707] collisiontwo

What I have also noticed when I NSLog the character Y coordinate is that it stays around the same point for a few interations (even when it should be colliding so it would coincide with the bunch of cocosGameLoop calls in the NSLog above), then when it actually collides (programmatically), it moves up about 14 points from the Y coordinate before. So there is a jitter of about 14 points up/down.

So the bottom line is, how do I make it so that the collision code gets called EVERY iteration and doesn't jitter about 14 points? I do not want to change anchor points since that will mess my animation positioning and so forth. Anyway, what do you recommend? Also keep in mind I am only using Box2D for collision detection so I must keep everything in Cocos2D code.

Lastly, I thought there were rounding point errors in my app because of the gravity but even when I messed around with the values like changing the gravity force to an integer, it did not fix the flickering so it is 100% the issue I mentioned above.

Anyway, this is the heart of the issue and I think that you can see something that I just can't! Let me know what you think :)

Thanks so much!

Upvotes: 5

Views: 556

Answers (1)

Rob B
Rob B

Reputation: 1485

First thing that looks odd is your checking of tags just after the Some other tag collision checking before comment. This would make more sense:

else if ((spriteA.tag == 1 && spriteB.tag == 5) || (spriteB.tag == 1 && spriteA.tag == 5))
...
else if ((spriteA.tag == 6 && spriteB.tag == 1) || (spriteB.tag == 6 && spriteA.tag == 1))
...

i.e. this assumes that the collision contact you are testing against could give you the two bodies in either order - e.g. for two objects colliding, say character and ground, the contact listener could give ground as body A and feet as body B, or feet as body A and ground as body B. The statement above would guarantee that regardless of order, you execute the same conditional block. Is it possible that your execution path is oscillating between the two else if blocks and therefore setting an incorrect position when it goes into the wrong block?

Ok, so assuming that those conditionals are all correct (and I just can't see that because I don't know what bodies the tags refer too, how the contact listener has been set up etc), then the oscillation must be caused by the physics simulation. What happens when you set the position of a CCSprite in Cocos2d with Box2D? Does setting the position of the sprite also change the position of the physics body, or are the two separate (with the sprite ordinarily rendered at the physics body's location)? It's been a while since I used Cocos2d, and it's changed significantly since the early days so I'm not sure on this without cracking open the latest version and taking a look. If changing the position of the CCSprite is also changing the position of the physics body, then this is a bad thing to do - all movement should be controlled by the physics simulation (so if you want to move a physics body around, you should be applying a force or impulse to it, not setting the position directly). Changing the position of the body outside of the physics will likely cause oscillations or incorrect impulses applied to a body when the next step of the physics simulation runs. If you want to change the position of a sprite without upsetting the physics simulation, then consider removing the physics body associated with that sprite from the physics simulation for all of the time you want to set the position directly, and then re-add it later if you want it to act under physics. This might mean deleting the physics body and recreating it later if there is no clean way to add/remove it from the physics world.

Another solution would be to create a joint between the bodies that you want to move together. A distance join or weld joint would be most appropriate. This will cause them to be connected, so when you move one around then the other will move too, maintaining the same distance between the two bodies. In this case, they will both be acting under the physics simulation.

Actually, just occurred to me that you might be changing the positions of the physics bodies of both the character and the CCSprite it is standing on top of. In which case, not only is this bad because the physics simulation is not in control, but your position calculations might also be causing the two bodies to overlap slightly, so when the physics simulation next runs it applies a big force to both of the bodies to force them apart, but then you change the positions back to be overlapping again so the next update results in a big force again.

If your changing the position of the CCSprite is not in fact changing the position of the physics body, and you are basically just setting the render position of the player to be in a different position to where the physics body is in the world, then you need to consider what is happening to the physics body. Is it rolling round, falling out of the world, oscillating between some other objects or something else? It's hard for me to judge what might be happening to it without knowing more about your game. But what's happening to it might be affecting your position calculations. Consider removing the physics object from the physics world if you want to move around the sprites without them being subject to the physics simulation. Having some kind of debug draw is invaluable when dealing with physics bodies, so you can see the exact shape and position of each body and what it is interacting with, regardless of any of your own sprites or other artwork. Not sure if the Cocos2D/Box2D combo provides this out of the box, but if not then strongly consider implementing it, with an easy way to toggle it on/off.

One more thing to consider are the two statements:

if (spriteA.position.x - spriteA.boundingBox.size.height*.5 <= spriteB.position.x + spriteB.boundingBox.size.height*.5)

and

if (spriteB.position.x - spriteB.boundingBox.size.height*.5 <= spriteA.position.x + spriteA.boundingBox.size.height*.5)

Have you tried removing those two statements, so that you set a new position every frame, instead of waiting for some error in the position to build up over a few frames? If you are moving one object around and you want the other to move around as if firmly attached, then you would want to update the position every frame. It could be that you are only passing this conditional say 10 frames out of every 30, so assuming 30 fps this would give a very visible jerky movement as opposed to the smooth movement you want.

Hope something mentioned above solves your issue, or at least gives you a better idea of where to start looking. Providing more info in your question - perhaps responding to some of the assumptions/unknowns that I've mentioned - should help us get to the bottom of your problems :)

EDIT 1

Thanks for filling us in with the extra details - that makes things much clearer. What you're doing makes total sense now that I know you are only doing collision detection with Box2D.

I would say that to fix your problem, in your for loop where you look at the contacts, where you detect that contact has been made, you should just set a flag instead of changing the position of the character at this point e.g. BOOL characterOnPlatform - set it to YES when contact is detected. Outside of that for loop (not necessarily in the same method though - it could be anywhere that is called every frame, and wherever makes most sense) you would check that flag and if characterOnPlatfom == YES, then set the position of the character to the position calculation you are currently doing in the contact listener for loop. i.e. don't do anything in the contact listener for loop except change some state that you will refer to elsewhere.

That change will make your character move exactly with the platform. If you still want the character to be able to jump off the platform, you'll need to set that flag back to NO if the player makes an input such as jump, otherwise their desired movement will be overridden by us forcing them into a position on the platform.

EDIT 2

It's not really possible to see exactly what is going on with your new code. For example, what is going on in your resetgravity method? How do you set hasCollided to NO (i.e. how do you detect the character is no longer on the platform)? Is your character still jittering in an up and down motion, or is the flickering you mention different from before?

Assuming you are still getting the up and down movement, I would still expect that this would be due to either the code you've posted is not being called every single frame, or it is fighting with some other positioning code (e.g. wherever you move the character around in response to the player's inputs, where you apply gravity etc).

Make sure that the code that keeps the character on the platform doesn't set the position too high and cause the character to be detected as not on the platform, as then you will end up in a vicious circle of:

  • character collides with platform
  • character position is set to be just above platform
  • character is no longer colliding with platform, so positioning code does not run to reposition him on the platform
  • character falls downwards due to the effects of gravity (or however you simulate this)
  • character collides with platform
  • ...

If that's the case, then you need to improve the detection of when the character is not colliding with the platform, or offset your positioning code so that the character remains intersecting with the platform slightly (not if this looks bad visually, you might want to offset the sprite slightly from the Box2D shape, so that the physics body intersects slightly with the platform but the character's feet appear to be exactly on the platform.

EDIT 3

It sounds like you've ruled out a lot of things that could be causing the jittering! I asked another game developer and he suggested that your problem could possibly be caused by the conversion from world space to screen space. So you could try logging out the world position of your character, and also the screen position where it is rendered (this might mean changing code in Cocos2D where this conversion is done). At the very least looking at the values logged out might help narrow down some values that are jittering/oscillating. You could also look for any rounding point errors that you might be introducing for example truncating floating point precision to integers. If your jittering looks like it is only in the region of + or - 1 pixel then it could well be worthing looking into world position vs screen position. Logging these values will be much easier to examine than stopping the debugger every frame.

Upvotes: 3

Related Questions