サーバー構築不要!スマートフォンアプリ向けの新クラウド

トップ >ドキュメント >チュートリアル(iOS):導入講座:パズルアプリを作ろう

チュートリアル(iOS)

ドキュメント内検索

導入講座:パズルアプリを作ろう

概要

このチュートリアルでは、前半と後半に分けて、パズルゲームへのニフティクラウド mobile backend導入方法を紹介しています。
前半では、3マッチパズルの作り方について、サンプルコードをもとに説明していきます。
後半では株式会社フジテレビジョンとイーグル株式会社からリリースされた
パズルゲームの「パズうま®」を例として、3マッチパズルを改良しつつニフティクラウド mobile backendを組み込む方法について解説しています。

このチュートリアルで解説しているサンプルコードは、以下のリンクでダウンロードできます。

ゲーム内容

今回、説明する3マッチパズルは、LINEゲームやパズドラ、Candy Crushなどでおなじみになっており定番とされているアプリです。
定番ではありますが、パズドラはパズルxRPG、LINEポップなどはLINEを活かした友達ランキング、Candy Crushは豊富な面と世界観など、パズル+αで売れているアプリがたくさんあります。

あまりにも有名で操作も簡単なため、容易に作れるアプリにも見えます。しかし、そろった時に消す判定や消えた分だけ新しく追加生成するなど、パズルゲームはロジックがとても複雑です。
今回のパズルアプリは指定した数字を変えるだけで玉の数やプレイ時間などさまざまな変更を可能にしています。自分なりに新しい要素を付け加え、新たなヒット作を狙ってみてはいかがでしょうか。

ロジック説明

それでは、今回のパズルアプリについて説明していきます。
ゲームの主要クラスである下記の3つのクラスについて重点的に解説していきます。

  • 大まかな動きを制御しているGameScene
  • パズルのタッチや消滅判定を制御しているPuzzleLayer
  • 玉の動きを制御しているBallSprite

GameScene

GameSceneは主にパズルのスタートや終了、残り時間の管理、パーツの生成をするためのクラスです。
トップでスタートボタンが押されるとGameSceneに遷移し、ここで PuzzleLayerが生成されパズルが開始されます。
実際にソースを見てみましょう。

GameSceneパーツ生成
//パーツの生成
-(void)setUpParts{
    //スコアやタイムの初期化
    _score = 0;
    _time = 0.0f;
    _comboMax = 0;
    CGSize size = [[CCDirector sharedDirector] winSize];
    
    //背景生成
    CCSprite *bg = [CCSprite spriteWithFile:@"pazzle_bg.png"];
    bg.anchorPoint = ccp(0.5,0.5);
    bg.position = ccp(size.width/2,size.height/2);
    [self addChild:bg];
    
    //パズルレイヤー生成
    _puzzleLayer = [PuzzleLayer node];
    [_puzzleLayer setGameScene:self];
    if (size.height>=568) {
        _puzzleLayer.position = kPuzzleLayerPosition;
    }else{
        _puzzleLayer.position = ccp(kPuzzleLayerPosition.x,kPuzzleLayerPosition.y-44);
    }
    [self addChild:_puzzleLayer];
    
    //スコアラベルの生成
    _scoreLabel = [CCLabelAtlas labelWithString:[NSString stringWithFormat:@"%d",0] 
    charMapFile:@"W29H32.png" itemWidth:29 itemHeight:34 startCharMap:'0'];
    _scoreLabel.anchorPoint = ccp(0.5,0.5);
    if (size.height>=568) {
        _scoreLabel.position = ccp(85,482);
    }else{
        _scoreLabel.position = ccp(85,482-44);
    }
    _scoreLabel.scale = 0.7;
    [self addChild:_scoreLabel];
    
    //制限時間ラベルの生成
    _timeLabel = [CCLabelTTF labelWithString:@"60:00" fontName:@"ArialRoundedMTBold" fontSize:20];
    _timeLabel.color = ccBLACK;
    _timeLabel.horizontalAlignment = kCCTextAlignmentCenter;
    _timeLabel.verticalAlignment = kCCVerticalTextAlignmentCenter;
    _timeLabel.anchorPoint = ccp(0.5,0.5);
    if (size.height>=568) {
        _timeLabel.position = ccp(264,482);
    }else{
        _timeLabel.position = ccp(264,482-44);
    }
    [self addChild:_timeLabel];
    //3秒後にパズル開始
    id delay = [CCDelayTime actionWithDuration:3.0];
    id call = [CCCallBlock actionWithBlock:^{
        [self startPuzzle];
    }];
    id seq = [CCSequence actions:delay,call, nil];
    [self runAction:seq];
}

