프로그래밍/Cocos2D2010. 7. 2. 17:54
MammothHunting은 우에서 좌로 움직이는 맘모스를 잡는 게임이다. 이제 타겟인 맘모스와 맘모스를 사냥하기 위한 총알 발사에 관해 살펴 보자.

타겟 추가
타겟은 화면 우측의 랜덤한 Y축을 기준으로 화면 좌측으로 이동한다. MammothHuntingScene.m에 다음의 addTarget 메소드를 init 메소드 이전에 추가한다.

-(void)addTarget {

CCSprite *target = [CCSprite spriteWithFile:@"target.png" rect:CGRectMake(007070)]; 

// 타겟(맘모스) Y 위치 결정.

CGSize winSize = [[CCDirector sharedDirectorwinSize];

int minY = target.contentSize.height/2;

int maxY = winSize.height - target.contentSize.height/2;

int rangeY = maxY - minY;

int actualY = (arc4random() % rangeY) + minY;

// 오른쪽  화면 밖에 X 위치와 위에서 결정한 Y축으로 타켓의 포지션 결정.

target.position = ccp(winSize.width + (target.contentSize.width/2), actualY);

[self addChild:target];

// 타겟의 속도 결정.

int minDuration = 2.0;

int maxDuration = 4.0;

int rangeDuration = maxDuration - minDuration;

int actualDuration = (arc4random() % rangeDuration) + minDuration;

// 액션 생성.

id actionMove = [CCMoveTo actionWithDuration:actualDuration position:ccp(-target.contentSize.width/2, actualY)];

id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)];

[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];

}


위 메소드 중 새로 등장하는 액션에 대해서 잠깐 정리하고 넘어가자. Cocos2D는 다양한 액션을 제공한다. 액션 클래스는 CocosNode 클래스를 상속한 클래스로 애니메이션 처리를 위한 클래이다. 대부분 스프라이트 클래스의 애내메이션 처리를 위해 사용된다.

액션 클래스를 이용한 애니메이션은 시간과 동작에 따라 나눌 수 있다. 우선 시간에 따라서는 다음과 같에 세 가지로 구분된다.

InstanceAction: 한 번만 실행된다.
IntervalAction: 주어진 시간 동안 같은 동작을 반복해서 실행한다.
RepeatAction: 일정한 시간 동안 일정한 패턴을 반복한다.

동작에 따른 구분은 다음과 같다.

Transformation Action: Move, Rotate, Scale, Jump 등.
Composable Action: Sequence, Spawn, Repeat, Revers.
Ease Action: Exp, Sin, Cubic 등.
Etc Action: CallFun, Orbit.

Transformation Action은 스프라이트의 이동, 회전 등과 같은 움직임의 일회성 처리나 효과 처리를 위한 액션이다. Composable Action은 Transformation Action 여러 개가 순차적으로 또든 동시에  애니메이션의 실행이 필요한 경우 사용한다. Ease Action은 애니메이션을 구현하는 속도와 관련된 액션들이다. Transformation Action과 Composable Action이 기본 속도 1로 동작한다면, Ease Action은 애니메이션을 점점 빠르게 하거나, 갑자기 빠르게 바꾸는 것과 같은 처리를 할 수 있다. 그 외에 콜백 메소드를 호출해서 실행하는 CallFunc나 궤도를 따라 움직이도록 할 수 있는 Orbit과 같은 액션들이 있다. CallFunc의 콜백 기능을 이용하면 애니메이션이 끝난 다음에 액션을 발생시키는 등의 처리를 쉽게 할 수 있다.

다음은 액션을 생성한 부분의 CCCallFuncN에 생성한 콜백 함수인 spriteMoveFinished 메소드를 addTarget 메소드 이전에 추가해야 한다.

-(void)spriteMoveFinished:(id)sender {

CCSprite *sprite = (CCSprite *)sender;

[self removeChild:sprite cleanup:YES];

}


spriteMoveFinished 메소드는 화면 밖으로 사라진 스프라이트를 제거하는데  사용된다. 메모리 릭과 관련하여 더 좋은 방법을 선택해야 하지만, 간단한 게임이므로 생략한다.

마지막으로, 실제 타겟을 추가하기 위해 다음 구문을 init 메소드 마지막(if 구문의...)에 추가하고, 실제 gameLogic 메소드를 addTarget 메소드 다음에 구현한다.

[self schedule:@selector(gameLogic:) interval:1.0];


-(void)gameLogic:(ccTime)dt {

[self addTarget];

}


다시 빌드앤런 하면 다음과 같이 맘모스가 우에서 좌를 이동하는 화면을 볼 수 있다.




총알 발사
실제 맘모스를 잡기위해서는 꽤 묵직한 총과 총알이 필요하겠지만, 간단한 예제 이므로, 맘모스는 케익을 던져 잡는 것으로 처리하기로 한다. 물론 케익은 고릴라가 던질 것이다.

케익을 던지는 방법은 여러 가지가 있겠지만, MammothHunting 게임에서는 화면을 터치하는 방식을 선택했다. 총알 발사 처리를 위해서는 간단한 수학 공식이 필요하다. 우선 다음 그림을 보자.


총알 발사 처리를 위해 CCMoveTo 액션을 사용할 것이다. CCMoveTo 액션의 API를 보면 액션 지정을 위해 시간과 거리가 필요하다. 플레이어(고릴라)가 발사(터치)한 총알(케익)까지의 거리를 계산하기 위해 먼저 한 가지 사실을 가정해야 한다. 발사된 총알은 터치한 지점을 지나 화면 밖까지 이동한다는 점이다. 이 내용을 기초로 그림을 그려 본 것이 위의 그림이다. 그림의 내용을 상기하면서 다음 단계로 넘어 가자. 작은 직삼각형과 같은 비율의 큰 직삼각형을 기억하라.

우선 터치로 총알을 발사하기 위해 다음 코드를 init 메소드(if 구문 상단에...)에 추가하자. 레이어에 터치 이벤트를 활성화 한 것이다.

self.isTouchEnabled = YES;


그리고 터치 이벤트를 처리하기 위한 콜백 메소드(ccTouchesEnded)를 다음과 같이 구현한다.

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

// 처리를 위한 터치 선택.

UITouch *touch = [touches anyObject];

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

location = [[CCDirector sharedDirectorconvertToGL:location];

// 총알(케익) 초기 위치 설정.

CGSize winSize = [[CCDirector sharedDirectorwinSize];

CCSprite *projectile = [CCSprite spriteWithFile:@"projectile.png" rect:CGRectMake(004040)];

projectile.position = ccp(20, winSize.height/2);

// 총알 위치의 오프셋(offset) 측정.

int offX = location.x - projectile.position.x;

int offY = location.y - projectile.position.y;

// 총알 발사를 / 수직이나 뒤로  경우 탈출함(발사되지 않음).

if (offX <= 0return;

// 포지션 이중 체크.

[self addChild:projectile];

// 총알을 발사할 위치 측정.

int realX = winSize.width + (projectile.contentSize.width/2);

float ratio = (float) offY / (float) offX;

int realY = (realX * ratio) + projectile.position.y;

CGPoint realDest = ccp(realX, realY);

// Determine the length of how far we're shooting

// 발사한 총알의 길이(궤적) 얼마인지 측정.

int offRealX = realX - projectile.position.x;

int offRealY = realY - projectile.position.y;

float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));

float velocity = 480/1// 480pixels/1sec

float realMoveDuration = length/velocity;

// 총알을 종점(endpoint, 화면 ...)까지 이동 시킴.

[projectile runAction:[CCSequence actions:

   [CCMoveTo actionWithDuration:realMoveDuration position:realDest],

   [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)], 

   nil]];

}


ccTouchesEnded 메소드의 내용의 첫 번째 부분은 처리할 하나의 터치를 선택하여 현재 뷰에서 위치(location)을 확인한다. 그리고 현재 화면 모드가 landscape이므로 convertToGL 메소드로 좌표를 변환한다. 이 것은 이전 포스트에서 언급했던 것으로 주의해야 할 부분이다. 왜냐하면, UIKit(Quartz)의 좌표계 Top-Left(좌상단)가 원점이 되는 좌표계를 쓰지만, Cocos2d는 OpenGL 기반이라 좌하단이 원점이 되는 좌표계를 사용하기 때문이다. 그래서 위치를 계산하기 전에 convertToGL 메소드로 좌표들을 꼭 변환해야 한다.

다음은 총알(케익) 스프라이트를 초기화 한다. 그리고 위에서 언급했던 수학으로 이동 시킬 총알의 위치를 계산해야 한다. 여기서 필요한 것이 바로 피타고라스의 정리이다.

임의의 직각삼각형에서 빗변을 한 변으로 하는 정사각형의 넓이는 다른 두 변을 각각 한 변으로 하는 정사각형의 넓이의 합과 같다.

물론 이 알고리즘은 총알을 발사하는 방법 중 가장 간단한 것의 하나일 뿐이다. 본 예제는 아주 단순한 경우이므로 이정도로 만족하고 넘어간다.

여기에서 중요한 또 한 가지의 내용은, 총알이 발사되는 시간과 속도의 관계를 이해하는 것이다. 학창 시절 배운 물리 법칙과 몇 가지 사항을 기억해 내야 한다.
  • 거리(distance)는 스칼라(scalar) 이고 변위(displacement)는 벡터(vector) 이다.
  • 벡터는 화살표를 사용해 나타낼 수 있다.
  • 거리 = 속도 * 시간
게임 개발을 하려면 기본적으로 수학과 물리학이 필수이다. 그렇다고 다시 교과서를 꺼내 볼 수도 없고... 이 문제를 해결하기 위해 다음 두 권의 책을 추천한다. 한 권은 마침 번역이 되어 있다.

어쨌든 알고리즘 같지 않은 알고리즘을 적용한 것이 액션을 추가하기 전까지의 소스 내용이다. 마지막으로 총알에 액션을 추가하고, 빌드앤런 하자. 이제 화면을 터치하면 총알이 발사될 것이다.


