iDev/Cocos2D

드래곤 플라이트 따라 만들기 - 3. 플레이어 케릭터

KraZYeom 2013. 1. 16. 08:35
반응형

이번 파트에서는 플레이어 케릭터를 추가하고, 터치입력을 받아서 좌우로 움직이는 것을 다루겠다. 


스프라이트 시트 

게임에서는 많은 스프라이트 이미지들이 사용된다. 아무것도 몰랐을 때는 이미지 한 장, 한 장을 불러서 사용했다. 물론 그렇게 해도 가벼운 게임에는 큰 문제가 없다. 아무리 작은 이미지라도 한 장이 차지 하는 최소 공간이 있다고 한다. (정확하게는 잘 모르겠다) 그래서 작은 이미지를 여러장 따로 불러 쓰면 필요 없는 공간이 많이 지므로 효율성도 떨어진다고 한다. 

그래서 게임에는 작은 이미지 파일을 통짜로 묶은 스프라이트 시트를 사용한다. 예를 들면 드래곤 라이드에서 사용한 이미지 시트는 아래 그림과 같다. 

그림은 한 장이지만, plist 파일로 각각 스프라이트의 이미지 정보를 담고 있다. 


개발자가 일일이 만들어 줄 수도 있겠지만, 사용화 툴도 많고 무료 툴도 있긴 하다. 무료 툴은 불편함과 기능이 좀 부족한 것 같아서. 필자는 TexturePacker라는 툴을 구입했다. $29 라는 약간 부담 스러운 가격이지만 맥/리눅스 지원을 하고 cocos2d외에도 다양한 게임 플렛폼을 지원한다. 워터마크가 들어 가긴 하지만 트라이얼로 사용할 수 있으니 다운로드 받아서 사용하면 된다. 

다른 툴로는 zwoptex 가 있고 기능이 떨어지긴 하지만 기본 기능은 다 되는 플레쉬 버젼은 무료다. 


이미지 리소스 파일 

dr.zip


위 이미지 파일을 다운 받아서 모든 이미지 파일을 TexturePacker에 모두 드레그&드롭 해서 넣으면 된다. Texture format은 zlib compr. PVR (.pvr.ccz, Ver.2)로 선택하는게 가장 효율성이 좋다. 그리고 Publish를 하면 dragonRideSprite.pvr.cczdragonRideSprite.plist 파일 두 개가 생성된다. 이 파일 두 개를 xcode 프로젝트에 드래그&드롭으로 추가 한다.

만들어진 스프라이트를 게임에 넣도록 하자. GameLayer.m 으로 이동해서 init 메소드 상단 부분에 아래코드를 추가하도록한다.

- (id)init

