UIアイコンアトラスを計画する方法

今回の主な話題：UIアイコンアトラスを計画する方法、Native DLLを使用せずにUnityでプロセス間通信を実現する方法、??演算子を使用してオブジェクトにコンポーネントを追加する方法、キャラクターを追跡するカメラが建物にカバーされる時の半透明案、スクリプトを使用してタイムラインにキーフレームを追加する方法。

---
###UI
Q1: アイテムのアイコンが数百または数千個あるかもしれません。同じアトラスに配置すると、サイズは2048Pになります。実際のゲーム中に余分なメモリ浪費が発生します。

タイプごとに分別するのもあまり良くありません。初心者には各タイプの小道具があり、全種類のアトラスが一度引用されます。これは、１つにするのと大差ありません。

レベル区間に従って計画する方が良いかもしれませんが、ストアまたは特定のUIで展示するアイコンに対しては、レベル区間が異なる可能性もあり、複数のアトラスを引用することを引き起こします。使用頻度に従ってアトラスを区別することにも、プランナーさんが全過程を注目する必要があり、間違いやすいです。

または、これらの大量のアイコンをアトラスにパッケージしません、全部散図にします。そうすると、メモリ浪費はそれほど多くありませんが、レンダリング効率は低くなります。

この問題に対して何か良い解決策ありますか？
>1、画像の量が1つの画面に表示できる量をはるかに超える場合、より現実的な方法は、自分で動的アトラスを実現することです。つまり、アイコンを散図にして、実行中に要求次第でリアルタイムで大きな画像に組み立てます。Render to Textureによって実現できます。
>
2、ほとんどのゲームに対して、それが常駐画面インターフェイスでない場合、今の設備のパフォーマンスで、DrawCallに耐えることも無理ではありません。

---
###Script
Q2: Native DLLを使用せずにUnityでプロセス間通信を実現するにはどうすればよいですか？UnityでIPCを実装する際にいくつかの問題が発生しました。主な理由は、.Net 3.5にSystem.IO.MemoryMappedFilesクラスがないことです。Unity Editorプラグインを作成するときは、File mappingの方式でIPCを実現する必要があります。さまざまなプロジェクトに普遍的に適応させる必要があり、.Net3.5の互換性がなければなりません。これで、Import C ++で記述されたDLL以外に良い方法はないようですが、プラグインは単純にEditorだけが使用していますから、このせいでゲームに関係のないDLLがBuildにパッケージしたくないです。ですから、何か.Net3.5に対応できるIPCの実現方法はありますか？

①
>
1、純粋なC#は、socketしてIPCを作成できます。通用性も高いしプラットフォームもクロスできます。
2、Native DLLを使用してもパッケージに入れるかどうかを選択できます。少なくとも現在バージョンのUnityでこういうのはできます。

②
>
最下層に近い一部の機能については、自分でNativeプラグインを作成する方が良いです。DLLのImport Settingとマクロ定義であなたが言った問題を回避できます。
PS：非レンダリング機能（例えば、Camera、Microphoneなど）に対して、ほとんどのUnityのパッケージ化は、非効率的で制御できません。

---
###Script
Q3: ??演算子を使用してコンポーネントをオブジェクトに追加できないのはなぜですか？

```ruby:①
public class PlayerMoveTest : MonoBehaviour
{
    CharacterController controller;
     private void Awake()
        {
        controller = transform.GetComponent<CharacterController>() ?? gameObject.AddComponent<CharacterController>();
    }
}
```

```ruby:②
public class PlayerMoveTest : MonoBehaviour
{
    CharacterController controller;
     private void Awake()
        {
        controller = transform.GetComponent<CharacterController>();
        if (controller == null)
            {
                    controller = gameObject.AddComponent<CharacterController>();
            }
    }
}
```
上記のコードのように、一段目は機能せず、controllerはnullとして表示され、二段目はコンポーネントを追加できます。これはなぜですか？

??演算子の場合、??の前の式がnullであれば、??の後の式が実行されると言うことではありませんか？

