본문 바로가기

프로그래밍/cocos2d

캐릭터에 인공지능 추가하기 (클래스 이용한 CCSprite 생성 및 각각의 애니메이션 적용)


1. 주인공의 위치를 확인한다.
2. 적과 주인공의 거리를 계산한다.
3. 공격할 수 있을 만큼 가까운 거리가 아니면 주인공 쪽으로 이동한다.
4. 공격할 수 있을 만큼 가까운 거리면 공격을 시도한다.


 작업할 파일은 다음과 같습니다.

(1) EnemySprite.h
(2) EnemySprite.m
(3) GameLayer.m 


//

//  EnemySprite.h

//  GameDemo

//

//  Created by Chang-Min Pak on 6/2/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


//#import <Foundation/Foundation.h>

#import "cocos2d.h"



@class GameLayer;


//@interface EnemySprite : NSObject {

@interface EnemySprite : CCSprite {

    GameLayer *gameLayer;

    CCAnimate *walkAnimate;

    CGFloat   walkingSpeed

}


@property(nonatomic, retain) CCAnimate *walkAnimate;


- (id) initWithSpriteFrameName:(NSString*)frameName position:(CGPoint)pos speed:(CGFloat)speed gameLayer:(GameLayer*)layer;


- (void) adjustPosition;

- (void) startWalking;


@end


(1) CCSprite 클래스를 상속받는 클래스를 정의합니다. 




//

//  EnemySprite.m

//  GameDemo

//

//  Created by Chang-Min Pak on 6/2/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "EnemySprite.h"

#import "GameLayer.h"


@implementation EnemySprite


@synthesize walkAnimate;


// GameLayer 있는 오브젝트들을 사용하여야 하므로, init 메소드에 gameLayer 넘겨줍니다.

- (id) initWithSpriteFrameName:(NSString *)frameName position:(CGPoint)pos 

                         speed:(CGFloat)speed gameLayer:(GameLayer*)layer

                  

{

    if( (self=[super initWithSpriteFrameName:frameName]) ) {

        gameLayer = layer;

        

        walkingSpeed = speed;

        

        // GameLayer에서 만들어 놓은 CCAnimation 이용하여 CCAnimate 객체를 만듭니다.

        CCAnimate *animate = [[CCAnimate alloc] initWithAnimation:gameLayer.enemyWalkAnimation restoreOriginalFrame:NO];

        self.walkAnimate = animate;

        [animate release];

        

        self.anchorPoint = CGPointMake(0.5, 0);

        self.position = pos;

    }

    

    return self;

}


// EnemySprite객체가 만들어지면, 바로 걷는 애니메이션을 시작합니다.

- (void) onEnter {

    [super onEnter];

    

    [self startWalking];

}


- (void) startWalking {

    [self schedule:@selector(walking)];

    

    [self runAction:[CCRepeatForever actionWithAction:self.walkAnimate]];

}


- (void) stopWalking {

    [self unschedule:@selector(walking)];

    

    [self stopAllActions];

}



- (void) walking {

    // 주인공에 가까이 왔으면(40픽셀 이내) 움직임을 멈춥니다.

    if(abs(self.position.x - gameLayer.princeSprite.position.x) <= 40) {

        // 걷는 동작을 멈춥니다.

        [self stopWalking];

        

        // 0.5에서 1.4 사이의 랜덤시간만큼 기다렸다가 공격을 합니다.

        // (레벨이 올라갈수록 지체하는 시간을 줄이면 그만큼 강한 적들이 되겠죠?)

        CGFloat delayTime = ((random() % 10) + 5) / 10.0f;

        NSLog(@" 공격 %1.2f ...", delayTime);

        [self runAction:[CCSequence actions:

                         // 위에서 계산된 랜덤시간만큼 기다립니다.

                         [CCDelayTime actionWithDuration:delayTime],

                         // 주인공을 공격 합니다.

                         [CCCallFunc actionWithTarget:self selector:@selector(attackPrince)],

                         nil]];

    }

    

    // 아직 주인공과 충분히 가깝지 않다면 계속 이동시킵니다.

    CGFloat tmpWalkingSpeed = walkingSpeed;

    

    // 주인공이 왼쪽에 있는지 오른쪽에 있는지 알아봅니다.

    if(gameLayer.princeSprite.position.x > self.position.x)

        self.flipX = YES;

    else {

        self.flipX = NO;

        tmpWalkingSpeed *= -1.0;

    }

    

    // 새로운 위치를 업데이트 합니다.

    self.position = CGPointMake(self.position.x + tmpWalkingSpeed, self.position.y);

}



// 메소드가 바로 주인공이 이동할 적들이 뒤쳐지도록 만드는 부분입니다.

// 주인공의 이동방향과 적의 이동방향에 맞추어 x 좌표의 값을 변경합니다.

- (void) adjustPosition {

    if(gameLayer.princeSprite.position.x > self.position.x && gameLayer.princeSprite.flipX == YES)

        self.position = CGPointMake(self.position.x + 1.0, self.position.y);

    else if(gameLayer.princeSprite.position.x < self.position.x && gameLayer.princeSprite.flipX == NO)

        self.position = CGPointMake(self.position.x - 1.0, self.position.y);

    else if(gameLayer.princeSprite.flipX == YES && self.flipX == NO)

        self.position = CGPointMake(self.position.x + 1.0, self.position.y);

    else if(gameLayer.princeSprite.flipX == NO && self.flipX == YES)

        self.position = CGPointMake(self.position.x - 1.0, self.position.y);

}


