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

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

【Objective-C】NSUserDefaultsに値を書き込んだらsynchronizeをすべきなのか?

音量調節ボタンやミュートボタンをアプリに追加しました。

ゲーム実行時画面には音量調節とミュートのON/OFFボタン、タイトル画面にはミュートのON/OFFボタンを配置して早速テストしてみると、ゲーム内でミュートしたはずなのに再起動後のタイトル画面ではミュートが外れていたり、音量調節しても再起動後に元に戻っていたりと意味不明の動作となっていました。

調節した音量やミュート状態はゲーム終了後も保持し、次回起動時に引き継ぐためNSUserDefaultsに保存していたのですが、これでは使い物になりません。

調べてみると、NSUserDefaultsに値を描き込んだ後、synchronizeしないと必ずしも即時値がセーブされないとのことでした。

これはボリュームを下げる処理なのですが、setInteger:forkey:メソッドの直後にsynchronizeメソッドを呼ぶようにしました。

- (void)volumeDown
{
    NSInteger volume = [[NSUserDefaults standardUserDefaults] integerForKey:@"volume"];
    volume--;
    if (volume < 0) {
        volume = 0;
    }
    [[NSUserDefaults standardUserDefaults] setInteger:volume forKey:@"volume"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [self adjustVolume:volume];
}

これにより、設定した値が即座にファイルに反映されるようになり、前述の矛盾は発生しなくなりました。
synchronizeをしない場合、実際にファイルに書き込まれるまでの遅延がどれほどかは不明ですが、念のため自分で明示的にsynchronizeしておいた方がいいんでしょうね。
https://itunes.apple.com/jp/app/travelshooting-jp-toraberushutingu/id917570972?mt=8&uo=4&at=10laCt

【TravelShooting JP】ゲーム開始時の処理をタイトル画面で済ませてレスポンスアップ!

順調に開発が遅れているTravelShooting JPですが、そろそろタイトル画面を付けて見たくなり、タイトル画面としてcocos2dのシーンを1つ追加しました。

タイトル画面にボタンを1つ配置し、押すとゲームがスタートするようにしてみたのですが、嫌なことに気がついてしまいました。
ボタンを押してもしばらく反応がなく、1秒くらい遅れてゲームが始まります。

初期化処理に掛かる時間を実測してみると、

2014-09-03 18:13:04.120 TravelshootingJP[29379:60b] Start
2014-09-03 18:13:05.203 TravelshootingJP[29379:60b] Finish

やはり1秒以上掛かっています。
このレスポンスの悪さだとユーザーから苦情が来るのは避けられないので、とにかく準備できる処理は全てタイトル画面でやっておく事にしました。
cocos2dのシーンをまたぐので、基本的にはシングルトンインスタンスが管理するデータが対象になります。

例えば、SimpleAudioEngineのpreloadBackgroundMusicなんかはタイトル画面でやっておいても全く問題ありません。

また、スプライトシートやテクスチャーファイルも読んでおくと高速化できます。
私の場合は、プロジェクトのSpriteSheetsというディレクトリーにTexturePackerというツールからファイルを吐き出すようにしており、そこから全ファイルをキャッシュに読み出してしています。

- (void)loadSpriteSheets
{
    NSBundle* bundle = [NSBundle mainBundle];
    NSArray* paths = [bundle pathsForResourcesOfType:@"plist" inDirectory:@"SpriteSheets"];
    NSAssert(paths.count != 0, @"No sprite .plist file found");
    
    for (NSString* path in paths) { //ディレクトリ内全.plistファイルをループ
        NSString* filename = [path lastPathComponent]; //ファイル名を取り出す
        NSString* relativePath = [@"SpriteSheets/" stringByAppendingString:filename]; //Resourcesディレクトリからの相対パス
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:relativePath];
    }
    
    paths = [bundle pathsForResourcesOfType:@"pvr.ccz" inDirectory:@"SpriteSheets"];
    NSAssert(paths.count != 0, @"No sprite .pvr.ccz file found");
    
    for (NSString* path in paths) {
        NSString* filename = [path lastPathComponent];
        NSString* relativePath = [@"SpriteSheets/" stringByAppendingString:filename];
        [[CCTextureCache sharedTextureCache] addImageAsync:relativePath withBlock:^(CCTexture2D *tex) {
        }];
    }
}

その他、自分で作ったシングルトンクラスのインスタンスも初期化しておきます。
シングルトンインスタンスはcocos2dのノードツリーに加えておかなくても解放されないので、タイトルシーンではとりあえず初期化だけしておき、ゲームシーンでaddChildすると良いと思います。

タッチ関係のシングルトンインスタンスはタイトル画面でも使いたいのでタイトルシーンでaddChildし、replaceSceneの直前でremoveChildしています。

これらの修正を加え起動してみると、スタートボタンのタッチ後、全く遅れが気にならないくらいのレスポンスでゲームがスタートするようになりました。

時間を計ってみると、

2014-09-03 18:18:40.203 TravelshootingJP[29445:60b] Start
2014-09-03 18:18:40.452 TravelshootingJP[29445:60b] Finish

約0.25秒

かなり短縮されています。
これなら恐らく苦情は来ないと思います。
https://itunes.apple.com/jp/app/travelshooting-jp-toraberushutingu/id917570972?mt=8&uo=4&at=10laCt