title: Cocos 2D - Changing a Game Characters state from another class
body: <p>I am learning Cocos2D from the Ray Wenderlich book in my spare time but also making changes to it if it does not explain how to do something that I could see myself needing in the future. I am currently stuck because of my very limited knowledge.</p>
  
  <p>I wish to use the GamePlayLayer Class to randomly change the character state of the TBT class.</p>
  
  <p>I have checked each State and they all work (checked by making them the spawn state) so the problem is most likely in the gamePlayLayer.m file where I have attempted to make my own method.</p>
  
  <p>I seem to be able to get it to change its state to kStateThrowing but it changes straight back (in 0.3 of a second!) to kStateIdle before the animation plays and has the following output...</p>
  
  <p><code>2013-11-21 13:45:07.938 Spaceviking[981:12c03] CHANGING STATE TBT!!!!!!</code></p>
  
  <p><code>2013-11-21 13:45:07.939 Spaceviking[981:12c03] TBT-&gt;Changing State to throwing</code></p>
  
  <p><code>2013-11-21 13:45:07.967 Spaceviking[981:12c03] TBT Going to Idle</code></p>
  
  <p><code>2013-11-21 13:45:07.970 Spaceviking[981:12c03] TBT-&gt;changing state to idle</code></p>
  
  <p>Any help with this would be greatly appreciated. </p>
  
  <p>Code listed below...</p>
  
  <pre><code>//  CommonProtocols.h
  
  #ifndef SpaceViking_CommonProtocols_h
  #define SpaceViking_CommonProtocols_h
  
  typedef enum {
      kDirectionLeft,
      kDirectionRight
  }
  PhaserDirection;
  
  typedef enum {
      kStatespawning,
      kStateIdle,
      kStateWalking,
      kStateIdleTilt,
      kStatebackTilting,
      kStateforwardTilting,
      kStateAttacking,
      kStateTakingDamage,
      kStateThrowing,
      kStateLosingALife,
      kStateDead,
      kStateTravelling,
      kStateRotating,
      kStatetest
  
  }
  CharacterStates; //1
  
  typedef enum {
      kObjectTypeNone,
      kPowerUpTypeHealth,
      kPowerTypeMallet,
      kEnemyType1BT,
      kEnemyType2BT,
      kEnemyType3BT,
      kEnemyTypePhaser,
      kVikingType,
  
  } GameObjectType;
  
  
  @protocol GamePlayLayerDelegate
  
  -(void)createObjectOfType:(GameObjectType)objectType
  withHealth:(int)initialHealth
  atLocation:(CGPoint)spawnLocation
                 withZValue:(int)ZValue;
  
  -(void)createPhaserWithDirection: (PhaserDirection)phaserDirection
                       andPosition:(CGPoint)spawnPosition;
  
  
  #endif
  </code></pre>
  
  <hr>
  
  <pre><code>//  GamePlayLayer.h
  
  #import &lt;Foundation/Foundation.h&gt;
  #import "cocos2d.h"
  #import "CCLayer.h"
  #import "SneakyJoystick.h"
  #import "SneakyButton.h"
  #import "SneakyButtonSkinnedBase.h"
  #import "SneakyJoystickSkinnedBase.h"
  #import "Constants.h"
  #import "CommonProtocols.h"
  #import "TBT.h"
  
  @interface GamePlayLayer : CCLayer &lt;GamePlayLayerDelegate&gt; {
      CCSprite *vikingSprite;
      SneakyJoystick *leftJoystick;
      SneakyButton *jumpButton;
      SneakyButton *attackButton;
      CCSpriteBatchNode *sceneSpriteBatchNode;
  }
  @end
  
  ------------------------------------------------
  
  //  GamePlayLayer.m
  #import "GamePlayLayer.h"
  
  @implementation GamePlayLayer
  -(void) dealloc {
      [leftJoystick release];
      [jumpButton release];
      [attackButton release];
      [super dealloc];
      }
  
      --DELETED BELOW 2 METHODS AS NOT NEEDED FOR THIS QUESTION--
  -(void)initJoystickAndButtons {
      }
  -(void)applyJoystick:(SneakyJoystick *)aJoystick toNode: (CCNode *)tempNode forTimeDelta:(float)deltaTime
  {  
      }
  
  -(void) update:(ccTime)deltaTime {
      CCArray *listOfGameObjects =
      [sceneSpriteBatchNode children];                     
      for (GameCharacter *tempChar in listOfGameObjects) {         
          [tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];                         
      }
  }
  -(void) createObjectOfType: (GameObjectType)objectType
                  withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
      if (objectType == kEnemyType1BT) {
          CCLOG(@"creating 1BT");
          TBT *tBT = [[TBT alloc] initWithSpriteFrameName:@"BT_anim_1.png"];
          [tBT setCharacterHealth:initialHealth];
          [tBT setPosition:spawnLocation];
          [sceneSpriteBatchNode addChild:tBT
                                       z:ZValue
                                     tag:k1BTTagValue];
          [tBT release]; 
      }
  }
  
  --BELOW IS THE METHOD I HAVE CREATED TO CHANGE THE STATE--
  
  -(void)changeState:(CharacterStates)newState {
      [self stopAllActions];
  
      int x =  (arc4random() % 3);
  
      TBT *tBT = [[TBT alloc]
                    initWithSpriteFrame:[[CCSpriteFrameCache
                                          sharedSpriteFrameCache]
                                         spriteFrameByName:@"BT_anim_1.png"]];
      if (x&gt;0) {
          CCLOG(@"CHANGING STATE TBT!!!!!!");
  
          [tBT changeState:kStateThrowing];
  
          [tBT setDelegate:self];
          [tBT release];    
      }   
  }
  
  -(id)init {
      self = [super init];
  
      if (self !=nil) {
          CGSize screenSize = [CCDirector sharedDirector]. winSize;
          self.TouchEnabled = YES;
          srandom(time(NULL)); 
  
          if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
              [[CCSpriteFrameCache sharedSpriteFrameCache]
               addSpriteFramesWithFile:@"scene1atlas.plist"];          // 1
              sceneSpriteBatchNode =
              [CCSpriteBatchNode batchNodeWithFile:@"scene1atlas.png"]; // 2
          } else {
              [[CCSpriteFrameCache sharedSpriteFrameCache]
               addSpriteFramesWithFile:@"scene1atlasiPhone.plist"];          // 1
              sceneSpriteBatchNode =
              [CCSpriteBatchNode batchNodeWithFile:@"scene1atlasiPhone.png"];
          }
          [self addChild:sceneSpriteBatchNode z:0];                
          [self initJoystickAndButtons];                           
          BC *viking = [[BC alloc]
                            initWithSpriteFrame:[[CCSpriteFrameCache
                                                  sharedSpriteFrameCache]
                                                 spriteFrameByName:@"BCmoving_anim_1.png"]];            
          [viking setJumpButton:jumpButton];
          [viking setAttackButton:attackButton];
          [viking setPosition:ccp(screenSize.width * 0.19f,
                                  screenSize.height * 0.19f)];
          [viking setCharacterHealth:3];
  
          [sceneSpriteBatchNode
           addChild:viking
           z:kVikingSpriteZValue
           tag:kVikingSpriteTagValue];          
  
  
  
          [self createObjectOfType:kEnemyType1BT withHealth:3 atLocation:ccp(screenSize.width * 0.0439f, screenSize.height * 0.822f) withZValue:10];
  
         ---THE BELOW SELECTOR IS WHAT I HAVE USED TO DO A TIMED STATE CHANGE (METHOD ABOVE)--
  
         [self schedule:@selector(changeState:) interval:1.0f];
  
          [self scheduleUpdate];
      }
      return self;   
  }
  @end   
  </code></pre>
  
  <hr>
  
  <pre><code>//  TBT.h
  
  #import &lt;Foundation/Foundation.h&gt;
  #import "GameCharacter.h"
  
  @interface TBT : GameCharacter {   
      //This is for the required animations
      CCAnimation *tiltingAnim;
      CCAnimation *transmittingAnim;
      CCAnimation *loseLifeAnim;
      CCAnimation *throwingAnim;
      CCAnimation *afterThrowingAnim;    
      CCAnimation *shootPhaserAnim;    
  
      GameCharacter *vikingCharacter;
      id &lt;GamePlayLayerDelegate&gt; delegate;
  
  }
  @property (nonatomic,assign) id &lt;GamePlayLayerDelegate&gt; delegate;
  @property (nonatomic, retain) CCAnimation *tiltingAnim;
  @property (nonatomic, retain) CCAnimation *transmittingAnim;
  @property (nonatomic, retain) CCAnimation *loseLifeAnim;
  @property (nonatomic, retain) CCAnimation *throwingAnim;
  @property (nonatomic,retain) CCAnimation *afterThrowingAnim;
  @property (nonatomic,retain) CCAnimation *shootPhaserAnim;
  
  -(void)initAnimations;
  -(void)changeState:(CharacterStates)newState;
  @end
  
  
  //  TBT.m
  #import "TBT.h"
  
  @implementation TBT
  
  @synthesize delegate;
  @synthesize tiltingAnim;
  @synthesize transmittingAnim;
  @synthesize loseLifeAnim;
  @synthesize throwingAnim;
  @synthesize afterThrowingAnim;
  @synthesize shootPhaserAnim;
  
  -(void) dealloc {
      delegate = nil;
  
      [tiltingAnim release];
      [transmittingAnim release];
      [loseLifeAnim release];
      [throwingAnim release];
      [afterThrowingAnim release];
      [shootPhaserAnim release];
  
      [super dealloc];
  }
  --BELOW METHOD NOT NEEDED FOR THIS QUESTION
  -(void)shootPhaser {}
  
  -(CGRect)TBTboundingBox {
      CGRect tbtBoundingBox = [self boundingBox];
      float xOffset;
      float xCropAmount = tbtBoundingBox.size.width * 0.5482f;
      float yCropAmount = tbtBoundingBox.size.height * 0.1f;
  
      tbtBoundingBox =
      CGRectMake(tbtBoundingBox.origin.x + xOffset,
                 tbtBoundingBox.origin.y,
                 tbtBoundingBox.size.width - xCropAmount,
                 tbtBoundingBox.size.height - yCropAmount);
  
      return tbtBoundingBox;
  
  }
  
  -(void)changeState:(CharacterStates)newState {
      [self stopAllActions];
      id action = nil;
  
      [self setCharacterState:newState];
  
      switch (newState) {
          case kStatespawning:
              CCLOG(@"TBT-&gt;Changing State to Spwaning");
              [self setDisplayFrame:
               [[CCSpriteFrameCache sharedSpriteFrameCache]
                spriteFrameByName:@"BT_anim_1.png"]];
              break;
  
  
          case kStateIdle:
              CCLOG(@"TBT-&gt;schaning state to idle");
              [self setDisplayFrame:
               [[CCSpriteFrameCache sharedSpriteFrameCache]
                spriteFrameByName:@"BT_anim_1.png"]];
  
              break;
  
          case kStateThrowing:
              CCLOG(@"TBT-&gt;Changing State to throwing");
              action = [CCSequence actions:
                        [CCAnimate actionWithAnimation:throwingAnim
                                  restoreOriginalFrame:NO],
                        [CCDelayTime actionWithDuration:1.0f],
                        //[CCAnimate actionWithAnimation:shootPhaserAnim
                        //          restoreOriginalFrame:NO],
                        [CCCallFunc actionWithTarget:self
                                            selector:@selector(shootPhaser)],
                        [CCAnimate actionWithAnimation:afterThrowingAnim
                                  restoreOriginalFrame:NO],
                        [CCDelayTime actionWithDuration:0.1f],
                        nil];
  
              break;
  
          case kStateLosingALife:
              CCLOG(@"TBT-&gt;Losing a life");
  
              break;
  
              case kStateDead:
              CCLOG(@"TBT-&gt;changing state to dead");
              action = [CCAnimate actionWithAnimation:loseLifeAnim];
  
              break;
  
          default:
              CCLOG(@"unhandled state %d in TBT", newState);
              break;
      }
      if (action !=nil) {
          [self runAction:action];
          }
  }
  
  -(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
      if (characterState == kStateDead)
          return;
      vikingCharacter =
      (GameCharacter*)[[self parent]
                       getChildByTag:kVikingSpriteTagValue];
      CGRect vikingBoudingBox =
      [vikingCharacter adjustedBoundingBox];
      CharacterStates vikingState = [vikingCharacter characterState];
      if ((vikingState == kStateAttacking) &amp;&amp; (CGRectIntersectsRect ([self adjustedBoundingBox], vikingBoudingBox))) {
          if (characterState != kStateTakingDamage) {
              [self changeState:kStateTakingDamage];
              return;
          }
      }
      if ((([self numberOfRunningActions] == 0) &amp;&amp; (characterState != kStateDead)) ) {
          CCLOG(@"TBT Going to Idle");
          [self changeState:kStateIdle];
          return;
      }
  
  }
  
  
  -(void)initAnimations {  
      [self setTransmittingAnim:[self loadPlistForAnimationWithName:@"transmittingAnim" andClassName:NSStringFromClass([self class])]];
  
      [self setThrowingAnim:[self loadPlistForAnimationWithName:@"throwingAnim" andClassName:NSStringFromClass([self class])]];
  
      [self setAfterThrowingAnim:[self loadPlistForAnimationWithName:@"afterThrowingAnim" andClassName:NSStringFromClass([self class])]];
  }
  
  -(id) initWithSpriteFrameName:(NSString*)frameName{
      if ((self=[super init])) {
          if ((self = [super initWithSpriteFrameName:frameName])) {
  
              CCLOG(@"### TBT initialized");
              [self initAnimations];                                   
              characterHealth = 3.0f;                               
              gameObjectType = kEnemyType1BT;                    
              [self changeState:kStatespawning];                       
              }
      }
      return self;  
  }
  @end
  </code></pre>