最初に
どうも、ろっさむです。
今回はユーザー定義したEnumの名前に対応したstring型のメンバ変数をリフレクションで適宜返すようなメソッドを作った話をします。
使いどころとしては、例えば顔のblendshape値を弄る時に表情毎の顔パーツそれぞれblendshape名を取得して諸々処理を掛ける時に便利かなと。
前提コード
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
[System.Serializable]
public class FacialData
{
// 以下メンバ変数にはblendshape名が含まれる
public string BrwMorph;
public string EyeMorph;
public string NoseMorph;
public string MouthMorph;
public FacialData(
string brwMorph,
string eyeMorph,
string noseMorph,
string mouthMorph)
{
BrwMorph = brwMorph;
EyeMorph = eyeMorph;
NoseMorph = noseMorph;
MouthMorph = mouthMorph;
}
}
public class FacialExpressionManager : MonoBehaviour
{
private enum MorphType
{
Brw,
Eye,
Nose,
Mouth,
}
private void UpdateMorphWeight(FacialData data, MorphType morphType)
{
// ここでblendshape名を取得したい…
// var morphPath = ?
// 後はなんやかんやパスを使った処理
...
}
private void Update()
{
// 表情名から対応したFacialDataを取得する
FacialData newFacialData = FacialData.GetFacialData(facialExpressionName);
foreach (MorphType type in Enum.GetValues(typeof(MorphType)))
{
UpdateMorph(newFacialData, type);
}
}
}
実装
blendshape名を取得するためにswitchやらifを使うと後からEnumなどにパラメータが増えたときに付け足すコードが増える上に、どんどん分岐が長くなってしまうのでそれを避けるためにリフレクションという方法を考えてみました。
public class FacialExpressionManager : MonoBehaviour
{
private enum MorphType
{
Brw,
Eye,
Nose,
Mouth,
}
private void UpdateMorphWeight(FacialData data, MorphType morphType)
{
// ここでblendshape名を取得したい
// Enumの値の文字列を含むFacialDataのメンバ変数が欲しい
var morphPath = data.GetMorphPath(morphType.ToString());
// 後はなんやかんやパスを使った処理
...
}
}
GetMorphPath
は以下のような実装にしました。
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
[System.Serializable]
public class FacialData
{
// 以下メンバ変数にはblendshape名が含まれる
public string BrwMorph;
public string EyeMorph;
public string NoseMorph;
public string MouthMorph;
public FacialData(
string brwMorph,
string eyeMorph,
string noseMorph,
string mouthMorph)
{
BrwMorph = brwMorph;
EyeMorph = eyeMorph;
NoseMorph = noseMorph;
MouthMorph = mouthMorph;
}
public string GetMorphPath(string name) => GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).Where(property => property.Name.Contains(name)).Select(property => property.GetValue(this) as string).SingleOrDefault();
}
順を追って GetMorphPath
の処理を見ていきましょう。
- まず
GetType()
で自身の型宣言を取得します。 -
GetFields
でメンバを取得しますが、その際にpublic
でありインスタンスメンバーを検索に含めるようにします。 - LINQの
Where
を使って取得したメンバ群から、メンバ名の中で渡したEnumの名前を含むものだけになるようフィルタリングします。 - LINQの
Select
でフィルタリング後のメンバ変数の値をstring型にキャストします。 - 最後の
SingleOrDefault()
で最終的に条件に合った単一のメンバ変数を返すか、それがなかった場合は型のデフォルト値を返すようにします。ちなみに複数見つかった場合は例外が発生します。
これでEnum
の値と増えるメンバ変数に合わせた処理ができました。
ただ、このテクニックは使う場面がかなり限られそうです…。