iOSにおいて,あなたのゲームがUnityエディタで完璧に動作することができるが,その後,動作しないか,多分,実際のデバイス上で起動しないなど,いくつかの状況があります。 問題は,多くの場合,コードやコンテンツの品質に関連してます。このセクションでは,最も一般的なシナリオについて説明します。
これが起こる理由はいくつかあり,よくある原因は次のとおりです。:
アプリケーションが NullReferenceException を受信したとき,このメッセージは通常,iOSデバイスに表示されます。障害が起こった場所を把握する2つの方法:
4以来Unityには,NullReferenceExceptionのソフトウェアベースの処理が含まれています。AOTコンパイラには,null参照する毎,または変数がオブジェクトにアクセスする毎に簡単にチェックする方法が含まれています。この機能が開発ビルドのために有効になっている理由であり,スクリプトのパフォーマンスに影響します。(基本ライセンスユーザーは,ビルド設定ダイアログの “開発ビルド” オプションが有効で十分であり,iOSプロライセンスユーザーは,追加的に ”スクリプトのデバッグ“オプションを有効にする必要があります)。 すべてが正しく行われ,実際に障害が.NETコードで発生している場合,その後,EXC_BAD_ACCESSは表示されません。代わりに,.NET例外テキストはXcodeのコンソールに出力されます(あるいはあなたのコードは,単に ”catch"文でそれを処理します)。典型的な出力は次のようになります。:
Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required.
at DayController+$handleTimeOfDay$121+$.MoveNext () [0x0035a] in DayController.js:122
これは,障害がDayControllerクラスのコルーチンとして動作しているhandleTimeOfDaymメソッドで起こったことを示しています。それがスクリプトコードであれば,通常,正確な行番号を通知します(例えば,“DayController.js:122”)。問題のある行は,以下のようなものが考えられます。
Instantiate(_imgwww.assetBundle.mainAsset);
例えば,スクリプトは最初にそれが正しくダウンロードされたことを確認せずにアセットバンドルにアクセスした場合に発生する可能性があります。
ネイティブスタックトレースは,障害調査のためのより強力なツールですが,使用する際,いくつかの専門知識を必要とします。さらに一般的にネイティブエラー(ハードウェアメモリアクセス)が起きた後には続行できません。ネイティブスタックトレースを取得するには,Xcodeのデバッガコンソールにbt allと入力します。出力されたスタックトレースを慎重に調べると - エラーが発生した場所についてのヒントを含んでいることがあります。次のように表示されることがあります。:
...
Thread 1 (thread 11523):
1. 0 0x006267d0 in m_OptionsMenu_Start ()
1. 1 0x002e4160 in wrapper_runtime_invoke_object_runtime_invoke_void__this___object_intptr_intptr_intptr ()
1. 2 0x00a1dd64 in mono_jit_runtime_invoke (method=0x18b63bc, obj=0x5d10cb0, params=0x0, exc=0x2fffdd34) at /Users/mantasp/work/unity/unity-mono/External/Mono/mono/mono/mini/mini.c:4487
1. 3 0x0088481c in MonoBehaviour::InvokeMethodOrCoroutineChecked ()
...
まず第一に, “Thread 1” のスタックトレースを見つける必要があり,それがメインスレッドです。スタックトレースの最初の行は,エラーが発生した場所を指しています。この例では,トレースはNullReferenceExceptionが “OptionsMenu” スクリプトの “Start” メソッド内で起こったことを示しています。このメソッドの実装を注意深く見ると,問題の原因が明らかになるでしょう。一般的に,NullReferenceExceptionsは,Startメソッドの内部で初期化の順序について間違った前提を行った際に起きます。 場合によってはスタックトレースの一部分のみがデバッガコンソールに表示されます。
Thread 1 (thread 11523):
1. 0 0x0062564c in start ()
これはネイティブのシンボルがアプリケーションのリリースビルドの際に取り除かれたことを示しています。フルスタックトレースは,以下の手順で得ることができます。
通常,外部ライブラリは,ARM Thumb命令セットでコンパイルされたときに生成されます。現在,このようなライブラリはUnityと互換性がありません。Thumb命令なしでライブラリを再コンパイルすることで簡単に問題を解決することができます。Xcodeプロジェクトのライブラリからは次の手順でのこれを行います:
ライブラリのソースが利用できない場合は,Thumbコードではないライブラリのバージョンをサプライヤーに依頼する必要があります。
(時には, Program received signal: “0” のようなメッセージが表示される場合があります。) この警告メッセージは,多くの場合,致命的なものではなく,単にiOSのメモリーが少なくなって,アプリケーションにいくつかのメモリを解放するを求めていることを示しています。一般的に,メールのようなバックグラウンドプロセスは,いくつかのメモリを解放し,アプリケーションを実行し続けることができます。ただし,アプリケーションがメモリを引き続き使用,またはそれ以上を求めた場合,iOSは最終的にアプリケーション達とそれらのいずれかを終了させる可能性があります。Appleはメモリ使用量がいくらあれば安全であるかを明文化していませんが,経験からデバイスの全RAM50%未満(第2世代iPadではおよそ200〜256MB)をアプリケーションが使用している場合,メモリ使用量について大きな問題ありません。 注意するべき指標は,アプリケーションがどのくらいのRAM使用するかです。あなたのアプリケーションのメモリ使用サイズは3つの主要なコンポーネントで構成されています。:
注意: 内部プロファイラは,.NETスクリプトによって割り当てられたヒープのみを表示します。総メモリ使用量は,上記のようにXcode Instrumentsを介して決定できます。この図では,アプリケーションバイナリの一部,いくつかの標準フレームワークバッファ,Unityエンジン内部ステートバッファ,.NETランタイムヒープ(内部プロファイラによって表示される番号 ),GLESドライバヒープ,いくつかの他の様々なものを含みます。
他のツールはアプリケーションによって行われた割り当てを全て表示し,ネイティブヒープとマネージドヒープの両方の統計を含みます。(アプリケーションの現在の状態を取得するために,Created and still livingボックスを忘れずにオンにしてください)。重要な統計は,Net bytes値です。
メモリ使用を抑えるために:
iOSの強力なストリッピングオプション(Advancedライセンス機能)を使用してアプリケーションのバイナリサイズを小さくし,異なる.NETライブラリからの不要な依存関係を避けるようにします。詳細については,プレイヤー設定とiOSプレイヤーのサイズ最適化のマニュアルページを参照してください。 * コンテンツのサイズを小さくしてください。テクスチャにPVRTC圧縮を使用し,ローポリゴンモデルを使います。詳細については,ファイル サイズの削減のマニュアルページを参照してください。
空きメモリの量についてのOSに照会することは,アプリケーションのパフォーマンスを評価するための良いアイデアのように思えるかもしれません。OSはダイナミックバッファとキャッシュを多く使用してるので,空きメモリの統計は信頼できない可能性が高いです。唯一信頼できるアプローチは,アプリケーションのメモリ消費量を追跡し,これを主な指標として使うことです。特に新しいレベルをロードしたとき,上記のツールでどのようにグラフが時間の経過とともに変化するかに注意を払います。
これにはいくつかの理由が考えられます。より多く詳細を確認するために,デバイスのログをチェックする必要があります。Macにデバイスを接続し,Xcodeを起動してメニューからWindow > Organizerを選択します。Organizerの左ツールバーで使っているデバイスを選択し,“Console” タブをクリックして慎重に最新のメッセージを確認します。さらにクラッシュレポートを調べる必要があるかもしれません。クラッシュレポートを取得する方法は以下にあります。:http://developer.apple.com/iphone/library/technotes/tn2008/tn2151.html
貧弱なドキュメントにiOSアプリケーションには最初のフレームをレンダリングして入力処理には時間制限があるとあります。アプリケーションがこの制限を超えるとSpringBoardに強制終了されます。これは,例えばアプリケーションの最初のシーンが大きすぎると起こるかもしれません。スプラッシュスクリーンを表示するだけの小さな初期のシーンを作成し,yield で1または2フレーム待ってから,実際のシーンのロードすることをお勧めします。これは次のような簡単なコードで実行できます。:
function Start() {
yield;
Application.LoadLevel("Test");
}
今のところ,Type.GetProperty() と Type.GetValue() は,.NET 2.0 Subset プロファイルでのみサポートされています。Player Settings での.NET APIの互換性レベルを選択することができます。
注:Type.GetProperty() と Type.GetValue() は,マネージコードストリッピングと互換性がない可能性があり,除外する必要があるかもしれません(これを達成するためにストリッピング処理中にカスタムの非ストリッピングタイプのリストを提供して除外することができます)。より詳しいことはiOSのプレイヤーサイズの最適化ガイドを参照してください。
1<SomeValueType>:.ctor ()' "ExecutionEngineException: Attempting to JIT compile method 'SometType
1<SomeValueType>:.ctor ()’ while running with –aot-only." と表示されてクラッシュします。iOS用のMono.NET実装は,AOT(ネイティブコードへahead of time compilation:事前コンパイル)に基づいており,制約があります。それは明示的に他のコードで使用されているジェネリック型のメソッド(バリュータイプがジェネリックパラメータとして使用される場合で)のみをコンパイルします。このような方法がreflectionまたは,ネイティブコード(すなわち,シリアライゼーションシステム)だけを経由して使われている時,AOTコンパイル中にスキップされます。AOTコンパイラは,スクリプトコードのどこかにダミーメソッドを追加することで,コードをincludeすると指示できます。これで足りないメソッドを参照できるので,それらを事前にコンパイルできます。
void _unusedMethod() {
var tmp = new SomeType<SomeValueType>();
}
注意: バリュータイプは基本的な enumsとstructsです。
.NETの暗号化サービスは,reflectionに大きく依存し,これは静的コード分析を伴うため,マネージコードは,ストリッピングと互換性がありません。時々するクラッシュの最も簡単な解決方法は,ストリッピングプロセスから System.Security.Crypography名前空間の全体を取り除くことです。
ストリッピングプロセスは,Unityプロジェクトの Assets フォルダにカスタム link.xml ファイルを追加することでカスタマイズすることができます。これでストリッピングから取り除く型と名前空間を指定します。詳細は iOSプレイヤーのサイズ最適化に記載されています。
<linker>
<assembly fullname="mscorlib">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
</linker>
上記のアドバイスを検討するか,スクリプトコード,特定のクラスに,参照を追加することでこの問題を回避できるかもしれません。
object obj = new MD5CryptoServiceProvider();
通常,再帰的なジェネリックを多く使用する場合に,このエラーは発生します。より多くのtype0,type1,type2 トランポリンを割り当てるようAOTコンパイラへ指示できます。追加のAOTコンパイラのコマンドラインオプションは プレイヤー設定の “Other Settings” セクションで指定できます。type 1トランポリンの場合,nrgctx-trampolines=ABCD を指定,つまりABCDが必要な新しいトランポリンの数(すなわち4096)を指定します。type2トランポリンは nimt-trampolines=ABCD を指定し,type0トランポリンは ntrampolines=ABCDを指定します。
いくつかの最新のXcodeリリースでPNG圧縮と最適化ツールを導入した変更がありました。これらの変更は,スプラッシュスクリーン変更時,UnityのiOS実行時チェックで誤った判定の原因となります。このような問題が発生した場合は,Unityを最新の公開された利用可能なバージョンにアップグレードしてみてください。このような問題が発生した場合は,Unityを最新の公開された利用可能なバージョンにアップグレードしてみてください。それで解決しない場合は,以下の回避策を考慮してください。
Unityからビルドするとき(それを追加するのではなく)Xcodeプロジェクトをスクラッチから作ってビルドしてください。
既にデバイスにインストールされているプロジェクトを削除します。
Xcodeでプロジェクトをクリーンします。 (Product->Clean)
XcodeのDerived Data folderをクリアします。 (Xcode->Preferences->Locations) これでもまだ解決しない場合はXcodeでPNGの再圧縮を無効にしてみてください:
Xcodeプロジェクトを開きます。
そこで“Unity-iPhone” プロジェクトを選択します。
そこで“Build Settings”タブを選択します。
“Compress PNG files”を見つけて,NOにセットします。
以前armv6サポートを受けて提出された既存のアプリケーションを更新するときには,そのようなメッセージが表示されることがあます。Unity4.xとXcodeの4.5はarmv6のプラットフォームをもうサポートしていません。提出問題を解決するために,Unity Player SettingsのTarget OS Versionを4.3以上に設定します。
よくある間違いは,WWWのダウンロードが常に別のスレッドで起きていることを前提にしていることです。一部のプラットフォームでは正しいかもしれませんが,当たり前のこととするべきではありません。WWWの状態を追跡するための最良の方法は, yield ステートメントを使用するか,または Update メソッド でステータスをチェックするかのどちらかです。 while ループはビジーになるため。使うのは no です。
UIのいくつかの操作はiOSがすぐにウィンドウを再描画させます(最も一般的な例は,メインのUIWindow へ UIViewControllerとUIViewを追加する場合です)。スクリプトからネイティブ関数を呼び出した場合,それはUnityのPlayerLoop内部で発生し,結果的にPlayerLoopで再帰的に呼び出されることになります。その場合,waitUntilDoneをfalseに設定し,performSelectorOnMainThreadメソッドの使用を検討する必要があります。UnityのPlayerLoopの呼び出しの間に実行する処理のスケジュールをiOSに通知します。
アプリケーションはエディタ上では実行が[OK]なのですが,iOSプロジェクトでエラーが出る場合,行方不明のDLL(例 I18N.dll, I19N.West.dll)によって引き起こされているかもしれません。この場合,それらのDLLをUnity.app内からプロジェクトのAssets/Plugins フォルダにコピーしてみてください。Unityアプリケーション内のDLLの場所は次のとおりです。: Unity.app/Contents/Frameworks/Mono/lib/mono/unity その後,ビルドが最適化されたとき,DLLクラスが削除されていないことを確認するために,プロジェクトのストリッピングレベルをチェックする必要があります。iOS ストリッピングレベルの詳細については,iOS Optimisation Pageを参照してください。
通常,このようなメッセージは,マネージドファンクションデリゲートがネイティブファンクションに渡し,アプリケーションを構築時,必要なラッパーコードが生成されなかった場合に受信されます。どのメソッドがネイティブコードにデリゲートとして渡すかをAOTコンパイラに指示し,助けることができます。これは,カスタム属性“MonoPInvokeCallbackAttribute”を追加することで行うことができます。現在,静的メソッドのみをネイティブコードにデリゲートとして渡すことができます。
サンプルコード:
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using AOT;
public class NewBehaviourScript : MonoBehaviour {
[DllImport ("__Internal")]
private static extern void DoSomething (NoParamDelegate del1, StringParamDelegate del2);
delegate void NoParamDelegate ();
delegate void StringParamDelegate (string str);
[MonoPInvokeCallback(typeof(NoParamDelegate))]
public static void NoParamCallback() {
Debug.Log ("Hello from NoParamCallback");
}
[MonoPInvokeCallback(typeof(StringParamDelegate))]
public static void StringParamCallback(string str) {
Debug.Log(string.Format("Hello from StringParamCallback {0}", str));
}
// Use this for initialization
void Start() {
DoSomething(NoParamCallback, StringParamCallback);
}
}
Xcodeが以下のコンパイルエラー(メッセージを)を投げてきた: “ld : unable to insert branch island. No insertion point available. for architecture armv7”,
そのエラーは,通常,単一のモジュール内にあまりにも多くのコードがあることを意味します。一般的には多くのスクリプトコードがあるか,大きな外部.NETアセンブリがビルドに含めることによって引き起こされます。そして,スクリプトのデバッグを有効にすると,各ファンクションにかなりの数の命令が追加されるため,事態を悪化させるかもしれず,それは制限した方が容易になります。
この問題解決のためにplayer settings でマネージドコードストリッピングを有効にすると役に立つかもしれません。特に,大きな外部.NETアセンブリが関与している場合は。しかし,問題が続く場合の最善の解決策は,ユーザのスクリプトコードを複数のアセンブリに分割することです。このための最も簡単な方法は,Pluginsフォルダにいくつかのコードを移動することです。この場所にあるコードは,別のアセンブリに配置されます。また,スクリプトのコンパイルにどのように影響するかを,special folder namesについて情報を確認してください。