다음 포스트에서는 게임 개발 시 가장 중요한 부분인 충돌 검사에 대해 다룰 것이다. 두 번째 포스트를 마무리하기 전에 [MammothHunting] - Cocos2D를 사용한 아이폰 게임 개발 튜토리얼 1 에서 언급했던 Director, Scene, Layer, Sprite에 대해 정리해 보자.

Cocos2D의 모든 클래스는 CocosNode를 상속한다. CocosNode 클래스는 위치, 색상, 투명도, 크기, 회전, 가시성 여부, 카메라, 그리드 속성을 가지고 있으며 이들 속성을 필요에 따라 변경이 가능하다. CocosNode의 또 다른 특징은 자식 노드를 가질 수 있다는 점이다. 이는 객체들의 계층 구조를 만들 수 있음을 의미한다. 계층 구조가 가능함으로써 부모의 속성이 변할 때 자식의 속성까지 쉽게 변경할 수 있다.

Cocos2D에서 게임 화면을 구성하기 위해 제공되는 네 개의 클래스가 바로 CocosNode를 상속한 Director, Scene, Layer, Sprite 이다.

Director
Director는 싱글톤 클래스이다. Cocos2D는 게임의 화면을 Scene이라는 장면 단위로 구성하여 처리할 수 있는 기능을 제공한다. Director는 바로 이들 장면을 관리한다.

Scene
Scene은 게임에서 하나의 장면을 표현한다. 영화의 장면과 유사하다. 게임 내에서 장면 변경이 필요한 경우, 생성하여 각 장면마다 장면의 Scene 클래스로 처리한다.

Layer
Layer는 Sprite와 함께 Scene을 구성한다. Scene과 Layer의 가장 큰 차이점은, Scene은 사용자의 처치 이벤트를 받아서 처리할 수 없지만 Layer는 TouchEventsDelegate 프로토콜로 사용자의 처치 이벤트를 처리할 수 있다는 점이다. 다음 그림을 보면 이해하기 쉬울 것이다.


Sprite 
Director, Scene, Layer가 추상적이라면, Sprite는 실제로 화면에 출력되는 구체적 개념이다. Sprite는 컴퓨터 그래픽스에서 화면을 구성하는 정적인 이미지나 애니메이션이 되는 일련의 이미지를 가리킨다. 보통 게임에 사용되는 이미지라고 이해하면 된다. 일반적인 이미지와 구별되는 가장 큰 특징 중 하나는 투명색을 가진다는 점이다. 
실제 게임 개발 시 개발의 대부분의 시간이 Sprite를 생성하고 출력한 다음 사용자 입력이나, 기타 이벤트가 발생할 때마다 이들 이벤트에 대응하는 처리를 하는데 소요된다.

---

Posted by windship
프로그래밍/Cocos2D2010. 7. 2. 17:53
Cocos2D-iPhone 엔진은 원래 Python으로 작성된 2D 기반의 게임 라이브러리 인데 이 것을 아이폰 용으로 포팅한 것이 바로 Cocos2D-iPhone이다. 아이폰 게임 개발에 관심을 갖게 된다면 가장 먼저 접합게 되는 게임 엔진일 것이다. Cocos2D는 모바일 환경에서 그래픽 출력을 위한 OpenGL ES 및 사운드 출력을 위한 OpenAL 라이브러리를 손쉽게 게임 개발에 사용할 수 있도록 래핑한 것이다. 물론 다양한 기능을 지원한다. 이에 대한 내용은 사이트에서 확인 바란다.

단 0.8.X  버전에서 0.99.0(cocos2d for iPhone v0.99.0 Release Notes) 버전으로 업그레이드 되면서 CC라는 네임스페이스를 사용하기 시작했다는 점에 주의해야 한다. 0.99.0 이전의 인터넷 자료를 참고한다면 샘플 소스에서 차이가 날 것이다.

이와 관련된 내용과 기본적인 사용법(Hello World, Hello Actions, Hello Events 등등) 등을 공부하고 나면 바로 다음 단계가 궁굼해 진다. "그럼, 어떻게 게임을 만들지?", 물론 사이트에 샘플 게임 소스가 공개되어 있긴 하지만 이제 막 게임 개발에 입문한 개발자에게는 그리 녹녹치 않다.

부분적으로 API를 훓어 본다고 해결될 문제도 아니다. 이에 대한 가장 좋은 해결책은 최대한 단순한 게임 형태를 살펴보는 것이 전체적인 사용법과 이해도를 높이는데 훨씬 많은 도움이 될 것이다. 

본 포스트는 "How To Make A Simple iPhone Game with Cocos2D Tutorial"의 내용을 한글로 재구성 한 것이며, 좋은 글을 공개해준 저자에게 감사 드린다. 이외에도 게임 개발과 관련된 유용한 정보가 상당히 많다. 기회가 된다면 다른 내용도 이와 유사한 형태로 포스팅해 볼 계획이다.

* 부분적으로 설명이 생략된 부분은 마지막 포스트 하단에 첨부한 샘플 프로젝트로 설명을 갈음한다.

우선 사전 준비부터 시작하자.
1. Cocos2D 다운로드 및 설치(현재 안정 버전: 0.99.1)

프로젝트 생성
MammothHunting이라는 이름으로 신규 프로젝트 생성. 물론 Cocos2D 템플릿을 이용한다.


이 상태에서 빌드앤런 하면 다음 화면을 볼 수 있을 것이다. 이미 알고 있는 것처럼 Hello World 예제이다.


이제 게임 개발에 관심을 갖는 단계라면 Xcode의 사용법은 익숙할 것이다. 자세한 설명은 생략한다.

Sprite 추가
* Director, Scene, Layer, Sprite 네 개의 클래스는 Cocos2D에서 게임 화면의 구성을 위한 가장 중요한 것 들이다. 이와 관련된 기본 내용은 숙지하자. 다행히도 미리 고생해 준 분이 계시다.

스프라이트를 추가하기 전에 다음 세 단계를 먼저 진행한다. 물론 기존 템플릿 코드로 진행해도 되지만 생애 최초의 게임을 만드는 마당에 네이밍도 중요하지 않은가? 

첫째, 다음 세개의 그림을 Resources에 추가한다.



둘째, MammothHuntingAppDelegate.m 파일을 열어 applicationDidFinishLaunching: 메소드에서 템플릿이 생성해준 맨 아래의 [[CCDirector sharedDirector] runWithScene: [HelloWorld scene]]; 코드를 다음과 같이 수정한다.

CCScene *scene = [CCScene node];

CCLayer *layer = [MammothHuntingScene node];

[scene addChild:layer];

