iDev/Cocos2D

드래곤 플라이트 따라 만들기 - 2. GameScene 기본과 배경화면 스크롤

KraZYeom 2013. 1. 15. 08:28
반응형

이 연재글은 필자의 심심함에 따라 작성되고 있는 글입니다. 좀 늦어질 수도 있습니다. 그리고 코드의 내용이 개판일 수도 있으니 나름 유념하고 봐주세요. 소스는 나중에 한 번에 통짜로 github에 올릴테니 버그, 수정 사항이 있으면 알려주시면 반영 하도록 하겠습니다.

만들고 라인수를 살펴보니 주석포함 1200 라인정도 밖에 안되네요. 정말 아무나 할 수 있습니다. 

본 글과 소스의 라이센스는 '만나면 커피 한잔 사주기' 라이센스 입니다. 쿨럭. 


GameScene 만들기 

이전에는 Layer에 Scene을 클래스 메소드를 사용하여 생성하였지만, Scene 하위에 다수의 레이어를 자식노드로 추가하기 위해서 CCScene을 상속받아서 Scene을 만들도록 하자. 

Cmd(⌘) + N으로 새로운 파일을 추가 한다. CCScene을 상속받고 이름을 GameScene으로 짓는다. 그리고 GameLayerHUDLayer도 CCLayer를 상속 받아서 각각의 이름으로 추가한다. 


GameLayer에는 플레이어 케릭터, 적 등등 게임을 하는 데 필수 요소가 포함되고, HUDLayer에는 점수, 거리와 같은 게임의 보조적인 열할을 하는 요소가 포함될 것이다. 


GameScene.h에 생성했던 클래스의 헤더를 추가하고, 각 Layer의 프로퍼티를 설정한다.

#import "GameLayer.h"

#import "HUDLayer.h"


@interface GameScene : CCScene


@property (nonatomic, weak) GameLayer *gameLayer;

@property (nonatomic, weak) HUDLayer *hudLayer;


GameScene.m으로 돌아가서 init 메소드에서 GameLayer와 HUDLayer를 자식노드로 추가한다. 

- (id)init

{

    self = [super init];

    if (self) {

        //HUD 레이어 추가하기

        _hudLayer = [HUDLayer node];

        [self addChild:_hudLayer z:1];

        //Game 레이어 추가하기

        _gameLayer = [GameLayer node];

        [self addChild:_gameLayer z:0];

    }

    return self;

}

GameScene에 GameLayer와 HudLayer 두 개의 레이어를 추가했다. 그리고 [self addChild:_gameLayer z:0];에서 z 는 z-order, 즉 수직으로 겹치는 층의 위치를 나타낸다. 숫자가 작을 수록 먼저 그려지게 된다. 숫자가 커지면 위로 올라오고, 0에 가까우면 아래로 내려간다. 


Note: 

