FreelyApps

個人によるアプリ開発の日記です。アプリの収入だけで生活できるようになるのが目標です。UnityでAndroid向けのゲームアプリを作成しています。


    アプリ『トランプ・ジン・ラミー』を公開しました。
    Android/iOS https://goo.gl/PYKFYG

    タグ:中級

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Invokeという便利な関数があります。これは好きなメソッドを何秒か遅れて呼ぶという機能です。ある時点で発生することは確定するが、少し遅らせて処理をしたいという時に使うと簡単に処理ができて便利です。

    「ボタンを押したときにSEが流れて、それからWebページに飛ぶ」という処理を作るときにこれを使いました。ボタンを押した瞬間でSEを再生するのですが、同じタイミングでWebページに飛ばしてしまうと制御がブラウザーの方に切り替わってしまい、SEが途切れてしまいます。
    これを防ぐため、 ボタンを押した瞬間でSEを再生し、同じタイミングでWebページに飛ばす処理をInvokeで予約しておくのです。0.5秒ほど(SEの長さによります。)遅れてWebページに飛ばすにはInvoke("GoWebpage", 0.5f);というようにします。

    すぐに処理を行わないので、1度しか実行したくない場合でも複数回実行してしまうミスが起こりやすいです。フラグによる制御でも良いし、CancelInvokeで呼ばれているものを消してからInvokeしても良いです。IsInvokingを調べて予約されていたら、Invokeしないというのでも同じことです。

    何回も処理を呼ぶInvokeRepeatingというのもあります。 間違って使うと処理の流れを複雑にしやすいですが、処理の遅延を簡単に書けるのは重宝すると思います。
    Invokeで呼んだ中でInvokeを呼ぶような構造にすると、厄介なので遅延のために1回だけ使うような単純な使い方が良いと思います。

    InvokeはMonobehaviourのメソッドで、アタッチしたゲームオブジェクトが消えると呼び出しは破棄されるようです。Invokeで重要な処理を呼ぶときは、ゲームオブジェクトが破棄されないようにしておくことになります。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unityで新しいC#スクリプトを作成すると以下のような状態になっていると思います。
    これをそのまま使っても、編集しても良いのですが、いつも使う名前空間などはテンプレートに入っていると楽で良いと思っていました。System.Collections.Genericを使うのでSystem.Collectionsは使ったことがないですし、英語のコメントも慣れてくると要らないです。

    あるC#スクリプトをDuplicate(Ctrl+DかEdit->Duplicateでアセットの複製ができます。)してリネームするでも良いのですが、ちょっと手間なのでテンプレートを変更することでより便利になります。
    スクリプトのテンプレートを追加する」 Unityな日々(Unity Geek)より
    でその方法を解説がされているので参考に試してみました。
    スクリプトのテンプレートが保存されているのは、私の場合
    C:\Program Files\Unity\Editor\Data\Resources\ScriptTemplatesでした。Unityのインストール先を見つけてEditor\Data\Resources\ScriptTemplatesと探していけば良いでしょう。
    script-template
    フォルダには以上のようなテンプレートが並んでいます。C#のスクリプトのテンプレートは81-C# newScript-NewBehaviourScript.cs.txtです。これを自分の好きなように編集して上書きしてやると、Unityで作られる新しいスクリプトが変更されます。(Unityが読み込むファイルの変更なので、全プロジェクトに影響します。)

    このファイルを「81-C# newScript-NewBehaviourScript.cs」という名前でスクリプトのテンプレートが保存されているフォルダにコピーすると、Createメニューに項目が以下のように追加されるはずです。(別名にすることで元々のテンプレートを保持したままメニューに新しいものを追加されるようです。古いのが要らなければ上書きでも構いません。テンプレートのファイル名はハイフン区切りで意味を持つようです。)
    myTemplate
    この新たなメニューでスクリプトを追加すると以下のようになります。 
    usingにSystem.Collections.GenericやUnity5になってから追加されているよく使う名前空間を入れています。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    a6-permission

    Android6.0ではパーミッションをユーザーが任意に変えられる仕組みが導入されました。アプリによっては動かなくなるような致命的な影響を与える変更なので、一度は公式のドキュメントを見た方が良いです。

    パーミッションには色々ありますが、一部のパーミッションは危険だという分類にされていて、そのパーミッションを使うにはユーザーが許可する必要があります。それも動的(アプリ実行時)に変わるので、パーミッションが必要な処理をするところではその都度許可されているか調べなくてはなりません。

    どのパーミッションが危険かはこの記事のNormal and Dangerous Permissionsを見てください。
    私はSNSで画像を添付するためのファイルの読み書き
    • android.permission.READ_EXTERNAL_STORAGE
    • android.permission.WRITE_EXTERNAL_STORAGE
    の許可が必要になりました。 (実はWRITEの方があれば良いんですが……)

    許可が与えられているかを調べるにはAndroidのContextCompat.checkSelfPermissionを使えば可能です。当然、この呼び出しはJavaになります。プラグインを作ることでも可能ですが、UnityにあるJavaクラスを呼び出すためのクラスAndroidJavaClassを使うとC#のコードだけで実装が可能です。以下にコードを置いておきます。


    Check("android.permission.WRITE_EXTERNAL_STORAGE")のようにするとパーミッションが許可されているか調べられます。

    このコードを利用しても次のエラーが出るでしょう。
    AndroidJavaException: java.lang.ClassNotFoundException: android.support.v4.content.ContextCompat
    これはライブラリがないため、クラスが見つからないということです。 Plugins/Androidフォルダを作り、そこにandroid-support-v4.jarを置いておけば解決できます。
    android-support-v4.jarはAndroid SDKをインストールしたフォルダにあるはずで、
    (AndroidのSDKフォルダ)\extras\android\support\v4にjarがあります。これをUnityのプロジェクトの適切な場所にコピーすれば良いです。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    OnApplication~という名前のイベント関数が3つ用意されています。MonoBehaviourを継承したクラスに特定の名前のメソッドを作ることで自動的に呼び出してくれます。

    • OnApplicationFocus
    • OnApplicationPause
    • OnApplicationQuit
    の3つがあります。

    これらのイベントがどのような時に呼ばれるのか、いくつかのプラットフォームでどのようなときに呼ばれるのか調べてみました。
    テスト用のコードは次のようにしています。


    PC版ではログが実行中に見にくいので、適当なUIに文字列を出力していっています。ログが出る場合は、そちらを参照するだけで十分です。(PC版の場合は、exeの名前_Data/output_log.txtにログが出力されていきます。)
    ClickQuitはボタンUIにつけてApplication.Quitを呼び出すためのものです。


    Editor

    Editor上でApplication.Quitを呼び出しても何の意味もないようです。ログから呼び出しを確認できましたが、実行が停止されることもなくそのままでした。

    起動時(再生ボタンを押す)には以下のように表示されました。
    editor-awake
    OnApplicationPauseはポーズでないということでfalse、OnApplicationFocusはフォーカスされたということでtrueになっています。ポーズフォーカスの意味についてはここでは曖昧ですが、挙動を確認していくことで意味がわかってきます。

    フォーカスについてですが、これはゲームビューが操作対象になっているということです。再生ボタンを押してから他の場所をクリックしていないか、最後にクリックした場所がゲームビューであればフォーカスされているということになります。
    gameview-focusongameview-focusoff
    非常に微妙な違いですが、上のようにフォーカス時(左の図)とフォーカスされてない時(右の図)ではゲームビューのタブの色合いが違います。フォーカス時には多少明るくなっています。
    フォーカスされているかされていないかが切り替わるときに、OnApplicationFocusが呼ばれていました。

    ポーズはゲームが中断されるという意味で、実行がされなくなることです。エディターではRun In Backgroundの設定次第で挙動が異なります。
    pc-rib

    Run In Backgroundのチェックが付いてないときはエディター以外のGUIをクリックすればOnApplicationPauseでtrueとして呼ばれます。それで再びエディターをクリックして実行が開始されると、OnApplicationPauseでfalseとして呼ばれます。

    Run In Backgroundのチェックが付いている場合は、実行が継続されOnApplicationPauseは呼ばれなくなります。

    最後に再生ボタンを再び押して、ゲームの実行をやめると次のようになります。(Run In Backgroundの設定には関係しないようです。)このときはフォーカスが失われ、OnApplicationQuitが呼び出されています。
    editor-end


    PC

    プラットフォームをPCにしてビルドしたものを実行したときの挙動です。Windows上での確認になります。
    基本的な挙動はEditorとほぼ同じでした。

    異なる点はApplication.Quitが効き、このときにはOnApplicationQuitが呼ばれています。OnApplicationQuitはアプリをウィンドウ状態で開いたときに右上に出る閉じるボタンを押したときも呼ばれます。

    起動時はエディターと一緒でした。

    Run In Backgroundのチェックを付けないでビルドしたときは、OnApplicationFocusとOnApplicationPauseは引数が異なるだけで同じタイミングで呼び出されます。フォーカスされるというのは実行されていると同じだからでしょう。

    Run In Backgroundのチェックを付けてビルドしたときは、OnApplicationPauseは呼ばれなくなりました。ウィンドウ以外のところをクリックすると、フォーカスが外れてOnApplicationFocusが呼び出されるというようにエディターと同じ挙動です。

    またアプリの終了時にはOnApplicationFocusは呼ばれず、OnApplicationQuitのみが呼ばれていました。


    Android

    起動時には、OnApplicationPauseがfalseでOnApplicationFocusがtrueで呼び出されました。

    ホームボタン(ナビゲーションバーの真ん中のボタン)、リセントボタン(ナビゲーションバーの右のボタン。正式名称はよくわかりません。)を押すと、OnApplicationFocusとOnApplicationPauseが引数の値違いで呼び出されます。アプリからシステムの方に行くと中断であり、アプリに戻るのが再開のようです。
    最近のアプリ一覧の画面でアプリをフリックすれば終了させられますが、このときは何のイベントも呼ばれませんでした。つまり、OnApplicationPause、OnApplicationFocus、OnApplicationQuitのいずれも呼ばれずにアプリが終了させられるということです。

    Application.Quitを呼び出してアプリの終了させた時には、OnApplicationQuitのみが呼ばれていました。

    AndroidではOnApplicationQuitが呼ばれずに終了ができるので、重要なデータをOnApplicationQuitで保存するといった処理はまずい可能性があります。セーブすべきデータが更新されたら、なるべく早く保持すべきです。



    このイベントの呼び出し順序が決まっているのかは不明です。ログを見るとタイミングは毎回同じような気がしますが、明言されてないのでわかりません。順番がどうであっても問題なく動くようにした方がいいですね。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    UIの位置を調整する場合はスクリーン座標を基準に考えるのが最も良いです。スクリーン座標とは表示するディスプレイ上でのピクセル単位の座標です。
    Unityでは左下が(0,0)、右上が(幅,高さ)となるように座標を定めています。カメラのところにちょろっと説明が載っています。
    こういったゲームビューなら右上の座標は画面のサイズになります。
    gameview

    なぜスクリーン座標で考えると良いのかというと、画面上でどこに表示されているかを直接表しているためです。画面上の位置が表示にそのまま関係するため最もわかりやすいのです。

    3DとUIを連携したい場合
    わかりやすい例で言うと3D空間上にいる物体の近くにUIを常に出したいといったことがあるでしょう。これはキャラクターにHPのバーを表示し続けるといったことが該当します。
    キャラクターが3Dだとすると、キャラクターの位置は3D空間上での位置を表しています。(モデルの中心位置に一致することでしょう。)x,y,zの3成分があるベクトルで表されますが、この数字はワールド座標であるはずです。空間上の位置から画面上のどこ(スクリーン座標)に表示されるのかを求める必要があるわけです。これはキャラクターを移しているカメラから変換行列をかけてもらい求めます。Unityではこの行列計算は、カメラコンポーネントを取得してWorldToScreenPointを位置に適用するだけなので簡単です。
    キャラクターのスクリーン座標がわかったら、UIの位置を補正するなどしてUIのスクリーン座標を決めます。頭上にHPを出したければ、キャラクターのスクリーン座標に適当な量yを増やせばいいです。このスクリーン座標をUIの座標にして、それをUIにセットすれば目的が達成されます。RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform rect, Vector2 screenPoint, Camera cam, out Vector2 localPoint)を使えば、あるRectTransform内でのローカル座標を求めることができます。rectは設定したいUIの親になることがほとんどになるでしょう。camはCanvasに使っているカメラを渡します。オーバーレイモードならnullにします。localPointには結果を取得するための変数を渡します。これで得られたlocalPointを設定したいUIのRectTransform.localPositionに設定すると完成です。

    UI同士を連携したい場合
    UIのCanvasが異なるなど単純には位置を求められない場合もスクリーン座標を介することで位置を連携することができます。
    あるUIの位置がスクリーン座標でどこになるかが分かれば良いことになります。これはRectTransformUtility.WorldToScreenPoint(Camera cam, Vector3 worldPoint)を使うと可能です。なぜかこれについてはドキュメントに載っていないようです。使い方は引数からわかるようにUIを表示しているカメラとRectTransform.position(ワールド座標)を渡します。
    スクリーン座標からUIの座標を求めることは既に述べたので、これでどちらにも変換ができます。


    UIのもつ座標→スクリーン座標とその逆であるスクリーン座標→UIのもつ座標の変換方法があれば自由に位置を調整できるということになります。
    • UIのもつ座標→スクリーン座標はRectTransformUtility.WorldToScreenPoint
    • スクリーン座標→UIのもつ座標はRectTransformUtility.ScreenPointToLocalPointInRectangle
    を使うことで可能です。
    注意点はピボットの位置がRectTransformの位置である(localPositionとかはピボットの位置を表しているということ)ということです。変換を行うときに微妙に位置がずれるときはピボットの位置がセンターだったり、トップだったりと統一がされていない可能性があります。

    意外とありそうなミスとしてはアクティブでないUIの座標を取得してしまうことです。このときの座標は正しくないようなので、起こりやすい間違いです。 これは消していたいUIの座標を取りたいという時に困るのですが、どうしようもないようです。

    以上のような変換はスクリプトで制御したい場合に必要になることが多いでしょう。Canvasの異なるUI同士の座標をそろえたりする機能が今のところなさそうなので、こういった方法が要ると思っています。
    もっと簡単な方法があると良いのですが…… 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    以前、同じ内容で記事を書いたのですが、より良い方法がUnity5.3では行えるようです。

    rn-custom_coroutine
    Unity5.3で新しく使えるようになったカスタムコルーチンというものを使います。 公式の説明を見るとCustomYieldInstructionクラスを継承して、keepWaitingプロパティのgetをオーバーライドすれば新しいyieldオペレーションが作れます。
    yieldオペレーションとは、yield return ~とするときの~の処理のことを言っているのだと思います。(この記事ではそうだと思って書くことにします。)
    keepWaitingがtrueであればyieldオペレーションが処理を返さず継続し、falseになると処理を抜けます。このプロパティにDebug.Logを仕込んだところ毎フレーム呼ばれていたので、コルーチンで無限ループ内にyield return 0;というような書き方で行うのと同じと考えて良いでしょう。

    以前の方法のあまり良くない点は、処理をクラス内に書いていなければいけなかったため、あるクラスのstaticメソッドにするかクラス内での利用しかできないようになっていたことです。色々なところから自由に呼び出すということを考えると、その処理は独立した1つのクラスになっている方が見通しが良いのでそうすべきだと思っていました。
    メソッドに処理がある場合は、呼び出し方がStartCoroutineを介するというのもいまいちだと思います。StartCoroutineを使うと、渡すパラメータの個数の制約があること、わずかにコードが増えること、文字列での呼び出しだとエディター上で編集しにくいことが良くないです。
    新たな方法では導入が手間ですが、その後の作業を減らしてくれるため良い方法だと思います。WaitUntilやWaitWhileを使っても同じことができますが、よくある同じ処理をいちいち書くのは無意味なので、以下のようにカスタムコルーチンを作りました。

    上のソースは、ある時間処理を待たせるがスキップも可能であるというyieldオペレーションになります。使い方は
    yield return new SkippableWaitForSeconds(10, () => Input.GetMouseButtonUp(0));
    のようにします。この例だと10秒待つ処理であり、もしその間にマウスの左ボタンが上げられたらスキップするということになります。
    スキップするかの判定を外から渡せるため、色々と使えるようになっています。

    • カテゴリ:
      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity 5.2以前のUnity UIでは文字は4つの頂点で作られていました。今回5.2にアップデートしてみると文字を作るための頂点数が6つに増えていました。一文字表示するのに6つの頂点(2つの三角形)で描画することになります。 
    正確に言うと頂点の数は4つですが、三角形を2回書くために左上と右下の頂点が2回使われます。これで6つの頂点が使われることになります。

    6verts
    画像を良く見ると黒い線が入っています。これはワイヤフレームも表示するようにしたためです。
    一文字につき2つの三角形で構成されているのがわかると思います。3角形ひとつを描くには3つの頂点が必要なので、6頂点で一文字になります。

    頂点の並びは次のようでした。
    vertex
    これはBaseMeshEffectというUIにエフェクトをつけるときに使う基底クラスを使って調べました。頂点の情報をいじることで変形したり、色を変えたりすることができます。
    上の画像では、各頂点に異なる6色を使うようなエフェクトを作り適用しています。ななめに色が分断されているのは面が分かれてしまっているためです。(これにより5.2ではグラデーションをかけるエフェクトをつくるのが面倒になりました。文字のグラデーションのエフェクトを作っている人がいましたので、リンクを張っておきます。さけのさかなのブログより「uGUIでグラデーション文字 その2」)

    Unity5.2以前では、エフェクトをかける基底クラスはBaseVertexEffectというものでした。Unity5.2からBaseMeshEffectというクラスに変わってしまったので、作り方が少し変わります。フォーラムにこのように作れば変更をあまりしなくて良いというのが載っていました。
    以前はvoid ModifyVertices(List<UIVertex> verts);をオーバーライドしていましたが、void ModifyMesh (Mesh mesh);をオーバーライドするように変わりました。MeshからList<UIVertex>をつくるVertexHelperというものができたので、それを使うと以前の書式で書くことができます。
    頂点を直接いじっていたのがメッシュをいじることに変わったので、メッシュから頂点に変換してからエフェクトをかけ、頂点からメッシュに変換するという手順にすれば、以前と同じように使えます。
     

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Resources.Loadを使うと、動的にアセットを読み込むことができます。 動的にというのはシーン内に置かれていないかシーン内に置かれたゲームオブジェクトからの参照もないアセットを読み込むという意味です。
    Unityはシーンに必要な全てのアセットを自動でロードするので、シーンに置かれているもの、シーンに置かれているものが参照しているものは全てシーンの起動前にロードされるのです。

    使うためにはアセットをまず特別なフォルダに置かなくてはいけません。Resourcesというフォルダをプロジェクトのどこかに作ります。プロジェクトビューの直下(Assetsの直下)に置いても良いですし、どこか別のフォルダの下でも構いません。複数作ることも可能ですが、慣れないうちはひとつにしておいた方がわかりやすいです。
    Unityはいくつかのフォルダ名を特別扱いしています。 Resourcesもその中のひとつでResources.Loadでアクセスできる場所になっています。
     
    Resources.Loadの引数は文字列であり、これはパスを表します。Resourcesフォルダ内のどこにあるかをパスで記述します。Resourcesフォルダ内のPlayerという名前のプレハブであればResources.Load("Player")で読み込むことができます。Object型で返ってくるので、キャストが必要になります。
    Resourcesフォルダ内にさらにフォルダAがあり、その下のBGMというオーディオクリップを取得したい場合はResources.Load<AudioClip>("A/BGM")として読み込むこともできます。
    /を使って階層的になっているフォルダも対応できます。

    どのようなアセットでも動的に読みだせるので、使い道は広いです。共通で使うものはシーンに置いた方が便利ですが、特定の条件でしか使わないものや容量の大きいアセットの読み込みをどこかで行うといった使い方がありえます。シーンに全て置くことが可能なら、それも扱いやすいので無理にResources.Loadを使う必要はありません。

    Resources.Loadで読み込んだアセットはシーンが変わると破棄されます。シーンがひとつしかないとかしばらくシーンを変更しないがアセットはもう使わないのでメモリから解放したいということがあると思います。そういったときは読み込んだアセットを参照しているところをすべてなくした後、Resources.UnloadUnusedAssetsを呼び使われていないアセットを解放することができます。

    • 今読み込まれているのは何か?
    • 読み込み中なのか?
    • どれくらい読み込みを行うか?(メモリの限界の問題)
    • シーン切り替え時の対処
    といった問題が出やすいので、それに気をつけて使ってください。読み込んだつもりがアセットがないとかはエラーを引き起こしたりします。ロードのしすぎでメモリが足りなくなる問題は防ぎようがないので、いくつかの実機で試すしかありません。
     

      このエントリーをはてなブックマークに追加 Clip to Evernote
    C#でスクリプト上で重い処理を作ったとします。大きなリストの複雑なソートをするとか、大量の計算を行うといったことです。
    そういったときスクリプトのある特定の部分に書いてあることが、どれくらいの時間で処理されるのかということを知りたいことがあるでしょう。 こういったときはC#のStopwatchクラスを使うと簡単に実行時間が測れます。以下にコードを載せておきます。


    ストップウォッチクラスを作り、Startします。処理が終わったタイミングでStopし、ElapsedMillisecondsでかかった時間をミリ秒単位で取得しています。非常に簡単なコードなので、簡単にパフォーマンスを測れます。
    リリース版に計測用のコードが紛れ込むのが嫌なら、定義を使っておくと良いと思います。(#if DEBUGなど)

    ProfilerやFPSの計測よりも細かな範囲でのパフォーマンス計測になります。はじめは大きな計測から始めて、徐々に計測範囲を狭めていくと処理が重い原因を調べやすいと思います。
    このスクリプトのあるメソッドが重いとわかっているなら、今回の計測方法でさらに詳しく原因を探れるはずです。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    プロジェクト(Unityの作業フォルダの意味の方)が大きくなってくると、どのアセット(ファイル)を使っているのかわからなくなってきます。
    あるプレハブが別のプレハブを参照していたり、シーン内のゲームオブジェクトの深い階層からアセットが参照されていたりすることになっていると、もう管理しきれなくなります。
    参照がされているということはそれが必要だということです。間違えて参照がされているアセットをプロジェクトから消してしまうと、それを参照できなくなり何らかの問題が起こるでしょう。

    不必要なアセットをプロジェクトに入れておくことは良くないです。不要なアセットを手違いで使ってしまったり、余計な作業の発生があり得るからです。
    できることなら不必要なアセットは定期的に削除してしまい、必要なものだけを残していくというのが理想的です。特に大きなプロジェクトだと重くなっていくので、要らないものは極力なくす方が作業を効率的に行うためには重要です。

    解決策は2つあります。
    ひとつはプロジェクトに追加はしても消すことはしないことです。このルールを守るだけで参照できなくなることはありません。解決というよりは臭いものにふた的な発想です。しかしながら、意外と悪くない方法なのです。Unityは使っていないアセットをビルド時に除外するということをやっています。したがって不要なアセットを参照さえしていなければ、ビルドしたものにおいてそれは存在していないのと同じなのです。(Resourcesフォルダにある場合は例外。)
    もうひとつはプロジェクトで確実に使われていないことを調べたうえでアセットを削除することです。 これを行うのは少し難しいです。また労力もかなりかかります。
    アセットのそれぞれにはGUIDと呼ばれる識別子が与えられていて、Unityはそれでアセットを区別することができます。リネームしたり、移動したりしても参照が切れないのはこのGUIDを使ってUnityが参照を管理しているからです。
    このようなUnityの管理方法をそのまま自分で再現すれば、どのアセットが使われているのかがわかります。少し手順が複雑になりますが、以下がその手順です。決まった作業なので、頑張れば自動化も可能だと思います。

    メニューEdit->Project Settings->Editorを選び、Version ControlのModeをVisible Meta Filesにし、Asset SerializationのModeをForce Textにします。(バージョン管理をしている場合はこれらが設定されているかもしれません。バージョン管理については、ここの記事を参考にどうぞ。)
    Version ControlのModeをVisible Meta Filesにすることによりmetaファイルができ、対応するアセットのGUIDを調べることができます。下のようにCube.prefabに対応してCube.prefab.metaというファイルが同じ場所に存在します。
    metafile

    Asset SerializationのModeをForce Textにする理由は、デフォルトでバイナリファイルであるシーンやプレハブのファイルをテキスト形式で保存するためです。これにより全てのアセット内を検索することが可能になります。GUIDを検索できるようにするために必要というわけです。

     metaファイルを開いてみると、こんな感じです。
    meta
     2行目にguidというものがあり、これがGUIDになります。0c65~でフォルダ内をgrep(複数のファイルを同時に検索)して、どこに同じGUIDがあるかを調べます。
    見つかったファイルが検索したGUIDに対応するファイル以外であれば、そこからは参照があるということです。Unityのヒエラルキーやインスペクタ―を使って参照がされていることを確認してみてください。
     
    検索したGUIDに対応するファイルしか見つからなかった場合、それを参照しているアセットは存在しません。よって、それは削除してもかまわないものと見なせそうです。
    最後に調べるべきこととして、Resources.Loadを使用して動的なアセットの読み込みを行っていないか調べなくてはいけません。Resources.Loadを使うにはResourcesフォルダ以下にアセットが置かれていなくてはならないので、消そうとしたアセットがResourcesフォルダ以下にないことを確認します。
    以上が確認できれば、そのアセットは使われることはないので削除しても問題はありません。 

    まとめると、安全にアセットを消したい場合は次の2つの条件を満たす必要があるということです。 
    1. 対応するmetaファイルにあるGUIDでプロジェクトフォルダをgrepし、検索結果に他のファイルが現れないこと
    2. Resourcesフォルダ以下にアセットがないこと
    ひとつのアセットを消すのにこれだけの手間がかかるので、アセットの参照関係を忘れてしまうと大変です。覚えていれば、上記の作業を省けます。

    このページのトップヘ