strange problem playing cocos2d animation from another class? - objective-c

strange thing. if someone could please,please help me. its 3 days, and i think i am going to be fired :(
i have cocos2d class with animation function,and another xcode class.
if i call from the cocos2d class init function to animation function, the animation is being played when app is starts.
if i call from another class to the cocos2d class-animation, so it does the init function and enter the animation,but i cant see it playing.
so the animation is working only if called from within the cocos class only.
WHY ?
this is how i call the animation :
ran=[[HelloWorld alloc] init];
[ran animation];
this is the animation:
-(void)animation
{
//[self removeChild:background cleanup:YES];
//[b_pic.parent removeChild:b_pic cleanup:YES];
//animation
CCSpriteBatchNode *danceSheet = [ CCSpriteBatchNode batchNodeWithFile:#"head.png"];
[self addChild:danceSheet];
CCSprite *danceSprite = [CCSprite spriteWithTexture:danceSheet.texture rect:CGRectMake(0, 0, 480, 320)];
[danceSheet addChild:danceSprite];
//danceSprite.anchorPoint=CGPointMake(0, 0);
CGSize s = [[CCDirector sharedDirector] winSize];
danceSprite.position = ccp(s.width/2,s.height/2);
CCAnimation *danceAnimation = [CCAnimation animation];
[danceAnimation setDelay:0.1f];
int frameCount = 0;
for (int y = 0; y < 4; y++)
{
NSLog(#"%#",animation);
for (int x = 0; x < 5; x++)
{
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:danceSheet.texture rect:CGRectMake(x*320,y*440,320,440)];
[danceAnimation addFrame:frame];
frameCount++;
if (frameCount == 25)
break;
}
}

Can you call the animation function twice within that same class...Try
[self animation];
and
[[CCScheduler sharedScheduler] scheduleSelector:#selector(animation) forTarget:self
interval:10 paused:NO];
and see if the animation gets called the second time.
If it doesn't which at this point I don't think it will, you should define all that you can in the init and then only run the pieces you need in the animation method (i.e. add the batchNode and Sprite in the init as ivars and reuse them in the animation).

solved , and i dont know why.
but if you want to call a cocos2d function from the outside, you have to do :
[(HelloWorld*)[[[CCDirector sharedDirector] runningScene] getChildByTag:42] HardwareEvent:DollPart];
and tag your layer like :
layer.tag=42;
on the scene method !
thats the only way it works great.

Related

SpriteKit - Rotate sprite towards point

I am working on a game where a bunch of enemies spawn outside the screen and move towards the center (where there's a space station). However, I need the spaceships to face in the direction of the center. Currently, I'm using this code:
- (void)rotateNode:(SKNode *)nodeA toFaceNode:(SKNode *)nodeB {
double angle = atan2(nodeB.position.y - nodeA.position.y, nodeB.position.x - nodeA.position.x);
if (nodeA.zRotation < 0) {
nodeA.zRotation = nodeA.zRotation + M_PI * 2;
}
[nodeA runAction:[SKAction rotateToAngle:angle duration:0]];
}
but it only gives this result:
How should I get the sprites to rotate correctly?
All suggestions are appreciated!
EDIT:
I am calling the code above in the init method for the Enemy sprite
-(id)initWithLevel:(int)level andSize:(CGSize)size{
if(self = [super initWithImageNamed:#"alien01"]){
//position and init stuff
[self setPosition:self withSeed:seed andSize:size];
SKNode *center = [[SKNode alloc] init];
center.position = CGPointMake(self.size.width/2, self.size.height/2);
[self rotateNode:self toFaceNode:center];
}
return self;
}
Your problem lies here:
SKNode *center = [[SKNode alloc] init];
center.position = CGPointMake(self.size.width/2, self.size.height/2);
[self rotateNode:self toFaceNode:center];
The center node's position is based on the size of the enemy sprite.
You need to move this part to the scene class. Call the method at the point you initialise the enemy node in the scene. I am assuming the name of the enemy class as EnemySprite and that of the space station node as spaceStation.
EnemySprite *enemy = [[EnemySprite alloc] initWithLevel:1 andSize:CGSizeMake(100, 100)]; //Assuming values as well
[self addChild:enemy];
[self rotateNode:enemy toFaceNode:spaceStation];
This should make it work.

Make CCSprite move towards other CCSprite

I know this has been asked a lot before, but I can't find the solution. I have a CCSprite on the screen, the player, that is steered with the accelerometer. Then on top of the screen other CCSprites are spawned every 2 seconds, the enemies. I want all the enemies to follow the player, if the player moves the player the enemies should change direction and go towards that CCSprite. This is my code this far:
- (void)spawnEnemies
{
monsterTxt = [[CCTexture2D alloc] initWithCGImage:[UIImage imageNamed:#"obj.png"].CGImage resolutionType:kCCResolutionUnknown];
monster = [CCSprite spriteWithTexture:monsterTxt];
...
//random spawn position etc.
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
[_monsters addObject:monster]; //adds the sprite to a mutable array
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
...
//determines new position and move sprite there
[monster stopAllActions];
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
}
Now when I start the game the sprites are going towards the player, but when the player moves the enemies doesn't update their destination, they just continue down and stops at the y-coordinate of the player. And after a while the app crashes. What am I doing wrong?
My guess is that the problem may be in that section of code that you did not post.
In v2.1 of Cocos2d, to receive accelerometer measurements in your layer:
Do this in your init or onEnterTransitionDidFinish call:
self.accelerometerEnabled = YES;
And overload the is method:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CCLOG(#"Acceleration (%f,%f,%f)",
acceleration.x,
acceleration.y,
acceleration.z);
// Do something meaningful...you probably want to send a notification to
// some other scene/layer/logic. It should cache that the acceleration value
// and then do something on an update(...) call with it. Then reset it so that
// it does not get used the next update cycle untila new value comes in (here).
}
It seems you have this...and you have indicated you are receiving them...so I'm a bit confused.
SO (SANITY CHECK TIME)...
I created an example project (here) which has a single player sprite being chased by multiple monster sprites. When the user touches the screen (I did not use the accelerometer), the player sprite changes location and the sprites "chase" him. I added some random offset to their target positions so they would not all cluster on top of the player.
Here is the code for (the most important parts of) the file, which is a modified version of the HellowWorldLayer that comes with a cocos2d v2.1 project (I created it from the template):
-(void)createPlayer
{
playerMoved = NO;
if(self.player != nil)
{
[self removeChild:player];
self.player = nil;
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
CCSprite* playerSprite = [CCSprite spriteWithFile:#"Icon.png"];
playerSprite.scale = 2.0;
playerSprite.position = ccp(scrSize.width/2,scrSize.height/2);
[self addChild:playerSprite];
self.player = playerSprite;
}
-(void)createMonsters
{
const int MAX_MONSTERS = 4;
if(self.monsters.count > 0)
{ // Get rid of them
for(CCSprite* sprite in monsters)
{
[self removeChild:sprite];
}
[self.monsters removeAllObjects];
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
for(int idx = 0; idx < MAX_MONSTERS; idx++)
{
CCSprite* sprite = [CCSprite spriteWithFile:#"Icon.png"];
float randomX = (arc4random()%100)/100.0;
float randomY = (arc4random()%100)/100.0;
sprite.scale = 1.0;
sprite.position = ccp(scrSize.width*randomX,scrSize.height*randomY);
[self addChild:sprite];
[monsters addObject:sprite];
}
}
-(void)update:(ccTime)delta
{
if(playerMoved)
{ // Modify all the actions on all the monsters.
CGPoint playerPos = player.position;
for(CCSprite* sprite in monsters)
{
float randomX = (1.0)*(arc4random()%50);
float randomY = (1.0)*(arc4random()%50);
CGPoint position = ccp(playerPos.x+randomX,playerPos.y+randomY);
[sprite stopAllActions];
[sprite runAction:[CCMoveTo actionWithDuration:3.0 position:position]];
}
playerMoved = NO;
}
}
I have a retained array of CCSprite* objects (monsters) and a CCSprite* for the player. I have a flag to tell me if the player has changed position (player moved). In the update loop, if the player has moved, stop all the actions on the monsters and update them.
On the screen it looks like this:
And then when I move the main sprite...
They follow it...
Was this helpful?

How can I get CCMenuItemSprite selector to call other layer void?

The below code works great when I'm calling the void in the same .m file but I want my selector to go to a 2nd layer in my scene for its 'MoveUpSelected' void (which contains my actions for the sprites movement). How can I do this?
HUDLayer.m Button code in my Layer to communicate with other layer
self.dpad = [CCSprite spriteWithFile:#"dpad.png"];
CCSprite *dpadSelectedSprite = [CCSprite spriteWithTexture:[dpad texture]];
dpadSelectedSprite.color = ccGRAY;
//float dpadHeight = flareSprite.texture.contentSize.height;
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:Level1 selector:#selector(MoveUpSelected)];
dpadButtons.position = CGPointMake(size.width / 2, 150);
[menu addChild:dpadButtons];
Level1.m Void in my 2nd layer waiting to be called by 1st layer button
- (void)MoveUpSelected {
int yPosition = self.Player.position.y;
yPosition += [self.Player texture].contentSize.height/2;
CGSize size = [[CCDirector sharedDirector] winSize];
if (yPosition >= (size.height - [self.Player texture].contentSize.height/2)) {
yPosition = (size.height - [self.Player texture].contentSize.height/2);
}
self.Player.position = CGPointMake(self.Player.position.x, yPosition);
}
I have a GameScene1.m holding both layers in separate files.
+(id) scene
{
CCScene *scene = [CCScene node];
HudLayer *HUD = [HudLayer node];
[scene addChild:HUD z:2];
Level1 *layer = [Level1 node];
[scene addChild:layer];
return scene;
}
Please explain with lines of code.
It is VERY hard to understand what do you want to do. As Stephen mentioned in his comment, "using a void" and "void action" phrases have no sense. Anyway, if you just want to call some instance's method on btn click, just set it as target of your menu item. In your case
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:self selector:#selector(MoveUpSelected)];
uses self as target. Change it to self.parent or any other instance and menu item will try to call selector on this target.
EDIT:
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:levelInstance selector:#selector(MoveUpSelected)];
I your edits you tried to use class object, not it's instance as target. So it cannot find static methods.

Cocos2d, moving a sprite to a touch location

myself and my friend have been told Stackoverflow is the place to ask questions regarding code. :)
We're pretty new to Objective-C, Xcode and Cocos2d and are trying to complete simple tasks to further our knowledge.
Looking at our code below, we have made an Ant Object and placed him on screen. We now want to use a touch location to move the ant to to location the user touches.
We're unsure what the correct way to go about it is. At the moment we're staying away from creating classes so have all the code in one place but we can't figure out how to get the ant that's on screen to go to the touch location.
Can anyone help?
//
// HelloWorldLayer.m
// Timer
//
// Created by Lion User on 06/06/2012.
// Copyright __MyCompanyName__ 2012. All rights reserved.
//
// Import the interfaces
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super initWithColor:ccc4(225, 225, 225, 255)])) {
// enable touches
self.isTouchEnabled=YES;
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// set time to zero
myTime = currentTime;
timeLabel = [CCLabelTTF labelWithString:#"00:00" fontName:#"Arial" fontSize:48];
timeLabel.position = CGPointMake(size.width / 2, size.height);
// set label color
timeLabel.color = ccBLACK;
// Adjust the label's anchorPoint's y position to make it align with the top.
timeLabel.anchorPoint = CGPointMake(0.5f, 1.0f);
// Add the time label
[self addChild:timeLabel];
// run create ant method
[self createAnt];
//update
[self schedule:#selector(update:)];
}
return self;
}
-(void)update:(ccTime)dt{
totalTime += dt;
currentTime = (int)totalTime;
if (myTime < currentTime)
{
myTime = currentTime;
[timeLabel setString:[NSString stringWithFormat:#"%02d:%02d", myTime/60, myTime%60]];
}
}
////* method for creating an ant and moving it*////
-(void)createAnt{
////*requirements for animation setup*////
// create cache object to store spritesheet in
CCSpriteFrameCache *cache=[CCSpriteFrameCache sharedSpriteFrameCache];
// add the sprite list to the cache object
[cache addSpriteFramesWithFile:#"antatlas.plist"];
// create frame array to store the frames in
NSMutableArray *framesArray=[NSMutableArray array];
//loop through each frame
for (int i=1; i<3; i++){
// increment the name to include all frames in sprite sheet
NSString *frameName=[NSString stringWithFormat:#"ant%d.png", i];
// create frame object set it to the cache object and add the frameNames to the cache
id frameObject=[cache spriteFrameByName:frameName];
// add the frame object into the array
[framesArray addObject:frameObject];
}
////* setup the actions for running the animation*////
// create animation object and pass the list of frames to it (it expects this as an array)
id animObject=[CCAnimation animationWithFrames:framesArray delay:0.05];
// setup action to run the animation do not return to frame 1
id animationAction=[CCAnimate actionWithAnimation:animObject restoreOriginalFrame:NO];
//loop the animation indefinitely
animationAction = [CCRepeatForever actionWithAction:animationAction];
// move ant action
id moveAnt=[CCMoveTo actionWithDuration:3 position:ccp(60, 160)];
// create sprite, set location and add to layer (starts with the name of the first frame in the animation
CCSprite *ant=[CCSprite spriteWithSpriteFrameName:#"ant1.png"];
ant.position=ccp(240, 160);
[self addChild:ant];
//run animation action
[ant runAction: animationAction];
// run move ant action
[ant runAction:moveAnt];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[touches anyObject];
CGPoint loc=[touch locationInView:[touch view]];
loc=[[CCDirector sharedDirector]convertToGL:loc];
NSLog(#"touch (%g,%g)",loc.x,loc.y);
// Move ant to point that was pressed
[ant runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:loc],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
[super dealloc];
}
#end
Seems like your ant is local to your createAnt method. So in the touchesBegan statement, it doesn't "see" the ant. Go over to your header file, and in the #interface declare the ant...
CCSprite *ant;
Then back in your createAnt statement, you can just write...
ant=[CCSprite spriteWithSpriteFrameName:#"ant1.png"];
Now any other method in the implementation ( .m ) file will know what you mean when you write "ant"
Hope it helps!

Animating an Array of UIViews

I have an array of UIViews which I animate with the following code:
for (int i = 0; i<(int)viewArray.count; i++) {
UIView *view = [viewArray objectAtIndex:i];
CALayer *layer = view.layer;
//animate the layer
}
My question is, is there any way to have a delay between every animation, so that, for example, it animates one UIView in the array and the next animation starts after 0.2 seconds or so? Any help would be greatly appreciated!
You may want to try the following
float timeDelay = 0.2
float duration = YOUR_DURATION
for (int i = 0; i<(int)viewArray.count; i++) {
UIView *view = [viewArray objectAtIndex:i];
//animate the layer
[UIView animateWithDuration:duration delay:(i+1)*timeDelay
options: {Check UIView Class Reference on developer.apple.com}
animations:^{
[view.layer fantastic animation here];
} completion^(BOOL finised){
if(finished){
//Leave blank if nothing, good practice to implement debuging here or post animation processes here (like removing a subview from a super)
}
}];
}
Keep in mind that if u send your program the the background it may break your animations so make sure u recall this method in ur app Delegates:
- (void)applicationWillEnterForeground:(UIApplication *)application;
Hope it helps
Use this guy:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
use the second parameter. Inside your loop, call that function, passing an every-increasing number to that parameter.