Unityに関連する記事です

 前回に引き続き、従来の記法を活用しながら、LINQ の学習を行います。
処理を比較し、置き換えていきながら理解を深めましょう。

 前回はこちらです。

  => 既存の処理をリファクタリングして学ぶ LINQ 活用例
  => 既存の処理をリファクタリングして学ぶ LINQ 活用例

 LINQ には非常に多数のメソッドが用意されています。

 その中から紹介します。



.灰譽ション内の、少なくとも1つの要素が指定した条件を満たすか判定する ーAny メソッドー


 このメソッドはシーケンスが少なくとも1つの要素を持つかどうかを判定します。

 条件メソッドを指定することで、特定の条件を満たす要素が存在するかどうかを判定することもできます。

 これは例えば敵のリストが空かどうか、またはリスト内に特定の敵が存在するかどうかを確認する際に使えます。

 まずは従来の処理です。
要素が存在するかどうかを確認する方法と、特定の条件を満たしている要素が存在するかを確認する方法をそれぞれ提示します。

<要素が存在するか>
List<int> numbers = new List<int> {1, 2, 3, 4, 5};

bool hasAny = numbers.Count > 0;

<特定の条件を満たしている要素が、少なくても1つ存在するか>
List<int> numbers = new List<int> {1, 2, 3, 4, 5};

bool anyNumberGreaterThanThree = false;

foreach (var number in numbers)
{
    if (number > 3)
    {
        anyNumberGreaterThanThree = true;
        break;
    }
}

 コレクション内に 3 よりも大きい要素が1つでも含まれているかどうかを判定しています。



 それではこの処理を LINQ を使い、Any メソッドに置き換えていきます。

List<int> numbers = new List<int> {1, 2, 3, 4, 5};

bool hasAny = numbers.Any();  // 要素が存在しているか
bool anyNumberGreaterThanThree = numbers.Any(number => number > 3);

 引数を指定することで、コレクション内のうち、1つだけでも条件に合致しているなら、というケースで利用できます。


▲灰譽ション内のすべての要素が指定した条件を満たすか判定する ーAll メソッドー


 このメソッドはシーケンスのすべての要素が指定した条件を満たすかどうかを判定します。

 まずは従来の処理です。

List<int> numbers = new List<int> {1, 2, 3, 4, 5};

bool allNumbersGreaterThanZero = true;

foreach (var number in numbers)
{
    if (number <= 0)
    {
        allNumbersGreaterThanZero = false;
        break;
    }
}

 コレクション内の数字が、すべて 0 よりも多いかを判定しています。



 それではこの処理を LINQ を使い、All メソッドに置き換えていきます。

List<int> numbers = new List<int> {1, 2, 3, 4, 5};

bool allNumbersGreaterThanZero = actions.All(number => number <= 0);

 これらのメソッドも、LINQの他のメソッドと組み合わせてメソッドチェーンで使用することができます。


コレクションの要素が持つ子コレクションのすべての要素をフラットにして取り出す ーSelectMany メソッドー


 SelectMany メソッドは、シーケンスの要素が持つ子コレクションのすべての要素をフラット(平坦化)にして取り出します。



 ゲーム内でキャラクターのリストを管理し、それらのキャラクターが持っているアイテムのリストを扱うシチュエーションを考えてみましょう。

 サンプルコードのため、以下のようなクラス構造を想定します。

public class Item
{
    public string Name { get; set; }
    public int Value { get; set; }
}

public class Character
{
    public string Name { get; set; }
    public List<Item> Items { get; set; }
}



 LINQを使用しない場合、全キャラクターが持つすべてのアイテムをリスト化するには、以下のように書くことができます。


List<Item> allItems = new List<Item>();
foreach (var character in characters)
{
    foreach (var item in character.Items)
    {
        allItems.Add(item);
    }
}

 LINQを使用する場合、同じことを以下のように書くことができます。


List<Item> allItems = characters.SelectMany(character => character.Items).ToList();


参考サイト
Qiita @youmts 様
SelectMany の使い方



せ慊蠅靴織ーに基づいてコレクションの要素をグループ化する ーGroupBy メソッドー


 GroupBy メソッドは、指定したキーに基づいてシーケンスの要素をグループ化します。

 LINQを使用しない場合、キャラクターを持っているアイテムの名前でグループ化するためには、かなり複雑なコードを書くことになります。


Dictionary<string, List<Item>> groupedItems = new Dictionary<string, List<Item>>();

foreach (var character in characters)
{
    foreach (var item in character.Items)
    {
        if (!groupedItems.ContainsKey(item.Name))
        {
            groupedItems[item.Name] = new List<Item>();
        }
        groupedItems[item.Name].Add(item);
    }
}

 このコードはまず、キーがアイテムの名前、値が該当するアイテムのリストであるDictionaryを作成します。
次に、各キャラクターのアイテムリストをループし、各アイテムについて、その名前が既にDictionaryのキーとして存在するかどうかをチェックします。
存在しない場合、新しいリストを作成し、その名前をキーとしてDictionaryに追加します。最後に、そのアイテムを対応するリストに追加します。

 LINQを使用する場合、以下のように書くことができます。

var groupedItems = characters.SelectMany(character => character.Items)
                            .GroupBy(item => item.Name);

 LINQを使用すると、コードは一見すると簡潔に見えますが、裏側で何が行われているのかを理解するのは少し難しくなります。
それに対して従来の方法では、その逆で、コードは少し冗長になりますが、それぞれのステップで何が行われているのかを直感的に理解することが容易になります。

 そのため、やはり、両方の処理を書き比べていくことで、理解を深めていく学習につながります。


インターフェースとジェネリックの活用事例


 インターフェースとジェネリックを使ったLINQの使用法について説明します。

 まず、以下のようなインターフェースとそれを実装した2つのクラスを考えてみましょう。

public interface IAnimal
{
    string Name { get; set; }
    string Speak();
}

public class Dog : IAnimal
{
    public string Name { get; set; }

    public string Speak()
    {
        return "Woof!";
    }
}

public class Cat : IAnimal
{
    public string Name { get; set; }

    public string Speak()
    {
        return "Meow!";
    }
}



 このとき、以下のようにIAnimalのリストを作り、その中にDogとCatのオブジェクトを混在させて入れることができます。

List<IAnimal> animals = new List<IAnimal>
{
    new Dog { Name = "Rover" },
    new Cat { Name = "Whiskers" },
    new Dog { Name = "Spot" }
};

 これは、IAnimal 型の List であるため、IAnimal インターフェースを実装しているクラスであれば List に保持できるためです。
 
 そのため、クラスを特定せず、汎用性のある管理が可能になります。



 そして、OfTypeメソッドを使うと、このリストから特定の型の要素だけを抽出することができます。

 以下は、OfTypeを使ってDogのみを抽出する例です。


List<Dog> dogs = animals.OfType<Dog>().ToList();

 この処理により、List 内に混在している Animal のうち、Dog だけを抽出した List を作成することが出来ます。



 また、Selectを使って特定の属性を抽出することもできます。以下の例では、全ての動物の名前を抽出しています。

List<string> names = animals.Select(animal => animal.Name).ToList();

 このように、LINQはインターフェースとジェネリックを使って、異なる型の要素を持つリストから特定の型の要素だけを抽出したり、
特定の属性を抽出したりする処理を容易に書くことができます。これにより、コードの再利用性と柔軟性が高まります。


コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます