How do we enumerate and modify positions array of CCsprite inside Update method? - cocos2d-iphone

How do we enumerate and alter our object's position (contained in array) for each delta time?
I put some CCsprite objects inside array, then I displayed them in scene, but also I wanted to make them move with modifying update method, I failed on last part.
How do I get around this ?
#implementation GameScene
{
Hunter *_hunter;
Bird *_bird;
NSMutableArray *_arrayOfBirds;
}
-(void)update:(CCTime)delta{
CGSize viewSize = [CCDirector sharedDirector].viewSize;
float birdSpeed = 50;
for (Bird *birds in _arrayOfBirds) {
if (birds.position.x < 0) {
birds.flipX = YES;
}
if (birds.position.x > viewSize.width) {
birds.flipX = NO;
}
float distanceToMove = birdSpeed * delta;
float direction = birds.flipX ? 1 : -1;
float newX = birds.position.x + direction * distanceToMove;
float newY = birds.position.y;
birds.position = ccp(newX, newY);
}
}
-(void)addBird{
CGSize viewSize = [CCDirector sharedDirector].viewSize;
for (int i=0; i < 4; i++) {
_bird = [[Bird alloc]initWithBirdType:(i)];
_bird.position = ccp(viewSize.width * 0.5f + 30 * i , viewSize.height * 0.9f - 15* i);
[self addChild:_bird];
[_arrayOfBirds addObject:_bird];
}
}

You forgot to initialize your array
here (assuming ARC)
-(id) init {
if(self=[super init]) {
_arrayOfBirds = [[NSMutableArray alloc] init];
// the rest
}
return self;
}

Related

Cocos2d-iphone v3 - draw a line and convert it so I could use it for CCSprite

http://i.stack.imgur.com/cjZBq.png
Right I've looked and I can seem to find the answer.
I'm using Cocos2d-iphone V3.
What I want to achieve is the above.
I want to take the devices width or height and and draw two lines.
I want to take the drawn lines and use the for the CCProgress.
Question:
How do I take the drawn lines and convert them to CCSprite?
If you can give me the code it would be great, but if you could give me a link to any tutorial or documentation so I can learn how to do this, that would be even better.
Thank you.
#LearnCocos2d
So i'm doing this now but I get a very weird result;
CGSize winSize = [[CCDirector sharedDirector] viewSize];
_progressWidth = winSize.width *2;
//CGPoint position = ccp(0.3, 0.5);
//CCTime interval = 1.0/60.0;
NSMutableArray * tempProgressFg = [[NSMutableArray alloc] initWithCapacity:[Utilities getRows]+1];
NSMutableArray * tempProgressBg = [[NSMutableArray alloc] initWithCapacity:[Utilities getRows]+1];
CCTexture * bg = [_progressBg texture];
CCTexture * fg = [_progressFg texture];
for (int i = 0; i < [Utilities getRows]; i++)
{
if (i == 0)
{
_progressBg.scaleX = _progressWidth;
_progressFg.scaleX = _progressWidth;
_progressBg.position = ccp(0, i * TILE);
_progressFg.position = ccp(0, i * TILE);
[tempProgressFg addObject:_progressFg];
[tempProgressBg addObject:_progressBg];
}
else
{
CCSprite * tempBg = [CCSprite spriteWithTexture:bg];
CCSprite * tempFg = [CCSprite spriteWithTexture:fg];
tempBg.scaleY = 1;
tempFg.scaleY = 1;
tempBg.scaleX = _progressWidth;
tempFg.scaleX = _progressWidth;
tempBg.position = ccp(0, i * TILE);
tempFg.position = ccp(0, i * TILE);
[self addChild:tempBg];
[self addChild:tempFg];
[tempProgressBg addObject:tempBg];
[tempProgressFg addObject:tempFg];
}
}
The result that i get is in this picture:
http://imgur.com/1dMpiUc,V47d65X
is the _progressBg and Fg;
are the tempBg and Fg (multiples of them);
I want to have them all look like number 1.
Am I doing something wrong? is that the correct way to coppy the bar?

Adding CCSprite on Tiled tile properties

