본문 바로가기

프로그래밍/cocos2d

터치 이벤트 처리 방법

터치 이벤트를 이용해 화면을 이동해 보고, 다양한 터치 이벤트를 어떻게 사용하는지 실습해 보겠습니다.


//

//  GameScene.h

//  GameDemo

//

//  Created by cmpak on 5/11/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "cocos2d.h"


@class GameLayer;


@interface GameScene : CCScene {

    GameLayer *gameLayer;

}


@end






//

//  GameScene.m

//  GameDemo

//

//  Created by cmpak on 5/11/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "GameScene.h"

#import "GameLayer.h"


@implementation GameScene

    

- (id) init {

    if( ![super init] )

        return nil;

    

    gameLayer = [GameLayer node];

    [self addChild:gameLayer];

    

    return self;

}


@end






//

//  GameLayer.h

//  GameDemo

//

//  Created by cmpak on 5/10/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "cocos2d.h"


@interface GameLayer : CCLayer {

    CGSize winSize;

    

    // 방향 전환에 쓰일 버튼

    // 눌리기 전과 눌렸을 때에 있도록 방향별로 두개씩 만든다.

    CCSprite  *rightSprite;

    CCSprite  *rightPressedSprite;

    CCSprite  *leftSprite;

    CCSprite  *leftPressedSprite;

    

    BOOL isLeftPressed;

    BOOL isRightPressed;

}


@property (nonatomic, retain) CCSprite  *rightSprite;

@property (nonatomic, retain) CCSprite  *rightPressedSprite;

@property (nonatomic, retain) CCSprite  *leftSprite;

@property (nonatomic, retain) CCSprite  *leftPressedSprite;


- (void) createBackgroundParallax;

- (void) createArrowButtons;

- (void) moveBackground;


@end







//

//  GameLayer.m

//  GameDemo

//

//  Created by cmpak on 5/10/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "GameLayer.h"


#define IMG_WIDTH 1600


enum {

    kTag_Parallax = 0,

    kTag_ArrowButtonPressed = 1,

    kTag_ArrowButton = 2

};



@implementation GameLayer


@synthesize rightSprite, rightPressedSprite, leftSprite, leftPressedSprite;


- (id) init {

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

        // CCLayer 터치이벤트를 처리할 있도록 활성화시킵니다.

        self.isTouchEnabled = YES;

        

        // 화면의 픽셀 크기를 구합니다.

        winSize = [[CCDirector sharedDirector] winSize];

        

        [self createBackgroundParallax];

        [self createArrowButtons];

    }

    

    return self;

}


- (void) createArrowButtons {

    // 왼쪽 화살표

    CCSprite *sprite = [[CCSprite alloc] initWithFile:@"arrow_left.png"];

    self.leftSprite = sprite;

    

    // 기본 anchorPoint 가운데, (0.5, 0.5)이므로 이미지의 가로 세로 크기의 반에 5픽셀의 

    // 여유를 두고 화면 아래에 표시합니다.

    self.leftSprite.position = ccp(10 + self.leftSprite.contentSize.width / 2

                                   self.leftSprite.contentSize.height / 2 + 5);

    

    [self addChild:self.leftSprite z:kTag_ArrowButton];

    [sprite release];

    

    // 눌렸을 쓰일 왼쪽 화살표 

    sprite = [[CCSprite alloc] initWithFile:@"arrow_left_s.png"];

    self.leftPressedSprite = sprite;

    

    // self.leftSprite 똑같은 위치에 표시합니다.

    self.leftPressedSprite.position = self.leftSprite.position;

    

    // 눌렸을 때의 화살표를 하위 z-order 넣습니다. 그럼, 위에 같은 크기의 화살표가 

    // 똑같은 위치에 있으니까 가려서 화면상에 보이지 않게됩니다.

    [self addChild:self.leftPressedSprite z:kTag_ArrowButtonPressed];

    [sprite release];

    

    // 오른쪽 화살표

    sprite = [[CCSprite alloc] initWithFile:@"arrow_right.png"];

    self.rightSprite = sprite;

    

    // 왼쪽 화살표에서 15픽셀 오르쪽에 위치시킵니다.

    self.rightSprite.position = ccp(self.leftSprite.position.x + self.rightSprite.contentSize.width + 15

                                    self.leftSprite.position.y);

    [self addChild:self.rightSprite z:kTag_ArrowButton];

    [sprite release];

    

    // 눌렸을 쓰일 오른쪽 화살표 

    sprite = [[CCSprite alloc] initWithFile:@"arrow_right_s.png"];

    self.rightPressedSprite = sprite;

    

    // self.rightSprite 똑같은 위치에 표시합니다.

    self.rightPressedSprite.position = self.rightSprite.position;

    

    // 눌렸을 때의 화살표를 하위 z-order 넣습니다. 그럼, 위에 같은 크기의 화살표가 

    // 똑같은 위치에 있으니까 가려서 화면상에 보이지 않게됩니다.

    [self addChild:self.rightPressedSprite z:kTag_ArrowButtonPressed];

    [sprite release];

}


