하나의 프로젝트에서 여러개의 플랫폼용으로 빌드 할 때, 아래 코드를 잘 조합해서 하면 하나의 소스로 여러개의 플렛폼으로 빌드를 할 수 있다. 


간단하게 설명.

1. workspace를 만든다. 

2. cocos2d iOS Project를 만든다. Add to, Group(처음 생성하는 프로젝트면 없을 수도 있다) 위치를 workspace root로 정한다. 



3. cocos2d Mac Project를 만든다. Add to, Group 위치를 workspace root로 정한다.  

4. 하나의 프로젝트에서 소스를 생성하고 다른 프로젝트로 옮길 때는 Project > Target > Build Phases > Compile Sources > + > Add Other ... > 원하는 소스코드 추가

(그냥 드래그 앤 드롭으로 하면 에러 남)

5. 소스 코드 외 컴파일 되지 않는 파일은 그냥 드래그 앤 드롭으로 하면 된다. copy는 하지 말고. 


그리고 소스 내부에는 아래 코드를 참조해서 apportable 용, iOS용, Mac 용을 구분해서 구현하면 된다. 


#ifdef ANDROID

#elif defined(__CC_PLATFORM_IOS)

#elif defined(__CC_PLATFORM_MAC)

#endif


Posted by KraZYeom

댓글을 달아 주세요

cocos2d-iPhone 게임을 안드로이드로 포팅시 그냥 아무런 옵션 없이 컴파일을 하면 갤럭시 노트 3와 같은 고해상도 단말에서는 아주 작게 나타난다. 이것을 해겨 하기 위해서 아래 코드를 앱이 실행 될 때 넣어주면 된다. 


-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

#ifdef ANDROID

  [UIScreen mainScreen].currentMode = [UIScreenMode emulatedMode:UIScreenBestEmulatedMode];

#endif

...


}



Posted by KraZYeom

댓글을 달아 주세요

iPhone 5 전용 배경화면 패치


iPhone 4S, iPhone 4에서는 아무런 문제가 없다. 

iOS에서는 *.png와 *@2x.png 로 이미지를 레티나용이냐 아니냐로 구분한다. cocos2d에서는 @2x 대신에 -hd를 사용하여 구분한다. 


iPhone 5는 세로가 1136이고 iPhone 4S이하는 960이기 때문에 배경화면을 기존 -hd로 설정을 하면 공백이 생긴다.


해결을 위해서 cocos2d를 살짝 수정을 해서 iPhone 5전용 이미지를 지원하도록 패치하도록 한다. 



“AppDelegate.m” 파일로 이동을 한다. (BOOL)application:didFinishLaunchingWithOptions:launchOptions 메소드에서 아래 코드 부분을 찾는다. 


[sharedFileUtils setiPhoneRetinaDisplaySuffix:@"-hd"]; // Default on iPhone RetinaDisplay is "-hd"


이 부분에서 파일 확장자에 따른 이미지를 구분하게 해준다. 이 부분을 살짝 고쳐주면 iPhone 5 전용 이미지 지원이 가능하다. 

setiPhoneRetinaDisplaySuffix 의 정의로 이동한다. Cmd와 왼쪽 버튼을 클릭해도 정의로 이동한다.



-(void) setiPhoneRetinaDisplaySuffix:(NSString *)suffix

{

[_suffixesDict setObject:suffix forKey:kCCFileUtilsiPhoneHD];

}


// 1.

// iPhone 5 전용 이미지 지원

-(void) setiPhone5RetinaDisplaySuffix:(NSString *)suffix

{

[_suffixesDict setObject:suffix forKey:kCCFileUtilsiPhone5HD];

}


1. iPhone 5 전용 이미지 지원을 위해서 기존 메소드를 복사 붙여넣기 해서 메소드 이름을 변경을 하고, forKey를  kCCFileUtilsiPhone5HD 로 변경한다. 


“CCFileUtils.h” 로 이동한다. 

-(void) setiPhoneRetinaDisplaySuffix:(NSString*)iPhoneRetinaDisplaySuffix;

아래 부분에 위에 추가한 메소드를 아래와 같이 정의 한다. 

-(void) setiPhone5RetinaDisplaySuffix:(NSString*)iPhoneRetinaDisplaySuffix;