I'm trying to add a Sprite beside the player, but only when the Tile beside the player is NOT a Wall. I know that the Tiled tiles are working properly, as they do their job in this method:
CGPoint p = CGPointMake(tileCoord.x, tileCoord.y - 1);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
t = YES;
I am checking for Tiled on coordinate with these two methods:
-(BOOL)isProp:(NSString*)prop atTileCoord:(CGPoint)tileCoord forLayer:(CCTMXLayer *)layer {
if (![self isValidTileCoord:tileCoord]) return NO;
int gid = [layer tileGIDAt:tileCoord];
NSDictionary * properties = [_tileMap propertiesForGID:gid];
if (properties == nil) return NO;
return [properties objectForKey:prop] != nil;
}
-(BOOL)isWallAtTileCoord:(CGPoint)tileCoord {
return [self isProp:#"Wall" atTileCoord:tileCoord forLayer:_bgLayer];
}
(Thanks RayWenderlich)
And my code for adding the sprite is
CGPoint tileCoord = ccp(_player.position.x - 24 +60, player.position.y);
CGPoint cTileCoord = [self tileCoordForPosition:tileCoord];
NSLog(#" t: %#, c: %#",
CGPointCreateDictionaryRepresentation(tileCoord),
CGPointCreateDictionaryRepresentation(cTileCoord)
);
if (![self isWallAtTileCoord:cTileCoord])
{
NSLog(#"False");
circle1.position = ccp(_player.position.x - 24 +60, _player.position.y);
[self addChild:circle1];
}
else
{
NSLog(#"True");
}
What I want this to do is to only add the Circle1 sprite to the left of the player when there isn't a Wall tile there. The problem is that this code always detects false, no matter if there is a wall there or not. Do any of you guys understand why it does not detect the walls properly and how I can fix it?
Last year me also observed same problem in retina mode. It worked in non retina but not detected in retina mode.
So finally used custom dictionary to check edge tile. Integer comparison takes less CPU time than string comparison. So replaced it with enum.
Go for this approach only if none other simple way worked.
I used this code to find edge of the tile that set in meta property.
typedef enum GRID_TYPE{
kGrideType_Normal = 4001,
kGrideType_Collidable,
kGrideType_Collectable,
kGrideType_Sea,
kGrideType_Edge,
kGrideType_enemy,
kGrideType_gold,
}GridType;
//Init tilemap and grid type
tileMap.meta = [tileMap layerNamed:PP_TILE_META_LAYER];
[tileMap initTileAnimation];
//Somewhere in code
CGPoint point = [self getTileCoordForPosition:position];
GridType type = [self getTileType:point];
if(type == kGrideType_Edge)
{
//touched edge tile....
}
Functions:
-(GridType)getTileType:(CGPoint)pos
{
// not defined USE_COCOS2D_FOR_TILE_IDENTIFICATION
#ifdef USE_COCOS2D_FOR_TILE_IDENTIFICATION
GridType type = kGrideType_Normal;
CGPoint tileCoord = position;//[self tileCoordForPosition:position];
unsigned int tileGid = [self.meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [self propertiesForGID:tileGid];
if (properties)
{
NSString *collision = [properties valueForKey:TILE_PROPERTY_COLLIDABLE];
if (collision && [collision caseInsensitiveCompare:#"true"] == NSOrderedSame)
{
type = kGrideType_Collidable ;
}
NSString *collectable = [properties valueForKey:TILE_PROPERTY_COLLECTABLE];
if (collectable && [collectable caseInsensitiveCompare:#"true"] == NSOrderedSame) {
type = kGrideType_Coin ;
}
NSString *collectable = [properties valueForKey:TILE_PROPERTY_CHEST];
if (collectable && [collectable caseInsensitiveCompare:#"true"] == NSOrderedSame) {
type = kGrideType_Chest ;
}
}
}
return type;
#else
int type = [[mTileInfoDict objectForKey:[NSString stringWithFormat:#"TILE(%d,%d)", (int)pos.x,(int)pos.y]] intValue];
GridType gType = kGrideType_Normal;
if(type!=0)
gType = (GridType)type;
return (GridType)gType;
#endif
}
-(void)initTileAnimation
{
mTileInfoDict = [[NSMutableDictionary alloc] init];
//Parse all the tile in map
mBgLayer = [self layerNamed:PP_TILE_MAP_BG_LAYER];
int rowSize = self.mapSize.width ;
int colSize = self.mapSize.height ;
for(int x=0; x<rowSize; x++)
{
for (int y=0; y<colSize; y++)
{
CGPoint tileCord = ccp(x,y) ;
NSString *key = [NSString stringWithFormat:#"TILE(%d,%d)", (int)tileCord.x,(int)tileCord.y];
GridType tileType = kGrideType_Normal;
unsigned int tileGid = [self.meta tileGIDAt:tileCord];
if (tileGid)
{
NSDictionary *properties = [self propertiesForGID:tileGid];
if (properties)
{
/* Check Tile : IS SEA - TILE */
NSString *sea = [properties valueForKey:TILE_PROPERTY_SEA];
if (sea && [sea isEqualToString:#"true"])
{
tileType = kGrideType_Sea;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
/* Check Tile : IS Inky - TILE */
/* Check Tile : IS COLLECTABLE - COIN */
NSString *collectable = [properties valueForKey:TILE_PROPERTY_COLLECTABLE];
if (collectable && [collectable isEqualToString:#"true"])
{
tileType = kGrideType_Collectable;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
PPCoins *coinSprite = [PPCoins spriteWithSpriteFrameName:#"coin_0000.png"];
coinSprite.tag = kTagCoinSprite;
coinSprite.anchorPoint = ccp(0.5f,0.5f);
CCSprite *sprite = [mBgLayer tileAt:tileCord];
coinSprite.position = ccp(sprite.position.x+coinSprite.contentSize.width*0.5f, sprite.position.y+coinSprite.contentSize.height*0.5f) ;
[self addChild:coinSprite z:3 tag:kTagCoinSprite];
[coinSprite runAnimation];
{
coinSprite.key = key;
[mCoinDict setObject:coinSprite forKey:key];
}
continue;
}
/* Check Tile : IS COLLIDABLE - TILE */
NSString *collidable = [properties valueForKey:TILE_PROPERTY_COLLIDABLE];
if (collidable && [collidable isEqualToString:#"true"])
{
tileType = kGrideType_Collidable;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
/* Check Tile : IS Edge - TILE */
NSString *edge = [properties valueForKey:TILE_PROPERTY_EDGE];
if (edge && [edge isEqualToString:#"true"])
{
tileType = kGrideType_Edge;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
continue;
}
NSString *redTargetCoin = [properties valueForKey:TILE_PROPERTY_TARGET_COIN];
if (redTargetCoin && [redTargetCoin isEqualToString:#"true"])
{
tileType = kGrideType_TargetCoins;
[mTileInfoDict setObject:[NSNumber numberWithInt:tileType] forKey:key];
}
}
else
{
[mTileInfoDict setObject:[NSNumber numberWithInt:kGrideType_Normal] forKey:key];
}
}
}
}
}
- (CGPoint)getTileCoordForPosition:(CGPoint)position
{
// CGPoint nodeSpace1 = [self convertToNodeSpace:position];
// float x = floor(nodeSpace1.x / self.tileSize.width);
// float y = floor(self.mapSize.height - (nodeSpace1.y / self.tileSize.height));
//
// if( x >= TILE_IN_ROW)
// x = TILE_IN_ROW - 1;
//
// if( y >= TILE_IN_COL)
// y = TILE_IN_COL - 1;
//
// return ccp(x, y);
int maxTileCol = self.mapSize.height;// (_tileMap.contentSize.height)/TILE_SIZE;
int x = ( (position.x-self.position.x)/TILE_SIZE);
int y = maxTileCol - ( ((position.y)-self.position.y)/TILE_SIZE);
if( x >= TILE_IN_ROW)
x = TILE_IN_ROW - 1;
if( y >= TILE_IN_COL)
y = TILE_IN_COL - 1;
return ccp(x, y);
}
You should check if your tile map has the correct layer settings. I did a mistake with this once, it is easy to forget

Cocos2D - adding support for normalized position

I want cocos2d to use the nodes' position as a normalized factor when coordinates are less than 1, and otherwise when coordinates are greater than 1, I want cocos2d to treat coordinates as it would normaly do
For instace position (1,1) would locate the node at the top-right corner of the screen, while (.5,.5) would locate the node on the center of the screen.
I was thinking on rewriting the code just where cocos2d pass on the coordinates to the vertex buffer like the following pseudo code
if (Position.x <= 1)
BufferPosition.x = Screen.Width * Position.x;
else
BufferPosition.x = Position.x;
if (Position.y <= 1)
BufferPosition.y = Screen.Height * Position.y;
else
BufferPosition.y = Position.y;
So my question is where would you put this code so that cocos2d works that way with no problem, or what approach would you take istead if it's different from the shown above
Update
I changed every single call/assignment of positionInPixels_ in the entire cocos2d library for positionInPixels
This means that each time, the corresponding getter and setter is caled instead of the CCNode's positionInPixels_ member variable.
Then I changed setPosition, and setPositionInPixels in CCNode.m like this
setPosition
-(void) setPosition: (CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
position_ = newPosition;
if (position_.x <= 1.0) {
positionInPixels_.x = position_.x * winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.x = position_.x;
else
positionInPixels_.x = newPosition.x * CC_CONTENT_SCALE_FACTOR();
}
if (position_.y <= 1.0) {
positionInPixels_.y = position_.y * winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.y = position_.y;
else
positionInPixels_.y = newPosition.y * CC_CONTENT_SCALE_FACTOR();
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
setPositionInPixels
-(void) setPositionInPixels:(CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
positionInPixels_ = newPosition;
if (position_.x <= 1.0) {
position_.x = positionInPixels_.x / winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.x = positionInPixels_.x;
else
position_.x = newPosition.x * 1/CC_CONTENT_SCALE_FACTOR() / winSize.width;
}
if (position_.y <= 1.0) {
position_.y = positionInPixels_.y / winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.y = positionInPixels_.y;
else
position_.y = newPosition.y * 1/CC_CONTENT_SCALE_FACTOR() / winSize.height;
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
This is working so far, even when using normalized coordinates with CCActionInterval
So you really think that this variant is the right one? To change the whole framework instead of just adding one method to the CCNode's extension? I think you will see a lot of problem with positioning very soon.
Also it is not good, that (1.f, 1.f) is always have coordinates (screenWidth, screenHeight). You will have a lot of troubles with small nodes. IMHO if you need such positioning, it is much more appropriate to connect this "factor" coordinates to the node's parent size.
Just do restore cocos2d framework and add your own extension to your project. For example,
#interface CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor;
#end
#implementation CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor
{
assert(self.parent != nil);
// do your calculations relatieve to the parent's content size
}
#end

CCRotateBy actions are intermittently jumpy/jerky? Cocos2d

I have a subclass of cpCCSprite, which is just for using chipmunk spaceManager and my class looks like this:
#implementation Helmet
+(id) helmetWithGame:(Game*)game {
return [[[self alloc] helmetInit:game] autorelease];
}
- (id) helmetInit:(Game*)game {
cpShape *helmet_1;
cpShape *helmet_2;
cpShape *helmet_3;
cpShape *reference;
reference = [game.spaceManager addCircleAt:cpvzero mass:STATIC_MASS radius:2];
helmet_1 = [game.spaceManager addCircleToBody:reference->body radius:20 offset:cpv(-5, 2)];
helmet_2 = [game.spaceManager addCircleToBody:reference->body radius:8 offset:cpv(16, -14)];
helmet_3 = [game.spaceManager addCircleToBody:reference->body radius:8 offset:cpv(8, -14)];
reference->group =1;
helmet_1->group =1;
helmet_2->group =1;
helmet_3->group =1;
[self initWithFile:#"Helmet.png"];
[self setBody:reference->body];
self.spaceManager = game.spaceManager;
self.autoFreeShapeAndBody = YES;
gameScreenSize = game.contentSize;
return self;
}
- (void) generateAndShowOn:(Game *)game {
float startX = (float)((arc4random_uniform(101)) + 100);//returns number from 100 to 200 and casts it as a float
float startY;
int endXRange = (game.contentSize.width * .8) - (game.contentSize.width * .5);
float endX = (float)((arc4random_uniform(endXRange)) + (game.contentSize.width * .5));
float endY;
BOOL shouldStartTop;
if ((arc4random_uniform(101)) < 50) {//returns number from 0 to 100 and checks to see if it is less than 50. If it is, than the helmut starts at the top
shouldStartTop = YES;
startY = game.contentSize.height + (self.contentSize.height * .5);
endY = -self.contentSize.height;
}
else {
shouldStartTop = NO;
startY = -(self.contentSize.height * .5);
endY = game.contentSize.height + self.contentSize.height;
}
self.position = ccp(startX, startY);
[game addChild:self];
ccBezierConfig bezier;
bezier.controlPoint_1 = ccp (startX, startY);
bezier.controlPoint_2 = ccp (endX, endY);
bezier.endPosition = ccp (endX, endY);
id rotate =[CCRotateBy actionWithDuration:1.5f angle:360];
id curve = [CCBezierTo actionWithDuration:1.5f bezier:bezier];
id spawn = [CCSpawn actions:rotate, curve, nil];
[self runAction:spawn];
[self schedule:#selector(doneAnimating) interval:1.6];
}
- (void) doneAnimating{
if ([[self delegate]respondsToSelector:#selector(helmetPastBounds:)]) {
[[self delegate]helmetPastBounds:self];
}
}
- (void) dealloc {
CCLOG(#"%s", __PRETTY_FUNCTION__);
[super dealloc];
}
#end
So a new instance of this class is called every second, so that there can be more than one helmet on the screen at a time. Then I have another method that is called just slightly after the acctions have finished to remove the current helmet and deallocate it. But the problem I am having is every so often the actions are jumpy/jerky and that is with the FPS at 60. Do I need to do something different with the actions or what could be causing this?

Block of code running in iPhone simulator and not on iPhone 4? (COCOS2D)

I am using the ccTouchBegan and ccTouchEnded, and for some reason there is a block of code that runs perfectly on iPhone simulator and doesn't run at all on iPhone 4. This is what my methods look like:
-(BOOL)ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event {
firstTouch = [self convertTouchToNodeSpace:touch];
if (!self.currentFootball.footballHasEmbarked) {
//arrow.position = ccp(fPos.x, fPos.y);
float newAnc = (120 + (self.currentFootball.contentSize.width * .5f) + (arrow.contentSize.width * .5f)) / arrow.contentSize.width;
arrow.anchorPoint = ccp(-newAnc, .5);//self.currentFootball.position;
arrow.position = self.currentFootball.position;//ccp(fPos.x-120, fPos.y);
arrow.rotation = 180;
arrow.visible = YES;
arrow.scale = 0.5f;
//powerLevel.position = fPos;
powerLevel.position = self.currentFootball.position;
powerLevel.rotation = 180;
powerLevel.percentage = 0;
powerLevel.visible = YES;
outLine.position = self.currentFootball.position;
outLine.rotation = 180;
outLine.visible = YES;
CCProgressFromTo *powerBarGoUp = [CCProgressFromTo actionWithDuration:1.0f from:0.0f to:100.0f];
CCProgressFromTo *powerBarGoDown = [CCProgressFromTo actionWithDuration:1.0f from:100.0f to:0.0f];
id action = [CCRepeatForever actionWithAction:[CCSequence actions:powerBarGoUp, powerBarGoDown, nil]];
[powerLevel runAction:action];
return YES;
}
else {
return NO;
}
}
-(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event {
const float max = 100;
CGPoint endTouch = [self convertTouchToNodeSpace:touch];
if (endTouch.x > firstTouch.x) {
endTouch = ccp(firstTouch.x, endTouch.y);
//CCLOG(#"I ran");
}
arrow.visible = NO;
powerLevel.visible = NO;
outLine.visible = NO;
self.currentFootball.footballHasEmbarked = YES;
self.currentFootball.spiraling = YES;
if (self.currentFootball) {
[smgr addBody:self.currentFootball.body];
}
if (CGPointEqualToPoint(endTouch, firstTouch)) {
CCLOG(#" I have run");
float anchorPointDist = ccpLength(endTouch);
float distFactor = distFromFb + anchorPointDist;
projectionAnchorPoint = ccp(firstTouch.x + distFactor,firstTouch.y);
}
CGPoint diff = ccpSub(endTouch, projectionAnchorPoint);
float len = powerLevel.percentage;
CGPoint norm = ccpNormalize(diff);
if (len > max){
len = max;
}
[self.currentFootball applyImpulse:ccpMult(norm, (len * 300))];
pos = self.currentFootball.position.y;
[self schedule:#selector(newFootball)];
}
This is the block of code that will not run on my iPhone.
if (CGPointEqualToPoint(endTouch, firstTouch)) {
CCLOG(#" I have run");
float anchorPointDist = ccpLength(endTouch);
float distFactor = distFromFb + anchorPointDist;
projectionAnchorPoint = ccp(firstTouch.x + distFactor,firstTouch.y);
}
Am I not doing something right?
Print out the values for endTouch and firstTouch. They may vary by a very small amount on device, as it's harder to keep your finger in the same location as opposed to a mouse pointer. If that's the case, you may want to accept end touches within a range of the first touch.
Perhaps there is some slight difference in the points you are comparing, making the result of that comparison always false. Try using this method instead of CGPointEqualToPoint.
BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float variance);