iOSシミュレータで音が鳴らない/ファイルが読み込めないことがある

いつも通りビルドしたつもりなのに、突然下記の症状が出ることがあります。

  • 音声が再生されない
  • 画像が表示されない
  • ファイルが読み込めない

特に音については、CocosDenshion(SimpleAudioEngine)の使用方法や
caf/mp3/wav/aifファイルの作成方法、
afconvertコマンドでの引数指定を間違えたのか、と思いがちです。

トリガーとなるのは、直前のクリーンとリソースファイルの登録し直しです。

原因は、
Project Navigator のプロジェクト→TARGETS→Build Phases→Copy Bundle Resources
に該当のリソースファイルが登録されていないからです。
しかしここに追加しても根本的な解決になりません。

.png以外のリソースファイルをXcodeウィンドウにドラッグアンドドロップすると、
直後の「Choose options for adding these files」ダイアログにて
Add to targetsオプションでターゲットのチェックが外れています。
(.pngファイルだと、ターゲットにデフォルトでチェックが付きます)

よって、リソースファイルが更新されてもコピーされずに以前のデータが使われていたが、
クリーンしたことでそのデータも消えてしまった、というわけです。

ドラッグアンドドロップ後のダイアログによく注意し、
ターゲットにチェックを付けるようにしましょう。

Xcodeをアップデートするとビルドエラーが発生する

Xcodeをアップデートした後の最初のビルド時に、
以下のようなエラーが発生することがあります。


error: PCH file built from a different branch ((clang-425.0.27)) than the compiler ((clang-425.0.28))

この時は、一旦クリーンしてビルドし直すとエラーが解消されます。
Product → Clean

なお、シミュレータ、実機のそれぞれでクリーンが必要になります。

actionは都度新規生成する必要がある

CCShow, CCHideなどの CCAction系クラスのインスタンスは、再利用すると動作がおかしくなります。
[CCShow action]などと、利用する都度新規生成しなくてはなりません。
(これに気付かず数時間を費やしてしまいました)

参考

以下がそのNGコードです。

id acShow = [CCShow action];
id acDelay = [CCDelayTime actionWithDuration:1.0];
id acHide = [CCHide action];
[sprite1 runAction:[CCSequence actions:adShow,acDelay,acHide,nil]];
[sprite2 runAction:[CCSequence actions:adShow,acDelay,acHide,nil]];

正しくは、actions内で都度生成する必要があります。

removeChildByTag で EXC_BAD_ACCESS が発生する

cocos2dにて、CCMenu を何度か追加・消去するプログラムを作成していたところ、
EXC_BAD_ACCESS が発生しました。

具体的には、CCMenuItem が3つ入った CCMenu を構築したあと、
メニューアイテム・メニュー全てを removeChildByTag できれいに削除しようとしました。

CCNode* menuanswers = [self getChildByTag:kTagAnswers];
[menuanswers removeChildByTag:kTagAnswer1 cleanup:YES];
[menuanswers removeChildByTag:kTagAnswer2 cleanup:YES];
[menuanswers removeChildByTag:kTagAnswer3 cleanup:YES];
[self removeChildByTag:kTagAnswers cleanup:YES];

最後の行で EXC_BAD_ACCESS が発生。
発生したのは、CCMenu -onExit にて以下の[selectedItem_ unselected]の部分。

- (void) onExit
{
	if(state_ == kCCMenuStateTrackingTouch)
	{
		[selectedItem_ unselected];
		state_ = kCCMenuStateWaiting;
		selectedItem_ = nil;
	}
	[super onExit];
}

CCMenu は、CCMenuItem が最後まで生きていることを前提にしているようなので、
CCMenu のみを削除することで対応しました。