unityでLinqの活用

LinkedListのremoveをLinqを利用して活用する



<サンプルコード>

using UnityEngine;
using System.Collections;

using System.Collections.Generic;
using System.Linq;

public class testLinkedList : MonoBehaviour {

  LinkedList<MyClass> list = new LinkedList<MyClass>(new MyClass[]{
       new MyClass(){coffee=true,name="ueda"},
       new MyClass(){coffee=true,name="sibata"},
       new MyClass(){coffee=false,name="kokoro"},
   });

  void Start () {
       list.AddFirst(new MyClass(){coffee=true,name="TARO"});
       list.AddFirst(new MyClass(){coffee=false,name="ikeda"});
       list.AddFirst(new MyClass(){coffee=true,name="tomoji"});

      var removeList = list.Where(node => node.coffee == false).ToList();
           
       foreach (var item in removeList) {
           list.Remove(item);
       }

      foreach (var item in list) {
           print(item.name);
       }
   }

  class MyClass
   {
       public bool coffee;
       public string name;
   }
}


出力結果

tomoji
TARO
sibata
ueda


ふたつの配列を連結する



using UnityEngine;
using System.Collections;
using System.Linq;

public class TestArray : MonoBehaviour {

  Vector3[] a,b,c;

  void Start () {
       a= new Vector3[] {new Vector3(0,0,0),new Vector3(1,2,3),new Vector3(4,5,6),};
       b= new Vector3[] {new Vector3(7,8,9),new Vector3(11,12,13),new Vector3(14,15,16),};
       c = a.Concat(b).ToArray();
   }

}


コレクションの中から最小の値を得る


  void Start () {
       float[] x = {58,23,43,42,15,46,27};
       print( x.Select(s => s).Min());
   }

複数のプレーヤーキャラクタから距離が一番近いものをTransform型のオブジェクトとして取り出す


Transform target = GameObject.FindGameObjectsWithTag("Player").OrderBy(x => (x.transform.position - transform.position).magnitude).First().transform;

敵側の初期化の際に利用する。やや重い処理なのでAwakeやStartで初期化用途に使う事

unityでLinqの活用


<資料>

<LINQ要約>
MonoDevelopのコードエディタはLINQに対するインテリセンスがほとんど機能しない
基本的にコード製作者が自分の各コードをしっかり理解していないと正常に動きにくいので注意

LINQ関連関数を利用したコードを書く際の順番は Select → Where → etc. が良い?
Selectで作成した値が後ろのLINQ関連関数のラムダ式で再利用できる為

