[iOS] TileMap使用教學(2) 加入角色吧

加入你的角色


如果你認為只有地圖,上面什麼都沒有,這實在是太孤單了!那你的想法跟我一樣,所以現在來增加角色吧!

完成的code放在最下面,大家也可以直接去看。

開始


放一個角色到地圖上,首先就是要指定你要讓角色出現在地圖上的哪個方塊裡面。

打開TileMap,然後打開剛做好的地圖,點選上方功能列的圖層,點選加入對象層,然後會在右方圖層中出現一個新的層,你可以改名稱為Objects。

點選操作視窗上方的插入對象,然後在你希望角色出現的方塊上面點一下,按右鍵,選擇對象屬性,進去之後在名稱的位置輸入SpawnPoint,這樣就好了。

存檔,然後接下來要介紹藍色部分的code了!

首先是在interface裡面新加入的變數,_player,用來記錄你的角色,可以尋找他的位置等等。

CCSprite *_player;
以及特性:

@property (nonatomic, retain) CCSprite *player;

當然就要實作:

@synthesize player = _player;

在dealloc裡面增加:


self.player = nil;

然後在init方法裡面,我們多了以下的code:

        self.isTouchEnabled = YES;
        開啟碰觸。

        CCTMXObjectGroup *objects = [_tileMap objectGroupNamed:@"Objects"];
        導入物件層。
        NSAssert(objects != nil, @"'Objects' object group not found");
        當物件層是空的時候,提出警告。
        NSMutableDictionary *spawnPoint = [objects objectNamed:@"SpawnPoint"];
        導入指定的位置。
        NSAssert(spawnPoint != nil, @"SpawnPoint object not found");
        當找不到這個位置的時候提出警告。
        int x = [[spawnPoint valueForKey:@"x"] intValue];
        int y = [[spawnPoint valueForKey:@"y"] intValue];
        把該位置的座標輸出。
       
        self.player = [CCSprite spriteWithFile:@"Player.png"];
        _player.position = ccp(x, y);
        把角色圖片定位在這個位置上。
        [self addChild:_player];
        把角色加入Layer裡。
       
        [self setViewpointCenter:_player.position];
        設定畫面的中心。

如果塗綠色會很刺眼,請留言跟我說。

保持player在螢幕的中心

當player移動時我們會希望鏡頭一直跟著,那這要怎麼辦到呢?就是加入以下方法:

-(void)setViewpointCenter:(CGPoint) position {

CGSize winSize
= [[CCDirector sharedDirector] winSize];int x = MAX(position.x, winSize.width /2);int y = MAX(position.y, winSize.height /2);
x
= MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width) - winSize.width /2);
y
= MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height) - winSize.height/2);
CGPoint actualPosition
= ccp(x, y);

CGPoint centerOfView
= ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint
= ccpSub(centerOfView, actualPosition);
self.position
= viewPoint;

}

首先,這先取得了螢幕的大小。然後看看position的x座標是否比螢幕的一半來的大?看哪一個比較大,就取哪一個做為x值。也用一樣的方法得到一個y值。這樣可以讓x和y的值有一個最小下界。

然後比較這個x值和整個地圖的寬度(_tileMap.mapSize.width * _tileMap.tileSize.width)減去畫面的一半,然後把最小的作為新的x值輸出。注意到後者是固定值,這表示說,只要x沒有到太右邊,也就是太接近右邊地圖的邊界的話,就以這個x值為主,反之,x的極限就是地圖寬度減去螢幕一半寬了。

新的y值亦同,於是可以得到一個實際的位置。再來,定出螢幕的中心,取得螢幕中心到實際位置的向量(這邊我要再確認一下,感覺我弄反了),然後把地圖往這個向量的方向和大小移動,就可以定位在中心了。請大家一定要把這個方法學起來啊。



觸碰的方法

如果有接觸到cocos2D的touch方法,那就會發現,怎麼會有touch和touches的分別?當你使用touches時,依照一般的寫法,通常是沒問題的,就是將touches的點找出來,然後轉換成全域座標,再拿這座標去用。

但是touch就不能這樣做了。如果你用一樣的想法去coding,你會遇到編譯上的問題。

原文裡面,他使用touch的方法,就是完整的解決方案。

首先,加入以下方法:

-(void) registerWithTouchDispatcher {
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}


然後再去寫ccTouchBegan的部分:



-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    return YES;
}

-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
    CGPoint touchLocation = [touch locationInView:[touch view]];
    touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
    touchLocation = [self convertToNodeSpace:touchLocation];
   
    CGPoint playerPos = _player.position;
    CGPoint diff = ccpSub(touchLocation, playerPos);
    if (abs(diff.x)>abs(diff.y)) {
        if (diff.x > 0) {
            playerPos.x += _tileMap.tileSize.width;
        }else {
            playerPos.x -= _tileMap.tileSize.width;
        }
    }else {
        if (diff.y>0) {
            playerPos.y += _tileMap.tileSize.height;
        }else {
            playerPos.y -= _tileMap.tileSize.height;
        }
    }
   
    if (playerPos.x <= (_tileMap.mapSize.width * _tileMap.tileSize.width) &&
        playerPos.y <= (_tileMap.mapSize.height * _tileMap.tileSize.height) &&
        playerPos.y>=0 && playerPos.x >=0) {
        [self setPlayerPosition:playerPos];
    }
    [self setViewpointCenter:_player.position];
}