①
>
試しました。
>
```ruby:test
public class PlayerMoveTest : MonoBehaviour
{
    CharacterController controller;
     private void Awake()
        {
        controller = transform.GetComponent<CharacterController>();
        if (controller == null)
            {
                    controller = gameObject.AddComponent<CharacterController>();
            }
    }
}
```
上記のコードを逆コンパイルすれば、
>
```ruby:逆コンパイル
if (GUI.Button(new Rect(100f, 100f, 200f, 80f), "Test") && (Object)base.transform.GetComponent<CharacterController>() == (Object)null)
    {
        base.gameObject.AddComponent<CharacterController>();
    }
    if (GUI.Button(new Rect(200f, 200f, 200f, 80f), "Test2") && (object)base.transform.GetComponent<CharacterController>() == null)
    {
        base.gameObject.AddComponent<CharacterController>();
    }
```
2つのコードが異なることがはっきり見られます。一段目はUnityEngine.Objectタイプを比較し、2つ目はSystem.objectタイプを比較します。
>
そしで、コードを書いてテストします。
>
```ruby:
var c1 = (object)base.transform.GetComponent<CharacterController>();
     if(c1 == null)
         Debug.Log(1);
     var c2 = (Object)base.transform.GetComponent<CharacterController>();
     if(c2 ==(Object)null)
         Debug.Log(2);
     if(c2 == null)
         Debug.Log(3);
```
**出力結果は2と3であります。**
>
これらの値をブレークポイントデバッグします。
![1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/2650a23e-fdc3-e05e-026f-c5f2ee00c2dd.png)
その原因は、UnityEngine.Objectタイプが==演算子をオーバーロードするため、”null”とnullは同じように見なされます。しかし、System.Objectタイプを使って比較すると、等しくないと見なされるためです。

②
>
確認しました、この”null”はあのnullではないことがわかりました。
![2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/23e29886-9693-70cf-142d-8592d58a8d22.png)
![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d74b4748-d286-4949-6cf6-3e553d7ab272.png)
実際、contor = transform.GetComponent()；が実行されると、値が割り当てられますが、割り当てられた値は”null”であります。プリントしたら２つのnullの違いがすぐにわかります。
![4.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/2b0dd33f-4629-d598-e4f1-0803e2d25f95.jpeg)
もう一言、contorのTypeをプリントする際に、oneのところで「オブジェクトの引用はインスタンスとして設定されていない」と報告しますが、twoのところでこのタイプをプリントします。つまり、??演算子の左側の値が割り当てられない場合のみで右側が実行します。例はコードbを参照してください。最後にプリントされたのはvで、cは私が定義した割り当てられないstring cであります。

---
###Script
Q4: キャラクターを追跡するカメラが建物にカバーされます。建物を半透明にされたいので、レイキャストが必要です。しかし、パフォーマンスの問題により、シーンオブジェクトでColliderコンポーネントを追加することはできません。単純なShaderで書いた時、効果には常にいくつかの問題があります。何か良い方案ありませんか？

①
>
1つの方法は、シーンを作成する時にBox Colliderを使用してオブジェクトの形状を真似して物理的な衝突を検出することです。Colliderコンポーネントの追加が許さないから、この形式は許すかどうかがわかりません。
単純なShaderで書く考えはわかりません。ただのShaderでこの要件は処理できないと感じます。
>
もっと良い方法はあるかどうかがわかりません。通用性も高い、またはtrickyな処理もしない状況で、物理がより直接的で効率的な方法であると感じています。
>
PS：視角が2.5Dであり、カメラが傾かない場合は、Runtimeで検索するために2Dデータを事前に計算できますが、制限と問題があります。

②
>
Unity 3Dでは、物理的な検出のためにシーンオブジェクトのような形状を作成するために、Box Colliderよりも直接的な方法は考えていませんでした。
>
前の人が「Runtimeで検索するためにデータを事前に計算できる」と言及しましたから、何かを思い出しました。Quakeシリーズに、Quakeシリーズでは、シーンレンダリングと物理的な衝突検出データがBSPツリーにプリベイクされていることを覚えています。ランタイムフェーズでのQuakeの衝突検出はBSPに基づいています。

---
###Timeline
Q5: 新しいタイムラインを使用すると、キーフレームを手動で追加するオプション/ボタンがなく、関連するAPIも見つからなかったので、キーフレームを手動で追加するにはどうすればよいですか？
>エディターでは、マウスでクリップの両側をドラッグして、クリップのヘッドまたはテールにキーフレームを追加できます。また、エディターには、現在操作中の+/-フレームの数が表示されます。
>
Runtimeがbuilt-inトラックにキーフレームを追加したい場合、現在のUnityには関連するインターフェースを提供していません。
スクリプトを通じでカスタムのTrack/Clipを実現できます。スクリプトでTimelineのPlayheadをコントロールして、固定時間にとどまるか、指定した時間にジャンプすることができます。
>
具体的には、TrackAsset、PlayableAsset、PlayableBehaviourを自分で実現する必要があります。
>
その中で：
>
TrackAssetの実現は、Trackを操作するための外部Monoスクリプトを提供するために使用されます。
PlayableAssetの実現は、カスタムClipを提供し、Trackに関連するデータを提供するために使用されます。
PlayableBehaviourの実現は、Timelineの再生をコントロールするためのコントロールロジックを実現するために使用されます。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com