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

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

【BeeCluster】難易度設定機能を追加。セッターメソッドでいろいろやってみた。

いろいろな方から「難しい!」とご指摘を受けております、BeeCluster。
1面しか遊んでもらえないのではもったいないので、次のアップデートでは難易度設定機能を追加します。

敵や敵の弾のスピード、敵の攻撃確率をコントロールする機能はすでにプログラムしてあるのですが、これまでは常に固定の値を使用していました。今回はこの値をEASY, NORMAL, HARDの難易度にあわせて変更します。
また、スタート時やコンティニュー時のハチの数を変える事で自分の攻撃力の調整もしたいと思います。

ユーザーインターフェースとしては、タイトル画面にラベルを追加し、タップする毎に難易度が切り替わるようにします。タイトル画面はこうなりました。画面左下のEASYをクリックするとNORMAL→HARD→EASYと切り替わります。今流行りのフラットデザインです。(多分違う)

f:id:takujidev:20130803103313j:plain:w360

タイトルが画面表示の実装はこのようになっています。
_difficultyLabel, _labelTextが今回追加したインスタンス変数です。
_labelTextはCCArrayで、NSStringのオブジェクト@"EASY", @"NORMAL", @"HARD"を入れておきます。タップされる毎にインデックスをインクリメントしsetStringメソッドでラベルに表示する内容を変更します。また、DifficultyManagerのdifficultyRankプロパティを書き換えて変更内容を通知します。

@implementation TitleScene
{
    CCLabelTTF* _playLabel;
    CCSprite* _speakerButton;
    NSInteger _volume;
    CCLabelTTF* _difficultyLabel;
    NSArray* _labelText;
}

+ (CCScene *)scene
{
	CCScene* scene = [CCScene node];
	TitleScene* layer = [TitleScene node];
	[scene addChild: layer];
	return scene;
}

- (void)onEnter
{
	[super onEnter];

	CGSize size = [CCDirector sharedDirector].winSize;
    CGSize sizeInPixels = [CCDirector sharedDirector].winSizeInPixels;
    CCSprite* background;
    if (size.height != 480) {
        background = [CCSprite spriteWithFile:@"Default-568h@2x.png"];
    } else if (size.height != sizeInPixels.height) {
        background = [CCSprite spriteWithFile:@"Default@2x.png"];
    } else {
        background = [CCSprite spriteWithFile:@"Default.png"];
    }
	background.position = ccp(size.width/2, size.height/2);
	[self addChild:background z:0];
    
    _playLabel = [CCLabelTTF labelWithString:@"PLAY" fontName:@"arial" fontSize:30.0];
    _volume = [[NSUserDefaults standardUserDefaults] integerForKey:@"volume"];
    _speakerButton = [CCSprite spriteWithFile:[NSString stringWithFormat:@"speaker%d.png", _volume]];
    _labelText = [NSArray arrayWithObjects:@"EASY", @"NORMAL", @"HARD", nil];
    NSInteger index = [DifficultyManager sharedDifficulty].difficultyRank;
    _difficultyLabel = [CCLabelTTF labelWithString:[_labelText objectAtIndex:index] fontName:@"arial" fontSize:20.0];
    [self setVolume:_volume];
    [self addChild:_playLabel];
    [self addChild:_speakerButton];
    [self addChild:_difficultyLabel];
    _playLabel.position = ccp(size.width/2.0, 80);
    _speakerButton.position = ccp(size.width/2.0 + 110, 80);
    _difficultyLabel.position = ccp(size.width/2.0 -110, 78);

    id wait = [CCDelayTime actionWithDuration:1.0];
    id scaleUp = [CCScaleBy actionWithDuration:0.5 scale:1.2];
    id ease = [CCEaseInOut actionWithAction:scaleUp rate:1.5];
    id scaleBack = [ease reverse];
    id sequence = [CCSequence actions: wait, ease, scaleBack, nil];
    id repeat = [CCRepeatForever actionWithAction:sequence];
    [_playLabel runAction:repeat];
    
    self.isTouchEnabled = YES;
    
    [self addChild:[AdLayer layer]];
    [[SimpleAudioEngine sharedEngine] stopBackgroundMusic];
    
}