1.1 はパズル画面に遷移する直前に呼ばれるパーツ生成のメソッドです。
ここで、背景や PuzzleLayer 、制限時間ラベル、スコアラベルを生成し表示しています。
さらに、三秒後にパズルをスタートするように書かれています。

GameSceneその他の主要メソッド
//トップへ戻るメソッド
-(void)goTop{
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[TopScene scene] withColor:ccWHITE]];
}
//リトライメソッド
-(void)doRetry{
    [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameScene scene] withColor:ccWHITE]];
}
//今回のスコア、ハイスコアの表示
-(void)setScoreBord{
    //省略//    
}
//ゲーム終了後にまだ消えている玉があるかのチェック
-(void)checkPuzzle{
    //省略//
}
//ゲーム終了メソッド
-(void)finishGame{
    [_puzzleLayer finishGame];
    [self unschedule:@selector(timer)];
    [self schedule:@selector(checkPuzzle) interval:0.1];
}
//スコア表示の更新
-(void)resetScoreLabel{
    _scoreLabel.string = [NSString stringWithFormat:@"%d",_score];
}
//ゲーム時間管理のタイマー
-(void)timer{
    //省略//
}
//パズル開始メソッド
-(void)startPuzzle{
    [_puzzleLayer startPuzzle];
    [self schedule:@selector(timer) interval:kBallFallTimerInterval];
}

1.2 はGameSceneにおける、その他の主要メソッドになります。
トップに戻る、リトライ、スコア表示、パズルの開始と終了を示すメソッドなどたくさん集まっています。
このようにGameSceneはゲームの大まかな動きを管理するように設計されています。

PuzzleLayer

続いて PuzzleLayer の説明に入ります。パズルレイヤーはタッチの制御や玉の消滅判定を制御しているクラスです。

PuzzleLayerタッチ制御(TouchesBegan、changeball)
# pragma mark - タッチ制御
//タッチによる玉の入れ替え
-(void)changeball:(BallSprite *)ball1 :(BallSprite *)ball2
{
    if (ball1&&ball2) {
        CGPoint coordinate1 = ball1.coordinate;
        CGPoint coordinate2 = ball2.coordinate;
        
        ball1.coordinate = coordinate2;
        ball2.coordinate = coordinate1;
        
        [ball1 moveTo];
        [ball2 moveTo];
    }
}
//タッチビギャン処理
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    _isTouching = YES;
    UITouch *touch = [touches anyObject];
    CGPoint currentPos = [touch locationInView:[touch view]];
    currentPos = [[CCDirector sharedDirector] convertToGL:currentPos];
    CGSize size = [[CCDirector sharedDirector] winSize];
    if (size.height>=568) {
        currentPos = ccp(currentPos.x,currentPos.y-kPuzzleLayerPosition.y);
    }else{
        currentPos = ccp(currentPos.x,currentPos.y-(kPuzzleLayerPosition.y-44));
    }
    
    _replacedBall = nil;
    _cacthingBall = [self ballAtPos:currentPos];
    if (_cacthingBall) {
        if (_cacthingBall.isSelected) {
            return;
        }
    if (_cacthingBall.isCrossBall) {
        [self rejectColor:_cacthingBall];
        return;
    }
    if (_cacthingBall.isFifthBall) {
        [self bomb:_cacthingBall];
        return;
    }
    _cacthingBall.isSelected = YES;
    _replaced = NO;
    }
}