[[CCDirector sharedDirectorrunWithScene: scene];


세째, HelloScene.h와 HelloScene.m 클래스 파일을 삭제한다. 그리고 NSObject의 서브 클래스로 MammothHuntingScene.h와 MammothHuntingScene.m 클래스를 생성한다. 다음은 MammothHuntingScene.h 클래스에서 "cocos2d.h"를 임포트 하고 CCLayer를 상속하도록 수정한다. MammothHuntingScene.m에 다음 init 메소드를 추가하고, 다시 빌드앤런!

-(id) init {

if( (self=[super init] )) {

CGSize winSize = [[CCDirector sharedDirectorwinSize];

CCSprite *player = [CCSprite spriteWithFile:@"player.png" rect:CGRectMake(005055)];

player.position = ccp(player.contentSize.width/2, winSize.height/2);

[self addChild:player];

}

return self;

}


아마도 문제가 없다면, 다음 화면을 접할 수 있을 것이다.


잘 보이지 않겠지만, 고릴라이다. 인터넷에서 무료로 구할 수 있는 아이콘을 활용했다. 원하는 그림으로 변경해도 튜토리얼을 진행하는 데는 전혀 지장이 없다. 단 그림 사이즈와 rect 사이즈는 동일해야 한다.

여기서 잠깐, 좌표계에 대해서 공부하고 다음 단계로 넘어가자. 화면의 위치를 이동하거나 지정할 때 Cocos2D에서는 좌표계에 주의해야 한다. UIView를 사용하는 일반적인 아이폰 앱은 UIView 좌표계를 사용하며 UIView에서는 좌측 상단(0, 0)이 원점이다. 즉, 오른쪽으로 가면서 x 값이 증가하고 아래로 갈 수록 y 값이 증가한다. 이와는 달리 Cocos2D는 OpenGL을 사용하므로 좌표계의 원첨이 좌측 하단 위로 갈수록 y 값이 증가한다.

위 내용을 그림으로 표현하면 다음과 같다. 나중에 좌표계를 변환해야 하므로 기억해 두어야 한다.


좌표계를 기준으로 플레이어(고릴라)의 위치는 init 메소드에 다음과 같이 설정했다.

player.position = ccp(player.contentSize.width/2, winSize.height/2);


여기서 플레이어(고릴라)의 스프라이트의 앵커포인트(anchorPoint: 고정점)의 x, y 좌표를 위와 같이 설정했다. 앵커 포인트는 모든 변환 및 위치 조작의 중심점인데, 상위 레이어에 부착한 레이어 상의 핀으로 생각하면 된다. 

레이어 관련하여 프레임(frame), 바운드(bounds), 위치(position), 고정점(anchorPoint), 모서리 반경(cornerRadius), 레이어 높낮이(zPosition) 등의 기하 관련 속성에 대한 이해가 전제되어야 하는데, 이는 Core Animation의  Layer Geometry and Transforms 부분을 참고하라.

다음 작업은 배경화면 변경이다. 검정색 배경을 흰색으로 변경할 것이다.
프로젝트에서 MammothHuntingScene.h 클래스 파일을 열어 다음과 같이 CCColorLayer를 상속 받도록 수정한다.

#import "cocos2d.h"



@interface MammothHuntingScene : CCColorLayer {


}


@end


그리고 MammothHuntingScene.m 클래스의 init 메소드의 if 조건을 다음과 같이 수정한다.

if ((self=[super initWithColor:ccc4(255,255,255,255)])) {


이제 다시 빌드앤런 하면 다음 그림과 같이 흰색 배경화면을 볼 수 있을 것이다.


"Cocos2D를 사용한 아이폰 게임 개발 튜토리얼 1"은 이것으로 마무리 하고 "Cocos2D를 사용한 아이폰 게임 개발 튜토리얼 2"에서는 타켓과 총알 발사에 관해서 살표 보겠다. 하나의 포스트로 진행하기에는 내용이 너무 많은 것 같아 4 단계 정도로 나누어서 포스팅할 계획이다.

---

Posted by windship
프로그래밍/Cocos2D2010. 1. 20. 22:46
개발Q&A / 2010.01.19 21:55 / drkhero79 / http://cafe.naver.com/mcbugi/27576  

코코스2D엔진에서 스프라이트 애니메이션 할때 스프라이트에 애니메이션 등록해놓고 사용하잖아요
근데 CCRepeatForever actionWithAction을 이용하면 모든 프레임을 전부다 처음부터 끝까지 반복재생해주잖아요..

[sprite runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO] ]];
 
그렇게 하지않고 특정 한 프레임 만 계속 띄워놓는 다든지 첫번째 프레임만 선택해서 계속 보이게 한다던지 혹은 2~5번째 만 선택적으로 재생한다던지 하는 선택 재생 방법은 없는 건가요?
 
또다른 질문은 aniframe으로 프레임을 만들고 aniframe2를 만들어서..
처음엔 aniframe으로 애니를 재생하다가 특정 이벤트가 발생되면 aniframe2로 교체해서 애니메이션을 재생해주는
그런 방법은 없는것 인가요?

제가 테스트 해볼라고 간단히 작성해본것 입니다.



CGSize size = [[CCDirector sharedDirector] winSize];

    //애니메이션에 필요한 이미지 파일로딩    
    CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:@"walk.png"];
    CCTexture2D *texture1 = [[CCTextureCache sharedTextureCache] addImage:@"dive.png"];
    
    // manually add frames to the frame cache
   //걷는 그림 프레임 세팅
    CCSpriteFrame *frame0 = [CCSpriteFrame frameWithTexture:texture rect:CGRectMake( 25*0, 0, 25, 66 ) offset:CGPointZero];
    CCSpriteFrame *frame1 = [CCSpriteFrame frameWithTexture:texture rect:CGRectMake( 25*1, 0, 25, 66 ) offset:CGPointZero];
    CCSpriteFrame *frame2 = [CCSpriteFrame frameWithTexture:texture rect:CGRectMake( 25*2, 0, 25, 66 ) offset:CGPointZero];
    
  //다이빙하는 그림 프레임 세팅
    CCSpriteFrame *frame_0 = [CCSpriteFrame frameWithTexture:texture1 rect:CGRectMake( 0, 0, 25, 40 ) offset:CGPointZero];
    CCSpriteFrame *frame_1 = [CCSpriteFrame frameWithTexture:texture1 rect:CGRectMake( 45, 0, 25, 40 ) offset:CGPointZero];
    CCSpriteFrame *frame_2 = [CCSpriteFrame frameWithTexture:texture1 rect:CGRectMake( 80, 0, 25, 40 ) offset:CGPointZero];
    CCSpriteFrame *frame_3 = [CCSpriteFrame frameWithTexture:texture1 rect:CGRectMake( 118, 0, 25, 40 ) offset:CGPointZero];
    
   //기본으로 걷는 첫번째 프레임이미지으로 스프라이트 세팅한다.
    CCSprite *sprite = [CCSprite spriteWithSpriteFrame:frame0];
    sprite.position = ccp( size.width/2-80, size.height/2);
    [self addChild:sprite];
    
   //걷는 프레임으로 세팅한 이미지를 에니메이션으로 할당한다.
    NSMutableArray *animFrames = [NSMutableArray array];
    [animFrames addObject:frame0];
    [animFrames addObject:frame1];
    [animFrames addObject:frame2];
 
     //다이빙하는 프레임으로 세팅한 이미지를 애니메이션으로 할당한다.   
     NSMutableArray *animFrames1 = [NSMutableArray array];
    [animFrames1 addObject:frame_0];
    [animFrames1 addObject:frame_1];
    [animFrames1 addObject:frame_2];
    [animFrames1 addObject:frame_3];
    
   //애니메이션 변수 할당
    CCAnimation *animation;

    switch([(CCMenuItemFont*)sender tag])    // callBack에 들어온 sender를 위에 아이템과 같은 형으로 변환을 한다.
    {
        case 0:    //걷는 프레임 재생
            animation = [CCAnimation animationWithName:@"run" delay:0.2f frames:animFrames];
            [sprite runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO] ]];
            break;
            
        case 2:   //다이빙하는 프레임 재생
            animation = [CCAnimation animationWithName:@"dive" delay:0.2f frames:animFrames1];
            [sprite runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO] ]];
            break;

    }
 
작동 방식좀 알아볼겸 해서 이렇게 간단히 작성해봤네요..
당연히 구조 변경할거구요..어떻게 동작하는지 보려고 대충 작성해본거니까 머라고 욕은 마세요^^;;;

암튼 위의 소스를 보면 저의 질문이 파악될지 모르겠네요.

이렇게 하니까 0일경우 걷는 프레임을 재생하고 2일경우는 다이빙을 재생하지만..
메모리에서 헤제를 안하고 계속 이전 애니메이션이 재생됩니다. 걷는것도 계속나오고 다이빙하는것도 계속나옵니다.

걷기를 하다가 중간에 다이빙동작으로 바꾼다는지 하는 방법이 무엇이며..

혹은 걷거나 다이빙 하는 프레임중 1~2프레임만 반복한다든지 1번프레임만 정지상태로 계속 보이게 하고도 싶습니다.

어떻게 하면될까요? 화면상에서 지워지지 않는 문제도 있네요..어떻게 하면되는지요^^

고수님들의 좋은 답변 부탁드려요~
 
에어조단(ckw0507) / 2010.01.19 23:43

애니 메이션 등록을 
CCAnimation *aniWaitRight = [[CCAnimation alloc] initWithName:@"waitRight" delay:0.1];
[aniWaitRight addFrameWithFilename:@"0r.png"];
[aniWaitRight addFrameWithFilename:@"9r.png"];
[self addAnimation:aniWaitRight];
[aniWaitRight release];
이런식으로 하시고 사용하실때는 아래처럼..하시면 될꺼에요.
[self stopAllActions];
[self setDisplayFrame:@"waitRight" index:waitFrame%2];
waitFrame++;
if(waitFrame > 1) waitFrame = 0; 

schedule 을 등록하여 위에방법대로 count 를 이용하여 사용하는 방법이 있을것 같네요.

---
GTekna Corp(gtekna) / 2010.01.20 08:42

Animation 변수를 하나만 사용하지 마시고, 걷는 것과 다이빙에 쓰일 것으로 두 개를 만드세요.   Animation은 미리 만들어 놓고 재 사용하는 것이 좋습니다. 안 그러면, performance에 조금 지장이 있지요.

Posted by windship
프로그래밍/Cocos2D2009. 11. 7. 14:44
cocos2d Texture2d 에 대한 질문입니다. ^^; | i개발Q&A
0 / 2009.10.19 21:02
조금 밑에 어플개발시 메모리에 관한 질문을 보시면 제가

1024 * 1024 * 32비트  PNG 가 엑티비티 모니터링 시 리얼메모리 12메가를 잡아먹는다고 적어놨었습니다.

이에대해 채원아빠님께서 1024 * 1024 * 32비트면 약 4메가 정도 먹지않겠냐 라고 답변 다셨는데

저도 어제 약 4메가정도 먹어야 정상일텐데 왜 12메가 인지 한참 고민해었습니다.  결국 어차피 컴퓨터가 맞겠지 하고 -_- 

넘어가려고 하기도했었구요.. (이유를 못찾았었습니다.)

그러다 오늘 채원아빠님께서 말씀하신거 보고 제가 계산을 잘못한게 아니라고 생각해서  테스트를 실시했습니다.

지금 사용중인 어플은 OpenGL로 만들고있지만 실제로는 cocos2d의  Texture2d.h를 가져와서 활용하고있습니다.

그래서 일단 cocos2d  프로젝트를 열고  AtlasSpriteManager로 똑같은 이미지(1024 * 1024 * 32) 를 얹어보았더니 약 4메가가

리얼메모리에 추가된걸 확인할수있었습니다.

같은 파일을 가지고 Texture2d의 initwithImage를 사용해 불러오니 리얼메모리에 12메가가 추가되는 것도 확인할수있었습니다.

혹시 이런 문제로 고민하셨던 적이 있으신 분이 있으신가 해서 글 남깁니다. -ㅁ-... 또  저같이 texture2d.h를 활용하고계신분이라면

더더욱이.. .........................................................

이제 AtlasSprite와 Texture2d의 차이점을 확인하러가야겠습니다. ㅜ_ㅜ

작성자의 카페글 더보기

