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

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

【cocos2d】CCActionを使わずにスプライトを動かす方法

今回は実験的にcocos2dのCCActionを使わずにスプライトを動かしてみました。
画面の中心を軸にグルグル回りながらだんだん中心に寄ってくる動きです。

CCActionで実装する場合は、ダミーの見えないスプライトを画面中央に配置し、メインのスプライトをダミーにaddChild。あとはダミーをCCRotetaByで回しながらCCMoveByでメインのスプライトをダミーに寄せて行くような形になると思います。
この場合、メインのスプライトはダミーのローカル座標になるためコリジョンはワールド座標に変換してから判定する必要があります。

CCActionを使わない実装はこんな感じになりました。

@implementation WaspStage5 {
    BOOL _blinking; //ダメージ処理中ならYES;
    SEL _actionMethod;
    float _angle;
    float _radius;
    float _angularRate;
    float _radiusRate;
}

- (id)initWithPosition:(CGPoint)position type:(NSString *)type name:(NSString *)name attackPoint:(float)attackPoint life:(float)life
{
    if ((self = [super initWithSpriteFrameName:@"wasp0.png"])) {
        self.radius = self.contentSize.width/3.0;
        self.tag =kEnemy;
        self.attackPoint = attackPoint;
        self.life = life;
        self.position = position;
        _angle = 0.0;
        _radius = 200.0;
        _angularRate = MYRAND(80.0, 250.0); // 1秒あたりの回転角
        _radiusRate = MYRAND(2, 8); // 1秒あたりの半径減少距離
        
        _blinking = YES; // 登場中はダメージを受けない
        _actionMethod = @selector(shoot);
        
        // アニメーション・運動特性の設定
        CCAnimation* animation = [self animationWithName:@"wasp" frameCount:4 delay:0.02];
        id animate = [CCAnimate actionWithAnimation:animation];
        id repeat = [CCRepeatForever actionWithAction:animate];
        [self runAction:repeat];
        
        [self scheduleUpdate];
    }
    return self;
}

- (void)update:(ccTime)delta
{
    if ([self numberOfRunningActions] == 0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:_actionMethod];
#pragma clang diagnostic pop
    }
    [self updatePosition:delta];
    if (!_blinking) {
        [self collision]; //点滅中はあたり判定しない
    }
}

- (void)updatePosition:(ccTime)delta
{
    _angle += delta * _angularRate;
    _radius -= delta * _radiusRate;
    float x = _radius * cos(-CC_DEGREES_TO_RADIANS(_angle));
    float y = _radius * sin(-CC_DEGREES_TO_RADIANS(_angle));
    
    CGSize winSize = [CCDirector sharedDirector].winSize;
    CGPoint winCenter = ccp(winSize.width/2.0, winSize.height/2.0);
    CGPoint position = ccpAdd(winCenter, ccp(x, y));
    
    self.position = position;
    self.rotation = _angle+90.0+90.0;
}

updatePosition:メソッドが今回のメインです。
このメソッドはupdate:メソッドがから呼ばれます。
delta時間に応じて前フレームの位置からのオフセットを計算します。
使用する座標は極座標系で、半径と角度を保持します。
画面表示用に極座標系から直交座標系に変換します。
x = r cosθ
y = r sinθ
で計算します。保持する角度は「°」なのでラジアンに変換し、cocos2dの時計回りの角度から通常の反時計回りの角度に直すためマイナスを掛けています。

スプライトの向きを進行方向に合わせるため、_テクスチャーの絵の方向が上向きに描かれているのを右向きに補正するために_angleに90°加え、半径方向から円周方向に直すのにさらに90°加えた値をself.rotationに入れています。

たまにはアクションを使わずに自力でスプライトを動かしてみるのも楽しいものです。