2.1 は玉の入れ替えメソッドとタッチされた瞬間に呼ばれる ccTouchesBeganメソッドです。
まず、画面をタッチすると ccTouchesBegan が呼ばれます。
ここでタッチしているポジションからそのポジションにある玉を判定し、_cacthingBall にセットしています。
また、タッチした玉が爆弾や、同色を消す効果の玉である場合はその効果を発動させています。

PuzzleLayerタッチ制御(TouchesMoved)
//タッチムーブ処理
- (void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint currentPos = [touch locationInView:[touch view]];
    currentPos = [[CCDirector sharedDirector] convertToGL:currentPos];
    CGSize size = [[CCDirector sharedDirector] winSize];
    if (size.height>=568) {
        currentPos = ccp(currentPos.x,currentPos.y-kPuzzleLayerPosition.y);
    }else{
        currentPos = ccp(currentPos.x,currentPos.y-(kPuzzleLayerPosition.y-44));
    }
    if (_cacthingBall) {
        if (_replaced) {
            return;
        }
        if (_replacedBall) {
            return;
        }else{
            _replacedBall = [self ballAtPos:currentPos];
            if (_replacedBall) {
                if (_replacedBall.isSelected) {
                    _replacedBall = nil;
                    return;
                }
                int x = _cacthingBall.coordinate.x-_replacedBall.coordinate.x;
                int y = _cacthingBall.coordinate.y-_replacedBall.coordinate.y;
                if (abs(x)>1||abs(y)>1) {
                    return;
                }
                    if (x==1||x==-1) {
                        if (y==1||y==-1) {
                            return;
                        }
                    }
                if (_replacedBall!=_cacthingBall) {
                    //移動
                    _replacedBall.isSelected = YES;
                    _replaced = YES;
                    
                    [self changeball:_cacthingBall :_replacedBall];
                    //消えるかどうか判定
                    BOOL reject = [self checkReject];
                    
                    if (reject) {
                        _cacthingBall.isSelected = NO;
                        _replacedBall.isSelected = NO;
                        _cacthingBall = nil;
                        _replacedBall = nil;
                    }
                }
            }
        }
    }
}

2.2 は画面をタッチしたまま指が移動した時に呼ばれる ccTouchesMovedメソッドです。
ここでは、移動した先の玉をreplacedBallにセットし、先ほどccTouchesBeganで保持した_cacthingBallの場所を入れ替え、
その時に玉が消えるかどうかを判定し、消える場合は消すようになっています。
消えた場合は
cacthingBall、_replacedBallともにnilを入れておきます。

PuzzleLayerタッチ制御(TouchesEnded、TouchesCancelled)
//タッチエンド処理
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_isTouching = NO;
if (_cacthingBall&&_replacedBall) {
    if (_cacthingBall.isSelected&&_replacedBall.isSelected) {
        if (_replaced) {
            [self changeball:_cacthingBall :_replacedBall];
        }
        _cacthingBall.isSelected = NO;
        _replacedBall.isSelected = NO;
    }
}
if (_cacthingBall) {
    _cacthingBall.isSelected = NO;
    _cacthingBall = nil;
}
}
//タッチキャンセル処理
- (void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self ccTouchesEnded:touches withEvent:event];
}

2.3 は画面から指を離した時に呼ばれる ccTouchesEnded、通話がかかってきた時などに呼ばれる ccTouchesCan-celledメソッドを示しています。
ccTouchesCancelledの時は ccTouchesEnded と同じ処理が行われるようになっています。
ここではccTouchesMovedで「玉を移動したが消えなかった時」、「玉を移動して消えた時」、「玉を移動していない時」と3つのパターンがあります。
「玉を移動して消えた時」は何もせず、「移動したが消えなかった時」は元の場所に戻るようになっています。
「玉を移動していない時」は_cacthingBallにnilを入れています。