.....없어그런거..
덧글 4개 | | 조회수 108 | | 추천 0 나도추천
 담기 | 인쇄 | 신고
  • 2009/10/19 21:09

    답글|신고

    아하... 그랬었군요... ^^;................... (도움은 못드려서 ㅈㅅ.... 쩌비... )
    상황파악이 되셨으니, 어렵지않게 해결하시리라 생각되네요...

    하루에 한시간씩 시간투자를 하는데... gl study 할시간은.. ㅡ,ㅡ;;;;;;;;;;;..........

    힘 내세요...
    ^^;...................................

  • 2009/10/19 21:30

    답글|신고

    저도 굉장히 궁금한 부분이네요.
    역시 cocos2d를 사용하고 있고..게임의 규모가 커지다보니 슬슬 메모리의 압박이 시작되는 것 같아서요..
    혹시 알게 되시면 소중한 정보 공유해주시면 정말 감사하겠습니다. (_ _);;

  • 2009/10/19 22:39

    답글|신고

    해결했습니다. 대충 답이 맞는지 어쩐지는 확신은 없으나 일단 용량문제나 실행상의 문제는 전혀없게됐습니다.
    대충 문제의 원인에 대해 말씀드리자면.. 해당 이미지를 불러올때..
    _texture[0] = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"A.png"]];
    이런식으로 불러옵니다. [Texture2D alloc] <- 이부분에서 한번 메모리를 추가하죠..
    [UIImage imageNamed:파일이름] 이녀석의 정의를 보면 + (UIImage *)imageNamed:(NSString *)name; 입니다.
    안을 열어볼순없지만 alloc를 한다는 거죠; initWithImage안을 보면 UIImage ImageNamed:""로 불러온 녀석의 메모리를
    해제하는 코드가 없더군요.. 해결방법은 맨 마지막줄 reutrn self; 전에 [uiImage release]; 한줄 추가해주시면 됩니다.
    cocos2d라이브러리 템플릿 사용하시는 분은 처음 라이브러리 만드시는 단계에서 수정하셔서 시뮬레이션 - cocos2d
    디바이스 - cocos2d 를 해주신후에 나온 라이브러리 파일과 함께 Texture2D.h파일을 복사해서 덮어쓰시면 됩니다.

  • 2009/10/19 22:58

    신고

    ^^;.................... 그렇군요... ㄳㄳ...

Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 21:00
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 20:46
게임 제작의 중심은 주로 그림을 어떻게 표현하느냐,

뭐가 화면에 보여야 내가 제대로 만들고 있는건지 알 수 있는거 아닐까?

iPhone 게임 개발로 많이 사용되는게 cocos2D 라는 라이브러리이다.

현재 0.8.1 버전까지 나온 상태다.

http://appsnet.co.kr/bbs/cocos2d


여기 개발자 포럼에 상세하게 cocos2D의 개발환경 설정부터 

애니메이션 구현까지의 강좌가 설명되어있다.

어제까지 아이폰 시뮬레이터에 메뉴를 만들어서 띄우는 것까지 해보았다.

다음은 메인 소스이다.
//
//  Cocos2DAppDelegate.m
//  Cocos2D
//
//  Created by MOAi on 09. 10. 8..
//  Copyright 전북대학교 2009. All rights reserved.
//

#import "Cocos2DAppDelegate.h"
#import "cocos2d.h"

@implementation Cocos2DAppDelegate

@synthesize window;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // UIWidow 개체를 생성
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    // Director에서 사용할 View등록
    [[Director sharedDirector] attachInView:window];
    
    // HelloScene 생성
    Scene *helloScene = [Scene node];
    
    // hello world 라벨 만들기
    Label *helloLabel = [Label labelWithString:@"Hello World of Cocos2d."
                                      fontName:@"Helvetica" fontSize:20.0f];
    helloLabel.position = ccp(150,200);    // 위치 지정(가운데를 글자의 가운데 부분을 위치시켜야함)
    //[helloScene addChild:helloLabel];
    //초록색 코드는 맨처음 헬로우 월드 찍어보는 코드이고 라벨 사용법을 알기 위해서 코드를 남겨 놓았음


    // 이 부분을 강좌 잘 따라해가면서 만들어봤음 전체 소스가 없어서 고민해서 만드느라 시간이 상당히 오래걸렸다.
    // 기본 메뉴 아이템 설정
    [MenuItemFont setFontSize:30];
    [MenuItemFont setFontName: @"Courier New"];
    
    // 메뉴 아이템
    MenuItem *item1 = [MenuItemFont itemFromString: @"시작" target:self selector:@selector(menuStartCallback:)];
    MenuItem *item2 = [MenuItemFont itemFromString: @"환경설정" target:self selector:@selector(menuConfigCallback:)];
       
    
    // 메뉴를 담는 레이어(메뉴는 배열 형식)
    Menu *menu = [Menu menuWithItems: item1,item2, nil];
    
    //[menu alignItemsInRows:(NSNumber *)2];
    [menu alignItemsInRows:[NSNumber numberWithInt:2],nil];
    
    // 메뉴의 위치설정
    //menu.position = ccp(100,300);
    
    // 메뉴들의 위아래 간격 조절
    //[menu alignItemsVerticallyWithPadding:100.0f];
    


    // 씬에 메뉴를 추가(메뉴가 레이어를 상속받기때문에 다른 레이어 추가없이 추가 가능)
    [helloScene addChild:menu];

    // HelloScene을 실행
    [[Director sharedDirector] runWithScene:helloScene];
    
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}
                        
-(void) menuStartCallback: (id) sender
{
    NSLog(@"called start menu");
}                        

-(void) menuConfigCallback: (id) sender
{
    NSLog(@"called config menu");
}

- (void)dealloc {
    [window release];
    [super dealloc];
}


@end



빌드하고 실행하면 다음과 같이 시뮬레이터에 코드에 작성한 것처럼 

시작과 환경설정이란 메뉴가 나타난다.

왼쪽화면은 메뉴를 클릭했을때 콜백메소드를 제대로 호출하는지 확인하기 위해 띄운 콘솔창

위 스샷과 같이 메뉴를 누를때마다 콜백메소드가 잘 호출되고 있는게 보인다.

일단 인트로 화면이 이렇게 구성이 되어야 하는데. 메뉴 글자부터 짤라서 이미지나 만들어 놓을까


 
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:45
2009.08.11 22:24:12(*.76.17.162)
조회수:1509

 이번 강좌는 Ease Actions와 Misc Actions 라는 Action들을 배울 것이다. 그런데 생각보다 어렵다. 잘 이해가 되지 않는 것은 예제도 실행해보면서 다시한번 살펴봐라. 그리고 그래도 이해가 되지 않는다면 댓글을 달아주시길...


그럼 강좌를 시작하겠다. 


Cocos2d로 애니메이션 구현하기 3


3. Ease Actions


Ease Action은 애니메이션을 구현하는 속도에 관련된 Action들이다. 이제까지 알아본 것은 모두 기본 속도 1로 작동하는 것들인데 Ease Action을 통해서 애니메이션을 점점빠르게 갑자기 빠르게 등을 구현해 볼 수 있다. 

3.1 EaseIn / EaseOut


EaseIn은 이동이나 변환등의 액션을 처음에는 천천히 시작했다가 점점 빨라지는 애니메이션을 구현한다. 그러니까. 점점 빨라지는 Action 이다. Move Action을 사용하면 쉽게 이해할 수 있는데 특정 지점으로 이동하는 Action을 EaseIn에 등록하고 실행시키면 해당 지점으로 빨려들어가듯이 점점 빨리 움직이는 것을 볼 수 있다. EaseOut은 EaseIn과 반대로 처음에는 빨리 시작되었다가 점점 느려지는 애니매이션을 구현한다. Move Action에서 다시 생각해 보자면 특정 위치에서 빨리 빠져나와서 점점 지정된 위치로 이동하는 것으로 이해하면 쉽게 이해할 수 있을 것이다. 

다음 그림을 보면서 이해를 하도록 노력해 보자. x축은 시간이고 y축은 전체를 1로 봤을 때 처리한 프레임 수의 비율이다. 만약 Move라면 이동한 거리라 볼 수 있고, Rotate라면 회전한 각도 비율이라 볼 수 있다.

EaseInOut.png 

파란색은 EaseIn을 빨간색은 EaseOut의 Rate계산식을 나타낸 것이다. 그리고 가운데 검은 선은 기본 애니메이션 구현속도이고, 양 옆의 희미한 색의 그래프는 rate값을 좀더 크게했을 때의 속도그래프이다. 이 그래프를 보면 알 수 있듯이 EaseIn은 처음에는 느린 속도로 애니메이션하다가 점점 빠르게 애니메이션을 구현한다. EaseOut은 이와 반대로 처음에는 매우 빠른 속도로 애니메이션을 구현하다가 점점 느린 속도로 구현되도록 계산하는 것을 확인할 수 있다. 
 

사 용방법은 다음과 같이 Action을 하나 만들고 그 Action을 [EaseIn actionWithAction:rate:]에 입력되는 Action을  rate만큼의 로그 비율로 점점 빨라진다. rate의 값이 크면 클 수록 처음에는 더 느리고 중간 이동하는 속도가 더 빨라진다.

    id move = [MoveBy actionWithDuration:3 position:ccp(350,0)];
    id move_ease_in = [EaseIn actionWithAction:[[move copy] autorelease] rate:3.0f];
    [someSprite runAction:move_ease_in];

EaseOut도 동일한 방법으로 구현할 수 있다. 단, rate값이 크면 클 수 록 처음에 실행되는 애니메이션의 속도가 빨라지고 중간에 이동하는 속도는 급격히 떨어진다.


다음은 EaseIn/EaseOut이 실행되는 화면이다.

맨 위는 EaseIn을 두번째는 EaseOut을 세번째는 비교를 위해 기본 속도의 애니메이션을 출력한 것이다. 

(용량상 캡춰를 짧게 해서 잘 모를지도 모른다. 그럴 경우에는 밑에 소스를 첨부했으니 실행해 보자.)


3.2 EaseInOut


EaseInOut은 EaseIn과 EaseOut을 연속해서 실행하는 것과 동일한 결과를 얻을 수 있다. 처음에는 느리게 실행되었다가 점점 속도가 빨라졌다가 다시 점점 느려지는 애니메이션이 구현된다. 


다음은 실행되는 화면이다.

처음은 비율을 3.0f로 했을 경우이고 두번째는 5.0f로 했을 경우이다. 어떻게 차이가 있는지 비교해 볼 수 있다. 


3.3 EaseSineIn / EaseSineOut