init를 치면 아래 그림과 같이 코드 생성 도우미가 알아서 init 메소드를 생성해준다.`



MenuLayer.m의 상단에 #import "GameScene.h"를 추가하고, init 메소드 안의 CCMenuItem 부분을 아래 코드로 변경한다.

//Start 메뉴 버튼이 눌렸을 경우, GameScene 화면 전환과 함께 호출한다.

CCMenuItem *startItem = [CCMenuItemFont itemWithString:@"Start" block:^(id sender)  {

     [[CCDirector sharedDirector] replaceScene:[GameScene node]];

}];


변경을 하면 init 코드가 아래와 같을 것 이다. 

- (id)init

{

    self = [super init];

    if (self) {

        //다이렉터에서 화면의 크기를 알아온다.

        CGSize size = [[CCDirector sharedDirectorwinSize];

        //제목으로 만들 레이블을 시스템 폰트를 사용해서 만든다.

        CCLabelTTF *label = [CCLabelTTF labelWithString:@"Dragon Rider" fontName:@"HelveticaNeue" fontSize:36];

        //레이블의 위치를 지정한다.

        label.position = ccp( size.width/2, size.height/2 + 100 );

        //레이블을 자식으로 추가한다.

        [self addChild:label];


        CCLabelTTF *label2 = [CCLabelTTF labelWithString:@"Made by @krazyeom" fontName:@"HelveticaNeue" fontSize:30];

        //레이블의 위치를 지정한다.

        label2.position = ccp( size.width/2, size.height/2 + 60 );

        //레이블을 자식으로 추가한다.

        [self addChild:label2];

        

        //메뉴 아이템의 폰트를 변경한다.

        [CCMenuItemFont setFontName:@"AppleSDGothicNeo-Medium"];

        //메뉴 아이템 블럭

        CCMenuItem *startItem = [CCMenuItemFont itemWithString:@"Start" block:^(id sender)  {

        //Start 메뉴 버튼이 눌렸을 경우, GameScene 화면 전환과 함께 호출한다.

            [[CCDirector sharedDirector] replaceScene:[GameScene node]];

        }];

        //메뉴 버튼을 메뉴에 추가한다.

        CCMenu *menu = [CCMenu menuWithItems:startItem, nil];

        //세로 정렬로  메뉴의 사잇값으로 20 준다.

        [menu alignItemsVerticallyWithPadding:20];

        //메뉴의 위치를 지정한다.

        [menu setPosition:ccp( size.width/2, size.height/2 - 50)];

        //메뉴를 자식으로 추가한다.

        [self addChild:menu];

    }

    return self;

}


Cmd(⌘) + R 으로 Run을 해서 잘 진행되고 있는지 체크를 한다. Start 버튼을 누르면 검정 화면만 덩그러니 떠 있다. 아무런 문제가 없으니 걱정하지 마라.


배경화면 스크롤 하기

드래곤 플라이트에서 자세히 보면 게임에서 사용자 케릭터가 앞으로 나아가고 있는 것 처럼 보이지만, 실제적으로는 고정위치에 있고 배경 화면이 아래로 스크롤 됨에 따라서 상대적으로 움직이는 것 처럼 보인다. 배경화면을 더 빠르게 움직이면 또한 사용자 케릭터는 더 빠르게 움직이는 것 처럼 보이게 된다. 


CCParallaxNode를 사용하면 배경화면을 마치 달리는 기차에서 창 밖을 바라보면 가까이 있는 것은 빠르게 움직이고 멀리있는 것은 느리게 움직이는 효과를 줄 수 있다. 하지만 자식 노드의 위치 값을 변경을 아직 지원하지 않는다.


그래서 그냥 동일한 이미지 두 장을 사용해서 무제한으로 도는 것 처럼 보이게 만들 것이다. 우선 배경화면에 필요한 이미지를 xcode 프로젝트로 드레그 앤 드롭을 해서 넣는다.  

(발로 그렸으니 이해바랍니다. 누가 이쁘게 그려주시면 감사! ㅠㅠb)

Destination과 Add to targets 체크 박스는 꼭 체크 하도록 하자. 안하면 나중에 골치 아프다.


GameLayer.h 파일로 이동해서 배경화면에 사용할 이미지 두 개의 프로퍼티를 추가 한다.

@interface GameLayer : CCLayer {

 //화면 싸이즈를 저장할 변수

    CGSize winSize;

}

@property (nonatomic, weak) CCSprite *backgroundImage1;

@property (nonatomic, weak) CCSprite *backgroundImage2;


배경화면 초기화를 위해서  - (void)initBackground(); 메소드를 init 메소드 밑에 추가한다.

- (void)initBackground{

    //배경에 사용할 1 이미지를 생성 , 화면에 차게 이동 시킨다.

    _backgroundImage1 = [CCSprite spriteWithFile:@"01.png"];

    _backgroundImage1.anchorPoint = CGPointZero;

    [self addChild:_backgroundImage1 z:-1];

    

    //배경에 사용할 2 이미지를 생성 , 1 이미지 위로 이동 시킨다.

    _backgroundImage2 = [CCSprite spriteWithFile:@"01.png"];

    _backgroundImage2.anchorPoint = CGPointZero;

    _backgroundImage2.position = ccp(0, [_backgroundImage2 boundingBox].size.height);

    [self addChild:_backgroundImage2 z:-1];

}

1번 이미지를 불러와서 앵커포인트를 (0, 0) 좌측 하단으로 설정한다. 그리고 화면에 노드로 추가한다. 기본위치는 (0, 0)이다. 그리고 1번 이미지와 동일한 2번 이미지를 1번 이미지 바로 위에 위치 시킨다. 


Note: 

iOS 좌표계와 다르게 cocos2d는 (0, 0) 좌표가 좌하단이다. 그래프에서 1사분면이라고 생각하면 쉽다. 


init 메소드를 만들고 아래 코드와 같이 추가 한다. 

- (id)init

{

    self = [super init];

    if (self) {

        //윈도우 화면 크기를 가져온다.

        winSize = [[CCDirector sharedDirector] winSize];

        //배경 초기화

        [self initBackground];

    }

    return self;

}



그리고 배경화면을 움직이게 하기 위해서 아래 코드를 추가 한다.

- (void)update:(ccTime)dt {

    // 배경화면 움직이는 속도, 현재 위치에 이동할 위치를 ccpAdd 더하는 방식

    CGPoint backgroundScrollVel = ccp(0, -100);

    // 현재 이미지1 위치 값을 불러온다.

    CGPoint currentPos = [_backgroundImage1 position];

    // 1 이미지가 스크롤 되서 사라지고, 2 이지미가 1 이미지의 초기 위치에 오면 최초위치로 이동

    if (currentPos.y < -winSize.height) {

        [_backgroundImage1 setPosition: CGPointZero];

        currentPos = ccp(0, [_backgroundImage2 boundingBox].size.height);;

        [_backgroundImage2 setPosition: currentPos];

    //현재 위치에서 backgroundScrollVel 한다.

    } else{

        _backgroundImage1.position = ccpAdd(ccpMult(backgroundScrollVel, dt), _backgroundImage1.position);

        _backgroundImage2.position = ccpAdd(ccpMult(backgroundScrollVel, dt), _backgroundImage2.position);

    }

}

이 - (void)update:(ccTime)dt 메소드는 [self scheduleUpdate]; 호출이 된다. 스케쥴러로써 cocos2d에 정해놓은 config값의 시간 간격에 따라 반복되서 호출된다. 기본 dt는 1/60


1, 2번 이미지가 동시에 아래로 내려가고 1번 이미지가 완전히 사라질 시점에 2번 이미지는 화면에 꽉차게 된다. 그 시점에 다시 1번 2번 이미지 모두 초기 위치로 이동 시킨다. 그렇게 계속해서 반복하면 배경화면이 끝없이 반복해서 움직이는 것 처럼 보인다. 


그리고 - (void)onEnter메소드를 오버라이드해서 아래 코드와 같이 변경한다.

- (void)onEnter {

    [super onEnter];

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

    [self scheduleUpdate];

}

GameLayer가 시작 시점에 [self scheduleUpdate];를 통해서 - (void)update:(ccTime)dt가 반복적으로 호출 될 것이다.


Cmd(⌘) + R으로 Run을 해서 잘 진행되고 있는지 체크를 한다.  아무런 문제가 없다면 아래 동영상 처럼 배경화면이 무한정으로 반복되는 것을 확인 할 수가 있다. 

 

뭔가 느리지만 하나 하나씩 진행 되고 있다. 다음장에서 계속 하도록 하겠다. 

반응형