ゲーム内のタッチの制御に関してはこれで以上です。
次にパズルの消滅判定について説明していきます。

PuzzleLayer消滅判定
# pragma mark - 消えた分の玉を追加
-(void)makeNewballs
{
    //省略//
}
# pragma mark - コンボの初期化
-(void)timeUpcombo
{
    //省略//
}
# pragma mark - タイルの消滅
-(void)reject:(CGPoint)pos array:(NSMutableArray *)array
{
    //省略//
}
# pragma mark - タイルの消滅判定
-(BOOL)checkReject
{
    BOOL rejected=NO;
    NSMutableArray *rejectArray = [NSMutableArray array];
    
    //クロスを先にチェック
    for (int i=0; i<ballCountX; ++i) {
        for (int j=0; j<ballCountY; ++j) {
              //省略//
        }
    }
    //次に五連のチェック
    for (int i=0; i<ballCountX; ++i) {
        for (int j=0; j<ballCountY; ++j) {
            //省略//
        }
    }
    //最後に普通に消す玉のチェック
    for (int i=0; i<ballCountX; ++i) {
        for (int j=0; j<ballCountY; ++j) {
            //省略//
        }
    }
    return rejected;
}

2.4 は玉の消滅判定です。ここは条件など複雑になっているため省略としていますが、7で一部を詳しく説明していきます。
消滅判定のメソッドでは初めにクロス(十字、T字)で消える形があるかを判定し、
次に5連以上で消える玉の判定、次に3、4個で消える玉があるかを順に判定しています。
消える形がある場合は消える玉を配列にいれ、玉の中心点(コンボの表記が出るポジション)と一緒に rejectメソッドに送られます。
rejectメソッドに送られると玉が消えるのと同時にコンボの表記、消えた分の玉の補充が行われます。

PuzzleLayer消滅判定(詳細)
//最後に普通に消す玉のチェック
for (int i=0; i<ballCountX; ++i) {
for (int j=0; j<ballCountY; ++j) {
    BallSprite *ball = [self ballAtCoordinate3:ccp(i,j)];
    if (ball) {
        if (![rejectArray containsObject:ball]) {
            NSMutableArray *rejectArrayX = [NSMutableArray array];
            NSMutableArray *rejectArrayY = [NSMutableArray array];
            [rejectArrayX addObject:ball];
            [rejectArrayY addObject:ball];
            for (int i2=i+1; i2<ballCountX; ++i2) {
                BallSprite *t = [self ballAtCoordinate3:ccp(i2,j)];
                if (t) {
                    if (![rejectArray containsObject:t]) {
                        if (t.ballColor==ball.ballColor) [rejectArrayX addObject:t];
                        else break;
                    }else break;
                }else break;
            }
            for (int i2=i-1; i2>=0; --i2) {
                BallSprite *t = [self ballAtCoordinate3:ccp(i2,j)];
                if (t) {
                    if (![rejectArray containsObject:t]) {
                        if (t.ballColor==ball.ballColor) [rejectArrayX addObject:t];
                        else break;
                    }else break;
                }else break;
            }
            for (int j2=j+1; j2<ballCountY; ++j2) {
                BallSprite *t = [self ballAtCoordinate3:ccp(i,j2)];
                if (t) {
                    if (![rejectArray containsObject:t]) {
                        if (t.ballColor==ball.ballColor) [rejectArrayY addObject:t];
                        else break;
                    }else break;
                }else break;
            }
            for (int j2=j-1; j2>=0; --j2) {
                BallSprite *t = [self ballAtCoordinate3:ccp(i,j2)];
                if (t) {
                    if (![rejectArray containsObject:t]) {
                        if (t.ballColor==ball.ballColor) [rejectArrayY addObject:t];
                        else break;
                    }else break;
                }else break;
            }
            int x = [rejectArrayX count];
            int y = [rejectArrayY count];
            if (x>=3) {
                CGPoint pos=ccp(0,0);
                for (int k=0; k<[rejectArrayX count]; ++k) {
                    BallSprite *rt = [rejectArrayX objectAtIndex:k];
                    pos = ccp(pos.x + rt.position.x, pos.y + rt.position.y);
                    [rejectArray addObject:rt];
                }
                pos = ccp(pos.x/[rejectArrayX count] , pos.y/[rejectArrayX count]);
                [self reject:pos array:rejectArrayX];
                rejected = YES;
            }
            if (y>=3) {
                CGPoint pos=ccp(0,0);
                for (int k=0; k<[rejectArrayY count]; ++k) {
                    BallSprite *rt = [rejectArrayY objectAtIndex:k];
                    pos = ccp(pos.x + rt.position.x, pos.y + rt.position.y);
                    [rejectArray addObject:rt];
                }
                pos = ccp(pos.x/[rejectArrayY count] , pos.y/[rejectArrayY count]);
                [self reject:pos array:rejectArrayY];
                rejected = YES;
            }
        }
    }
}
}