只要配上registerWithTouchDispatcher,就可以安心地使用了。

再來說明一下began和end兩部分的方法。

began只是回傳Yes,表示觸碰了。

end是要去設定player的位置以及將player至於螢幕的中心。首先,取得player現在的位置,然後取得觸碰點和player現在位置的向量,使用if迴圈判斷該向量的x方向是否為正,若為正,就設定player的位置往右一格,反之,往左一格。如果該向量y方向的值為正,就設定player的位置往上一格,反之,往下一格。簡單說,一次動一格,簡單吧?

然後再用一個if迴圈,判斷如果player座標x部分小於等於地圖的寬度、y部分小於等於地圖的高度並且x和y都大於零,簡單說,就是player的座標還在地圖內,那麼就把player的位置用這個座標去設定,反之,就不設了,保持在原位。最後一行是讓player留在螢幕中心。
編譯看看吧!你應該會看到player,並且可以移動他。

成品


#import "cocos2d.h"

// HelloWorldLayer
@interface HelloWorldLayer : CCLayer
{
    CCTMXTiledMap *_tileMap;
    CCTMXLayer *_background;
    CCSprite *_player;
   
}
@property (nonatomic, retain) CCTMXTiledMap *tileMap;
@property (nonatomic, retain) CCTMXLayer *background;
@property (nonatomic, retain) CCSprite *player = _player;


+(CCScene *) scene;

@end

以下是實作檔裡面的code:

#import "HelloWorldLayer.h"


// HelloWorldLayer implementation
@implementation HelloWorldLayer

@synthesize tileMap = _tileMap;
@synthesize background = _background;
@synthesize player = _player;

@synthesize player;


+(CCScene *) scene
{
    CCScene *scene = [CCScene node];

    HelloWorldLayer *layer = [HelloWorldLayer node];
   
    [scene addChild: layer];
   
    return scene;
}

-(void) setViewpointCenter:(CGPoint) position {
    CGSize winSize = [CCDirector sharedDirector].winSize;
   
    int x = MAX(position.x, winSize.width/2);
    int y = MAX(position.y, winSize.height/2);
    x = MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width) - winSize.width/2);
    y = MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height) - winSize.height/2);
    CGPoint actualPosition = ccp(x, y);
   
    CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
    CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
    self.position = viewPoint;
}


// on "init" you need to initialize your instance
-(id) init
{
    if( (self=[super init])) {
        self.isTouchEnabled = YES;
       
        self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile:@"TileMap.tmx"];
        self.background = [_tileMap layerNamed:@"Background"];
       
        CCTMXObjectGroup *objects = [_tileMap objectGroupNamed:@"Objects"];
        NSAssert(objects != nil, @"'Objects' object group not found");
        NSMutableDictionary *spawnPoint = [objects objectNamed:@"SpawnPoint"];
        NSAssert(spawnPoint != nil, @"SpawnPoint object not found");
        int x = [[spawnPoint valueForKey:@"x"] intValue];
        int y = [[spawnPoint valueForKey:@"y"] intValue];
       
        _player = [CCSprite spriteWithFile:@"Player.png"];
        _player.position = ccp(x, y);
        [self addChild:_player];
       
        [self setViewpointCenter:_player.position];
       
        [self addChild:_tileMap z:-1];
    }
    return self;
}

-(void) registerWithTouchDispatcher {
    [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}

-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
    return YES;
}

-(void) setPlayerPosition:(CGPoint)position {
    _player.position = position;
}

-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
    CGPoint touchLocation = [touch locationInView:[touch view]];
    touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
    touchLocation = [self convertToNodeSpace:touchLocation];
   
    CGPoint playerPos = _player.position;
    CGPoint diff = ccpSub(touchLocation, playerPos);
    if (abs(diff.x)>abs(diff.y)) {
        if (diff.x > 0) {
            playerPos.x += _tileMap.tileSize.width;
        }else {
            playerPos.x -= _tileMap.tileSize.width;
        }
    }else {
        if (diff.y>0) {
            playerPos.y += _tileMap.tileSize.height;
        }else {
            playerPos.y -= _tileMap.tileSize.height;
        }
    }
   
    if (playerPos.x <= (_tileMap.mapSize.width * _tileMap.tileSize.width) &&
        playerPos.y <= (_tileMap.mapSize.height * _tileMap.tileSize.height) &&
        playerPos.y>=0 && playerPos.x >=0) {
        [self setPlayerPosition:playerPos];
    }
    [self setViewpointCenter:_player.position];
}


// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
    self.tileMap = nil;
    self.background = nil;
    self.player = nil;
    [super dealloc];
}
@end









     

留言