sprite not updating to the correct position - ios

I have written a code to display using CCLog the exact position of a sprite when a mousejoint moving it is released. Below is the Sprite.mm class and the ccTouchesEnded method (which is in the HelloWorldLayer.mm class). The sprite position is not updating, the output is constantly x: 0.00 and y: 0.00.
Sprite.mm:
-(id)addSprite:(CCLayer *)parentLayer
inWorld:(b2World *)world
{
PhysicsSprite *aSprite = [PhysicsSprite spriteWithFile:#"spriteIm.png"];
aSprite.tag = 1;
[parentLayer addChild:aSprite];
b2BodyDef spriteBodyDef;
spriteBodyDef.userData = aSprite;
spriteBodyDef.type = b2_dynamicBody;
CGSize s = [CCDirector sharedDirector].winSize;
spriteBodyDef.position = [Convert toMeters:ccp(s.width * 0.25,s.height-400)];
b2FixtureDef fixtureDef;
fixtureDef.density = 0.01;
b2CircleShape circleShape;
circleShape.m_radius = aSprite.contentSize.width/2 / PTM_RATIO;
fixtureDef.shape = &circleShape;
spriteBody = world->CreateBody( &spriteBodyDef );
spriteFixture = spriteBody->CreateFixture( &fixtureDef );
[aSprite setPhysicsBody:spriteBody];
return aSprite;
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *mySprite = (CCSprite *)b->GetUserData();
if (mySprite.tag == 1) {
CGPoint spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f, y:%0.2f", spritePosition.x, spritePosition.y);
}
}
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
Please help. I've been at it for a few days.

I finally managed to get it working. I guess the x:0.00 and y:0.00 were because I was taking the position of the sprite not the body. The sprite is parented to the body so it was giving its position inside it's parent, which is 0,0. This is how I understand it. Here are the changes I made to the ccTouchesEnded code.
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CCLOG(#"the sprite postion is x:%0.2f , y:%0.2f", mySprite.position.x, mySprite.position.y);
}
}
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}

Related

How can I keep track of how many times a sprite has been touched in cocos2d?