2.5 は、効果が発動しない玉の、消え方判定を行っている部分になります。
(玉が3または4つで縦または横どちらかで消える時)1つの玉を基準に左右に同じ色であるか1つずつ見て、次に上下にも同じように同じ色であるかどうかを見ていきます。
この時、同じ玉が続いていれば横、縦用の配列 rejectArrayX、 rejectArrayY に入れていきます。
違う色が出るまで判定を続けます。
最後に配列の中に3個以上玉が入っていれば消滅、入っていなければそのままというようになります。
この判定を左下の玉から順番に6x7個の玉すべてで行うことで、そろっている玉を消すようになっています。
クロス(十字、T字)、5連では条件に多少の違いがありますが、基本は同じように判定を行っています。
puzzleLayer01

PuzzleLayer玉の追加
//指定列に何個のタイルがあるか
-(int)ballCountOfColumn:(int)x
{
    int count = 0;
    for (int i=0; i<[_ballsArray count]; ++i) {
        BallSprite *ball = [_ballsArray objectAtIndex:i];
        if (ball) {
            if (!ball.isRejecting) {
                if (ball.coordinate.x==x) {
                    ++count;
                }
            }
        }
    }
    return count;
}
# pragma mark - 消えた分の玉を追加
-(void)makeNewballs
{
    for (int i=0; i<ballCountX; ++i) {
        int count = [self ballCountOfColumn:i];
        for (int j=count; j<ballCountY; ++j) {
            BallSprite *ball = [BallSprite initWithBallType:[self getBallType] atCoordinate:ccp(i,j)];
            [ball setPuzzleLayer:self];
            ball.isInitialBall = NO;
            ball.isWateing = YES;
            ball.isFalling = YES;
            [self setDelayCall:kBallRejectTime block:^{
                [ball setIsWateing:NO];
            }];
            [self addChild:ball];
            [_ballsArray addObject:ball];
        }
    }
}

2.6 は玉が消された後に呼ばれる、 makeNewballs (玉の補充メソッド)です。
その上の ballCountOfColumn は指定列に今何個の玉があるかを返してくれるメソッドです。
このメソッドを使って、それぞれの列に何個の玉があるかを確認し、足りない分の玉を補充するようにしています。
puzzleLayer02

BallSprite

続いてBallSpriteクラスについて説明します。
BallSpriteは1つ1つの玉の状態管理、落下、演出などを管理しているクラスです。
PuzzleLayerで消す玉が決まった際、実際に消えるアニメーションなどはこのクラスで行われています。