- (void) attackPrince {

    // 공격하는 애니매이션을 시작합니다다음 강좌에서 실재 에니메이션을 구현해보도록 하겠습니다.

    

    // 주인공과의 거리를 다시 계산합니다이렇게 다시 계산하는 이유는 위에서 잠깐 지체하는동안

    // 주인공이 이동을 했을 수도 있기 때문입니다.

    if(abs(gameLayer.princeSprite.position.x - self.position.x) <= 50) {

        NSLog(@" 공격 성공!");

    }else {

        NSLog(@" 공격 실패!");

    }

    

    [self startWalking];

}


- (void) dealloc {

    [walkAnimate release];

    

    [super dealloc];

}


@end


(1) 캐시에 들어있는 프레임 이름을 베이스(Base) 클래스의 initWithSpriteFrameName에 넘겨주어 스프라이트 객체를 생성합니다.
(2) 각 EnemySprite 객체는 자신만의 schedule을 사용해서 프레임마다 walking 메서드를 호출하게 하여 스프라이트의 좌표를 업데이트 합니다. 애니메이션을 무한반복 합니다.
(3) unschedule 메서드를 사용하여 등록된 스케줄을 스케줄러에서 삭제합니다. 애니메이션을 stopAllActions 메서드로 중지시킵니다.




//  GameLayer.m

- (void) createEnemyAndAnimation {

    // 위치정보 파일을 읽어들여 바로 CCSpriteFrame 만들어 캐시에 저장합니다.

    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"enemy.plist"];

    

    // 위치정보 파일과 같은 이름을 가진 스프라이트 시트로 CCSpriteSheet 객체를 만듭니다.

    self.enemySpriteSheet = [CCSpriteSheet spriteSheetWithFile:@"enemy.png"];

    [self addChild:self.enemySpriteSheet z:kTag_Enemy];

    

    // 프레임을 담을 Array 만듭니다.

    NSMutableArray *aniFrames = [NSMutableArray array];

    

    // 아주 간단히 프레임을 만들어 배열에 저장할 있습니다. 스프라이트 시트를 만들 사용된

    // 개별 이미지의 파일 이름을 사용하여 개별 프레임을 읽어들입니다.

    for(NSInteger idx = 1; idx <= 15; idx++) {

        CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache

                                spriteFrameByName:[NSString stringWithFormat:@"zombie_walk_%04d.png", idx]];

        [aniFrames addObject:frame];

    }

    

    // 프레임으로 CCAnimation 만듭니다 프레임당 시간을 0.15초로 정해줍니다.

    CCAnimation *walkAnimation = [[CCAnimation alloc] initWithName:@"enemy_walk" delay:0.15f frames:aniFrames];

    self.enemyWalkAnimation = walkAnimation;

    [walkAnimation release];

    

    // 임시로 두명의 적을 만들어 주인공 캐릭터의 처음 위치의 왼쪽 오른쪽에 각각 위치시킵니다.

    /*for(NSInteger idx = 0; idx < 2; idx++) {

        // 첫번째 프레임으로 캐릭터 sprite 만듭니다.

        CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:@"zombie_walk_0001.png"];

        sprite.anchorPoint = ccp(0.5, 0);

        

        if(idx == 0) {

            // 첫번째 적은 화면 중앙에서 왼쪽에

            sprite.position = ccp(winSize.width / 2 - 100, 13);

            sprite.flipX = YES;

        }else {

            // 두번째 적은 화면 중앙에서 오른쪽에 위치시킵니다.

            sprite.position = ccp(winSize.width / 2 + 100, 13);

            sprite.flipX = NO;

        }

        

        // 만들어진 스프라이트를 스프라이트 시트에 넣습니다.

        [self.enemySpriteSheet addChild:sprite];

        

        // 걷기 애니메이션을 시작합니다.

        CCAnimate *walkAnimate = [CCAnimate actionWithAnimation:self.enemyWalkAnimation restoreOriginalFrame:NO];

        [sprite runAction:[CCRepeatForever actionWithAction:walkAnimate]];

    }*/

    

    // TEMP

    // 움직이는 속도와 위치를 달리하여 캐릭터 세개를 만든다.

    // 캐릭터 1

    EnemySprite *enemy = [[EnemySprite alloc] initWithSpriteFrameName:@"zombie_walk_0001.png" 

                                             position:CGPointMake(winSize.width / 2 + 100, 13

                                                speed:0.5f 

                                            gameLayer:self];                        

    [self.enemySpriteSheet addChild:(CCSprite*)enemy];

    [enemy release];

    

    // 캐릭터 2

    enemy = [[EnemySprite alloc] initWithSpriteFrameName:@"zombie_walk_0001.png" 

                                            position:CGPointMake(winSize.width / 2 - 150, 13

                                               speed:0.3f 

                                           gameLayer:self];

    [self.enemySpriteSheet addChild:(CCSprite*)enemy];

    [enemy release];

    

    // 캐릭터 3 - 화면 바깥에 위치시킨다.

    enemy = [[EnemySprite alloc] initWithSpriteFrameName:@"zombie_walk_0001.png" 

                                            position:CGPointMake(-100, 13

                                               speed:0.7f

                                           gameLayer:self];

    [self.enemySpriteSheet addChild:(CCSprite*)enemy];

    [enemy release];

}