In my program, I have sprites fall from the top of the screen and the user taps each sprite to keep them from falling to the ground, which results in game over. These sprites are in the shape of stars.
My question is, how can I keep track of how many times each star has been tapped, so when a user taps the star 3 times, it disappears?
I currently have 2 different types of stars fall from the sky, and whenever they are added to the game, they are added into an array. Hopefully my code explains what I'm doing.
I'm new to objective C, so please feel free to let me know what else is wrong in my code... looking to get better!
#import "MainScene.h"
#import <CoreGraphics/CGGeometry.h>
#include <stdlib.h>
#import "Star.h"
#import "redStar.h"
#implementation MainScene {
CCSprite *_star;
CCSprite *_redStar;
CCPhysicsNode *_physicsNode;
CCNode *_ground;
CCNode *_leftWall;
CCNode *_rightWall;
CCNode *_ceiling;
CCLabelTTF *_scoreLabel;
NSInteger _points;
BOOL _gameOver;
CCButton *_restartButton;
NSInteger _taps;
NSMutableArray *_allStars;
NSInteger _rando;
}
- (void)didLoadFromCCB {
_rando = (arc4random_uniform(1000));
self.userInteractionEnabled = TRUE;
self.starList = [NSMutableArray array];
[self addNewStar];
// set collision txpe
_ground.physicsBody.collisionType = #"level";
// set this class as delegate
_physicsNode.collisionDelegate = self;
}
-(void)addNewStar {
float ranNum01 = (arc4random_uniform(200)+60);
float pos1 = ranNum01;
_star = (Star *)[CCBReader load:#"Star"];
_star.physicsBody.collisionGroup = #"starGroup";
_star.physicsBody.collisionType = #"star";
_star.position = ccp(pos1,500);
[_physicsNode addChild:_star];
[self.starList addObject:_star];
}
-(void)addRedStar {
float ranNum01 = (arc4random_uniform(200)+60);
float pos1 = ranNum01;
_redStar = (redStar *)[CCBReader load:#"redStar"];
_redStar.physicsBody.collisionGroup = #"starGroup";
_redStar.physicsBody.collisionType = #"star";
_redStar.position = ccp(pos1,500);
[_physicsNode addChild:_redStar];
[self.starList addObject:_redStar];
}
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair star:(CCNode *)star level:(CCNode *)level {
[self gameOver];
return TRUE;
}
- (void)update:(CCTime)delta {
// clamp velocity. -1*MAXFLOAT means no falling speed limit.
float yVelocity = clampf(_star.physicsBody.velocity.y, -1 * MAXFLOAT, 1000.f);
_star.physicsBody.velocity = ccp(0, yVelocity);
}
- (void)starDropper
{
if (_taps == 0)
{
return;
}
if ((_taps + (_rando)) % 5 == 0)
{
[self addNewStar];
}
if ((_taps + (_rando)) % 8 == 0)
{
[self addRedStar];
}
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
float ranNum1 = (arc4random_uniform(100));
float ranNum2 = (arc4random_uniform(100));
float sideForce = ranNum1 - ranNum2;
self._tappedStar = NO;
if (!_gameOver) {
for (_star in self.starList) {
CGPoint touchLocation = [touch locationInNode:_physicsNode];
if(CGRectContainsPoint([_star boundingBox], touchLocation)) {
[_star.physicsBody applyImpulse:ccp(sideForce, 2500.f)];
[_star.physicsBody applyAngularImpulse:2500.f];
self._tappedStar = YES;
_taps++;
_points++;
_scoreLabel.string = [NSString stringWithFormat:#"%d", _points];
}
}
if (self._tappedStar == NO)
{
return;
}
if (self._tappedStar == YES)
{
[self starDropper];
}
}
}
- (void)restart {
CCScene *scene = [CCBReader loadAsScene:#"MainScene"];
[[CCDirector sharedDirector] replaceScene:scene];
}
- (void)gameOver {
if (!_gameOver) {
_gameOver = TRUE;
_restartButton.visible = TRUE;
_star.rotation = 90.f;
_star.physicsBody.allowsRotation = FALSE;
[_star stopAllActions];
CCActionMoveBy *moveBy = [CCActionMoveBy actionWithDuration:0.2f position:ccp(-2, 2)];
CCActionInterval *reverseMovement = [moveBy reverse];
CCActionSequence *shakeSequence = [CCActionSequence actionWithArray:#[moveBy, reverseMovement]];
CCActionEaseBounce *bounce = [CCActionEaseBounce actionWithAction:shakeSequence];
[self runAction:bounce];
}
}
#end
UPDATE: I think I'm close..this is what I've got, but I must be missing something because the stars still won't disappear...ALSO - "world" is an undeclared identifier - not sure how to identify
#implementation MainScene {
CCSprite *touchedStar;
CCSprite *_star;
CCSprite *_redStar;
CCSprite *oneTappedStar;
CCSprite *twoTappedStar;
CCSprite *threeTappedStar;
CCPhysicsNode *_physicsNode;
CCLabelTTF *_scoreLabel;
NSInteger _points;
BOOL _gameOver;
CCButton *_restartButton;
NSInteger _rando;
CCActionFadeIn *fadeIn;
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
float ranNum1 = (arc4random_uniform(100));
float ranNum2 = (arc4random_uniform(100));
float sideForce = ranNum1 - ranNum2;
self._tappedStar = NO;
if (!_gameOver) {
for (_star in self.starList) {
CGPoint touchLocation = [touch locationInNode:_physicsNode];
if(CGRectContainsPoint([_star boundingBox], touchLocation))
{
[_star.physicsBody applyImpulse:ccp(sideForce, 2500.f)];
[_star.physicsBody applyAngularImpulse:2500.f];
self._tappedStar = YES;
_taps++;
_points++;
_scoreLabel.string = [NSString stringWithFormat:#"%d", _points];
// starTaps++;
if (touchedStar == threeTappedStar) {
[self removeChild:touchedStar];
world->DestroyBody(touchedStar.physicsBody);
}
//3
if (touchedStar == twoTappedStar) {
touchedStar = threeTappedStar;
}
//2
if (touchedStar == oneTappedStar) {
touchedStar = twoTappedStar;
}
//1
if (touchedStar == _star) {
touchedStar = oneTappedStar;
}
}
}
if (self._tappedStar == NO)
{
return;
}
if (self._tappedStar == YES)
{
[self starDropper];
}
}
}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
float ranNum1 = (arc4random_uniform(100));
float ranNum2 = (arc4random_uniform(100));
float sideForce = ranNum1 - ranNum2;
CCSprite *starTouched;
if (!_gameOver) {
for (starTouched in starList) {
CGPoint touchLocation = [touch locationInNode:_physicsNode];
if(CGRectContainsPoint([starTouched boundingBox], touchLocation)) {
[starTouched.physicsBody applyImpulse:ccp(sideForce, 2500.f)];
[starTouched.physicsBody applyAngularImpulse:2500.f];
_taps++;
_points++;
_scoreLabel.string = [NSString stringWithFormat:#"%ld", (long)_points];
if (starTouched.scale == 1.000002) {
[_physicsNode removeChild:starTouched];
CCLOG(#"DESTROY");
}
if (starTouched.scale == 1.000001) {
starTouched.scale = 1.000002;
CCLOG(#"2");
}
// FOR DIFFERENT TYPE OF STARS
//star
if (starTouched == _star) {
starTouched.scale = 1.000001;
CCLOG(#"1");
}
//redStar
if (starTouched == _redStar) {
starTouched.scale = 1.000001;
CCLOG(#"1");
}
}
}
}
}

"How To Make a Tile-Based Game with Cocos2D 2.X" Make this tutorial with cocos2d V3

I have a small problem. In this tutorial How To Make a Tile-Based Game with Cocos2D 2.X used cocos2d V2.0, I wanna make this in cocos2d V3.0. So, it doesn't work! Thanks! (I don't speak english)
I think problem in this line - self.position = viewPoint;
#property (strong) CCTiledMap *tileMap;
#property (strong) CCTiledMapLayer *background;
#property (strong) CCSprite *player;
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
self.tileMap = [CCTiledMap tiledMapWithFile:#"TileMap.tmx"];
self.background = [_tileMap layerNamed:#"Background"];
[self addChild:_tileMap z:-1];
CCTiledMapObjectGroup *objectGroup = [_tileMap objectGroupNamed:#"Objects"];
NSAssert(objectGroup != nil, #"tile map has no objects object layer");
NSDictionary *spawnPoint = [objectGroup objectNamed:#"SpawnPoint"];
int x = [spawnPoint[#"x"] integerValue];
int y = [spawnPoint[#"y"] integerValue];
_player = [CCSprite spriteWithImageNamed:#"Player.png"];
_player.position = ccp(x,y);
[self addChild:_player];
[self setViewPointCenter:_player.position];
// done
return self;
}
- (void)setViewPointCenter:(CGPoint) position {
CGSize winSize = [CCDirector sharedDirector].viewSize;
int x = MAX(position.x, winSize.width/2);
int y = MAX(position.y, winSize.height/2);
x = MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width) - winSize.width / 2);
y = MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height) - winSize.height/2);
CGPoint actualPosition = ccp(x, y);
CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
self.position = viewPoint;
}
-(void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView:touch.view];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = _player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
if ( abs(diff.x) > abs(diff.y) ) {
if (diff.x > 0) {
playerPos.x += _tileMap.tileSize.width;
} else {
playerPos.x -= _tileMap.tileSize.width;
}
} else {
if (diff.y > 0) {
playerPos.y += _tileMap.tileSize.height;
} else {
playerPos.y -= _tileMap.tileSize.height;
}
}
CCLOG(#"playerPos %#",CGPointCreateDictionaryRepresentation(playerPos));
// safety check on the bounds of the map
if (playerPos.x <= (_tileMap.mapSize.width * _tileMap.tileSize.width) &&
playerPos.y <= (_tileMap.mapSize.height * _tileMap.tileSize.height) &&
playerPos.y >= 0 &&
playerPos.x >= 0 )
{
[self setPlayerPosition:playerPos];
}
[self setViewPointCenter:_player.position];
NSLog(#"%#", NSStringFromCGPoint(touchLocation));
}
-(void)setPlayerPosition:(CGPoint)position {
_player.position = position;
}
The problem is that the area of the user interaction is by default bound to the contentSize of the scene (screen size).
When calling your setViewPointCenter method, you are moving the scene position out of this area where the touch event is not handled.
You have to extend this area of the contentSize of the tile map like that :
[self setContentSize:[_tileMap contentSize]];
self.userInteractionEnabled = YES;

How to continuously check collisions in Tiled tilemaps during a CCMoveTo

Hello good people of stack overflow
I stand before you today with a (not very great) cocos2d issue.
How can I check collisions WHILE in an action, instead of checking the target tile?
I am working with a tilemap that has a meta layer where there are collidable tiles. The following code works when I click the collidable tile, but not when I click beyond it, if that happens he will just walk straight through.
Currently, I detect collisions with this code:
-(void)setPlayerPosition:(CGPoint)position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = properties[#"Collidable"];
if (collision && [collision isEqualToString:#"True"]) {
return;
}
}
}
float speed = 10;
CGPoint currentPlayerPos = _player.position;
CGPoint newPlayerPos = position;
double PlayerPosDifference = ccpDistance(currentPlayerPos, newPlayerPos) / 24;
int timeToMoveToNewPostition = PlayerPosDifference / speed;
id moveTo = [CCMoveTo actionWithDuration:timeToMoveToNewPostition position:position];
[_player runAction:moveTo];
//_player.position = position;
}
By the way, this gets called from this method:
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
[_player stopAllActions];
CGPoint touchLocation = [touch locationInView:touch.view];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = _player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
int diffx = diff.x;
int diffy = diff.y;
int adiffx = diffx / 24;
int adiffy = diffy / 24; //Porque my tile size is 24
if ( abs(diff.x) > abs(diff.y) ) {
if (diff.x > 0) {
playerPos.x = playerPos.x + adiffx * 24;
} else {
playerPos.x = playerPos.x + adiffx * 24;
}
} else {
if (diff.y > 0) {
playerPos.y = playerPos.y + adiffy * 24;
}else{
playerPos.y = playerPos.y + adiffy * 24;
}
}
[self setPlayerPosition:playerPos];
}
I tried
[self schedule:#selector(setPlayerPosition:) interval:0.5];
Without any luck, that just instantly crashed the app at this
NSAssert( pos.x < _layerSize.width && pos.y < _layerSize.height && pos.x >=0 && pos.y >=0, #"TMXLayer: invalid position");
And i can't really blame it for crashing there.
How do I constantly check for collisions with the collidable meta tiles while a CCMoveTo is running?
In your init, schedule a collision detecting method:
[self schedule:#selector(checkCollisions:)];
Then define it as follows:
-(void)checkCollisions:(ccTime)dt
{
CGPoint currentPlayerPos = _player.position;
CGPoint tileCoord = [self tileCoordForPosition:currentPlayerPos];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid)
{
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties)
{
NSString *collision = properties[#"Collidable"];
if (collision && [collision isEqualToString:#"True"])
{
[_player stopAllActions];
return;
}
}
}
}
I using a similar meta layer. I personally skipped using MoveTo, and created my own update code that both moves the sprite and does collision detection.
I figure out what the potential new position would be, then I find out what tile position is in all four corners of the sprite at that new position, then I cycle through all tiles that the sprite would be on and if any are collidable, I stop the sprite.
I actually go a step further and check if moving only the x, or only the y changes the results, then move to that position instead if it does.

tracking a sprite body position as it moves

This might sound quite straightforward. I want to keep track of a sprite body position only AFTER it has been moved by a mouseJoint so I can limit it's movement by comparing it's position (at any given time after mouseJoint is released) with a given position. Please help.
UPDATED
Here's what I did. I made a method that returns the sprite's position, which I called in the ccTouchesEnded method:
- (CGPoint)spritePositionRelease {
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f , y:%0.2f", spritePosition.x, spritePosition.y);
return spritePosition;
}
}
}
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
[self spritePositionRelease];
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
In the tick method I added the following code:
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CGPoint spriteCurrentPosition = mySprite.position;
if ( spritePosition.x != spriteCurrentPosition.x &&
spritePosition.y == spriteCurrentPosition.y) {
CCLOG(#"the sprite limit for y is y:%0.2f has been reached", spriteCurrentPosition.y);
}
}
}
}
I initialized the spritePosition in the HelloWorldLayer.h class. I know I've done something wrong. I don't think the spritePosition I am accessing in the tick method has the same value as the spritePosition in the ccTouchesEnded method, hence the condition in the tick method never gets satisfied. I am not sure how to get this corrected. Please help
You can use ccpdistance(X2 , X1); to find the distance between two points. And from that distance you can limit the range that sprite can move.

destroying a weldjoint when a sprite reaches a specific position

I have created a weldJoint between two sprites in my code (see update method below) and I also created a method that returns the exact position of a sprite when a mouseJoint is released. I want to compare the current position of the sprite to the spritePositionRelease value and destroy the weldJoint if the y values are the same and x values are different. Please help.
spritePositionRelease:
- (CGPoint)spritePositionRelease {
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CGPoint spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f , y:%0.2f", spritePosition.x, spritePosition.y);
return spritePosition;
}
}
}
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
[self spritePositionRelease];
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
update:
-(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);
// using the iterator pos over the set
std::set<BodyPair *>::iterator pos;
for(pos = bodiesForJoints.begin(); pos != bodiesForJoints.end(); ++pos)
{
b2WeldJoint *weldJoint;
b2WeldJointDef weldJointDef;
BodyPair *bodyPair = *pos;
b2Body *bodyA = bodyPair->bodyA;
b2Body *bodyB = bodyPair->bodyB;
weldJointDef.Initialize(bodyA,
bodyB,
bodyA->GetWorldCenter());
weldJointDef.collideConnected = false;
weldJoint = (b2WeldJoint*) world->CreateJoint(&weldJointDef);
// Free the structure we allocated earlier.
free(bodyPair);
// Remove the entry from the set.
bodiesForJoints.erase(pos);
}
}
Are you sure that mouseJoint->GetBodyB() will always return the right body? Maybe you should check mouseJoint->GetBodyA() to?
Anyway your check would be quite simple:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
CGPoint releasePoint = [self spritePositionRelease];
CGPoint touchPoint = [[touches anyObject] locationInView:[[touches anyObject] view]];
if((releasePoint.y==touchPoint.y) &&(releasePoint.x!=touchPoint.x))
{
//Destroy weld joint
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}

Resources