夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【cocos2d, Objective-C】オブジェクトにタップを知らせる機能をつけた

BeeClusterでは基本的にタッチ機能はキャラクターの移動に使用します。
タッチを管理するTouchLayerクラスのlocationプロパティを使用して自キャラや敵キャラなどのオブジェクトはタッチ位置を知る事ができます。
マップのあちこちにいる味方キャラをタップすると仲間になる機能をつけたいので、タップのタイミングをオブジェクトに通知する機能を追加しました。
通知を受けたオブジェクトはそのときのタッチ位置が自分の座標と重なっていれば自分がタップされたと判断できます。

TouchLayerのインターフェースは次のようになっています。
オブジェクトがregisterRecipientメッセージをTouchLayerに送ると、タップの連絡を受け取る事ができます。unregisterRecipientメッセージを送るとタップの連絡を停止できます。unregisterRecipientメッセージを送り忘れるとオブジェクトはTouchLayerのNSMutableArrayにリテインされたままになるので注意です。
オブジェクトがタップの通知を受け取るメソッドをObjective-CプロトコルTouchNotifyingとして宣言しています。オブジェクトがTouchNotifyingに準拠し、touchNotificationメソッドをオブジェクトのクラスが実装しておけば、タップ検出時にタッチ位置をパラメーターとしたメッセージを受け取る事ができます。

@interface TouchLayer : CCLayer {
    
}

+ (TouchLayer *)sharedTouch;
- (void)registerRecipient:(id)receipient;
- (void)unregisterRecipient:(id)recipient;

@property (nonatomic, readonly) BOOL touching;
@property (nonatomic, readonly) CGPoint location;

@end

@protocol TouchNotifying <NSObject>

@required
- (void)touchNotification:(CGPoint)location;

@end

実装ファイルはこのようになっています。NSMutableArrayに通知を送るべきオブジェクトを登録しておいて、ccTouchesEndedでタッチ終了が検出されたら高速列挙で順に通知を送って行きます。タッチ終了しか見ていないので、厳密にはタップだけではなく、ドラッグしたあとに指を離した場合も通知が送られます。(2013/5/12追記:タップ通知のタイミングをccTouchesBeganに変更しました。ゲームオーバー時にタップ検出を見てタイトル画面に遷移したかったのですが、ccTouchesEndedだと手を離した瞬間に画面がきりかわってしまうので。)

@implementation TouchLayer
{
    BOOL _touching;
    CGPoint _location;
    NSMutableArray* _recipients;
}

+ (TouchLayer *)sharedTouch
{
    static TouchLayer* sharedInstance = nil;
    if (sharedInstance == nil) {
        sharedInstance = [[self alloc] init];
    }
    return sharedInstance;
}

- (id)init
{
    if ((self = [super init])) {
        _touching = NO;
        CGSize winSize = [CCDirector sharedDirector].winSize;
        _location = ccp(winSize.width/2, winSize.height/4);
        _recipients = [NSMutableArray array];
        self.isTouchEnabled = YES;
    }
    return self;
}

- (void)registerRecipient:(id)receipient
{
    [_recipients addObject:receipient];
}

- (void)unregisterRecipient:(id)recipient
{
    [_recipients removeObjectIdenticalTo:recipient];
}

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    _touching = YES;
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    _location = location;
    for (id recipient in _recipients) {
        [recipient touchNotification:_location];
    }
}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    _touching = YES;
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    _location = location;
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    _touching = NO;
    // とりあえず_locationは前の位置を保持
}