isometric Framework 개발과정

Repository : https://bitbucket.org/ls_pp/isometricscene

컬드셉트/모노폴리/블루마블 같은 보드게임 프레임웍 **설계 중**

* 주사위, 멈춘 숫자만큼 이동 기능구현 데모

* 각 땅의 다음칸이 어느 녀석인지 정의 할 수있도록 스테이지 구성 기능에 추가 하고 애니메이션을 한칸, 한칸 순서대로 움직이도록 하는게 의외로 힘들었음

* 오른쪽 방향으로 또는 왼쪽 방향으로, 순환 구조로 끝과 처음을 이어서 빙빙 돌 도록 하는 구현

* 2명 이상을 플레이어가 참가 할 수 있도록 순서(StateControl) 클래스 구현

* 땅을 소유 하고, 점수를 기록하는 시스템 구현

** 진행 중 **

Twitter Weekly Updates for 2012-02-19

  • OOP 에 아주 조금 익숙해 지고 있다 #
  • 외부적인 문제로 단골 카페를 잃은 것 같다 #
  • 어렸을때 동네 꼬마들의 놀이에서도 그랬던 것 같은데, 보드게임 같은 것을 만들면서 가장 어려운 건 승리조건인가보다 #

Powered by Twitter Tools

invader – 02 화면회전, 터치 이벤트 처리 1

이 예제는 facebook의 게임/앱 스터디 그룹을 위해 작성 되었습니다. “왜 이렇게 하는가?” 에 대해서는 따로 설명하겠습니다.

화면 방향을 세로(포트레이트=portrait)방향으로 세팅하고 터치 이벤트를 받아들여 플레이어 스프라이트를 움직여 보도록 하겠습니다.

프로젝트 네비게이터에서 GameConfig.h 파일의 다음 부분을 수정 합니다.

[code language=”objc”]
//#define GAME_AUTOROTATION kGameAutorotationUIViewController ==> 아래 행으로 수정
#define GAME_AUTOROTATION kGameAutorotationNone
[/code]

다음은 AppDelegate.m 파일의 다음 부분을 수정 합니다.

[code language=”objc”]
/*
#if GAME_AUTOROTATION == kGameAutorotationUIViewController
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
#else
[director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
#endif
==> 아래 행으로 수정 */
[director setDeviceOrientation:kCCDeviceOrientationPortrait];[/code]

이렇게 하면 자동 회전 기능에 영향을 받지 않고 화면을 세로로 길게(portrait)로 고정 시킬 수 있습니다. (더 좋은 방법이 있을 수도?)

처음 기획 단계에서 UI를 어떻게 할 지를 빼먹긴 했는데 위와 같은 화면(아이폰3 기준 가로320/세로480)을 좌-중-우로 3등분 해서

  1. 좌 지역이 터치 되면 플레이어 캐릭터를 좌측으로 움직이고
  2. 우 지역도 마찬가지
  3. 계속 누르고 있으면, 계속 이동
  4. 좌,우 화면 끝에 다다르면 더이상 이동하지 않음
  5. 중 지역은 다음 예제로 미사일을 발사

로 정의 하고 좌우 움직임에 해당하는 1,2 애니메이션을 만들어 봅시다. HelloWorldLayer.h 파일에 다음 내용을 기록 합니다.

[code language=”objc”]
@interface HelloWorldLayer : CCLayer
{
BOOL onMoving;
CCSprite *playerSprite;
}[/code]

onMoving 변수는 터치를 손을 계속 대고 있을 때 연속해서 플레이어 스프라이트를 움직이게 할 수 있도록 계속 움직일 것인지 멈출 것인지를 정의한 변수 입니다.
playerSprite 는 이름 그대로 플레이어 스프라이트를 클래스 전역으로 정의 한 변수 입니다.

[code language=”objc”]
// returns a CCScene that contains the HelloWorldLayer as the only child

+(CCScene *) scene;
-(void) moveLeftCheck:(id)anObject;
-(void) moveRightCheck:(id)anObject;