그리고 다시 “AppDelegate.mm” 파일로 이동한다.

  // 1.

  [sharedFileUtils setiPhone5RetinaDisplaySuffix:@"-5hd"]; // Default on iPhone 5 RetinaDisplay is "-5hd"


  1. iPhone 5 전용 이미지를 -5hd를 사용하여서 지원하도록 한다. 

이렇게 하면 -5hd를 사용해서 iPhone 5, -hd를 사용해서 iPhone 4S, iPhone 4 그리고 아무것도 붙이지 않고 iPhone 3GS 이하로 구분해서 배경화면을 설정 할 수 있다.

패치한 파일 

Posted by KraZYeom
TAG cocos2d, Patch

댓글을 달아 주세요

게임에 있어서 가장 중요한 것들 하나가 배경음악이라고 생각이 든다. 애니팡도 그렇고 드래곤 플라이트도 그렇고 단순하면서 반복적인 배경음악이 게임에 몰입을 하고 중독을 일으키게 할 만큼 엄청나게 중요하다. 이번 파트에서는 배경음악과 사운드 효과의 재생을 구현해 보도록 하자. 


배경음악과 사운드 효과

cocos2d에서 배경음악과 사운드 효과는 SimpleAuidoEngine을 사용한다. 즉각 즉각 필요할 때 마다 메모리에 올려서 재생을 해도 상관은 없지만, 사운드의 경우에는 용량도 크고 해서 게임 시점에 메모리에 올릴경우 지연이 발생한다. 그래서 게임 초반에 preload를 사용하여 미리 메모리에 올려 놓고 필요할 때는 재생만 해서 사용한다. mp3, wav 등 다양한 형식의 음악 파일을 지원한다. 단, 너무 파일 크기가 크면 게임 구동에 문제가 있을수 있으니 삼가하도록 하자. 


게임 시작 시점에 preload를 하기 위해 AppDelegate.m 로 이동한다. application:didFinishLaunchingWithOptions: 메소드의 가장 아래부분에 아래와 같이 코드를 추가한다.  

(* 배경 음악 및 효과음은 저작권 문제로 첨부하지 못하였습니다. 적절한 음악 및 효과음을 찾으면 추가하도록 하겠습니다.)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

...

    // 배경음악을 위한 음악

    [[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:@"background_music.mp3"];

    // 적이 죽을 경우을 위한 사운드 효과

    [[SimpleAudioEngine sharedEngine] preloadEffect:@"mon_die.wav"];

    // 적이 피해를 입었을 경우을 위한 사운드 효과

    [[SimpleAudioEngine sharedEngine] preloadEffect:@"mon_damage.wav"];

  return YES;

}

각각의 상황에 따른 음악 및 효과 파일을 싱글톤을 사용하여 preload 한다. 배경음악의 경우에는 preloadBackgroundMusic를 사용하여 preload를 하고, 사윤드 효과의 경우에는 preloadEffect를 사용하여 preload 한다.


배경음악 및 효과음 재생을 구현하기 위해서 GameLayer.m 으로 이동한다. 그리고 SimpleAudioEngine을 사용하기 위해서 아래와 같이 헤더 파일을 임포트한다. 

#import "SimpleAudioEngine.h"


게임이 실행되면 바로 배경음악을 재생하기 위해서 onEnter 메소드의 마지막에 아래와 같이 코드를 추가한다.

