【Unity】 mod2演算とは

ある数字を+1ずつ足していき、〇〇以上になった場合に0に戻す。 こういったロジックを実装するときにmod2を使うと簡単に書くことができます。

普通に計算する

「ある数字を+1ずつ足していき、〇〇以上になった場合に0に戻す。」をwhile文を使って実装した場合、このようななります。

while (index != 0)
{
    index = index + 1;
    if (index > 5)
    {
        index = 0;
     }
}

mod2を使う

先程の処理はmod2を使えば次のように置き換えることができます。

while (index != 0)
{
    index = (index + 1) % 5;
}

mod2とは

mod2とは剰余演算子と呼ばれています。 ある数字で割ったときに余りを返します。

Debug.Log(1 % 3); // 1
Debug.Log(2 % 3); // 2
Debug.Log(3 % 3); // 0
Debug.Log(4 % 3); // 1
Debug.Log(5 % 3); // 2

docs.microsoft.com

独習C# 第3版

独習C# 第3版

【C#】LinqのToArrayとToListについて

LinqでToArrayやToListを使うときの注意点なのです。 使い方によっては負荷が上がってしまうので用途により使い分けたほうがよいです。

パフォーマンス比較

次のコードを書いてUnityのProfilerで調べてみます。

 Profiler.BeginSample("None");
 var sample1 = Enumerable.Range(1, 10).Select(GetString);
 Profiler.EndSample();
        
 Profiler.BeginSample("ToArray");
 var sample2 = Enumerable.Range(1, 10).Select(GetString).ToArray();
 Profiler.EndSample();
        
 Profiler.BeginSample("ToList");
 var sample3 = Enumerable.Range(1, 10).Select(GetString).ToList();
 Profiler.EndSample();

画像が見にくいですが1番右がCC Allocです。

f:id:albatrus:20210403213719p:plain
Profilerの比較

見てわかるようにToArrayとToListを使った場合の負荷が上がっています。

用途で使い分ける

配列やListとして扱う必要がある場合にはToArrayやToListを使っていきましょう。 ただ、for文などで中身を取り出すだけで使うのであればIEnumerable<>のまま使ったほうがよいです。

foreach (var s in sample1)
{
    Debug.Log(s);
}

【Unity】switch式について

とあるプルリクエストを見ていたときに、今までみたことのない書き方があり調べてみるとことにしました。

switch式

調べたとこと「switch式」という演算子でした。

docs.microsoft.com

具体例

まず、2つのタイプを用意します。

public enum TestType
{
    None,
    Test,
}

public enum TestChangeType
{
    ChangeNone,
    ChangeTest,
}

これをこのような形で利用します。

var changeType = testType switch
{
      TestType.None => TestChangeType.ChangeNone,
      TestType.Test => TestChangeType.ChangeTest,
       _ => TestChangeType.ChangeNone
};

こうすれば、あるenum値を別のenum値へと置き換えることができます。

_ パターン

先程のコードの次の部分は、switch時にどのパターンにも当てはまらない時にどうするかの指定です。 switch文のdefaultのような感じです。

_ => TestChangeType.ChangeNone

最後に

switch式はc#8.0から使える機能でした。 新しい機能を追っていないと、こういった方法を選択することができなくなるので、もう少し勉強しないといけないなと感じました。 C# 8.0 の新機能 - C# ガイド | Microsoft Docs

【Unity】ZStringを使って文字を操作する

ある文字とある文字を組み合わせて文字列を作る際に今までは次のようなコードを書いていました。

var value = 10;
var maxValue = 100;
var text = string.Format("{0}/{1}", value, maxValue);
Debug.Log(text); // 10/100

上記コードは、Riderを使っているとこのように書けば良いととアドバイスをくれます。

var value = 10;
var maxValue = 100;
var text = $"{value}/{maxValue}";

こちらを使っても同じ文字列が作成できますので、どちらを使っても結果的にはどちらでも良さそうです。

違い