BallSprite、初期化
# pragma mark - 初期化
- (id) initWithTileType:(enum ballColors)color atCoordinate:(CGPoint)coordinate {
    self = [super initWithFile:[NSString stringWithFormat:@"pazzle_ball_%d.png",color]];
    
    if (self) {
        _isFifthEffectBall = NO;
        _isFifthBall = NO;
        _isCrossEffectBall = NO;
        _isCrossBall = NO;
        _isRejecting = NO;
        _isFalling = NO;
        _isSelected = NO;
        _isMoving = NO;
        _isWateing = NO;
        _ballColor = color;
        _coordinate = coordinate;
        
        self.anchorPoint = CGPointMake(0.5, 0.5);
        self.scale = 1.0;
        self.position = kBallInitialPosFromCoordinateInit(_coordinate.x,_coordinate.y);
    }
    
    return self;
}
+ (id) initWithBallType:(enum ballColors)color atCoordinate:(CGPoint)coordinate {
    return [[[self alloc] initWithTileType:color atCoordinate:coordinate] autorelease];
}

3.1 は玉の種類、座標を指定し、生成と初期化するメソッドです。
動きを管理するためにたくさんのフラグがあるので、この初期化メソッドで初期値を設定するようにしています。

BallSprite、移動
# pragma mark - 落下メソッド
-(void)fall{
    if (self.position.y > kBallSize*_coordinate.y + kBall0position.y) {
        BallSprite *t = [_puzzleLayer ballAtCoordinate4:ccp(self.coordinate.x,self.coordinate.y-1)];
        if (t&&self.position.y<t.position.y+kBallSize) {
            _isFalling = NO;
        }else if (_isMoving||_isRejecting||_isWateing||_isSelected) {
            _isFalling = NO;
        }else{
            _isFalling = YES;
        }
    }else{
        if (!_isMoving&&!_isRejecting&&!_isWateing&&!_isSelected) {
            self.position = kBallPosFromCoordinate(_coordinate.x,_coordinate.y);
        }
        if (_isFalling) {
            id scale1 = [CCScaleTo actionWithDuration:0.05 scaleX:1.0 scaleY:0.8];
            id scale2 = [CCScaleTo actionWithDuration:0.1 scaleX:1.1 scaleY:1.1];
            id scale3 = [CCScaleTo actionWithDuration:0.1 scaleX:1.0 scaleY:1.0];
            id seq = [CCSequence actions:scale1,scale2,scale3, nil];
            [self runAction:seq];
        }
        _isFalling = NO;
    }
    if (_isFalling) {
        self.position = ccp(kBallSize*_coordinate.x + kBall0position.x, self.position.y - kBallFall - Speed);
    }
}
# pragma mark - 移動メソッド
-(void)moveTo{
    _isMoving = YES;
    id move = [CCMoveTo actionWithDuration:kBallReplaceAnimationDuration position:kBallPosFromCoordinate(_coordinate.x,_coordinate.y)];
    id call = [CCCallBlock actionWithBlock:^{
        [self setIsMoving:NO];
    }];
    id seq = [CCSequence actions:move,call, nil];
    [self runAction:seq];
}

3.2 は玉の落下メソッドと、タッチで移動する際に使用される移動メソッドです。
落下メソッドは 1/60.0秒間隔で呼ばれており、着地していない場合は少しずつ下に移動するようになっています。
移動メソッドはどこに移動するかを引数で送っていませんが、このメソッドを呼ぶ前に BallSpriteクラスが持っている
プロパティの1つ coordinate(座標)に移動する先の座標をセットしているので、その座標のポジションまで移動するよう設計されています。