EaseSineIn과 EaseSineOut은 EaseIn과 EaseOut의 rate를 Sine값으로 대체한 것이다. 그래서 Sine그래프처럼 처음에는 느리게 실행되다고 점점 빨라지다가 다시 느려지고, 말로 설명을 하려니까. 좀 힘이 든다. ㅜㅜ. 

다음의 이미지는 실제로 SineIn과 SineOut을 할 때 식의 그래프이다.

EaseSine.png 

빨간색이 SineIn의 애니메이션의 속도 변화값이고, 파란색이 SineOut의 애니메이션의 속도 변화값이다. 그리고 검은색은 Rate가 1일 때 일반적인 속도를 나타낸 것이다. 
이 그림을 보면 대충 짐작하겠지만 SineIn은 기본 애니메이션보다 느리게 출발하지만 점점 빨라지고, SineOut은 처음에는 빨리 출발하지만 점점 느려진다.

다음에 실행되는 화면을 추가할 테니 보시면서 잘 이해하길 바란다. ^^;;;


구현하는 방법은 다음과 같이 단순히 속도 조절을 원하는 Action을 입력받는다.

    id move = [MoveBy actionWithDuration:3 position:ccp(350,0)];
    id move_ease_in = [EaseSineIn actionWithAction:[[move copy] autorelease]];
    [someSprite runAction:move_ease_in];


다음은 실행되는 화면이다.

위의 것이 EaseSineIn이고 중간의 것이 EaseSineOut이고 마지막의 것이 기본 속도로 움직이는 것이다. 


3.4 EaseSineInOut


EaseSineInOut은 EaseSineIn과 EaseSineOut을 합쳐놓은 것과 같은 속도로 애니메이션을 구현하고 이를 계산하는 식의 그래프는 다음과 같다. 

EaseSineInOut.png 

구현하는 방법은 EaseSineIn이나 EaseSineOut과 동일하다.


다음은 실행되는 화면이다. 

 


3.5 EaseExponential


EaseExponential은 영어를 해석하자면 지수적인이란 접미어가 붙었는데 말그대로 지수로 애니메이션의 속도가 변한다. 그러므로 급격히 빨라지거나 급격히 느려지는 것을 구현할 때 사용하면 된다. 
다음은 EaseExponentialIn과 EaseExponentialOut의 속도 계산식을 그래프로 그린 것이다. 


EaseExponetial.png

빨간색이 EaseExponentialOut을 파란색이 EaseExponentialIn의 속도 변화를 그린 것이다. 그래프를 봐서 쉽게 이해되지 않는다면 다음 애니메이션을 참고해보자.

구현하는 방법은 EaseSine과 동일하게 속도를 변화하려는 Action만을 다음과 같이 입력하면 된다. 

    id move = [MoveBy actionWithDuration:3 position:ccp(350,0)];
    id move_ease_in = [EaseExponentialIn actionWithAction:[[move copy] autorelease]];
    [someSprite runAction:move_ease_in];


다음은 실행하는 화면이다.

 맨위가 EaseExponentialIn이고 두번째가 EaseExponentialOut이고 세번째에 비교를 위해서 기본 속도의 애니를 추가했다.



3.6 EaseExponentialInOut


EaseExponentialInOut 은 EaseExponentialIn과 EaseExponentialOut을 연속해서 실행시킨 것과 동일한 기능을 한다. 먼저 처음에는 느리게 시작되다가 갑자기 빨라지다가 목적지에 거의 도달해서 갑자기 느려진다. 초기위치에 도달위치를 알려주고 순간이동과 같은 경우를 구현하고자 할 때 사용하면 좋을 것 같다. 

실행하는 방법은 EaseExponentialIn이나 EaseExponentialOut과 같다. 

3.7 Speed


Speed는 Action의 속도를 중간 중간에 변경할 때 유용하게 사용할 수 있다. 초기에 생성시에 [Speed actionWithAction:speed:]로 해당 Action을 speed파라미터로 입력된 속도로 일정하게 애니메이션을 동작시키다가 [speedAction setSpeed:]로 해당 애니메이션 동작 속도를 변경할 수 있다. API에 보면 슬로모션이나 빠른 건너뛰기를 할 때 유용하게 사용할 수 있다고 합니다. 

다음은 점프하면서 회전하는 이미지를 1초마다 Scheduler를 통해서 속도를 변경시키는 예제이다. 매 초마다 변경할 속도는 랜덤한 값으로 설정하도록 설정한 예제이다.

- (void)onEnter
{
    [super onEnter];

    // rotate and jump
    IntervalAction *jump1 = [JumpBy actionWithDuration:4 position:ccp(-400,0) height:100 jumps:4];
    IntervalAction *jump2 = [jump1 reverse];
    IntervalAction *rot1 = [RotateBy actionWithDuration:4 angle:360*2];
    IntervalAction *rot2 = [rot1 reverse];

    id seq3_1 = [Sequence actions:jump2, jump1, nil];
    id seq3_2 = [Sequence actions: rot1, rot2, nil];
    id spawn = [Spawn actions:seq3_1, seq3_2, nil];
    id action = [Speed actionWithAction: [RepeatForever actionWithAction:spawn] speed:1.0f];
    [action setTag: kTagAction1];

    [someSprite runAction:action];

    [self schedule:@selector(altertime:) interval:1.0f];    // schedule을 등록한다.
}

- (void)altertime:(ccTime)dt
{
    id action = [someSprite getActionByTag:kTagAction1];

    [action setSpeed: CCRANDOM_0_1() * 2];
}

4. Mics Actions


4.1 CallFunc


CallFunc는 callback 메소드를 호출해서 실행시킨다. Sequence로 애니메이션을 다 구현한 후에 메소드를 실행시키려도 할 때 유용하게 사용할 수 있다. 
CallFunc는 [CallFunc actionWithTarget:selector:]를 사용해서 callback메소드를 입력하는 Action을 만들 수 있다. 

다음은 가로로 200이동한 후에 Callback을 호출하는 예제이다. 

-(void) onEnter
{
    [super onEnter];
    
    id action = [Sequence actions:
                 [MoveBy actionWithDuration:2 position:ccp(200,0)],
                 [CallFunc actionWithTarget:self selector:@selector(callback)],
                nil];
    [someSprite runAction:action];
}

-(void) callback
{
    [tamara setVisible:YES];
}

4.2 Orbit(궤도)