{

    self = [super init];

    if (self) {

        ....


        //스프라이트 프레임 케쉬에 스프라이트를 저장한다.

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


        ....

    }

게임이 실행할 때 스프라이트 케쉬에 넣어서 계속해서 다른 곳에서도 재사용 할 수 있다. 

플레이어 케릭터 추가

Cmd(⌘) + N으로 CCSprite를 상속받은 Player 클래스를 새로 생성한다.

플레이어 케릭터를 몸통과 양 날개를 가진 Sprite 3개로 구성된다. 



플레이어 몸통에 필요한 player.png와 날개에 필요한 player_wing.png를 스프라이트 시트를 통해서 사용할 것이다. player_wing.png는 한 장으로 좌, 우 날개에 사용 될 것이다.


Player.h에 아래 코드를 추가 한다.

@property (nonatomic, weak) CCSprite *leftWing;

@property (nonatomic, weak) CCSprite *rightWing;

왼쪽, 오른쪽 날개를 CCSprite로 프로퍼티를 생성한다. 


Player.minit 메소드에 아래 코드를 추가한다.


- (id)init

{

    //플레이어 몸통의 스프라이트를 생성한다.

    self = [super initWithSpriteFrameName:@"player.png"];

    if (self) {

        //윈도우 크기

        CGSize winSize = [CCDirector sharedDirector].winSize;

        //화면의 가운데 아래측에 위치 시킨다.

        self.position = ccp( winSize.width/2, 50 );

        

        //왼쪽 날개를 만든다.

        _leftWing = [CCSprite spriteWithSpriteFrameName:@"player_wing.png"];

        //날개의 엥커포인트를 우측 상단으로 한다.

        _leftWing.anchorPoint = ccp( 1.0, 1.0 );

        //중간 정도로 위치 시킨다.

        _leftWing.position = ccp( 10, 30 );

        //몸통아래에 위치 하도록 z-order 변경한다.

        [self addChild:_leftWing z:-1];


        //오른쪽 날개를 만든다.

        _rightWing = [CCSprite spriteWithSpriteFrameName:@"player_wing.png"];

        //날개의 엥커포인트를 촤측 상단으로 한다.

        _rightWing.anchorPoint = ccp( 0.0, 1.0 );

        //Flip 회전 시킨다.

        [_rightWing setFlipX:YES];

        //Flip 회전 하면 축으로 회전 된다. 그래서 몸통의 가로만큼 추가해준다.

        _rightWing.position = ccp( self.boundingBox.size.width - 10, 30 );

        //몸통아래에 위치 하도록 z-order 변경한다.

        [self addChild:_rightWing z:-1];

    }

    return self;

}

몸통과 좌우날개를 만들어 주고 자식 노드에 추가한다. 오른쪽 날개는 이미지를 Flip 회전해서 사용한다. 


만들어진 Player 클래스를 GameLayer에 추가하기 위해 GameLayer.h에 아래 코드를 추가한다. 

@class Player;

...

@property (nonatomic, weak) Player *player;

...

Player 객체를 위한 프로퍼티를 추가한다. 


GameLayer.m에 아래 코드를 추가한다. 

- (void)initPlayer {

    //플레이어 케릭터를 생성한다.

    _player = [Player node];

    //가장 위에 위치 시킨다.

    [self addChild:_player z:99];

}

player 객체를 생성하고 자식노드로 상단 레이어에 추가한다. 


그리고 init 메소드 하단에 아래 코드를 추가한다.

        //플레이어 케릭터 초기화

        [self initPlayer];

플레이어를 생성하는 메소드를 init에서 호출한다. 


실행하면 아래 화면과 같이 배경화면은 스크롤 되고 아래쪽 가운데에 플레이어 케릭터가 위치 하게 된다.

역시 그림빨이 있어야 하나보다. ㅠㅠ



플레이어 움직이기

다음으로는 터치 이벤트를 받아서 플레이어 케릭터를 움직이게 만들 것이다. 플레이어 케릭터는 Y축으로는 화면 하단에 고정되어 있고, 좌우로 X축만 움직이게 된다. 사용자가 화면을 좌우로 스와이프 하면 그 움직이는 거리를 계산해서 플레이어 케릭터를 움직인다. 


움직이는 거리계산을 위한 이전 위치값을 저장하는 변수를 GameLayer.h에 추가한다.

@interface GameLayer : CCLayer {

    CGSize winSize;

    CGPoint previousPoint;

}


터치 이벤트를 받기 위해 아래 코드를 GameLayer.m 하단에 추가한다.

- (void)onEnter {

    [super onEnter];

    //터치 이벤트를 받는다.

    [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];

    //배경 움직임과 충돌을 체크할때 사용하는 메인 스케쥴

    [self scheduleUpdate];

}

GameLayer가 시작되고 onEnter 메소드가 호출 되면 터치 이벤트를 받는다. 


기본적으로 UITouchEvent와 비슷하게 시작 시점에 호출하는 TouchBegan, 움직임이 있을때 호출하는 TouchMoved, 터치가 끝났을 때 호출하는 TouchMoved가 있다. 


아래 코드를 onEnter 메소드 하단에 삽입한다. 

#pragma mark Touch


-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {

    //터치가 시작되면 이전 값과 비교를 위해 저장한다. UI좌표계를 cocos 좌표계로 변환

    previousPoint = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];

    return YES;

}

터치가 시작되면 이전 값과 비교를 위해서 저장한다. iOS에서는 UI좌표계를 사용하므로 cocos2d 좌표계로 변환하는 메소드를 호출한다. 정확하게 말하면 만드는 게임에서는 X축 좌표 이동만 하기 때문에 변환이 필요가 없을 수도 있지만 추후를 위해서 그대로 남겨 두도록 하자. 


Note: 

-(CGPoint)convertToGL:(CGPoint)uiPoint

{

CGSize s = winSizeInPoints_;

float newY = s.height - uiPoint.y;


return ccp( uiPoint.x, newY );

}

터치 포인트를 받아와서 X축 값은 그대로 전달해주고 Y축 값은 화면 크기에서 터치 지점 Y값을 뺀 값이다.

-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {

    //이전 값과 비교를 위한 움직였을때 위치 . UI좌표계를 cocos 좌표계로 변환

    CGPoint location = [[CCDirector sharedDirectorconvertToGL:[touch locationInView:[touch view]]];

    //플레이어 케릭터의 위치를 ( 기존 위치 X  - 움직인 거리 ), Y 값은 동일

    _player.position = ccp_player.position.x - (previousPoint.x - location.x) * 2_player.position.y );

    //왼쪽이나 오른쪽으로 벗어나면 넘어가지 않도록 고정 시킨다.

    if (_player.position.x < 0) {

        _player.position = ccp(0_player.position.y);

    } else if (_player.position.x > winSize.width) {

        _player.position = ccp(winSize.width_player.position.y);

    }

    //현재 위치를 이전 값으로 저장한다.

    previousPoint = location;

}

터치를 한 상태에서 움직이면 그 움직인 거리를 계산해서 플레이어 케릭터에 적용을 한다. 그리고 화면 좌우 영역을 벗어나는 경우를 생각해서 min 값은 0, max 값은 화면 크기의 너비 값으로 한다. 


-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {

    //터치가 끝났을때는 특별한 이벤트가 없다.

}

터치가 끝났을 때는 특별한 이벤트가 없다. 


Cmd(⌘)+R 로 실행을 해보자. 

그리고 터치를 해서 좌우로 스와이프를 하면 케릭터가 좌우로 움직이는 것을 확인 할 수 있다. :-]


하나씩 완성 되어가는 모습을 보니 뿌듯하지 않은가? 다음 파트에서는 적을 만들어 보도록 하겠다.

반응형