-(BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
-(BOOL) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
-(BOOL) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
@end[/code]

cocos2d 프레임웍의 터치 이벤트 3종세트를 오버라이드 하여 사용 합니다.

  1. ccTouchesBegan : 터치를 하는 순간 이벤트를 처리 합니다.
  2. ccTouchesEnded : 손을 떼는 순간 이벤트를 처리 합니다.
  3. ccTouchesMoved : 누르고 이동(드레그) 하는 이벤트를 처리 합니다.

그리고 커스텀 메쏘드로 아무 생각 없이 지은 이름의 메쏘드 들 입니다

  1. moveLeftCheck : 왼쪽으로 이동 애니메이션 중에 계속 할지 말지를 검사하는 메쏘드
  2. moveRightCheck : 마찬가지로 오른쪽으로 이동 에니메이션 중에 검사 메쏘드

다음은, 터치 3종세트 이벤트에 어디에 터치가 이루어 졌는지는 확인 하는 코드 입니다. cocos2d에서 어떤 스프라이트에 터치가 되었는지를 정리해서 확인 하는 “특별한” 방법은 아직 없습니다. 그래서

[code language=”objc”]
-(BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch)
{
CGPoint touchedlocation = [[CCDirector sharedDirector] convertToGL: [touch locationInView:touch.view]];
NSLog(@"touchBegan:ccp(%.0f, %.0f)", touchedlocation.x, touchedlocation.y);
}
return YES;
}[/code]

위의 코드와 같은 방법으로 touch객체를 통해 Director에서 GL로 변환된(몰라도 됨) x,y 좌표 경로를 가져올 수 있습니다. touchedlocation.x 가 좌 1/3 인지 우 1/3 인지 판별해서 각각에 대한 에니메이션을 작성 하면 되겠습니다.

invader – 01 리소스 배치, cocos2d

이 예제는 facebook의 게임/앱 스터디 그룹을 위해 작성 되었습니다.
cocos2d 템플릿을 이용 한 프로젝트 시작과 스프라이트 이미지를 등록 하고 화면에 출력하겠습니다.

프로젝트를 생성하기 위해 xcode를 실행 합니다. 아래 화면과 코드 작성은 Mac OS X 10.7(Lion)에서 XCode 4.1(app)에 cocos2d-iphone 1.0.1 로 했습니다. 특별한 기능을 사용하는 것은 아니기 때문에 iOS3이상을 지원하는 다른 버전의 환경에서도 충분히 작동합니다.

cocos2d 프레임웍의 템플릿으로 새 프로젝트를 선택 하는데, cocos2d 설치는 사이트의 메뉴얼을 참조 해 주세요.

새로 만들어진 프로젝트를 시뮬레이터로 실행하면 관련 프레임웍과 클래스를 어느정도 세팅을 미리 해 놓아서 아래와 같은 결과를 보여주는 예제를 볼 수 있습니다.

스프라이트?

사전적의미로는  “비디오 게임의 2D 평면적인 사물” (http://cglink.com/1024) 이라고 할 수 있는데, 게임에서 움직인다든지 배경에 깔린다든지 애니메이션을 보여준다든지 등 어떠한 역할을 할 수 있는 것의 단위입니다.

우선 플레이어, 인베이더 스프라이트 크기를 32*32 픽셀 정도 크기로 생각 했는데 정식으로 작업 된 디자인은 아직 없으니 구글링 해서 대강 비슷한 아이콘 하나를 골라 플레이어 스프라이트로 사용 해 보겠습니다.

이런 이미지를 다운로드 받아 둔 다음에 드래그 해서 xcode (기본으로 보이는) 프로젝트 네비게이터의 Resources 항목 안으로 드롭 해 넣으면 다음과 같이 됩니다. (저는 Sprites라는 그룹을 생성해서 넣었지만 상관 없습니다)

특이할 점은 xcode 에서는 리소스가 다른 그룹(폴더)에 있다고 해서 네임스페이스를 따른 다든지 하지 않습니다. 오직 파일이름으로만 구분되니 주의 해야 합니다.

다음은 위의 프로젝트 네비게이터에도 보이는 HelloWorldLayer.m 에 플레이어를 올려 놓을 수 있는 코드를 작성 해 보겠습니다.
코드 중에 init 메소드부분을 보면 템플릿을 통해 생성했으므로 다음과 같은 코드가 미리 들어 가 있습니다.

[code language=”objc”]
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {

// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];

// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];

// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );

// add the label as a child to this Layer
[self addChild: label];
}
return self;
}[/code]