BallSprite、効果演出
# pragma mark - 効果のある玉の演出
//玉の色の変更メソッド
-(void)crossAnimation{
    if (_ballColor==red) {
        _ballColor = yellow;
        }else if (_ballColor==yellow) {
        _ballColor = green;
        }else if (_ballColor==green) {
        _ballColor = blue;
        }else{
        _ballColor = red;
    }
    [self setTexture:[[CCTextureCache sharedTextureCache] addImage:[NSString stringWithFormat:@"pazzle_ball_%d.png",_ballColor]]];
}
//色が変化するアニメーションのストップメソッド
-(void)stopCrossAnimation{
    [self unschedule:@selector(crossAnimation)];
}
//T字or十字で消した時の演出(ルーレットみたいになる、タッチした時の色のパネルが全部消える)
-(void)cross{
    _isCrossBall = YES;
    id scaleUp = [CCScaleTo actionWithDuration:kBallRejectScaleUpTime scale:1.2];
    id scaleDown = [CCScaleTo actionWithDuration:kBallRejectScaleDownTime scale:1.0];
    id seq = [CCSequence actions:scaleUp,scaleDown, nil];
    [self runAction:seq];
    [self schedule:@selector(crossAnimation) interval:0.1];
}
//5連で消した時の演出(爆弾になる)
-(void)fifth{
    _isFifthBall = YES;
    id scaleUp = [CCScaleTo actionWithDuration:kBallRejectScaleUpTime scale:1.2];
    id scaleDown = [CCScaleTo actionWithDuration:kBallRejectScaleDownTime scale:1.0];
    id seq = [CCSequence actions:scaleUp,scaleDown, nil];
    [self runAction:seq];
    
    [self setTexture:[[CCTextureCache sharedTextureCache] addImage:@"pazzle_ball_bomb.png"]];
    id delay = [CCDelayTime actionWithDuration:2.2];
    id scale1 = [CCScaleTo actionWithDuration:0.2 scaleX:0.9 scaleY:1.1];
    id scale2 = [CCScaleTo actionWithDuration:0.2 scaleX:1.1 scaleY:0.9];
    id scale3 = [CCScaleTo actionWithDuration:0.2 scaleX:1.0 scaleY:1.0];
    id seq2 = [CCSequence actions:delay,scale1,scale2,scale3, nil];
    id repeat = [CCRepeatForever actionWithAction:seq2];
    [repeat setTag:411];
    [self runAction:repeat];
}

3.3 は効果のある玉の演出が書かれているメソッドです。
5連で消された場合は玉が爆弾になり、一定間隔でプニプ二と動くようしています。
クロスで消された場合は玉の色が0.1秒間隔で赤→黄→緑→青→赤...と変化するようにしています。
効果のある玉の演出を修正したい場合はこの部分を修正することになります。

BallSprite、消滅演出
# pragma mark - 消滅演出
-(void)reject{
    [self stopActionByTag:411];
    id spawn;
    id call = [CCCallBlock actionWithBlock:^{        
        [self removeFromParentAndCleanup:YES];
    }];
    
    id scaleDown = [CCScaleTo actionWithDuration:kBallRejectScaleUpTime*2 scale:0];
    id ease = [CCEaseBackIn actionWithAction:scaleDown];
    spawn = [CCSpawn actions:ease,nil];//fade, nil];
    if (_isFifthBall) {
        CCParticleSystem *system = naMakeParticle(@"wavePt.plist",ccp(self.position.x+kPuzzleLayerPosition.x,self.position.y+kPuzzleLayerPosition.y), 1.0);
        [_puzzleLayer.gameScene addChild:system];
    }

    id block = [CCCallBlock actionWithBlock:^{
        NSString *particleStr = nil;
        switch (_ballColor) {
            case green:particleStr = @"GreenFlower.plist";break;
            case blue:particleStr = @"blueFlower.plist";break;
            case yellow:particleStr = @"flower4.plist";break;
            case red:particleStr = @"redFlower.plist";break;
            default:break;
        }
        CCParticleSystem *system = naMakeParticle(particleStr,ccp(self.position.x+kPuzzleLayerPosition.x,self.position.y+kPuzzleLayerPosition.y), 1.0);
        [_puzzleLayer.gameScene addChild:system];
        NSString *particleStr2 = @"shortFlower.plist";
        CCParticleSystem *system2 = naMakeParticle(particleStr2,ccp(self.position.x+kPuzzleLayerPosition.x,self.position.y+kPuzzleLayerPosition.y), 1.0);
        [_puzzleLayer.gameScene addChild:system2];
        NSString *particleStr3 = @"shortPt.plist";
        CCParticleSystem *system3 = naMakeParticle(particleStr3,ccp(self.position.x+kPuzzleLayerPosition.x,self.position.y+kPuzzleLayerPosition.y), 1.0);
        [_puzzleLayer.gameScene addChild:system3];
        
        [[SimpleAudioEngine sharedEngine]playEffect:@"panel-4.mp3"];
    }];
    id seq = [CCSequence actions:spawn,block,call,nil];
    [self runAction:seq];
    id delay = [CCDelayTime actionWithDuration:kBallRejectScaleUpTime];
    id call2 = [CCCallBlock actionWithBlock:^{
        [[SimpleAudioEngine sharedEngine]playEffect:@"panel-2.mp3"];
    }];
    id seq1 = [CCSequence actions:delay,call2,nil];
    [self runAction:seq1];
}