Orbit는 화면의 가운데를 중심으로 하는 구형의 좌표계에서 해당 Sprite를 궤도를 따라 움직이게 하는 Action이다. Orbit의 구현은 다음의 메소드를 사용한다. 
    [OrbitCamera actionWithDuration:        // 애니메이션 구현 시간
                        radius:deltaRadius:        // 시작지 및 목적지의 거리(반경) - 원근을 표시할 때 사용하면 좋음
                        angleZ:deltaAngleZ:        // Z축의 시작지 및 목적지 각도
                        angleX:deltaAngleX:        // X축의 시작지 및 목적지 각도

Orbit 구현은 구형 좌표계가 익숙하지 않으니까 이해하기가 쉽지가 않다. 다음의 예제를 가지고 값을 바꿔가면서 실행해보면 어느 정도는 이해가 될 것이다. 

    id orbit = [OrbitCamera actionWithDuration: 2 radius:0.5f deltaRadius:1 angleZ:0 deltaAngleZ:180 angleX:0 deltaAngleX:0];
    id action = [Sequence actions:orbit, [orbit reverse], nil];
    [someSprite runAction:action];



이제 Cocos2d로 구현할 수 있는 대부분 애니메이션의 Action을 알아봤다. 어떤 것이든 지금까지 배운 것을 다 조합하면 다 구현할 수 있을 것이다. 마지막으로 3번째 애니메이션 구현의 예제를 추가하겠다. SpriteTest3.zip 이것은 Cocos2d Test의 sample예제를 강좌를 위해서 편집한 것이다. 그럼 열심히 공부하시길... 


다음에는 터치이벤트를 받아서 처리하는 법을 배우겠다. 머 다들 알겠지만 간단히 훌른다는 기분으로 준비하려고 한다. 

그럼 다음 강좌를 기대하시라... ^^

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2D - 각종 레퍼런스  (0) 2009.11.06
cocos2D로 시작해보자  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:44
2009.08.11 21:43:39(*.112.98.77)
조회수:1309

 역시 글을 쓴다는 것은 어려운 것 같다. 이번에도 시간이 자료를 준비하는데 꽤 많은 시간이 소요됬다. 그래도 재미있는 기능들이 많이 있으니까. 기대하시라. 그런데 강좌를 쓰다보니까. 상당히 길어서 2,3으로 끊어서 여기에 연재하기로 결정했다. 그래서 이번에는 유래없이 연이어 두개의 강좌글이 올라갈 것이다. ㅋㅋ


이번에는 그리 어렵지않게 이해할 수 있을지 모르겠지만 다음 장은 좀 어려우니까. 잘 보고 이해하길 바란다. 그럼 강좌를 시작하겠다. 


Cocos2d로 애니메이션 구현하기 2


2. Composable Actions


Composable Actions는 전 시간에 배웠던 Transformation Actions를 여러개를 순차적으로 또는 동시에 같이 애니메이션을 실행시키는 것을 말한다. 예를 들자면 빙빙 돌면서 특정 위치로 이동을 한다던가.. 점점 커지면서 사라진다던가 또는 커지고나서 다른 위치로 이동한다음 다시 원래의 상태로 돌아가는 순차적인 애니메이션 등을 구현할 수 있다. 
실행시키는 방법은 Transformation Action을 만든 방식과 동일하다. 단 만들 때 여러 Action들을 조합하므로 Array를 생성하는 방식으로 만들어야 한다. 다음과 같이 말이다.

    id action = [Sequence actions:
                 [MoveBy actionWithDuration: 2 position:ccp(240,0)],
                 [RotateBy actionWithDuration: 2 angle: 540],
                 nil];
                
이제 Composable Actions의 종류별로 하나씩 알아보도록 하자.


2.1 Sequence

이는 순차적으로 등록된 Action들을 실행시키게 하는 것이다. Action인 A, B, C를 차레로 등록했다면 A Action이 끝난 뒤에 B Action이 실행되고, B Action이 끝난 뒤에 C Action이 실행되는 식이다. 그리고 눈치가 빠르신 분은 알겠지만 A, B등 입력되는 Action이 역시 Composable Actions이어도 된다. 그래서 다음과 같은 것도 가능하다. (다음은 설명을 위해서 로직을 간단히 한거다.)

    id seq = [A, B, C, nil] : A -> B -> C 실행

    id subSeq = [B, D, E, nil];
    id seq = [A, subSeq, C, nil]; // 이는 A를 실행한 후에 subSeq에 등록된 B -> D -> E를 실행하고 나서 다시 E를 실행시킨다.

이제 실제로 코딩된 예제를 보자. 다음은 현재 위치에서 가로로 240만큼 이동한 후에 제자리에서 540를 돌는 예제이다.

    id action = [Sequence actions:
                 [MoveBy actionWithDuration: 2 position:ccp(240,0)],
                 [RotateBy actionWithDuration: 2 angle: 540],
                 nil];

    [someSprite runAction:action];

그리고, 주의 해야할 것이 NSArray를 사용해 봤으면 알겠지만 Composable Actions는 내부에서 NSArray로 관리되기 때문에 배열의 끝을 nil로 꼭 알려줘야 한다. 그렇지 않으면 실행 시에 에러난다.

2.2 Spawn


Spawn 은 등록된 모든 Action들을 동시에 실행시키는 Action이다. 즉, 점점 커지는 액션과 이동 액션을 등록했을 경우, 점점 커지면서 해당 위치로 이동을 한다. 그런데 여기서 주의해야할 것은 시작 시간이 같은 것이지 애니메이션이 끝나는 시간은 각각의 Action에서 지정한 시간에 따라 천차 만별이 될 수 있다. 개발을 할 때 동시에 애니의 끝까지 완료되게 하고 싶다면 각각의 Action들의 애니메이션 시간을 동일하게 해줘야 한다. 만약, 끝나는 시간이 다른 Spawn Action을 Sequence Action의 element로 등록을 했다면 가장 애니메이션이 긴 액션을 완료한 후에 다음에 등록된 Action이 실행된다.

다음은 Spawn을 사용한 예제이다. Sequence와 동일한 방식으로 사용할 수 있다.

    id action = [Spawn actions:
                 [JumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4],
                 [RotateBy actionWithDuration: 2 angle: 720],
                 nil];

    [someSprite runAction:action];

2.3 Reverse


처 음엔 이 Action은 왜 있을까라는 생각도 했지만, 아주 유용하게 쓸모있는 Action이다. Action을 반대로 실행하게 한다. 그러니까. 만약, A에서 B로 이동하는 Action을 다음과 같이 reverse를 호출하면 B에서 A로 이동하게 된다. 

    id action = [MoveTo actionWithDuration:1 position:ccp(250, 250)];
    id reverseAction = [action reverse];

만약, Sequence와 다음에 배우게 될 Repeat이나 RepeatForever를 사용한다면 스타크래프트의 순찰지정과 같이 무한반복도 가능한다.

다음은 Sequece를 활용해서 가로로 250 이동하고 나서 세로로 50 이동한다음 다시 처음위치로 원위치되는 애니메이션을 구현한 예제이다. 간단하게  [seq reverse]로 원래 위치로 이동시킬 수 있다.

    id move1 = [MoveBy actionWithDuration:1 position:ccp(250,0)];
    id move2 = [MoveBy actionWithDuration:1 position:ccp(0,50)];
    id seq = [Sequence actions: move1, move2, [move1 reverse], nil];
    id action = [Sequence actions: seq, [seq reverse], nil];

    [someSprite runAction:action];

2.4 DelayTime


DelayTime은 지정된 시간동안 대기시키는 Action이다. Sequence로 각 Action들을 등록할 때 일정시간 동안 지연효과를 줄 때 아주 유용하게 사용할 수 있다. 다음과 같이 초단위 시간으로 지연시킬 수 있다.

    [DelayTime actionWithDuration:초단위 시간]; 

다음은 DelayTime을 사용해서 가로로 150이동했다가 2초동안 지연후 다시 가로로 150이동하는 애니메이션을 구현한 예제이다.

    id move = [MoveBy actionWithDuration:1 position:ccp(150,0)];
    id action = [Sequence actions: move, [DelayTime actionWithDuration:2], move, nil];

    [someSprite runAction:action];

2.5 Repeat, RepeatForever


Repeat과 RepeatForever는 단어에서도 볼 수 있듯이 Repeat은 지정한 횟수만큼 반복하는 것이다. RepeatForever는 영원히 등록된 Action들을 반복시키는 Action이다.
Repeat는 횟수를 지정하기 위해서 다음과 같이 times 파라미터가 추가되지만 RepaetForever는 반복할 Ation들을 등록하는 파라미터만 입력하면 된다.

    [Repeat actionWithAction:times:]
    [RepeatForever actionWithAction:]

다음은 Reverse시에 사용한 것을 Repeat을 사용해서 3번 반복 시키는 예제이다.

    id move1 = [MoveBy actionWithDuration:1 position:ccp(250,0)];
    id move2 = [MoveBy actionWithDuration:1 position:ccp(0,50)];
    id seq = [Sequence actions: move1, move2, [move1 reverse], nil];
    id action = [Repeat actionWithAction:[Sequence actions: seq, [seq reverse], nil]
                                   times:3];

이를 무한 반복 시키려면 다음 같이 사용하면 된다.

    id action = [RepeatForever actionWithAction:[Sequence actions: seq, [seq reverse], nil]];



여기까지 간단하게 Cocos2d의 Composable Actions에 대해서 알아봤다. 예제를 SpriteTest2.zip  보면서 실행해보면 쉽게 이해할 수 있을 것이다. 

'프로그래밍 > Cocos2D' 카테고리의 다른 글

cocos2D로 시작해보자  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:44
2009.07.26 21:08:06(*.112.98.77)
조회수:2046

 저번 주에 한주 강좌를 올리지 않았더니 이번주에 만드는 데 좀 힘들었습니다. ^^; 한주 쉬었더니 역쉬 다시 시작하기 힘들더군요.

그래도 강좌를 기다리신다는 쪽지를 보내신 분도 있고 제 글을 읽어주신다니 뿌듯할 뿐입니다. 


이번에는 기본적인 애니메이션을 구현하는 방법에 대해서 알아보겠습니다. 원래 움직이는 것이라 이미지를 많이 넣어야 하는데 이미지를 화나하나 캡춰하는 것이 좀 힘들더군요. 그래도 소스를 첨부했으니 해당 소스를 보시고 하나씩 따라하시면 쉽게 이해하실 수 있을 겁니다. 


자 그럼 본격적으로 강좌를 시작하겠습니다. 



Cocos2d로 애니메이션 구현하기 1


Cocos2d에서 애니메이션은 Sprite와 Action을 사용해서 구현할 수 있다. 내가 Cocos2d로 제작을 결정하게 했던 가장 큰 이유가 애니메이션을 쉽게 구현할 수 있다는 것이었다. Cocoa로 애니메이션을 구현하려면 여러가지 설정을 많이 해줘야 한다. 물론 랩핑을 해서 간단하게 만들 수 있지만 필요할 때마다 매번 만들어 줘야 한다는 것은 여간 불편한 것이 아니다. 그런데 Cocos2d는 이러한 것들을 단순히 Action을 상속한 클래스들을 사용해서 해당 애니메니션의 모션을 정의해 준 다음에 runAction을 호출해주면 애니메이션이 간단하게 구현된다. 뭔가 짜릿함을 느끼는 순간이다. (나만 그런가, 해킨토시에서 처음으로 Hello의 동영상을 음성과 함께 볼 때의 그 희열과 짜릿함과 같은 느낌이었다. ㅋㅋㅋ)


Cocos2d에서는 애니메이션을 편의상 다음과 같이 4개의 Action군으로 분류를 했다. 

  1. Transformation Actions
  2. Composable Actions
  3. Ease Actions
  4. Mics Actions

어짜피 편의상 분류를 했기 때문에 이해할 때만 이런식으로 구분했구나를 이해하고 사용할 때는 아무렇게나 마구 써도 상관없다. 어짜피 모두다 Action 클래스를 상속했기 때문에 모두다 결합해서 사용할 수 있기 때문이다.

애니메이션 구현은 위에서도 말했듯이 정말 간단한데 먼저 action 인스턴스를 생성한다. 

    id actionTo = [MoveTo actionWithDuration:2.0f position:ccp(s.width - 40, s.height - 40)];

여 기서는 MoveTo를 예시로 했는데 다른 것들도 비슷하게 생성할 수 있다. actionWithDuration은 애니메이션을 동작 시간을 초단위로 입력한다. 위 예제는 해당 위치까지 2초 동안 ccp(s.width - 40, s.height - 40)로 이동하라고 정의하는 것이다. 

그런다음에 이런 action 인스턴스를 실행시키려면 움직이려고 하는 Sprite의 runAction을 호출해준다.

    [someSprite runAction:actionTo];

이렇게 입력되는 순간부터 someSprite는 움직이기 시작해서 2초의 시간을 두고 해당 위치로 이동을 한다. 
전 에 내가 Java로 모바일 게임을 제작할 때 애니메이션을 구현할 때에는 매 프레임시간마다 해당 위치를 계산해서 매번 그려줬어야 하는데 Cocos2d는 그럴 필요없이 간단히 위와 같이 해주면 중간 계산도 알아서 해서 화면에 출력해 준다. 정말 대단하지 않은가...... ㅋㅋ

그리고 애니메이션은 Sprite에 줄 수 있기 때문에 여러개의 Sprite가 동시간에 각각 다른 움직임을 처리할 수 있다. 
이렇게 구현할 수 있는 애니메이션의 종류가 이동시키기, 회전하기 등등 여러개가 있는데 그것들을 몽땅 하나씩 살펴보도록 하자.

1. Transformation Action

Transformation Action은 Sprite의 이동, 회전 등 일회성의 움직임이나 효과를 분류해 놓은 것들이다. 

1.1 Move


Sprite를 이동시키는 Action으로 MoveTo와 MoveBy가 있다. 그리고 다음과 같이 사용할 수 있다. 

    id actionTo = [MoveTo actionWithDuration: 2 position:ccp(200, 200)];

    id actionBy = [MoveBy actionWithDuration:2  position: ccp(80,80)];

MoveTo 는 해당 위치로 이동을 하는 것이고 MoveBy는 현재 위치에서 position으로 지정한 좌표만큼을 이동한다. 위의 예제에서 보자면  actionTo는 Sprite가 어디에 위치해 있든지 200, 200의 위치로 이동을 한다. 그렇지만 actionBy는 sprite가 현재 있는 위치에서 80, 80을 더한 위치로 이동한다.


1.2 Rotate

Sprite에서 회전을 시키는 Action으로는 RotateTo와 RotateBy가 있는데, Move에서는 position을 설정해 주었는데 Rotate에서는 회전할 각도를 다음과 같이 설정해 주어야 한다. 

    id actionTo = [RotateTo actionWithDuration: 2 angle:45];
    id actionBy = [RotateBy actionWithDuration:2  angle: 180];

RotateTo 는 어떤 각도에 있든 angle로 설정한 각도록 회전한다. 하지만 RotateBy는 angle각 만큼을 회전을 한다. 위의 예제로 예를 들자면 actionTo는 어떤한 각도에 있더라도 기울기가 45도로 회전하지만, actionBy는 어떠한 각도에 있어도 현재 각도에서 360도를 회전한다. 그래서 actionTo는 애니메이션이 끝나면 항상 45도이지만 actionBy는 만약 0도에서 시작하면 180도에서 끝나고 45도에서 시작했으면 225도에서 애니메이션이 끝난다.

이제 To와 By의 차이점을 어느 정도 알 수 있지 않을까? To는 절대적인 위치나 각도 등에 절대적인 값을 설정한든데, By는 현재 값에 추가되는 상대적인 값을 설정하는 것이다.


1.3 Scale

Sprite 의 크기를 변경하는 Action으로는 ScaleTo와 ScaleBy가 있고, ScaleTo는 scale로 설정한 배율로 크기를 변경한다. 1보다 작으면 작게 1보다 크면 해당 배수만큼 scale값으로는 1.0f와 같은 실수값을 설정한다.

    id actionTo = [ScaleTo actionWithDuration:2 scale:0.5f];
    id actionBy = [ScaleBy actionWithDuration:2  scale: 2];

위의 예제에서 actionTo는 0.5배의 크기로 Sprite를 2초동안 변환시키고, actionBy는 2초동안 현재 크기의 2배로 크기를 변경시킨다.


1.4 Jump

Cocos2d 에서는 애니메이션에서 특이하게 Jump도 설정할 수 있는데, JumpTo와 JumpBy로 Jump를 설정할 수 있는데 To와 By의 차이점은 각각 position에 대한 값의 절대적 상대적 차이로 설정된다. 즉, JumpTo는 절대적으로 설정한 position 위치로 Jump를 하면서 이동을 하는데 JumpBy는 현재 위치에서 position으로 설정된 값만큼을 더한 위치로 Jump하면서 이동한다.

    id actionTo = [JumpTo actionWithDuration:2 position:ccp(300,300) height:50 jumps:4];
    id actionBy = [JumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4];

위 의 예제에서는 actionTo는 2초동안 300, 300의 위치로 높이는 50픽셀로 4번의 점프를 하면서 이동을 한다. actionBy는 2초동안 현재 위치에서 가로로만 300픽셀을 더한 위치로 높이는 50픽셀로 4번 점프를 하면서 이동을 한다.


1.5 Bezier(곡선)


Sprite 가 이동할 경로를 곡선을 하고 싶을 때에는 BezierBy actionWithDuration:bezier:를 사용한다. Bezier 를 설정할 때에는 이동할 경로를 잘 계산을 해야 한다. 다음의 그림을 보면서 확인해 보자.


Cocos2d 곡선 애니1.png 


위의 그림에서와 같이 파란색으로 된 부분을 point로 start, controlPoint_1, controlPoint_2, end의 네군데를 point로 설정하면 빨간색으로 된 곡선을 이동 경로로 Sprite가 이동하게 된다. 
그 런데 여기서 Sprite가 위치한 현재 위치에서 설정한 상대적인 위치로 이동한다, 즉 다시 말해서 0,0에서 시작한다면 위의 그림의 빨간색 경로로 이동을 하지만 100,100에서 시작을 한다면 다음과 같이 중간의 경로는 변경없이 시작위치만 변경된 경로로 이동을 하게된다.


Cocos2d 곡선 애니2.png 


bezier의 파라미터로는 ccBezierConfig의 값을 설정해서 입력한다.

    ccBezierConfig bezier;
    bezier.startPosition = ccp(0,0);
    bezier.controlPoint_1 = ccp(0, s.height/2);
    bezier.controlPoint_2 = ccp(300, -s.height/2);
    bezier.endPosition = ccp(300,100);

    id bezierForward = [BezierBy actionWithDuration:3 bezier:bezier];

아무래도  Bezier를 사용할 때에는 확실히 계산을 하고 사용해야할 것 같다. ^^


1.6 Blink

Blink는 Sprite를 깜박이게 만든다. Blink actionWithDuration:blinks: 메소드를 사용하는데 여기서 blinks로 actionWithDuration에 설정한 시간만큰 깜박일 횟수를 설정한다.

    id action1 = [Blink actionWithDuration:2 blinks:10];

위 예제는 2초동안 10회 깜박이게 하는 Action의 예제이다.


1.7 Fade

Sprite 를 점점 나타나게도 점점 사라지게도 할 수 있는데 이렇게 할 수 있도록 하는 Action이 FadeIn과 FadeOut이다. FadeIn과 FadeOut은 간단하게 나타나거나 사라질 시간을 설정을 하면 된다. (FadeIn/Out은 Opacity를 사용하는 것이기 때문에  나타나게 하려면 먼저 Opacity값을 0으로 해서 먼저 안보이게 해야한다.)

    id action1 = [FadeIn actionWithDuration:1.0f];
    id action2 = [FadeOut actionWithDuration:1.0f];

위의 예제에서 action1은 1초 동안 점점 나타나게, action2는 1초 동안 점점 사라지게 한다.


1.8 Tint

Sprite 의 농도를 마음대로 변경도 할 수 있다. 이는 TintTo actionWithDuration:red:green:blue: 와 TintBy actionWithDuration:red:green:blue:를 사용해서 수정할 수 있다. 앞에서도 많이 경험했다 싶이 이번에도 To와 By가 나와았는데 To는 절대적으로 rgb에 맞는 농도가 변경되고 By는 현재의 값에서 rgb가 추가되어 색상이 변경되어 진다. 

다음은 이를 사용한 예제이다. 

    id action1 = [TintTo actionWithDuration:2 red:255 green:0 blue:255];
    id action2 = [TintBy actionWithDuration:2 red:0 green:-127 blue:-127];


1.9 Animate

지금까지는 하나의 Sprite를 가지고 이동시키거나 크기를 변경시키거나 회전시키는 등의 애니메이션을 살펴봤는데 이번에는 여러개의 Sprite이미지들을 사용해서 각각을 연속적으로 보여주면서 하나의 애니메이션을 출력하는 것을 알아보려고 한다. 

이를 위해서는  기존과 다른 방식을 사용해야하는데 우선 Animation 인자를 먼저 생성하고,

    Animation* animation = [Animation animationWithName:@"dance" delay:0.2f];

 여기에 이 애니메이션에서 출력할 이미지들의 파일이름으로 로드해서 적재하게 한다.

    for( int i=1;i<15;i++)
        [animation addFrameWithFilename: [NSString stringWithFormat:@"dance_%02d.png", i]];

그런 다음에 이 애니메이션을 Action으로 생성한다.

    id action = [Animate actionWithAnimation: animation];

이를 다음과 같이 실행하면 된다. 

    [someSprite runAction:action];


지금까지 아주 기본적인 애니메이션을 구현하는 법을 공부해 봤다. 
이것만 가지고도 같단한 애니메이션을 구현할 수 있는데 좀 더 복잡한 Sprite가 커지면서 이동하는 등의 애니메이션을 구현을 하려면 몇가지만 더 알면 된다. 이것은 다음에 알아보려고 한다. 


위에서 설명한 내용들을 Cocos2d의 SpriateTest를 가지고 강좌에 맞도록 수정한 소스를 첨부합니다. 

공부하시면서 보면 쉽게 이해하실 수 있으실 것입니다. SpriteTest.zip 

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
메뉴 만들기  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
cocos2d 개발환경 설정  (0) 2009.11.06
Posted by windship
프로그래밍/Cocos2D2009. 11. 6. 16:43
zemyblue
2009.07.12 21:03:02(*.112.98.77)
조회수:1717

 ㅎㅎ 다시 Cocos2d의 강좌가 시작되었습니다. 

열심히 쓰기는 했는데 다들 어떻게 받아들이고 있는지 모르겠네요. 흠흠.. 피드백이 없으니 별 도움이 안되었는지 아님 글만 보고 만 것인지.. 암튼 이번주에는 Cocos2d를 사용해서 메뉴를 만드는 법을 배워보도록 합시다.

이번에도 설명글이 좀 많으니까 주의깊게 읽어야 할 것입니다. offline모임에 나오면 실제로 어떻게 구현하는지 볼 수 있을 것입니다. 


1 메뉴 만들기 

1.1 기본 메뉴

Cocos2d에서 메뉴 관련 클래스는 Menu, MenuItem, MenuItemFont, MenuItemAtlasFont, MenuItemFont, MenuItemImage, MenuItemToggle이 있다. 이 중에서 기본적으로 사용하는 것은 Menu, MenuItem, MenuItemFont 정도이고, 다음에 설명할 이미지를 사용한 메뉴를 만들 때에는  MenuItemImage 클래스를 사용한다.

기본 메뉴는 일반적인 글씨를 사용해서 메뉴를 생성하는 것이다. 글씨를 사용하기 때문에 어떤 폰트의 어떤 크기를 사용할지를 미리 설정해야 한다. 그러기 위해서 다음과 같이 MenuItemFont를 설정해 둔다. 

    [MenuItemFont setFontSize:30];
    [MenuItemFont setFontName: @"Courier New"];

여기서 setFontSize와 setFontName은 static변수이기 때문에 한번 설정해 놓으면 다시 변경하기 전까지 계속 같은 값을 참조할 수 있다. 
이제 이 폰트를 사용하는 메뉴는 다음과 같이 생성한다.

    MenuItem *item1 = [MenuItemFont itemFromString: @"Start" target:self selector:@selector(menuCallback:)];

itemFromString 에 표시할 메뉴의 내용을 target에는 메뉴가 click되었을 경우에 호출된 메소드를 가지고 있는 클래스의 인스턴스를, selector에는 메뉴가 click되었을 경우에 호출된 메소드의 주소를 입력해 준다. 메소드의 주소는 @selector를 통해서 가져온다.

이렇게 만들어진 MenuItem *item1을 Menu에 추가하고 다시 이 Menu를 화면에 표시할  Layer에 추가해야 메뉴를 화면에 출력할 수 있다. 

    Menu *menu = [Menu menuWithItems: item1, nil];

    [someLayer addChild: menu];

메뉴는 위와 같이 menuWithItems를 사용해서 각각 생성된 MenuItem의 하나 하나 넣어주고 마지막을 nil로 채워주면 해당 값들이 Array형태로 차례로 입력되어져서 화면 출력에 사용된다. 간단하지 않은가? 그런데 여기서 끝나는 것이 아니라 이제 Start라는 메뉴를 만들었는데 이 메뉴가 click 되었을 경우에 호출될 메소드를 구현해 주어야 한다. 위에서 menuCallback 을 호출하도록 했으니까. 다음과 같이 menuCallback 메소드를 만들어 준다.

-(void) menuCallback: (id) sender
{
    NSLog(@"called start menu");
}


1.2 이미지 메뉴 만들기

이미지 메뉴는 MenuItemImage를 사용해서 정의할 수 있다. 이미지 메뉴를 사용하기 위해서는 일반 이미지와 메뉴가 선택되어져서 눌려졌을 경우의 이미지가 있으면 된다. 추가적으로 비활성화 되었을 경우에 보여질 이미지도 추가할 수 있다. 

    MenuItemImage *item1 = [MenuItemImage itemFromNormalImage:@"normalImage.png" selectedImage:@"selectedImage.png" target:self selector:@selector(menuCallbackBack:)];

normalImage.png는 일반적으로 보여질 이미지를 selectedImage.png는 click되었을 경우에 보여질 이미지를 입력해 준다. target은 기본 메뉴에서와 동일하게 click되었을 경우에 호출될 메소드가 있는 클래스의 인스턴스를 selector를 호출될 메소드명을 입력한다. 이 메뉴도 일반 메뉴와 동일하게 [Menu menuWithItems:]를 통해서 입력한다.

1.3. 토글 메뉴

토 글 메뉴는 MenuItemToggle 클래스를 사용한다. MenuItemToggle클래스는 하나의 컨테이너로 기본 MenuItem 를 배열로 입력받아서 매번 click시마다 순서대로 다음 메뉴를 표시하게 해준다. 예를 들어서  "ON", "OFF"를 각각 표시하는 메뉴일 때 다음과 같이 사용하면 된다. 

    [MenuItemToggle itemWithTarget:self selector:@selector(menuCallback:) items:
        [MenuItemFont itemFromString:@"ON"],
        [MenuItemFont itemFromString:@"OFF"], nil];

그 리고 여기서 callback으로 호출되는 -(void)menuCallback:(id)sender 에서 sender는 현재 연결된 MenuItemToggle이 입력된다. 그리고 [sender selectedItem]를 호출하면 현재 click을 통해서 변경된 MenuItemFont가 반환된다. 그러니까 다시 말을 하자면, ON일 때 click을 하면 OFF로 변경이 되는데 이 때 [sender selectedItem]으로는 [MenuItemFont itemFromString:@"OFF"] 로 생성된 클래스가 반환된다. ^^ 설명이 잘되었는지 모르겠다.


2. 설정하기

등록된 MenuItem들은 위지를 조절할 수 있고, 수직, 수평정렬을 할 수 있고 메뉴의 행과 열의 개수도 다음과 같은 메소드들을 사용해서 설정할 수 있다.


2.1 출력 위치 조절

메뉴는 기본적으로 스크린의 가운데를 기점으로 상하 또는 좌우로 동일한 간격으로 메뉴들이 위치하게 된다. 그러므로 메뉴를 특정한 위치에 시키고 싶다면 Menu의 setPosition의 메소드를 사용하거나 position을 직접 제어해서 Menu의 중앙의 값을 설정하면 된다. 
만약 각각의 MenuItem들을 각기 다른 위치에 위치시키고 싶다면 Menu에 MenuItem을 하나만 입력하고 각각의 위치를 설정하면 된다. 

다음 이미지는 메뉴를 구성할 때 간격과 위치에 대한 설명이미지이다.

menutest_small.png 



2.2 수직 정렬

수직 정렬은 alignItemsVertically으로 postion값을 중심으로 KDefaultPadding(5) 간격으로 나열된다. 
각각의 간격을 다른 값으로 하려면 alignItemsVerticallyWithPadding:(float) padding 메소드로 간격값을 실수로 입력하면 된다.

그림 3.png 


2.3 수평 정렬

수평 정렬은 alignItemsHorizontally 메소드로 수직정렬과 마찬가지로 position을 중심으로 kDefaultPadding간격으로 나열된다. 
이 역시 각 간격은 alignItemsHorizontallyWithPadding: (float) padding 메소드로 간격값을 변경시킬 수 있다.

그림 1.png 


2.4 Column 개수 설정

만약에 다음 그림과 같이 일률적으로 정렬만 되는 메뉴를 행마다 다른 수로 정렬시키고 싶다면 alignItemsInColumns: (NSNumber *) columns, ... 메소드를 사용해서 각 행마다 개수를 다음과 같이 입력한다.

    [menu alignItemsInColumns:
     [NSNumber numberWithUnsignedInt:2],  // Sound, Music
     [NSNumber numberWithUnsignedInt:2],  // On, On
     [NSNumber numberWithUnsignedInt:2],  // Quality, Volume
     [NSNumber numberWithUnsignedInt:2],  // High, 66%
     [NSNumber numberWithUnsignedInt:1],  // Go Back
     nil
    ]; // 2 + 2 + 2 + 2 + 1 = total count of 9

그림 4.png 


2.5 Row 개수 설정

Row도 Column과 동일한 방법으로 설정할 수 있다. 이 부분은 각자 연구해 보기 바란다.



3. 응용하기


3.1 이미지 메뉴 크기 변경

이미지 메뉴의 크기는 CocosNode에 정의된 속성인 scaleX와 scaleY를 이용하면 된다. 각 값은 실수값으로 1.0f는 실제 배율의 크기이고 이 값보다 작을 경우에는 작게 클 경우에는 큰 이미지로 변경된다. 

     MenuItemImage *item1 = ...;
    item1.scaleX = 1.5f
    item1.scaleY = 0.5f

scale1.png 기본이미지

scale 2.png scaleX = 0.5f

scale 3.png scaleY = 0.5f

scale 4.png scaleX = 1.5f

scale 5.png scaleY = 1.5f


3.2 메뉴 비활성화 시키기

메뉴를 비활성화 시키기 위해서는 isEnabled를 NO로 설정하면된다. 

    MenuItem *item1 == ...;
    item1.isEnabled = NO;


메뉴를 비활성화 시키면 다음 그림과 같이 선택도 안되지만 Opacity값이 줄어든채 색으로 설정된다.

그림 5.png (비활성화 된 경우)

그림 6.png (활성화 된 경우)


3.3 종료 메뉴 만들기

메뉴가 선택되었을 때 다음을 호출하면 어플이 종료된다. 그런데 이 명령은 iPhone 2.2.1이하에서만 동작한다. 3.0에서는 동작하지 않는데 아는 분은 답글 부탁한다.

    [[Director sharedDirector] end];
    if( [[UIApplication sharedApplication] respondsToSelector:@selector(terminate)] )
        [[UIApplication sharedApplication] performSelector:@selector(terminate)];




Cocos2d에서 제공하는 테스트용 소스를 좀 편집해서 강좌에서 설명한 내용을 다음과 같이 만들어봤다. 소스(MenuTest.zip)를 보면 좀더 이해하기 쉬울 것이다. 

그림 10.png 

<메인 메뉴>


그림 8.png 

<MenuItemImage 클래스로 구현>


그림 9.png 

<MenuItemToggle 클래스로 구현>



다음에는 Sprite의 사용법에 대해서 알아보겠습니다. 애니메이션 구현이 주를 이룰 것 같은데 두번의 강좌를 통해서 Cocos2d의 애니메이션 구현 방법을 알아보겠습니다. 다음 강좌도 기대하시라...^^a

'프로그래밍 > Cocos2D' 카테고리의 다른 글

Cocos2d 애니메이션 구현하기 3  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 2  (0) 2009.11.06
Cocos2d 애니메이션 구현하기 1  (0) 2009.11.06
Cocos2d의 구성  (1) 2009.11.06
cocos2d 개발환경 설정  (0) 2009.11.06
Posted by windship