이 중에 쓰지 않는 부분을 삭제 하고 다음과 같이 스프라이트를 화면에 위치 시키는 코드를 입력 합니다.

[code language=”objc”]
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {

CCSprite *playerSprite = [CCSprite spriteWithFile:@"imgres-1.jpeg"];
playerSprite.position = ccp(50, 50);

[self addChild: playerSprite];
}
return self;
}[/code]

그리고 시뮬레이터로 실행 하면 다음과 같이 나옵니다.

invader – 00 기획

이 예제는 facebook의 게임/앱 스터디 그룹을 위해 작성 되었습니다.

교육용 예제로 스패이스 인베이더와 비슷한 게임을 만들어 보기로 하겠습니다.

Space Invaders – Wikipedia, the free encyclopedia

저작권문제

기존에 있는 게임을 소위 말하는 벤치마크 대상으로 했기 때문에(정확히는 베끼는 것을 보고 벤치마크라고 부르는 것이 널리 사용되는게 현실) 라이센스 문제를 생각하지 않을 수 없는데, 스패이스인베이더의 저작권은 스퀘어에닉스(TAITO)가 가지고 있고 어페럴 포함 함부로 만들 수 없도록 되어 있습니다. 하지만 여기서는 상용화까지 가지 않을 것이기 그대로 베낀다고 하기 보다는 대강 비슷한 게임을 만드는 것이니 문제는 되지 않을 것이라고 봅니다. 만약 진짜로 무언가를 만든다면 처음 기획 들어가기 전에 아이디어 단계에서 이 문제가 검토가 반드시 되어야 하겠습니다.

게임 구성

  1. 화면은 포트레이트로
  2. 10개 씩 5줄의 인베이더가 화면 좌측끝에서 우측끝으로 1줄 내려가서 다시 좌측 끝으로 지그재그로 이동. 5줄 인베이더 중에 제일 가장자리 1열 이 없어지면 1캐릭터 길이 만큼 더 움직인다는 것 구현을 고려 해야 . (인베이더를 잡을 수록 움직임이 점점 빨라지는 건 일단 빼기로…)
  3. 인베이더 중 일부은 시간차를 두고 미사일 투하
  4. 게임 화면 최하단에 플레이어는 좌우로 이동 미사일 발사
  5. 발사한 미사일을 인베이더가 맞으면 스코어 +10 해당 인베이더는 화면에서 사라짐
  6. 플레이어 바로 앞에 방어구조물. 구조물은 한 덩어리가 아니라 2*3 블록
  7. 방어구조물 인베이더, 플레이어가 발사 한 미사일에 맞으면 손상 – 다시 맞으면 파괴
  8. 파괴 된 구조물은 허공으로 판정
  9. 인베이더가 전멸하면 게임오버 (다음스테이지는 일단 고려하지 않음)
  10. 인베이더가 구조물 보다 아래로 내려오거나 플레이어가 미사일에 맞으면 게임오버 (목숨은 1대)
  11. 화면 제일 위를 가로질러 날라가는 보너스 UFO는 삭제

 추가 아이디어

  1.  fps에서의 스트릭 킬 보너스 같이 탄을 낭비하지 않고 연속으로 격파하면 추가 점수를 주는 요소

개발 프레임워크