3.4 は玉が消える時に呼ばれる消滅演出のメソッドです。
ボールの色、爆弾か否か、クロスで消した際の効果のある玉か否かによって玉の消滅の仕方をそれぞれ変えることができます。
ここでは玉の色毎に演出用のパーティクルを違うものにし、
爆弾の時はさらにプラスで大きなパーティクルが出るように設定しています。

CommonDefine

最後にCommonDefineクラスの説明を行います。
CommonDefineはゲーム内で使用するゲーム時間や玉のポジションなど変更頻度の多い値や修正の可能性が高い値をまとめたヘッダーファイルです。
Defineで変更をしやすいようにしているため、簡単に調整などが行えます。
例えば、ボールの縦、横の数などは、for文を使うたびに使用したりするので、変更する際に多くの箇所を修正しなくては行けなくなってしまいますが、
この様にDefine にしておくと1カ所を変更するだけで済みます。

CommonDefine
//ゲーム時間
#define kGameTime 60
//パズルのレイヤーのポジション *アンカーポイントは(0,0)
#define kPuzzleLayerPosition ccp(7,96)
//玉の数(横)
#define ballCountX 7
//玉の数(縦)
#define ballCountY 8
//玉のサイズ
#define kBallSize 44
//基本の玉の種類数
#define kBallTypeCount 4
//玉の原点(1番左下の玉のポジション)
#define kBall0position ccp((kBallSize/2),(kBallSize/2))
//座標からの玉のポジション
#define kBallPosFromCoordinate(_x,_y) ccp(kBallSize*_x + kBall0position.x, kBallSize*_y + 
kBall0position.y)
//座標からの玉の初期ポジション
#define kBallInitialPosFromCoordinateInit(_x,_y) ccp(kBallSize*_x + kBall0position.x, kBall-
Size*(_y+ballCountY) + kBall0position.y)
//玉の落ちるスピード
#define kBallFallSpeed 8
//コンボが切れるまでの時間
#define kComboTime 1.0
//玉の削除アニメーションタイム
#define kBallRejectScaleUpTime 0.2
#define kBallRejectScaleDownTime 0.2
#define kBallRejectTime (kBallRejectScaleUpTime + kBallRejectScaleDownTime)
//ボールの入れ替えアニメーションの時間
#define kBallReplaceAnimationDuration 0.1
//玉落下タイマーインターバル
#define kBallFallTimerInterval 1/60.0f

実際にCommonDefineクラスを見てみましょう。
例えば、玉の数を横x縦を5x6 にしたい場合は ballCountXを 5、ballCountYを 6 にし、 kPuzzleLayerPosition を画像に合わせて修正します。
ゲーム時間を変更したり、玉の落ちるスピードを変更する時もこのファイルを修正するだけです。
デバッグ時などはゲーム時間が60秒では長くて大変なので、3秒に変更し効率良く行うこともできます。

以上で、3マッチパズルのロジック解説は終わりになります。
次は、この3マッチパズルを改変してリリースされた『パズうま®』を例にmBaaSシステムの導入方法を説明していきます。

後半はこちら

お探しの内容が見つからなかった場合はユーザーコミュニティ もご活用ください。(回答保証はいたしかねます)
なお、 Expertプラン以上のお客様はテクニカルサポートにてご質問を承らせて頂きます。

推奨画面サイズ1024×768px以上

ページの先頭へ