#define TOUCH_CIRCLE 50

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    float distancePlay = ccpDistance(_playLabel.position, location);
    float distanceSpeaker = ccpDistance(_speakerButton.position, location);
    float distanceDifficulty = ccpDistance(_difficultyLabel.position, location);
    
    if (distancePlay < TOUCH_CIRCLE) {
        [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.8 scene:[LoadingScene node] withColor:ccBLACK]];
    } else if (distanceSpeaker < TOUCH_CIRCLE) {
        _volume++;
        if (_volume > 3) {
            _volume = 0;
        }
        [[NSUserDefaults standardUserDefaults] setInteger:_volume forKey:@"volume"];
        [_speakerButton setTexture:[[CCTextureCache sharedTextureCache] addImage:[NSString stringWithFormat:@"speaker%d.png", _volume]]];
        [self setVolume:_volume];
    } else if (distanceDifficulty < TOUCH_CIRCLE) {
        NSInteger index = [DifficultyManager sharedDifficulty].difficultyRank;
        index++;
        if (index > 2) {
            index = 0;
        }
        [_difficultyLabel setString:[_labelText objectAtIndex:index]];
        [DifficultyManager sharedDifficulty].difficultyRank = index;
    }
}


@end

DifficultiManagerの実装です。シングルトンパターンで実装しています。
NSUserDefaultに難易度を保存することで次回起動時も値が保持されるようにしました。NSUserDefaultに設定を保存する方法については「【Objective-C】NSUserDefaultsを使ってハイスコアをセーブ」をご参照ください。

敵の難易度は_difficulty, 自機のハチの数は_initialBeeCountというインスタンス変数に保持します。EASY, NORMAL, HARDの各難易度は_difficultyRankというインスタンス変数に保持し、difficultyRankプロパティでこの3個のインスタンス変数を集中管理します。
具体的には、difficultyRankのセッターメソッドsetDifficultyRankを上書きしています。

例えば、
[DifficultyManager sharedDifficulty].difficultyRank = 1;

とすると、各インスタンス変数は次の値に設定されます。
_difficultyRank: 1
_difficulty: 1.0
_initialBeeCount: 2

難易度調整のパラメーターが今後増えても外部インターフェースは変わらず使えるので便利ですね。

@implementation DifficultyManager
{
    float _difficulty; //0.0 以上の値
    NSInteger _initialBeeCount; // 1以上の値
    NSInteger _difficultyRank; // 0 - 2の値
}

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

- (id)init
{
    if ((self = [super init])) {
        self.difficultyRank = [[NSUserDefaults standardUserDefaults] integerForKey:@"difficultyRank"];
    }
    return self;
}

- (void)setDifficultyRank:(NSInteger)difficultyRank
{
    _difficultyRank = difficultyRank;
    [[NSUserDefaults standardUserDefaults] setInteger:difficultyRank forKey:@"difficultyRank"];
    if (_difficultyRank == 0) {         // EASY
        _difficulty = 0.5;
        _initialBeeCount = 4;
    } else if (_difficultyRank == 1) {  // NORMAL
        _difficulty = 1.0;
        _initialBeeCount = 2;
    } else {                            // HARD
        _difficulty = 3.0;
        _initialBeeCount = 2;
    }
}

@end

テストプレイの結果は・・・
難易度EASYでは超激甘になり、残機42機でクリア。
これなら腕に自信がないプレイヤーでも十分楽しんでいただけるのではないでしょうか?
腕に自信があるプレイヤーにはHARDをおすすめします。
弾の量、スピードが格段にアップしています。私は残機1機ギリギリでクリアできました。