여기 까지 생각 했다면 이제 어떻게 현실화 할 것인가? 라는 문턱을 맞닥들이게 됩니다. 특히 지금 우리처럼 경험이 없다면 가지고 있는 지식(기술)을 어떻게 쓸 수 있는지 얼마나 시간과 노력이 필요한지 가늠하기도 힘들지만 그런 수고를 경험한 선배들이 만들어 놓은 결과를 오픈소스라는 방법으로 공개한 좋은 예가 앵그리버드 등 유명한 게임을 만드는데 사용 된 box2d라는 물리엔진도 포함 되어 있는 등 유용한 프레임웍인 cocos2d 를 소개 합니다. 원래는 python으로 만들어졌던 것이 플레시, iOS(Objective-C)로 포팅 되었습니다. 플레시 게임을 만들던 사람들이 iOS 개발에 많이 전환하게 된 계기가 되기도 했다고 합니다.

디자인

  1. 인베이더 스프라이트 펼친형태, 움추린형태 2개
  2. 플레이어 스프라이트 1개
  3. 플레이어의 미사일 1개, 인베이더의 미사일 1개 – 서로 다른 색으로
  4. 방어구조물 2*3 = 6개 + 위치별로 손상된 조각 각 1개
  5. 점수 표시는 내장 폰트 이용
  6. 폭파되는 그림
  7. 발사되는 뿅 소리 2가지 하고 폭파되는 펑 소리 1가지
  • 사실 초기 단계에서 디자인은 기획과 개발 과정을 명확하게 하는데 아주 중요한 역할을 하기도 하지만
  • 프로토타입 형태로 만드는데는 형태, 크기 정도만으로 프로그래밍과 작동 되는 것을 보여 주는 것은 가능 함
  • 디자인은 조작 인터페이스 뿐만아니라 사용자 경험에 가장 직접적으로 영향을 줌
  • 디자인 결과물은 굳이 판매하지 않더라도 “완성” 단계까지 올려놓는 역할

cocos2d CCSprite Flip-X Animation

[code language=”objc”]
– (void) flipReveal : (CCNode *) node
{
float d = 1.0; // duration
CCEaseExponentialIn *flipHalf = [CCEaseExponentialIn actionWithAction:[CCActionTween actionWithDuration:d key:@"scaleX" from:-1.0 to:0.0]];
CCCallFuncN *showSprite = [CCCallFuncN actionWithTarget:self selector:@selector(showSprite:)];
CCEaseExponentialOut *flipRemainingHalf = [CCEaseExponentialOut actionWithAction:[CCActionTween actionWithDuration:d key:@"scaleX" from:0.0 to:1.0]];
CCSequence* seq = [CCSequence actions:flipHalf,showSprite,flipRemainingHalf, nil];
[node runAction:seq];
}
– (void) showSprite : (id) node
{
[node setVisible:YES];
}
[/code]

Usage :
[code language=”objc”]
[self flipReveal:(id)mSprite];
[/code]

cocos2d custom CCSprite Class Example

EnemySprite.h

[code language=”objc”]
#import <Foundation/Foundation.h>
#import "cocos2d.h"

@interface EnemySprite : CCSprite {
int healthpoint;

CCSprite *shadow;
}
@property (readwrite) int hp; // <-
@property (nonatomic, retain) CCSprite *shadow; // <-

@end
[/code]

EnemySprite.m

[code language=”objc”]
#import &quot;EnemySprite.h&quot;

@implementation EnemySprite
@synthesize hp; // <-
@synthesize shadow; // <-

-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
{
if( (self=[super initWithTexture:texture rect:rect]))
{
}
return self;
}
@end
[/code]

scene_blahblah.m
[code language=”objc”]
#import "EnemySprite.h"

EnemySprite *ep = [EnemySprite spriteWithFile:@&quot;redbar_20x2.png&quot;];
[/code]

cocos2d CCMenu CCMenuItemImage

[code language=”objc”]
CCMenuItemImage *redButton = [CCMenuItemImage itemFromNormalImage:@"redbtn.png" selectedImage:@"redbtn_pushed.png" target:self selector:@selector(timerGo:)];
CCMenu *menu =  [CCMenu menuWithItems:redButton, nil];
menu.position = ccp(370,175);
[self addChild:menu];
[/code]