- (void)onEnter {

    [super onEnter];

...

    //시작 되면 배경 백그라운드 음악이 재생

    [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background_music.mp3" loop:YES];

}

싱글톤으로 SimpleAudioEngine이 만들어져서 어디서나 접근을 할 수가 있다. 음악 재생을 위해서 playBackgroundMusic 메소드를 사용하여 배경음악을 재생하고 계속적으로 반복을 위해서 loop값을 YES로 설정한다. 

그리고 충돌시 일어나는 효과음을 구현하기 위해서 update: 메소드의 충돌이 일어나는 부분에 아래 코드를 추가한다. 

- (void)update:(ccTime)dt {

...

            //총알과 적이 충돌이 나는지를 체크

            if (!isCollision && CGRectIntersectsRect(bullet.boundingBox, enemy.boundingBox)){

...

                //싸운드 효과를 재생한다.

                [[SimpleAudioEngine sharedEngine] playEffect:@"mon_damage.wav"];

                //미사일로 적을 공격해서 0점을 받아오는지를 체크

                if (![enemy attackedWithPoint:[bullet bulletType]]){

                    //싸운드 효과를 재생한다.

                    [[SimpleAudioEngine sharedEngine] playEffect:@"mon_die.wav"];


                    //적이 폭발되면 먼지를 뿌려주기위한 노드 생성

                    Dust *dust = [Dust node];

...

}

배경음악과 마찬가지로 싱글톤으로 SimpleAudioEngine이 만들어져서 어디서나 접근을 할 수가 있다. 효과음 재생을 위해서  배경음악 재생과 다른 playEffect 메소드를 사용하여 효과음을 한번만 재생한다. 총알과 적이 충돌 했을 경우와 적이 폭발하는 경우의 각각의 효과음을 재생하도록 한다. 

Cmd(⌘) + R을 눌러서 실행을 해보자.

게임이 시작되면 배경음악이 재생되고 총알과 적이 충돌이 되었을 때, 적이 죽었을 때의 경우 모두 효과음이 나오는 것을 확인 할 수 있다.


이로서 드래곤 플라이트 따라 만들기 (손)연재가 모두 끝났다. 따라하면서 느꼈겠지만 크게 어려움이 없을 것이다. 물론 게임 개발을 엔진부터 개발을 하면 엄청나게 어려움이 있을 것이다. 하지만 뛰어난 개발자분들 께서 좋은 게임 엔진을 많이 만들어 놨고, 우리들은 그냥 그것을 사용해서 만들면 된다. :-) 겨우 약 600줄로 게임을 만들수 있다. (주석 포함 1200줄) 코딩을 할 수 있으면 누구나 도전을 했으면 하는 바람이다. 

게임에 대해 전혀 모르는 저도 했으니 여러분들 또한 전혀 문제가 없을 겁니다. 다른 시리즈로 조만간 찾아 오겠습니다.
그리고 '모바일 게임 따라 만들기 시리즈'로 조만간 eBook 형태로도 찾아뵙겠습니다. :-)  커피숍에서 코딩 할 수 있게 많은 도움 부탁드립니다. 꾸벅. 

Posted by KraZYeom

댓글을 달아 주세요

  1. 2013.02.13 03:46  댓글주소  수정/삭제  댓글쓰기

    정말 도움 많이 됐어요. 감사합니다.
    이런거 더 만들어주시면 좋겠어요 :D

  2. s0und0ne 2013.02.14 06:22  댓글주소  수정/삭제  댓글쓰기

    감사합니다!!!
    사실 저 같은 경우는 xcode를 사용해본적이 한번도 없어서 이거 보면서 문법도 같이 공부하는 계기가 되었습니다^^ 아직 3부 보고 있지만 꼭 끝까지 따라해볼려구요~

    이런거 또 만들어 주실거죠??

    라즈베리 관련 글도 감사합니다^^;;

  3. 트랄라 2013.02.19 12:51  댓글주소  수정/삭제  댓글쓰기

    안녕하세요, 강좌 감사합니다.
    ARC 적용시에 빌드나 실기기 테스트엔 문제없는데 ADHOC(웹)으로 배포시에 해당 레이어나 씬이 하나도 나오지 않는 현상이 있는데 혹시 테스트 가능하신가요?
    프로젝트 생성 후 라벨은 잘 나오는 걸 봐서는 다른 문제 같긴 합니다.
    다시 한번 강좌 감사드립니다.

  4. Favicon of https://red-submarine.tistory.com BlogIcon 빨간잠수함。 2013.04.14 17:43 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~ cocos2d-x로 포팅 성공해서 댓글 남깁니다 ㅎㅎ
    음원은 econovation에서 무료제공하는 음원들 중에 제 맘대로 선택해서...배경음이...ㅋㅋ

    https://github.com/redsubmarine/DragonRider
    아 지금 생각났는데 첫화면에 제 닉넴을 박아버린거... 봐주세요 ㅋㅋ

폭발 효과 에니매이션 

총알에 맞은 적 또는 플레이어 케릭터가 적과 충돌할 경우에 폭발하는 애니메이션이 보이게 된다. cocos2d에서 제공하는 파티클을 사용하여서 폭발하는 효과를 더 현실감 있고 화려하게 구현 하는 방법도 있다. 이 튜토리얼에서는 한두가지의 이미지로 폭발 파티클 효과를 비슷하게 구현하도록 하겠다. 


폭발 스프라이트 애니메이션을구현 하기 위해서 CCSprite를 상속 받아서 Dust 라는 이름으로  클래스를 만든다. 


Dust.h 로 이동하여 아래 코드를 추가한다. 

@interface Dust : CCSprite {

  CCArray *dusts;

  CCArray *explosions;

}


@property (nonatomic, weak) CCSpriteBatchNode *batch;


-(void)animateDusts;

-(void)animateExplosions;

폭발할 때 일어나는 먼지 스프라이트를 저장하는 배열과 폭발효과의 스프라이트를 저장하는 배열을 생성한다. 그리고 효과적으로 스프라이트를 사용하기 위한 배치노드를 프로퍼티를 만든다. 먼지효과 애니메이션과 폭발 애니메이션을 실행하는 메소드 2개를 생성한다. 


구현을 위해 Dust.m 으로 이동한다. 

폭발을 위한 초기 설정을 위해 init 메소드에 아래 코드를 추가한다. 

- (id)init

{

  self = [super init];

  if (self) {

    self.anchorPoint = CGPointZero;

    //동일한 이미지의 반복 사용의 효율성을 위해서 배치노드 생성

    _batch = [CCSpriteBatchNode batchNodeWithFile:@"dragonRideSprite.pvr.ccz"];

    //배치노드 추가

    [self addChild:_batch];

    

    //10개의 먼지 이미지를 사용할 배열을 만든다.

    dusts = [CCArray arrayWithCapacity:kMaxDust];

    for ( int i = 0 ; i < kMaxDust; i++ ) {

      //먼지 이미지를 스프라이트 시트에서 가져온다.

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

      //에니메이션 될때 보여 주기 위해 처음에는 숨긴다.

      dust.visible = NO;

      //작은거에서 커지는 에니메이션을 위해 1/10 으로 작게 만든다.

      dust.scale = 1.0/10.0;

      //배치 노드에 자식 노드로 추가 한다.

      [_batch addChild:dust];

      //배열에 먼지를 추가한다.

      [dusts addObject:dust];

    }

    

    //플레이어 캐릭터가 폭발될 때의 스프라이트를 위한 배열

    explosions = [CCArray arrayWithCapacity:kMaxDust];

    

    //오른쪽  폭발 효과 스프라이트

    CCSprite *rightDamage = [CCSprite spriteWithSpriteFrameName:@"damage.png"];

    //폭발시에만 보이게, 숨김 처리

    rightDamage.visible = NO;

    //터지는 효과를 위해 작게 설정한다.

    rightDamage.scale = 0.5;

    rightDamage.anchorPoint = ccp( 0.0, 0.0 );

    [self addChild:rightDamage];

    [explosions addObject:rightDamage];


    //왼쪽 폭발 효과 스프라이트

    CCSprite *leftDamage = [CCSprite spriteWithSpriteFrameName:@"damage.png"];

    //폭발시에만 보이게, 숨김 처리

    leftDamage.visible = NO;

    //터지는 효과를 위해 작게 설정한다.

    leftDamage.scale = 0.5;

    // 동일한 이미지를 왼쪽에 사용하기 위해서 X, Y 뒤집기(플립)처리

    leftDamage.flipX = YES;

    leftDamage.flipY = YES;

    leftDamage.anchorPoint = ccp( 1.0, 1.0 );

    [self addChild:leftDamage];

    [explosions addObject:leftDamage];

    

    // 가운데 처지는 스프라이트

    CCSprite *explosion = [CCSprite spriteWithSpriteFrameName:@"explosion_01.png"];

    //폭발시에만 보이게, 숨김 처리

    explosion.visible = NO;

    //터지는 효과를 위해 작게 설정한다.

    explosion.scale = 0.5;

    [self addChild:explosion];

    [explosions addObject:explosion];

  }

  return self;

}

코드는 길지만 원리는 간단하다. 우선 먼지 애니메이션의 경우에는 하나의 이미지를 효과적으로 사용하기 위해서 배치노드를 생성한다. 그리고 먼지 이미지 10개를 숨김으로 해서 1/10 크기로 배치노드에 추가 한다. 폭발 애니메이션의 경우에도 이미지를 숨김으로 해서 1/2 크기로 추가 한다. 왼쪽 이미지의 경우에만 뒤집기 효과인 플립을 줘서 X, Y축으로 뒤집는다.  그리고 터지는 스프라이트도 동일하게 추가 한다. 


초기화는 되었으니 플레이어와 적이 충돌하였을 경우에 일어나는 애니메이션을 구현하도록 하겠다. animateExplosions 메소드에 아래코드를 추가한다. 

-(void)animateExplosions{

  //배열에서 각각의 폭파 스프라이트를 꺼낸다.

  for (CCSprite *explosion in explosions) {

    //보이게 설정을 한다.

    explosion.visible = YES;

    //0.4 동안 3 커지게 설정

    CCScaleTo *scale = [CCScaleTo actionWithDuration:0.4 scale:3.0];

    //애니메이션이 끝나면 숨기고, 삭제한다.

    CCCallBlock *block = [CCCallBlock actionWithBlock:^{

        explosion.visible = NO;

        [self removeFromParentAndCleanup:YES];

    }];

    //움직임 에니메이션이 끝나면 block 순차적으로 실행하기 위한 시퀀시 생성

    CCSequence *seq = [CCSequence actions:scale, block, nil];

    [explosion runAction:seq];

  }

}


배열에서 각각의 스프라이트를 꺼낸 뒤, 보이게 설정하고, CCScaleTo 을 사용하여 특정 시간동안 스프라이트를 확대하는 액션을 생성한다. 그리고 애니메이션이 끝나면 숨기고, 삭제하는 블럭 타입의 콜백을 생성한다. 그리고 액션과 블럭 콜백을 순차적으로 실행하기 위해서 시퀀시를 생성한 후, 액션을 실행한다. 


플레이어 폭발 애니메이션을 간단하였지만, 총알과 적의 충돌시 샐행되는 먼지 효과 애니메이션을 살짝 복잡하다. 어렵지 않으니 차근차근 따라해보자. 우선 애니메이션을 위해서 animateDusts 메소드를 생성하고 아래코드를 붙여넣는다. 

-(void)animateDusts{

  //에니메이션을 위해서 먼지를 세팅

  for (CCSprite *dust in dusts) {

    //숨긴 이미지를 다시 보이게

    dust.visible = YES;

    

    //10개의 이미지의 크기를 랜덤으로 가져옴

    float scaleRandom = 0.1 + ( (double)arc4random() / (double)0xffffffff );

    //0.3 시간동안 크기 변경 에니메이션

    CCScaleTo *scale = [CCScaleTo actionWithDuration:0.3 scale:scaleRandom];

    

    // 터지는 x위치를 위해서 랜덤 1 or -1

    int x = ( (double)arc4random() / (double)0xffffffff ) < 0.5 ? -1 : +1;

    float xRandom = 5 + 4 * ( ((double)arc4random()/(double)0xffffffff) * 10 * x );

    // 터지는 y위치를 위해서 랜덤 1 or -1  

    int y = ( (double)arc4random() / (double)0xffffffff ) < 0.5 ? -1 : +1;

    float yRandom = -30 + 4 * ( ((double)arc4random() / (double)0xffffffff ) * 10 * y );

    //0.3 시간동안 위치 변경 에니메이션

    CCMoveTo *move = [CCMoveTo actionWithDuration:0.3 position:ccp(xRandom, yRandom)];


    //에니메이션이 끝나면 숨기고 자신은 삭제를 위해서 블럭 함수 생성

    CCCallBlock *block = [CCCallBlock actionWithBlock:^{

        dust.visible = NO;

        [self removeFromParentAndCleanup:YES];

    }];

    //움직임 에니메이션이 끝나면 block 순차적으로 실행하기 위한 시퀀시 생성

    CCSequence *seq = [CCSequence actions:move, block, nil];

    //runAction 연이어 하면 동시에 크기와 움직임 엑션이 일어난다.

    [dust runAction:scale];

    [dust runAction:seq];

  }

}

기본 원리는 플레이어 폭발 애니메이션과 동일하다. 추가적으로 CCMoveTo를 사용해서 가운데서 터져서 양사방으로 움직이는 효과가 들어간다. 우선 숨겼던 이미지를 보이고 10개의 이미지가 모두 다른 크기로 커지게 하기 위해서 랜덤값을 생성한다. 그리고 CCScaleTo를 사용하여 0.3초 동안 확대한다. 그리고 10개의 이미지 모두 터지면 적의 중심으로 양사방으로 움직이게 하기위해서 x, y좌표를 랜덤으로 설정한다. 그리고 CCMoveTo를 사용해서 이동시킨다. 그리고 애니메이션이 끝나면 숨기고, 삭제하는 블럭 타입의 콜백을 생성한다. 그리고 액션과 블럭 콜백을 순차적으로 실행하기 위해서 시퀀시를 생성한 후, 액션을 실행한다. 


먼지 스프라이트 클래스는 완성되었다. 적용을 하기 위해서 GameLayer.m 로 이동한다. 

상단에 아래 코드를 추가하여서 클래스를 추가할 수 있도록 한다. 

#import "Dust.h"


총알과 적이 충돌해서 적의 에너지가 0 일때를 체크해서 폭발하는 효과 애니메이션을 실행할 것이. update:(ccTime)dt 에서 //미사일로 적을 공격해서 0점을 받아오는지를 체크하는 부분안에 아래 코드를 추가한다. 

- (void)update:(ccTime)dt {

...

  //미사일로 적을 공격해서 0점을 받아오는지를 체크

  if (![enemy attackedWithPoint:[bullet bulletType]]){


    //적이 폭파되면 먼지를 뿌려주기위한 노드 생성

    Dust *dust = [Dust node];

    //위치는 적이 폭파한 위치

    dust.position = enemy.position;

    //화면에 뿌려주기 위해 자식노드로 추가

    [self addChild:dust];

    //폭파하는 에니메이션 실행

    [dust animateDusts];

  }


...

}

적의 에너지가 0 이면 폭발 스프라이트 노드를 생성하고 적의 현재 위치에 먼지를 배치하고 난뒤에 폭발하는 애니메이션을 실행한다.


그리고 적과 플레이어가 충돌했을 경우에는 update:(ccTime)dt //적과 플레이어가 충동하는지를 체크하는 부분의 미사일을 없에는 부분 바로 밑에 아래 코드를 추가한다. 

- (void)update:(ccTime)dt {

...

        //적과 플레이어 케릭터가 충돌하는지를 체크

        if (!isCollision && CGRectIntersectsRect(enemy.boundingBox, _player.boundingBox)) {

            isCollision = YES;

            if (isCollision){

                _player.visible = NO;

                // 충돌하게 되면 마사일을 없엔다.

                [self unschedule:@selector(updateBullet)];

                for (Bullet *bullet in bulletsArray) {

                    bullet.visible = NO;

                    [bullet removeFromParentAndCleanup:YES];

                }

                

                //적이 폭발되면 먼지를 뿌려주기위한 노드 생성

                Dust *dust = [Dust node];

                //위치는 적이 폭발된 위치

                dust.position = _player.position;

                //화면에 뿌려주기 위해 자식노드로 추가

                [self addChild:dust z:1000];

                //폭발하는 에니메이션 실행

                [dust animateExplosions];

             ...
         }
...
}

적과 충돌을 하게 되면 폭발 노드를 생성하고 사용자 케릭터 배치시키고 폭발하는 애니메이션을 실행한다. 


Cmd(⌘) + R을 눌러서 실행을 해보자. 

총알과 적이 충돌할 경우, 플레이어 케릭터와 적이 충돌할 경우 폭발하는 애니메이션이 실행되는 것을 확인 할 수 있다. 


기본적인 게임 구현은 모두 끝났다. 다음 파트에서는 배경음악 재생과 총알과 적이 충돌했을 때 효과음, 적이 폭발할 때 효과음을 재생하는 것을 구현하도록 하겠다. 

Posted by KraZYeom

댓글을 달아 주세요