LINQ内の値の型指定は(C#では考えられないほど)非常に曖昧でOK。コンパイラが勝手に推論してくれる

Select 最終的に得られる値。必要であれば匿名クラスやクラスをここで作成できる
Where 抽出する条件。if文の中身。trueであれば抽出される
OrderBy 指定した関数内の値の順番に並べる
   
ToArray 配列に収める
ToList Listに収める
ToLookup Lookupに収める
ToDictionary Dictionaryに収める


var 女性客の年齢分布 =
  from c in 顧客一覧
   where c.性別 == "女"
   group c.年齢 by c.年齢 into g
   orederby g.key
   select new {年齢 = g.key, 数 = g.count()};

  • SQL的なクエリを書ける
  • selectが末尾にくるくらいで、かなりSQLそのまんま
  • C#の型をそのまま使える(補完も効く)

拡張メソッドで書くと

var 女性客の年齢分布 = 顧客一覧
  .Where(c => c.性別 == "女")
   .GroupBy(c => c.年齢, c=> c.年齢)
   .OrderBy(g => g.Key)
   .Select(g => new { 年齢 = g.Key , 数 = g.Count() });

利用するにはusing System.Linq;が必要


拡張メソッドって何?


静的メソッドを後置き記法で書く機能
第一引数に this 修飾子をつける

考察:第一引数に「this IEnumerable<T> source」とあるのがミソでIEnumerableを継承したクラスであれば利用できるのはこの為らしい
foreachで中身を取り出してyieldで再びコレクションに加えている。中身自体は単純な構造だがこれでコレクションを加工できる
<T>はジェネリックとして、<U>がよく分からない。これは後に考察することにする

public static class Enumerable
{
  public static IEnumerable<U> Select<T,U>(this IEnumerable<T> source,Func<T,U> selector)
   {
       foreach(var x in source)
       {
           yield return selector(x);
       }
   }
}


<サンプルコード>

Tag名毎にコレクションを作成

  void Start ()
   {
       var lookUpByTag = GameObject.FindObjectsOfType (typeof(GameObject))
           .Cast<GameObject> ()
               .Where (go => !string.IsNullOrEmpty (go.tag))
               .ToLookup (go => go.tag);
       
       foreach (var item in lookUpByTag["Enemy"]) {
           if(item != null)
               print (item.name + " / " + item.tag);
       } 
   }

Lookup型は Dictionary に似ているが Dictionaryがキーを単一の値に割り当てるのに対し
Lookup はキーに対応するコレクションが割り当てられる

ToDictionaryを使ってコレクションを作成


オブジェクトネーム(string)をキーとしてTransformを値に辞書コレクションを作成する例

  void Start ()
   {
       var tr = GetComponentsInChildren<Transform> ()
           .Select (user => new { name = user.name , trans = user.transform })
           .ToDictionary (s => s.name, s => s.trans);
       
       foreach (var item in tr) {
           print (item.Key + " / " + item.Value);
       }
       
       tr ["head"].transform.position = new Vector3 (1, 1, 1);
   }

LINQ内でクラスをnewしてコレクションを作成

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
  
public class Level1 : MonoBehaviour
{
  
   public GameObject enemyPref, playerPref;
   
   void Start ()
   {
       for (int i = 0; i < 20; i++) {
           GameObject go = Instantiate (enemyPref
               , new Vector3 (Random.Range (-5f, 5f), 5, Random.Range (-5f, 5f))
               , Quaternion.identity) as GameObject;
           go.name = go.name + i.ToString ();
       }
   }
   
   void Update ()
   {
       if (Input.GetButtonDown ("Jump")) {
           Method ();
       }
   }
   
   void Method ()
   {
       MyClass[] lookup = GameObject.FindGameObjectsWithTag ("Enemy")
           .Select (t => new MyClass ()
               { tr = t.transform , distance = (t.transform.position - playerPref.transform.position).magnitude })
           .Where (t => t.distance < 3)
           .OrderBy (t => t.distance)
           .ToArray ();
       
       foreach (MyClass item in lookup) {
           if (item != null) {
               item.tr.renderer.material.color = Color.blue;
               print (item.tr.name + " / " + item.distance);
           }
       }
   }
}

public class MyClass
{
  public Transform tr;
   public float distance;
}


匿名クラスをLINQ内で生成してコレクションを作成


匿名クラスをLINQ内のSelectで作成してforeachで取り出している点に注目
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class Level2 : MonoBehaviour {

  public GameObject enemyPref, playerPref;
   
   void Start ()
   {
       for (int i = 0; i < 20; i++) {
           GameObject go = Instantiate (enemyPref
               , new Vector3 (Random.Range (-5f, 5f), 5, Random.Range (-5f, 5f))
               , Quaternion.identity) as GameObject;
           go.name = go.name + i.ToString ();
       }
   }
   
   void Update ()
   {
       if (Input.GetButtonDown ("Jump")) {
           Method ();
       }
   }
   
   void Method ()
   {
       var lookup = GameObject.FindGameObjectsWithTag("Enemy")
           .Select(t => new { tr = t.transform , distance = (t.transform.position - playerPref.transform.position).magnitude})
           .Where(t => t.distance < 3)
           .OrderBy(t => t.distance)
           .ToArray();
       
       foreach (var item in lookup) {
           if (item != null) {
               item.tr.renderer.material.color = Color.blue;
               print (item.tr.name + " / " + item.distance);
           }
       }
   }
}

メニュー




  • 最終更新:2014-06-13 10:32:18

このWIKIを編集するにはパスワード入力が必要です

認証パスワード