- (void) createBackgroundParallax {

    // 이미지로 백그라운드에 쓰일 CCSprite 만듭니다.

    CCSprite *bgSprite1 = [CCSprite spriteWithFile:@"background1.png"];

    CCSprite *bgSprite2 = [CCSprite spriteWithFile:@"background2.png"];

    

    // Transform 사용되는 anchorPoint 왼쪽 아래 귀퉁이 (0, 0) 잡습니다

    bgSprite1.anchorPoint = ccp(0, 0);

    bgSprite2.anchorPoint = ccp(0, 0);

    

    // 위에서 만든 sprite 담을 parent CCParallaxNode 만듭니다.

    CCParallaxNode *voidNode = [CCParallaxNode node];

    

    // 배경 sprite Parallax 넣습니다.

    // parallaxRatio 가로/세로로 움직이는 속도라고 보시면 되겠습니다

    // 우리는 가로로만 움직이므로 y 값을 0으로 줍니다.

    

    // background1.png 파일의 세로 크기가 160 픽셀이기 때문에 positionOffset 이용하여 

    // 화면 위에 위치하도록 좌표를 조정합니다.

    // 뒤쪽에 깔릴 배경인 background1.png 천천히 움직이도록 parallaxRatio x 값을 1보다 작은

    // 0.4 설정합니다.

    [voidNode addChild:bgSprite1 z:0 parallaxRatio:ccp(0.4f, 0) positionOffset:ccp(0, winSize.height / 2)];    

    [voidNode addChild:bgSprite2 z:1 parallaxRatio:ccp(1.0f, 0) positionOffset:CGPointZero];


    [self addChild:voidNode z:kTag_Parallax tag:kTag_Parallax];

}


/*

- (void) onEnter {

    [super onEnter];

    

    [self moveBackground];

}

*/


#pragma mark -

#pragma mark Game Play


- (void) startMovingBackground {

    // 만약 버튼 두개가 눌려졌으면 화면을 이동시키지 않습니다.

    if(isLeftPressed == YES && isRightPressed == YES)

        return;

    

    // schedule 이용하여 프레임마다 배경화면을 움직입니다.

    //[self unschedule:@selector(moveBackground)];

    

    NSLog(@"start moving");

    [self schedule:@selector(moveBackground)];

}


- (void) stopMovingBackground {

    NSLog(@"stop moving");

    [self unschedule:@selector(moveBackground)];

}



- (void) moveBackground {

    // GameLayer 들어있는 parallax node 받습니다.

    CCNode *voidNode = [self getChildByTag:kTag_Parallax];

    

    /*

    // 배경화면을 왼쪽끝에서 오른쪽 끝까지 8초간 움직입니다.

    CGFloat duration = 8.0f;

    CGFloat xRatio = 1.0;  // CCParallaxNode 만들 정해주었던 X parallaxRatio

    id go = [CCMoveBy actionWithDuration:duration position:ccp(-(IMG_WIDTH - winSize.width) / xRatio, 0)];

    id goBack = [go reverse];

    

    // 왼쪽/오른쪽으로 계속해서 움직여봅니다.

    id seq = [CCSequence actions:go, goBack, nil];

    [voidNode runAction:[CCRepeatForever actionWithAction:seq]];

    */

    

    // 프레임마다 움직일 거리

    CGPoint moveStep = ccp(3, 0);

    

    // 오른쪽 버튼이 눌려졌을 때는 반대로 움직임

    if(isRightPressed)

        moveStep.x = -moveStep.x;

    

    CGFloat bgParallaxRatio = 1.0f;

    

    CGPoint newPos = ccp(voidNode.position.x + moveStep.x, voidNode.position.y);

    

    // 배경이 양쪽 끝에 도달하면 이상 움직이지 않음

    if(isLeftPressed == YES && newPos.x > 0)

        newPos.x = 0;

    else if(isRightPressed == YES && newPos.x < -(IMG_WIDTH - winSize.width) / bgParallaxRatio)

        newPos.x = -(IMG_WIDTH - winSize.width) / bgParallaxRatio;

    

    voidNode.position = newPos;

}


#pragma mark -

#pragma mark Touch Event Handling


// 터치가 버튼 Sprite안에서 이루어졌는지 확인합니다.

- (BOOL) isTouchInside:(CCSprite*)sprite withTouch:(UITouch*)touch {

    // Cocoa 좌표 

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

    

    // Cocoa 좌표를 cocos2d 좌표로 변환합니다

CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];

    

    CGFloat halfWidth = sprite.contentSize.height / 2.0;

    CGFloat halfHeight = sprite.contentSize.height / 2.0;

    

    if(convertedLocation.x > (sprite.position.x + halfWidth) ||

       convertedLocation.x < (sprite.position.x - halfWidth) ||

       convertedLocation.y < (sprite.position.y - halfHeight) ||

       convertedLocation.y > (sprite.position.y + halfHeight) ) {

        return NO;

    }

    

    return YES;

}


// 손가락이 닫는 순간 호출됩니다.

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

    UITouch *touch = [touches anyObject];

    

    // 아래 Boolean 변수 대신에 leftSprite rightSprite visible 값을 직접 사용해도 무방합니다.

    isLeftPressed = NO;

    isRightPressed = NO;

    

    // 터치가 왼쪽 또는 오른쪽 화살표 안에 들어왔는지 확인합니다.

    if([self isTouchInside:self.leftSprite withTouch:touch] == YES) {

        // 왼쪽 화살표를 안보이게 합니다. 그럼 아래에 있던 눌릴 보여지는 이미지가 나타날 것입니다.

        self.leftSprite.visible = NO;

        

        isLeftPressed = YES;

    }else if([self isTouchInside:self.rightSprite withTouch:touch] == YES) {

        // 오른쪽 화살표를 안보이게 합니다.

        self.rightSprite.visible = NO;

        

        isRightPressed = YES;

    }

    

    // 버튼이 눌려졌으면 화면을 움직입니다.

    if(isLeftPressed == YES || isRightPressed == YES)

        [self startMovingBackground];

}


// 손가락을 떼는 순간 호출됩니다.

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

    // 배경화면을 멈춥니다.

    if(isLeftPressed == YES || isRightPressed == YES)

        [self stopMovingBackground];

    

    // 감춰졌던 버튼이미지를 다시 보이게 합니다.

    if(isLeftPressed == YES)

        self.leftSprite.visible = YES;

    

    if(isRightPressed == YES

        self.rightSprite.visible = YES;

}


// 손가락을 움직일 계속해서 호출됩니다.

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

    UITouch *touch = [touches anyObject];

    

    // 손가락이 버튼을 벗어나면 움직임을 중단합니다.

    if(isLeftPressed == YES && [self isTouchInside:self.leftSprite withTouch:touch] == NO) {

        self.leftSprite.visible = YES;

        [self stopMovingBackground];

    }else if(isRightPressed == YES && [self isTouchInside:self.rightSprite withTouch:touch] == NO) {

        self.rightSprite.visible = YES;

        [self stopMovingBackground];

    }

}



//- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}


#pragma mark -

#pragma mark Memory Release


- (void) dealloc {

    [rightSprite release];

    [rightPressedSprite release];

    [leftSprite release];

    [leftPressedSprite release];

    

    [super dealloc];

}


@end







//

//  GameDemoAppDelegate.m

//  GameDemo

//

//  Created by cmpak on 5/11/10.

//  Copyright 2010 thefirstgood.com. All rights reserved.

//


#import "GameDemoAppDelegate.h"

#import "cocos2d.h"

#import "GameScene.h"


@implementation GameDemoAppDelegate


@synthesize window;


- (void) applicationDidFinishLaunching:(UIApplication*)application

{

// Init the window

window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// cocos2d will inherit these values

[window setUserInteractionEnabled:YES];

[window setMultipleTouchEnabled:YES];

// Try to use CADisplayLink director

// if it fails (SDK < 3.1) use the default director

if( ! [CCDirector setDirectorType:CCDirectorTypeDisplayLink] )

[CCDirector setDirectorType:CCDirectorTypeDefault];

// Use RGBA_8888 buffers

// Default is: RGB_565 buffers

[[CCDirector sharedDirector] setPixelFormat:kPixelFormatRGBA8888];

// Create a depth buffer of 16 bits

// Enable it if you are going to use 3D transitions or 3d objects

    // [[CCDirector sharedDirector] setDepthBufferFormat:kDepthBuffer16];

// Default texture format for PNG/BMP/TIFF/JPEG/GIF images

// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565

// You can change anytime.

[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];

// before creating any layer, set the landscape mode

[[CCDirector sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];

[[CCDirector sharedDirector] setAnimationInterval:1.0/60];

[[CCDirector sharedDirector] setDisplayFPS:YES];

// create an openGL view inside a window

[[CCDirector sharedDirector] attachInView:window];

[window makeKeyAndVisible];

    

    

//[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];

    

    GameScene *gameScene = [[GameScene alloc] init];

    [[CCDirector sharedDirector] runWithScene:(CCScene*)gameScene];

    [gameScene release];

}