同じ結果ですが、この2つの実装方法は違います。 プロファイラーで確認すると次のようになります。

f:id:albatrus:20210321095822p:plain:w400
Profiler

どちらも同じ結果なのですが、Concatを使ったほうがパフォーマンスが良いみたいです。

複数の文字列を連結する時、連結演算子やConcatメソッドを使って一度に連結する方法が、String.FormatやStringBuilder.Appendメソッドを使った方法よりもパフォーマンスが良いということです。 文字列を連結する - .NET Tips (VB.NET,C#...)

これはプロファイラーを見ても確認ができます。 Concatを使ったほうがCG Allocが小さくなっています。

f:id:albatrus:20210321100611p:plain
string.Formatとstrng.ConcatのGCAlloc

ZString

ここからが本題なのですが、実はさらにパフォーマンスを上げる方法があります。 これが「ZString」です。

くわしい内容はこちらに記載されています。 tech.cygames.co.jp

var text = ZString.Format("{0}/{1}", value, maxValue);

同じようにプロファイラーを見てみるとCGAllocが減っていることがわかります。

f:id:albatrus:20210321101336p:plain
ZString.Format

Concat

ZStringにもConcatメソッドがあります。

var text = ZString.Concat("hoge" ,"hoge", "hoge");

こちらもパフォーマンスが良くなっています。

f:id:albatrus:20210321103613p:plain
Cocatの比較

最後に

ZStringに関しては、結合する文字が多ければ多いほどパフォーマンスに違いが生まれたので、特に理由がないのであれば使わない手はないかと思いました。 Stringに関してはゲーム制作ですと利用頻度も多いと思います。

【Unity】VideoPlayerで 「video unsupported by hardware」のエラーが出た

UnityのVideoPlayerを何気なく使ったところ、エディタ上では動画が再生されるが、実機にビルドすると再生されない問題が出たときの対処方法です。

Codecを変更する

おそらく動画ファイルの設定が問題なのですが、何も考えずに動画をProjectへ入れると、CodecがAutoになっているはずです。 私の動画はmp4というファイル形式でしたので、ここをH264へ変更すると実機でも再生することができました。(iOSとAndorid共に変更)

f:id:albatrus:20210320123525p:plain:w400
CodecをH264に変更する

AAC videos encoded using H.264 or H.265

公式にも記載のあるように、AAC(mp4)はCodecを変更しないと再生できないみたいです。もし、実機で動画が再生できない場合にはまず動画ファイルのCodecを疑ってみるのが良いかもしれません。

docs.unity3d.com

Enumerable.Rangeを調べてみた

とあるListを作成したいときに通常なら次のようなコードを書くとします。

var lists = new List<int>();
for (int i = 0; i < 5; i++)
{
    lists.Add(i);
}

実はこのような記述はLinqを使うと次のように置き換えることができます。

var lists = Enumerable.Range(0, 5).Select(x => x).ToList();

Enumerable

EnumerableはLinqで定義をされている IEnumerableを継承したクラスで利用できる拡張メソッド郡です。 Enumerable クラス (System.Linq) | Microsoft Docs

IEnumerator とは

ここで登場するIEnumeratorとは何でしょうか? このインタフェースはListやDictionayで使われています。

何を定義しているか

IEnumerator で定義されているのは次のメソッドです。

IEnumerator GetEnumerator();

公式によるとこのメソッドは次のように説明されています。

指定した型のコレクションに対する単純な反復処理をサポートする列挙子を公開します。

IEnumerator

GetEnumeratorはIEnumeratorという型が戻り値として定義されています。 次のメソッドが定義されています。

public interface IEnumerator
{
    object Current { get; }

    bool MoveNext();

    void Reset();
}

【Unity】LayoutGroupについて

LayoutGroupについて

LayoutGroupのコンポーネント配下のGameObjectを自動的に配置します。 位置の調整や要素の大きさなど、LayoutGroupの設定項目に合わせてレイアウトします。

種類

LayoutGroupは次の3つ種類が用意されています。

  • Horizontal Layout Group (水平)
  • Vertical Layout Group (垂直)
  • Grid Layout Group (グリッド)

設定項目

Padding

要素の位置関係をPaddingで設定します。

  • Left
  • Right
  • Top
  • Bottom
  • Spacing

Child Alignment

要素の配置位置を設定します。

f:id:albatrus:20210307154114p:plain:w300
Upper Leftの場合
f:id:albatrus:20210307154038p:plain:w300
Middle Centerの場合

Reverse Arrangement

要素の並び順を逆転させます。

Control Child Size

自身のサイズとPaddingの設定に合わせて、要素のLayout Element大きさを変更します。 Layout Element - Unity マニュアル

Use Child Scale

要素のScaleを加味して位置や大きさを調整します。

f:id:albatrus:20210307153115p:plain:w300
Use Child Scaleを使わない場合、Scaleを無視して要素が配置される

f:id:albatrus:20210307153202p:plain:w300
Use Child Scaleを使わう場合、Scaleを加味して要素が配置される

Child Force Expand

要素をFlexible Sizeに合わせて変更を行います。 Flexible SizeとはLayoutGroupコンポーネントがアタッチされているGameObjectのwidthとHeightになります。

f:id:albatrus:20210307152728p:plain:w300
Child Force Expandを使わない場合、Preferred Sizeに要素が配置される

f:id:albatrus:20210307152835p:plain:w300
Child Force Expandを使った場合、Flexible Sizeに合わせて要素が配置されます

できること

実装例

実際にLayoutGroupを使った実装例を紹介します。

可変Textを加味してUIを並べる

次の2つのコンポーネントをアタッチします。 PaddingやChild Alignmentなどは好きなように設定をします。

f:id:albatrus:20210307155227p:plain:w350
Inspector

要素はImage、Text、Imageの順番に配置しています。 (ImageにはLayout ElementコンポーネントをアタッチしてMin Sizeを設定しています)

f:id:albatrus:20210307155320p:plain:w350
要素の配置

Textの文字数を変更しても要素の位置関係はそのままです。(画像ではわかりにくいですが...)

f:id:albatrus:20210307155617p:plain:w350
Textの文字を変えても、Imageの位置関係はそのままです。

パフォーマンス

公式にも書かれていますが、このLayoutGroupはパフォーマンスが良くありません。

自身のレイアウトをダーティとしてマークする UI 要素は、少なくとも GetComponents を 1 つ以上呼び出します。この呼び出しは、まずレイアウト要素から、親の有効なレイアウトグループを検索します。該当するレイアウトグループが見つかった場合は、検索を停止するか、ヒエラルキーのルート階層に到達するかのいずれか早い方で、ヒエラルキー内の Transform を上っていきます。したがって、各レイアウトグループは、それぞれの子レイアウト要素のダーティ化処理に GetComponents の呼び出しを 1 回追加することになり、ネスト化されたレイアウトグループのパフォーマンスを極度に悪化させます。

forpro.unity3d.jp

LayoutRebuilder.MarkLayoutForRebuild

LayoutGroupのコードを見てみると、このメソッドが重い処理のようです。 このメソッドは、OnRectTransformDimensionsChange、OnEnable時にも呼び出されているで、何かしらの更新があったときに、その都度UIの再構築処理が走るのが原因っぽいです。

最後に

処理は重いのは確かなのですが、複雑なUIが実装できるのがLayoutGroupの良いところです。 ですので、使わないと判断するよりも要所要所パフォーマンスを見て使っていくのが個人的には良いと思っています。 例えば3DゴリゴリのSceneでは利用を控えるとか、ほぼUIだけで構成されているのであれば必要に応じて使うなどです。 スマートフォンの性能も年々上がっているので、多少LayoutGroup使ってもそれほど全体のパフォーマンスには影響が出ないというのがこれまでの経験上あります。