FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unityリファレンス
このページの記事一覧

【Unity】【C#】テキストファイルの読み書きをする(リソース/ストレージ)  


 Unity では内部リソースからテキストファイルを読み込む方法が非常に簡単に用意されているが、それとは別に外部のリソース(ストレージ等)でテキストファイルを読み書きする方法も書いておこう。

 またついでに Android でプラグインを利用して読み書きする方法も紹介しておく。ちなみに Unity から C# で直接 SDカードに書き込もうとすると、アクセス拒否で失敗するが、プラグインを利用する場合は、SDカードにも書き込むことが可能だ。エクスプローラみたいなファイル選択も使えるので、ユーザーが自由にファイルの読み書きを行えるようなシステムを作るには役に立つだろう。

 ここでは C# スクリプト例はインスタンスメソッドとして書いておくが、static なライブラリとして利用したいなら、例外処理を throw などすれば良いと思う。とりあえず簡単な実装を書いておこう。自由に改造でもして使って欲しい。


(※) Unity 5.6.3p1 - 2018.1.8f1 / Windows10(x64) で確認



■リソースからテキストアセットを読み込む(Unity ビルトイン機能)

 ここでは Unity の機能を使って、アプリに内包したテキストファイルを読み込んでみよう。

 これは Unity のエディタ上であらかじめリソースとして持っておき、ランタイム時にロードする方法だ。Unity では「Resources」というフォルダを作っておくと、起動後にリソースとしてテクスチャやデータなどを読み込むことができる。ここにテキストファイルを置いておけば、「TextAsset」として簡単に扱える。それを利用して読み込んでみよう。

●リソースからテキストアセットを読み込む(メソッド定義)
using UnityEngine;
using UnityEngine.UI;

public string textAssetName = "Texts/テスト"; //拡張子はいらない
public Text displayText; //表示するUI-Text

//リソースからテキストアセットを読み込む
void LoadTextAsset(string name)
{
if (string.IsNullOrEmpty(name) || displayText == null)
return;

TextAsset textAsset = (TextAsset)Resources.Load(name, typeof(TextAsset));
if (textAsset != null)
displayText.text = textAsset.text;
else
Debug.Log("Not found TextAsset : " + name);
}

●使用例(メインでのコードなど)
using UnityEngine;

//ロードボタンのコールバックハンドラ
public void OnLoadTextAsset()
{
LoadTextAsset(textAssetName);
}

 読み込み先テキストには UI-Text(displayText)を用いているが、もちろん string 型の引数のメソッドを作って直接読み込んでも良い。これはあくまでも例なので、その辺りは用途に応じて書き換えて欲しい。

●「Resources」フォルダを作り、その配下にテキストファイル(UTF-8)をインポートしておく。

 テストするには UI-Button などを適当に配置し、「Button」の「OnClick()」にスクリプトの「OnLoadTextAsset()」を登録すれば良い。





●「テスト用スクリプトのアタッチ例


●ファイルの内容は任意(UTF-8)

※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。



■ローカルストレージなどでテキストファイルの読み書きをする(C# スクリプト)

 次にパス(ファイル名)を指定してテキストファイル(UTF-8)を読み書きすることをやってみよう。

 例ではフォルダ(ディレクトリ)に「Application.persistentDataPath」を使っているが、これはプラットフォームによって変わるデフォルトの永続的な保存先となる。また、会社名(company name)やアプリ名(product name)でもフォルダ分けされる。詳しくは公式マニュアルで確認して欲しい。

(参考)Application.persistentDataPath

●ローカルストレージなどでテキストファイルの読み書きをする(メソッド定義)
using System;
using System.IO;
using System.Text;

//テキストをファイルから読み込む(行読み)
//※Android で SD カードから読み込みをするには、「AndroidManifest.xml」にパーミッション("READ_EXTERNAL_STORAGE" または "WRITE_EXTERNAL_STORAGE")が必要。
string LoadText(string path)
{
StringBuilder sb = new StringBuilder(1024); //※capacity は任意

try
{
using (StreamReader reader = new StreamReader(path))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
sb.Append(line).Append("\n");
}
}
}
catch (Exception e)
{
Debug.Log(e.Message);
return null;
}

return sb.ToString();
}

//テキストをファイルに保存
//※Android で External Storage に書き込みをするには、「AndroidManifest.xml」にパーミッション("WRITE_EXTERNAL_STORAGE")が必要。
//※セキュリティ上、Unity から直接 SD カードには保存できない。
bool SaveText(string text, string path)
{
try
{
using (StreamWriter writer = new StreamWriter(path))
{
writer.Write(text);
writer.Flush();
writer.Close();
}
}
catch (Exception e)
{
Debug.Log(e.Message); //Access to the path "filename" is denied. → パーミッションが無い, 書き込みアクセス不可(SDカードなど)
return false;
}
return true;
}

●使用例(メインでのコードなど)
using UnityEngine;
using UnityEngine.UI;

public string filename = "test.txt";
public Text displayText; //表示するUI-Text

//ロードボタンのコールバックハンドラ
public void OnLoadClick()
{
if (string.IsNullOrEmpty(filename) || displayText == null)
return;

string path = Path.Combine(Application.persistentDataPath, filename); //プラットフォームによってパスは異なる

if (!File.Exists(path))
{
Debug.Log("Not found : " + path);
return;
}

string text = LoadText(path);
if (!string.IsNullOrEmpty(text))
displayText.text = text;
}

//セーブボタンのコールバックハンドラ
public void OnSaveClick()
{
if (string.IsNullOrEmpty(filename) || displayText == null)
return;

string path = Path.Combine(Application.persistentDataPath, filename); //プラットフォームによってパスは異なる

if (SaveText(displayText.text, path))
Debug.Log("Save to : " + path);
}

 テストするには UI-Button などを適当に配置し、「Button」の「OnClick()」にスクリプトの「OnLoadClick()」「OnSaveClick()」をそれぞれ登録すれば良い。
●UI-Button 配置とテスト用スクリプトのアタッチ例

●UI-Button のコールバック登録例

 読み書きするテキストには UI-Text(displayText)を用いているが、もちろん string 型の引数のメソッドを作って直接読み書きしても良い。これはあくまでも例なので、その辺りは用途に応じて書き換えて欲しい。

 なお、Android でSDカードに読み書きする場合は以下のパーミッションが必要になる。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />'

 パーミッションについての説明が必要なら以前の記事を参照して欲しい。


※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。



■Android でファイル選択+テキストファイルの読み書きをする(プラグイン利用)

 ついでにプラグインに入っている「StorageLoadTextController」と「StorageSaveTextController」(~/FantomLib/Prefabs/System/ 以下。検索で探すと簡単)というテキストファイル読み書きの機能を使ってみよう。これら「~Controller」は Android 標準のファイル選択機能を使い、テキストファイルを読み込み・書き込みをしてくれるものだ。これら機能はプラグイン内部に実装されているので、自分でコードを書く部分は、機能呼び出しと結果受け取りだけで良い。

 ではまず、その機能呼び出しと結果受け取りのコードだけ書いてしまおう。名前は任意で良い。これらは各「~Controller」や UI-Button のコールバックハンドラとなる。

●コールバックハンドラ例(メインでのコードなど)
using UnityEngine;
using UnityEngine.UI;
using FantomLib;

public Text displayText; //表示するUI-Text

//読み込んだテキストを UI-Text に表示する
public void OnStorageLoad(string text)
{
if (string.IsNullOrEmpty(text) || displayText == null)
return;

displayText.text = text;
}

public StorageSaveTextController storageSaveTextController; //※インスペクタで登録

//UI-Text のテキストをファイルに保存する
public void OnStorageSave()
{
if (displayText == null || string.IsNullOrEmpty(displayText.text)) //※空は保存しない場合
return;

if (storageSaveTextController != null)
storageSaveTextController.Show(displayText.text);
}

//エラーステータス用コールバックハンドラ
public void OnError(string message)
{
Debug.Log(message);
}

 あとはヒエラルキーに「StorageLoadTextController」と「StorageSaveTextController」(~/FantomLib/Prefabs/System/ 以下。検索で探すと簡単)を置き、UI-Button など(※名前は任意)を Canvas に配置したら、インスペクタでコールバックを設定するだけだ。

●「StorageLoadTextController」と「StorageSaveTextController」を置く。テスト用スクリプトのアタッチ例。

●スクリプト(TextFileTest)へコールバック登録する
※または、StorageLoadTextController.OnResult に直接 UI.Text.text を登録しても良い。

●UI-Button から機能呼び出しをする


 あとはビルドするだけだが、プラグインを含む場合のビルドは「AndroidManifest.xml」が必要になる。その辺りは以前の記事にまとめてあるので参照して欲しい。もちろん「Build Settings...」にシーンを追加するのを忘れずに。

●ファイルの内容は任意(UTF-8)

※ランタイム時にログを見るにはプラグインライブラリのプレファブ「DebugConsole」をシーンに置き、コード中の Debug.Log() を XDebug.Log() に置き換えて下さい。また、インスペクタで「displayText」に「DebugConsole」以下の「Text」を登録します。


※この記事のUnityアセットはプラグインとして配布されています。


※とりあえず試してみたい方は、最新版をビルドした apk デモをダウンロードできます。動作確認にもどうぞ。

プラグインデモをダウンロード
(Google Drive を利用)


Android 4.2以上
※「提供元不明アプリのインストール」許可が必要です。


(関連記事)
【Unity】AssetStore版 FantomPlugin のセットアップ
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録


スポンサーサイト

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  Unityプラグイン  C# 
tb: 0   cm: --

【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる  


 利用頻度はあまり高くはないかもだが、インスペクタでの UnityEvent のコールバック登録があるか否かを調べたいときがある。実際に利用するには調べたい条件によってコードを変えるしかないかも知れないが、簡単な例を2つばかり挙げておこう。また、UnityEvent にはジェネリックパラメタの数違いのパターンがあるので、それらをカバーするためのオーバーロードも簡単に作っておく。

 ここでは汎用的に使えるように、拡張メソッドとして定義したものを書いておこう。ライブラリとして保存しておけば使い回しもできると思う。拡張メソッドにしたくない場合は、メソッドの引数の this を取り除けば良い。


(※) Unity 5.6.3p1 - 2018.1.8f1 / Windows10(x64) で確認



■インスペクタの UnityEvent にコールバックが1つでも登録されてるか否かを返す

 このメソッドたちは1つでもコールバック名が有るか無いかという判別をするものである。UnityEvent が null のときは常に false になる。ジェネリックパラメタの数違いはオーバーロードとして定義している。

●インスペクタの UnityEvent にコールバックメソッドが存在するか否かを返す(関数定義)
using System;
using UnityEngine;
using UnityEngine.Events;

public static class Extensions //※クラス名は任意
{
//インスペクタの UnityEvent にコールバックが1つでも登録されてるか否かを返す。
//※AddListener() で登録したものは無視されるので注意。
public static bool HasRegistered(this UnityEventBase obj)
{
if (obj != null)
{
int count = obj.GetPersistentEventCount(); //※AddListener() には無効
for (int i = 0; i < count; i++)
{
if (!string.IsNullOrEmpty(obj.GetPersistentMethodName(i))) //※AddListener() には無効
return true;
}
}
return false;
}

//※以下、パラメタ違いオーバーロード
public static bool HasRegistered(this UnityEvent obj)
{
return HasRegistered((UnityEventBase)obj);
}

public static bool HasRegistered<T0>(this UnityEvent<T0> obj)
{
return HasRegistered((UnityEventBase)obj);
}

public static bool HasRegistered<T0, T1>(this UnityEvent<T0, T1> obj)
{
return HasRegistered((UnityEventBase)obj);
}

public static bool HasRegistered<T0, T1, T2>(this UnityEvent<T0, T1, T2> obj)
{
return HasRegistered((UnityEventBase)obj);
}

public static bool HasRegistered<T0, T1, T2, T3>(this UnityEvent<T0, T1, T2, T3> obj)
{
return HasRegistered((UnityEventBase)obj);
}
}

●使用例(メインでのコードなど)
using UnityEngine;

Debug.Log("OnNothing.HasRegistered = " + OnNothing.HasRegistered());
Debug.Log("OnSingle.HasRegistered = " + OnSingle.HasRegistered());
Debug.Log("OnDouble.HasRegistered = " + OnDouble.HasRegistered());
Debug.Log("OnTriple.HasRegistered = " + OnTriple.HasRegistered());
Debug.Log("OnQuadruple.HasRegistered = " + OnQuadruple.HasRegistered());


//コールバックハンドラなど
public void ReceiveNothing()
{
Debug.Log("ReceiveNothing called.");
}

public void ReceiveSingle(string s)
{
Debug.Log("ReceiveSingle called.");
}

public void ReceiveDouble(string s, int i)
{
Debug.Log("ReceiveDouble called.");
}

public void ReceiveTriple(string s, int i, float f)
{
Debug.Log("ReceiveTriple called.");
}

public void ReceiveQuadruple(string s, int i, float f, bool b)
{
Debug.Log("ReceiveQuadruple called.");
}

● False になる例

OnNothing.HasRegistered = False
OnSingle.HasRegistered = False
OnDouble.HasRegistered = False
OnTriple.HasRegistered = False
OnQuadruple.HasRegistered = False

● True になる例

OnNothing.HasRegistered = True
OnSingle.HasRegistered = True
OnDouble.HasRegistered = True
OnTriple.HasRegistered = True
OnQuadruple.HasRegistered = True

 注意して欲しいのは「GetPersistentEventCount()」や「GetPersistentMethodName()」というメソッドを使っているが、これらは「AddListener()」でランタイム時に追加したメソッドには無効ということだ。なので、基本的にはインスペクタでの登録のみと考えて欲しい(タイトルが「インスペクタでの~」となってるのはそのため)。

 また、メソッドの存在は「GetPersistentMethodName()」で空か否かで判断しているだけなので、何らかミスなどで "<Missing ~>"(※ここではわざと無効な Dummy() メソッドを登録している)となっているメソッドも存在していると判断されてしまうので注意しよう。しかし通常、インスペクタでの登録は正常であるという前提なら問題はない。



■インスペクタの UnityEvent にシグニチャが合致しているコールバックが1つでも登録されてるか否かを返す

 次のメソッドたちは前述のコールバック名の存在有無より厳密に、コールバックの引数のシグニチャと同じものだけを検出するものである。実際インスペクタでの登録は違うシグニチャも登録できるわけで(例:コールバック引数が(string s, int i)であっても、引数が(string s)[※インスペクタで文字列を入力] のメソッドを登録することができる)、便利ではあるが前述の方法では正しくコールバックできるメソッドが存在するかは判定できないことにもなる。以下の方法では完全に正しいシグニチャを持つメソッドしか検出しないので、"<Missing ~>"(※ここではわざと無効な Dummy() メソッドを登録している)となっているものも排除できる。

●インスペクタの UnityEvent に引数が正しいコールバックメソッドが存在するか否かを返す(関数定義)
using System;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;

public static class Extensions //※クラス名は任意
{
//インスペクタの UnityEvent にシグニチャが合致しているコールバックが1つでも登録されてるか否かを返す。
public static bool HasValidMethod(this UnityEventBase obj, Type[] argumentTypes)
{
if (obj != null)
{
int count = obj.GetPersistentEventCount(); //※AddListener() には無効
for (int i = 0; i < count; i++)
{
string method = obj.GetPersistentMethodName(i); //※AddListener() には無効
if (!string.IsNullOrEmpty(method))
{
object target = obj.GetPersistentTarget(i); //※AddListener() には無効
MethodInfo info = UnityEventBase.GetValidMethodInfo(target, method, argumentTypes);
if (info != null)
return true;
}
}
}
return false;
}

//※以下、パラメタ違いオーバーロード
public static bool HasValidMethod(this UnityEvent obj)
{
return HasValidMethod(obj, new Type[]{});
}

public static bool HasValidMethod<T0>(this UnityEvent<T0> obj)
{
return HasValidMethod(obj, new Type[]{typeof(T0)});
}

public static bool HasValidMethod<T0, T1>(this UnityEvent<T0, T1> obj)
{
return HasValidMethod(obj, new Type[]{typeof(T0), typeof(T1)});
}

public static bool HasValidMethod<T0, T1, T2>(this UnityEvent<T0, T1, T2> obj)
{
return HasValidMethod(obj, new Type[]{typeof(T0), typeof(T1), typeof(T2)});
}

public static bool HasValidMethod<T0, T1, T2, T3>(this UnityEvent<T0, T1, T2, T3> obj)
{
return HasValidMethod(obj, new Type[]{typeof(T0), typeof(T1), typeof(T2), typeof(T3)});
}
}

●使用例(メインでのコードなど)
using UnityEngine;

Debug.Log("OnNothing.HasValidMethod = " + OnNothing.HasValidMethod());
Debug.Log("OnSingle.HasValidMethod = " + OnSingle.HasValidMethod());
Debug.Log("OnDouble.HasValidMethod = " + OnDouble.HasValidMethod());
Debug.Log("OnTriple.HasValidMethod = " + OnTriple.HasValidMethod());
Debug.Log("OnQuadruple.HasValidMethod = " + OnQuadruple.HasValidMethod());

//・・・コールバックハンドラなどは前述と同じで良いので省略・・・

● False になる例

OnNothing.HasValidMethod = False
OnSingle.HasValidMethod = False
OnDouble.HasValidMethod = False
OnTriple.HasValidMethod = False
OnQuadruple.HasValidMethod = False

● True になる例

OnNothing.HasValidMethod = True
OnSingle.HasValidMethod = True
OnDouble.HasValidMethod = True
OnTriple.HasValidMethod = True
OnQuadruple.HasValidMethod = True

 使い分けは大まかに「何でも良いからメソッドが登録されてる」ときと「正確に値を返せるメソッドが登録されてる」となるだろう。どちらを使うかはケースバイケースだと思う。


(関連記事)
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】ピンチ操作を取得してコールバックする
【Unity】【C#】VideoPlayer で動画の終了判定をする


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityライブラリ  Unityリファレンス  C# 
tb: 0   cm: --

【Unity】AnimationCurve をコードで初期化する  


 ただのメモ。簡単にイージングなどの効果の得られる「AnimationCurve」をインスペクタで設定できるようにしたとき、あらかじめ初期値を入れておきたいことってあるよね。カーブは一度エディタで登録しておけば使い回しできるけど、デフォルトに無い曲線などのときはコードで設定おくと便利。ググれば複雑なカーブのライブラリもあるけど、手軽に作ってしまいたいときの簡易リファレンス的な。


(※) Unity 2017.2.0b9 / Windows10(x64) で確認



■直線的なカーブ(Static Function)

using UnityEngine;

public class AnimationCurveTest : MonoBehaviour {

public AnimationCurve curve = AnimationCurve.Linear(0, 0, 1, 1); //timeStart, valueStart, timeEnd, valueEnd

}

●カーブエディタでは

●移動に使ってみると

 AnimationCurve.Linear() は static なメソッドである。引数は開始と終了の時間と値のペアを列挙したものになる。



■イージング的なカーブ(ゆっくりと始まり、ゆっくりと終わる)(Static Function)

using UnityEngine;

public class AnimationCurveTest : MonoBehaviour {

public AnimationCurve curve = AnimationCurve.EaseInOut(0, 0, 1, 1); //timeStart, valueStart, timeEnd, valueEnd

}

●カーブエディタでは

●移動に使ってみると

 AnimationCurve.EaseInOut() も static なメソッドである。引数は開始と終了の時間と値のペアを列挙したものになる。ゆっくりと始まり、ゆっくりと終わる移動などが簡単に作れる。



■トップスピード→だんだんゆっくりになっていくイージング[※プリセットと同じ](カスタム:Keyframe を2つ使う)

using UnityEngine;

public class AnimationCurveTest : MonoBehaviour {

public AnimationCurve curve = new AnimationCurve(
new Keyframe(0f, 0f, 0f, 2f), //time, value, inTangent, outTangent
new Keyframe(1f, 1f, 0f, 0f) //time, value, inTangent, outTangent
);
}

●カーブエディタでは

●移動に使ってみると

 このカーブはエディタのプリセットにも入っているが、上記のようにコードで設定することもできる。独自にカーブのポイントを打っていくには「Keyframe」を「AnimationCurve」のコンストラクタに列挙していく。

 「inTangent, outTangent」とはエディタのグラフから見れば、それぞれのポイントの左側・右側の傾きで、タンジェント(Tangent)というのは数学的に y/x で表せる傾きのことだから、0.5f(=1/2) なら「上に1, 右に2」の傾き、2f(=2/1) なら「上に2, 右に1」の傾きと考えれば良いのではないだろうか。時間軸に対する値なので、時間や値の逆数(分子と分母を入れ替える)を設定すれば簡単に反転したカーブも作れる。



■ジャンプのようなイージング(カスタム:Keyframe を3つ使う)

using UnityEngine;

public class AnimationCurveTest : MonoBehaviour {

public AnimationCurve curve = new AnimationCurve(
new Keyframe(0f, 0f, 0f, 4f), //time, value, inTangent, outTangent
new Keyframe(0.5f, 1f, 0f, 0f), //time, value, inTangent, outTangent
new Keyframe(1f, 0f, -4f, 0f) //time, value, inTangent, outTangent
);
}

●カーブエディタでは

●移動に使ってみると(Y軸方向)

 「トップスピード→だんだんゆっくり→頂点まで来たら反転→だんだん速く」といったジャンプのような動きになる。独自にカーブのポイントを打っていくには「Keyframe」を「AnimationCurve」のコンストラクタに列挙していく。

 「inTangent, outTangent」とはエディタのグラフから見れば、それぞれのポイントの左側・右側の傾きで、タンジェント(Tangent)というのは数学的に y/x で表せる傾きのことだから、0.5f(=1/2) なら「上に1, 右に2」の傾き、2f(=2/1) なら「上に2, 右に1」の傾きと考えれば良いのではないだろうか。時間軸に対する値なので、時間や値の逆数(分子と分母を入れ替える)を設定すれば簡単に反転したカーブも作れる。



■線形補間に利用する

 一応使い方を書いておくと、「AnimationCurve.Evaluate()」を使って、時間軸に対する値を取り出せばカーブのような変化になる。特に線形補間系のメソッド「~.Lerp()」と組み合わせると使い勝手が良い。以下のサンプルはオブジェクトの位置をスイっと動かす、かなりの手抜きのコードだが、応用すればアルファでフェードイン・アウトしたり、スケールを変化してアクションを表現したり、スワイプでスイっと動かすなんてこともできる(もちろんエディタ上ではアニメーションクリップを使う手もある)。

using UnityEngine;

public class AnimationCurveTest : MonoBehaviour {

public AnimationCurve curve = new AnimationCurve(
new Keyframe(0f, 0f, 0f, 2f), //time, value, inTangent, outTangent
new Keyframe(1f, 1f, 0f, 0f) //time, value, inTangent, outTangent
);

public Vector3 destination;
public float duration = 1f;

Vector3 origin;
float passed = 0;

// Use this for initialization
void Start () {
origin = transform.position;
}

// Update is called once per frame
void Update () {
passed += Time.deltaTime;
float t = curve.Evaluate(Mathf.Clamp01(passed / duration));
transform.position = Vector3.Lerp(origin, destination, t);
}
}


 もっと複雑なカーブが欲しい・イージングタイプを知りたい場合は、以下の参考URLを使うのも良いだろう。

(参考)
AnimationCurveのプリセットにペナーイージングを加える
AnimationCurveをInspectorで設定し、スクリプトから使う
Easing Function早見表

※トゥイーンとして使うなら DOTween みたいなものでも良いかも。
DOTween (Asset Store)
DOTween (公式:英語)
DOTweenをふわっとまとめてみた
【Unity】Tween アニメーション(DOTween)の話

■DOTween 参考書籍



●床や柱のイルミネーションにイージングを入れて、いい感じにフェードイン・アウトさせている


(関連記事)
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  C# 
tb: 0   cm: --

【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録  


 ここ最近、「スワイプ」「長押し」イベントの検出を書いたが、そのコールバックのやり方については投げっぱなしだったので、改めてコードパターンとして簡単にまとめておこう。


(※) Unity 5.6.1p3 / Windows10(x64) で確認



■UnityEvent でコールバックする

 UnityEvent は名前の通り Unity 特有のイベントコールバックの方法だ。その一番大きな特徴はインスペクタでコールバックを受信するメソッドを登録できることで、これを使えば毎回コードを書き換えないで済むというメリットがある。1度作ってしまえば、コンポーネントベースっぽい既存のものを組み合わせるだけでその機能が使えるので、再利用が非常にやりやすい。

 引数をとるか否かで書き方が違うので、その辺りだけ覚えれば利用は非常に簡単だ。なお、UnityEvent の引数は4つまでになってるが、引数が多い場合は専用のクラスや構造体を作って、それを1つの引数にしてしまえば良いだけなので、それほど多く使う機会はないと思う。

●呼び出し側(イベント発生側)
using System;
using UnityEngine;
using UnityEngine.Events;

public class UnityEventCallbackSample : MonoBehaviour {

//引数なしの場合
public UnityEvent OnLongClick; //引数なし

//引数を1つとる場合
[Serializable]
public class SwipeHandler : UnityEvent<Vector2> //引数に Vector2 を1つとる
{
}

public SwipeHandler OnSwipe; //引数に Vector2 を1つとる(インスペクタに表示される)

//引数を2つとる場合
[Serializable]
public class ChangeHandler : UnityEvent<int, int> //引数に int を2つとる
{
}

public ChangeHandler OnChange; //引数に int を2つとる(インスペクタに表示される)


// Update is called once per frame
private void Update () {
if (イベントの発生条件など)
{
if (OnLongClick != null)
OnLongClick.Invoke(); //ここでコールバック(引数なし)

if (OnSwipe != null)
OnSwipe.Invoke(Vector2.up); //ここでコールバック(引数を1つとる)

if (OnChange != null)
OnChange.Invoke(1, 2); //ここでコールバック(引数を2つとる)
}
}
}

●コールバック受信側
using UnityEngine;

public class MainSample : MonoBehaviour {

public void OnLongClick() //このメソッドをインスペクタで登録する
{
//ここにコールバックされたときの処理
Debug.Log("OnLongClick called.");
}

public void OnSwipe(Vector2 dir) //このメソッドをインスペクタで登録する
{
//ここにコールバックされたときの処理
Debug.Log("OnSwipe called. dir = " + dir);
}

public void OnChange(int before, int after) //このメソッドをインスペクタで登録する
{
//ここにコールバックされたときの処理
Debug.Log("OnChange called. before = " + before + ", after = " + after);
}
}

 コードはかなり簡略されたものなので、あくまでも雛形として自由に書き換えて使って欲しい。

 UnityEvent の定義の仕方だが、引数なしのときはそのまま

//定義
public UnityEvent CallbackMethod;

//コールバックを実行するときは
CallbackMethod.Invoke();

のように書くが、引数が1つ以上ある場合、UnityEvent を継承したクラスを作る必要がある。そしてそのジェネリックなパラメータの列挙が引数に対応してる感じになる。

//定義
[Serializable]
public class CallbackHandler : UnityEvent<T1, T2, T3, T4> //引数1~4つまで
{
}
public CallbackHandler CallbackMethod; //インスペクタで表示される

//コールバックを実行するときは
CallbackMethod.Invoke(t1, t2, t3, t4);

 また、コールバックを受信するメソッドを登録するとき(「+」ボタンを押し、コールバック受信するオブジェクトを登録>プルダウンからメソッドを選択)、引数なしの場合はリストに名前の順で出てくるが、引数が1つ以上で、かつ引数の型が合致しているものは、リストの上位の方に「Dynamic (引数の型)」のように出てくる。これを選択すれば、動的な引数の値を受信できる。それとは逆に、引数が1つで int や string などの場合(型による)、リストで下の方にある同じメソッド名を選択すると固定値にできる(値を入力できるようになる)。その他の詳しい内容は以下に参考URLを載せておこう。

(参考)
【Unity】UnityEventの用法と用量


上の方にある Dynamic (型) を選ぶと、
動的に引数の値を変えられる

引数が1つで int や string のときは(型による)、
下の方を選ぶと固定値を設定できる


 なお、コードからコールバックするメソッドを登録したい場合は、「UnityEvent.AddListener」を使う。その方法は以下のURLが参考になるだろう。資料は uGUI での例だが、考え方は同じだ。

(参考)
uGUIのイベントコールバック登録をコードから行う方法とUnityActionデリゲート群




■Action でコールバックする

 次に C# の標準機能(System)にある Action を使ってコールバックをやってみよう。実は Unity にも UnityAction というものが使えるが、基本的には機能は同じものなので、その辺りは自分で入れ替えて考えて欲しい。実際にそのままそっくり Action → UnityAction に書き換えても使える。ただし、現時点では Action は引数16個まで、UnityAction は引数4個までのようだ。しかし実際には上記の UnityEvent の使い方と同じように多くの引数を持ちたい場合、クラスや構造体にまとめて1つの引数にしてしまえば、少なくても問題はない。使い方も引数増えたら、ジェネリックなパラメタを増やす感じになるので、表記上も簡単だ。UnityEvent のときと全く同じ動作をする例を Action で書き直してみよう。

●呼び出し側(イベント発生側)
using System;
using UnityEngine;

public class ActionCallbackSample : MonoBehaviour {

//引数なしの場合
public static Action OnLongClick; //引数なし

//引数を1つとる場合
public static Action<Vector2> OnSwipe; //引数に Vector2 を1つとる

//引数を2つとる場合
public static Action<int, int> OnChange; //引数に int を2つとる

// Update is called once per frame
private void Update()
{
if (イベントの発生条件など)
{
if (OnLongClick != null)
OnLongClick(); //ここでコールバック(引数なし)

if (OnSwipe != null)
OnSwipe(Vector2.up); //ここでコールバック(引数を1つとる)

if (OnChange != null)
OnChange(1, 2); //ここでコールバック(引数を2つとる)
}
}
}

●コールバック受信側
using UnityEngine;

public class MainSample : MonoBehaviour {

void OnEnable()
{
//コールバックするメソッドを登録
ActionCallbackSample.OnLongClick += this.OnLongClick;
ActionCallbackSample.OnSwipe += this.OnSwipe;
ActionCallbackSample.OnChange += this.OnChange;
}

void OnDisable()
{
//コールバックするメソッドを解除
ActionCallbackSample.OnLongClick -= this.OnLongClick;
ActionCallbackSample.OnSwipe -= this.OnSwipe;
ActionCallbackSample.OnChange -= this.OnChange;
}

public void OnLongClick()
{
//ここにコールバックされたときの処理
Debug.Log("OnLongClick called.");
}

public void OnSwipe(Vector2 dir)
{
//ここにコールバックされたときの処理
Debug.Log("OnSwipe called. dir = " + dir);
}

public void OnChange(int before, int after)
{
//ここにコールバックされたときの処理
Debug.Log("OnChange called. before = " + before + ", after = " + after);
}
}

 コードはかなり簡略されたものなので、あくまでも雛形として自由に書き換えて使って欲しい。

 定義の仕方を除いて、一番大きな違いは、コールバック受信側(MainSample)の「OnEnable()」「OnDisable()」でコールバックメソッドの登録と解除を行ってる点だ。これはよく使われる定型パターンというだけで、必ずしもここでやらないといけないというわけではない。ちなみにメソッド登録に「+=」を使っているが、これを複数回やると、コールバックも複数回発生する。誤動作を防ぐために、登録したか否かのフラグで管理した方が安全なこともある。アプリで必ず1回のみなら Awake(), OnDestroy() でも良いと思う。

 あと、定義部分でフィールドを static で宣言しているが、これもコード簡略のためで、インスタンスでも構わない。というより、システムで全体でのイベントとしてユニークで良いのなら static を、オブジェクトごとにイベントの発生とコールバック受信したいのならインスタンスにすることになるだろう。その場合は Awake() でコンポーネント取得、またはインスペクタでインスタンス登録などの方法を用いれば良い。

●コールバックの定義部分をインスタンスにする
//呼び出し側(イベント発生側)
public class ActionCallbackSample : MonoBehaviour {

//引数なしの場合
public Action OnLongClick; //インスタンスを登録する

//(以下略)・・・
}

//コールバック受信側
public class MainSample : MonoBehaviour {

public ActionCallbackSample callback; //インスペクタで登録する

void Awake()
{
if (callback == null) //登録されてない場合はコンポーネント取得を試みる
callback = GetComponent<ActionCallbackSample>();
}

void OnEnable()
{
callback.OnLongClick += this.OnLongClick;
//(以下略)・・・
}

void OnDisable()
{
callback.OnLongClick -= this.OnLongClick;
//(以下略)・・・
}

//(以下略)・・・
}





●delegate でコールバックする

 この方法が本来言語に備わっている機能での書き方とも言える。上記の ActionUnityEventdelegate の機能をラップして簡単に扱う、または短く表記するようにしたものと考えてもいいだろう。そのため、他のプラットフォーム・言語に移植するときなどは互換性が高いかも知れない。ここでもこれまでの2例と全く同じ動作をするコードを書いてみよう。

●呼び出し側(イベント発生側)
using UnityEngine;

public class DelegateCallbackSample : MonoBehaviour {

//引数なしの場合
public delegate void LongClickHandler();
public static event LongClickHandler OnLongClick; //引数なし

//引数を1つとる場合
public delegate void SwipeHandler(Vector2 dir);
public static event SwipeHandler OnSwipe; //引数に Vector2 を1つとる

//引数を2つとる場合
public delegate void ChangeHandler(int befor, int after);
public static event ChangeHandler OnChange; //引数に int を2つとる

// Update is called once per frame
private void Update()
{
if (イベントの発生条件など)
{
if (OnLongClick != null)
OnLongClick(); //ここでコールバック(引数なし)

if (OnSwipe != null)
OnSwipe(Vector2.up); //ここでコールバック(引数を1つとる)

if (OnChange != null)
OnChange(1, 2); //ここでコールバック(引数を2つとる)
}
}
}


●コールバック受信側
using UnityEngine;

public class MainSample : MonoBehaviour {

void OnEnable()
{
//コールバックするメソッドを登録
DelegateCallbackSample.OnLongClick += this.OnLongClick;
DelegateCallbackSample.OnSwipe += this.OnSwipe;
DelegateCallbackSample.OnChange += this.OnChange;
}

void OnDisable()
{
//コールバックするメソッドを解除
DelegateCallbackSample.OnLongClick -= this.OnLongClick;
DelegateCallbackSample.OnSwipe -= this.OnSwipe;
DelegateCallbackSample.OnChange -= this.OnChange;
}

public void OnLongClick()
{
//ここにコールバックされたときの処理
Debug.Log("OnLongClick called.");
}

public void OnSwipe(Vector2 dir)
{
//ここにコールバックされたときの処理
Debug.Log("OnSwipe called. dir = " + dir);
}

public void OnChange(int before, int after)
{
//ここにコールバックされたときの処理
Debug.Log("OnChange called. before = " + before + ", after = " + after);
}
}

 コードはかなり簡略されたものなので、あくまでも雛形として自由に書き換えて使って欲しい。

 上記の Action での書き方とあまり変わらないので、それほど説明はいらないだろう。引数のパラメタがそのまま定義で書かれているだけだ。

 定義部分の static をインスタンスにする方法も Action での書き方を参考にして欲しい。考え方は全く同じだ。用途によって使い分けた方が良いだろう。




●interface でコールバックする

 この方法はオマケなので無視して良い(笑)。C# では delegate を使った方が楽なので、わざわざこういう書き方をする人はいないだろう。どちらかと言うと Java での書き方に近い。まぁ、delegate を持たない他の言語に移植するときとか、こういう書き方もあるという手法を学ぶのには良いだろう。

●呼び出し側(イベント発生側)
using UnityEngine;

public class InterfaceCallbackSample : MonoBehaviour
{
//引数なしの場合
public interface ILongClickHandler {
void OnLongClick();
}

public static ILongClickHandler LongClickHandler;

//引数を1つとる場合
public interface ISwipeHandler {
void OnSwipe(Vector2 dir);
}

public static ISwipeHandler SwipeHandler;

//引数を2つとる場合
public interface IChangeHandler {
void OnChange(int before, int after);
}

public static IChangeHandler ChangeHandler;

// Update is called once per frame
private void Update()
{
if (イベントの発生条件など)
{
if (LongClickHandler != null)
LongClickHandler.OnLongClick(); //ここでコールバック(引数なし)

if (SwipeHandler != null)
SwipeHandler.OnSwipe(Vector2.up); //ここでコールバック(引数を1つとる)

if (ChangeHandler != null)
ChangeHandler.OnChange(1, 2); //ここでコールバック(引数を2つとる)
}
}
}

●コールバック受信側
using UnityEngine;

public class MainSample : MonoBehaviour,
InterfaceCallbackSample.ILongClickHandler,
InterfaceCallbackSample.ISwipeHandler,
InterfaceCallbackSample.IChangeHandler
{

void OnEnable()
{
//コールバックするメソッドを登録
InterfaceCallbackSample.LongClickHandler = this;
InterfaceCallbackSample.SwipeHandler = this;
InterfaceCallbackSample.ChangeHandler = this;
}

void OnDisable()
{
//コールバックするメソッドを解除
InterfaceCallbackSample.LongClickHandler = null;
InterfaceCallbackSample.SwipeHandler = null;
InterfaceCallbackSample.ChangeHandler = null;
}

public void OnLongClick()
{
//ここにコールバックされたときの処理
Debug.Log("OnLongClick called.");
}

public void OnSwipe(Vector2 dir)
{
//ここにコールバックされたときの処理
Debug.Log("OnSwipe called. dir = " + dir);
}

public void OnChange(int before, int after)
{
//ここにコールバックされたときの処理
Debug.Log("OnChange called. before = " + before + ", after = " + after);
}
}

 コードはかなり簡略されたもので、あくまでも前述の3例に似せた書き方にしているので、実際に使うにはもう少し手を加えた方が良いだろう。内容的には前述の3例と違い、コールバックメソッドの登録が単ーとなる

 実際の所、interface にはメソッド名の縛りがあるので(delegate はメソッド名が異なっていてもシグネチャが合ってれば使える)、コールバックとしては自由度が低い。とは言え、例えば複数のシステム間で必ず決まったメソッド名でコールバックしたい場合などはこちらでも良い。ただし、delegate の例のように複数のメソッドを登録することはできないので(※上記の例の場合)注意。複数のメソッドを登録できるようにするなら、以下のようにそれぞれのコールバックの参照(LongClickHandler, SwipeHandler, ChangeHandler)を List などにして、AddListener(登録するメソッド)、RemoveListener(解除するメソッド) のような仕組みを作った方が良いだろう。もちろんイベント発生時にはそれら List を回して、登録してあるすべてのコールバックメソッドを実行する必要がある(null がないように注意)。

●interface でのコールバック登録を複数対応にする
using System.Collections.Generic;
using UnityEngine;

//呼び出し側(イベント発生側)
public class InterfaceCallbackSample : MonoBehaviour {

//(以上略)・・・

//コールバックの参照リスト
static List<ILongClickHandler> longClickListener = new List<ILongClickHandler>();

//コールバックを登録
public static void AddLongClickListener(ILongClickHandler callback)
{
longClickListener.Add(callback);
}

//コールバックを解除
public static void RemoveLongClickListener(ILongClickHandler callback)
{
longClickListener.Remove(callback);
}

//(以下略)・・・

// Update is called once per frame
private void Update()
{
if (イベントの発生条件など)
{
foreach (var callback in longClickListener) //※↓本来は null チェックした方が良い
callback.OnLongClick(); //ここでコールバック(引数なし)

//(以下略)・・・
}
}

//(以下略)・・・
}

//コールバック受信側
public class MainSample : MonoBehaviour,
InterfaceCallbackSample.ILongClickHandler,
(以下略)・・・
{

void OnEnable()
{
InterfaceCallbackSample.AddLongClickListener(this);
//(以下略)・・・
}

void OnDisable()
{
InterfaceCallbackSample.RemoveLongClickListener(this);
//(以下略)・・・
}

//(以下略)・・・
}

 定義部分の static をインスタンスにする方法は Action での書き方を参考にして欲しい。考え方は全く同じだ。用途によって使い分けた方が良いだろう。


(関連記事)
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】【C#】長押し(ロングタップ)を取得してコールバックする
【Unity】【C#】スワイプ(フリック)を判定、方向を取得してコールバックする
【Unity】【C#】ピンチ操作を取得してコールバックする
【Unity】【C#】VideoPlayer で動画の終了判定をする
【Java】interface を使って簡単なコールバック機能を作る


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  C# 
tb: 0   cm: --

【Unity】【C#】uGUI ドロップダウンの要素をコードで設定と取得、外観のカスタマイズなど  


 どちらかというと備忘録。調べているとき色々な方法があったので、少しまとめておいた感じ。どのやり方でも違いはないので、用途によって使いやすい方を選べば良いと思う。

※ Unity 5.5.0f3 / Windows10(x64) で確認




■ドロップダウンの要素をコードで設定する

 まずはドロップダウンを画面に置いてしまおう。「GameObject」メニューから「UI>Dropdown」で「Canvas」以下に生成される。インスペクタで下の方へスクロールして見てみると、予め3つの要素が設定されている。これをコードで変更することになる。なお、GUI 関連をシーンビューで見るときは、「2D」のボタンをオンした方が見やすいかも知れない。







 要素を設定するコードは以下のようになる。インスペクタで操作するドロップダウンオブジェクトを設定して、スクリプトで変更する感じだ。「using UnityEngine.UI」を入れて置いた方が簡潔だろう。なお、クラス名は任意で良い。

●要素を文字列のリストで設定する
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropdownTest : MonoBehaviour {

public Dropdown dropdown; //操作するオブジェクトを設定する

// Use this for initialization
void Start () {
if (dropdown) {
dropdown.ClearOptions(); //現在の要素をクリアする
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("5");
dropdown.AddOptions(list); //新しく要素のリストを設定する
dropdown.value = 1; //デフォルトを設定(0~n-1)
}
}
}

 このスクリプトを適当なオブジェクトにアタッチして、操作するオブジェクトをドラッグ&ドロップで設定しておく。今回は「Canvas」にアタッチしてあるが、どこでも良い。プレイして「Dropdown」をインスペクタで見てみると要素が変わっていることがわかる。なお、デフォルトの値(value)は要素の先頭を0とした連番となっている。ここでは2番目([1])の要素を設定している。



●プレイ中




 もう1つ、直接プロパティに設定する方法もある。内容的には上記の例と同じものだが、追加するリストには「Dropdown.OptionData」クラスの要素を使う。リストの内容はこの形式で保持されていると考えれば良いだろう。

●要素を「Dropdown.OptionData」のリストに直接設定する
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropdownTest : MonoBehaviour {

public Dropdown dropdown; //操作するオブジェクトを設定する

// Use this for initialization
void Start () {
if (dropdown) {
dropdown.options.Clear(); //現在の要素をクリアする
dropdown.options.Add(new Dropdown.OptionData("1"));//新しく要素を追加する
dropdown.options.Add(new Dropdown.OptionData("2"));
dropdown.options.Add(new Dropdown.OptionData("5"));
dropdown.value = 1; //デフォルトを設定(0~n-1)
//dropdown.RefreshShownValue();//更新を確認(画像を設定したとき)
}
}
}

 こちらを使った場合はアイコン画像(Sprite)なども一緒に設定できる。以下に資料を載せておくので必要があれば参照して欲しい。「Dropdown.RefreshShownValue()」は画像も更新したときは必要になるようだ。文字列だけなら無くても構わない。

(参考)
Unity5のドロップダウンのオプション値をスクリプトで設定する




■ドロップダウンの要素を取得する

 次に設定した要素を取得してみよう。今回は値(value)とテキストを取得することにする。ちなみに値の方は先頭からの連番(0~n-1)になる。

 この場合はコールバックイベントの「Dropdown.onValueChanged(int)」が使える。まずはスクリプトを用意して、「+」ボタンを押してインスペクタに設定しよう。なおここでは「DropdownTest」スクリプトは「Canvas」オブジェクトにアタッチしてあるものとする。

●「OnValueChanged(int)」コールバックを使って要素を取得する
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropdownTest : MonoBehaviour {

public Dropdown dropdown; //操作するオブジェクトを設定する

// Use this for initialization
void Start () {
if (dropdown) {
dropdown.ClearOptions(); //現在の要素をクリアする
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("5");
dropdown.AddOptions(list); //新しく要素のリストを設定する
dropdown.value = 1; //デフォルトを設定(0~n-1)
}
}

public void OnValueChanged(int value) {
Debug.Log("value = " + value); //値を取得(先頭から連番(0~n-1))
Debug.Log("text(options) = " + dropdown.options[value].text); //リストからテキストを取得
Debug.Log("text(captionText) = " + dropdown.captionText.text); //Labelからテキストを取得
}
}


 プレイしてドロップダウンを変更してみると値がコンソールにデバッグ表示される(はじめの1回はデフォルト設定の変更のため。0からならデフォルト設定は必要ない)。


 値(連番)ではなくて、直接選択されたテキストを使いたいときもあるだろう。この場合は大まかに2つの方法がある。1つは上記のドロップダウンに設定したリストを取得する方法と、選択したときに表示されるラベルを取得する方法だ。


 内容的にはリストの方は設定した値を取得しているだけなので説明はいらないと思うが、「Label」からというのはヒエラルキーで「Dropdown>Label」と開くと、このオブジェクトに選択したテキストが設定されているのがわかるからだ。「Dropdown.captionText」はそのテキストを保持しているオブジェクトと考えれば良いだろう。




■ボタンを押したとき、ドロップダウンの要素を取得する

 まずはドロップダウンがある状態から、「GameObject」メニューの「UI>Button」でボタンを追加しよう。このボタンを押したとき、選択されているドロップダウンの要素を取得するようにする。基本的には今までの方法と変わらない。取得タイミングをボタン押下にしただけだ。

 ボタンには「Button.onClick()」というコールバックが標準で備わっている。ドロップダウンのときと同じようにスクリプトを用意して、「+」ボタンを押してインスペクタで設定しよう。先のドロップダウンの例をそのまま使うなら、以下のようになる。


●「OnClick()」コールバックを使って要素を取得する
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropdownTest : MonoBehaviour {

public Dropdown dropdown; //操作するオブジェクトを設定する

// Use this for initialization
void Start () {
if (dropdown) {
dropdown.ClearOptions(); //現在の要素をクリアする
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("5");
dropdown.AddOptions(list); //新しく要素のリストを設定する
dropdown.value = 1; //デフォルトを設定(0~n-1)
}
}

public void OnClick() {
Debug.Log("dropdown.value = " + dropdown.value); //値を取得(先頭から連番(0~n-1))
Debug.Log("text(options) = " + dropdown.options[dropdown.value].text); //リストからテキストを取得
Debug.Log("text(captionText) = " + dropdown.captionText.text); //Labelからテキストを取得
}
}

 この例のようにドロップダウンの初期化とボタンの操作を1つのクラスで管理する場合は上記のやり方で十分だが、別々のクラスで管理したいときはどうすれば良いのだろうか。もちろん同じように操作するオブジェクトを設定させても良いが、コールバックのオーバーロードをすればもう少し簡潔に書くこともできる。

●コールバックのオーバーロードを使って要素を取得する
(初期化のみのクラス「DropdownTest」)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DropdownTest : MonoBehaviour {

public Dropdown dropdown; //操作するオブジェクトを設定する

// Use this for initialization
void Start () {
if (dropdown) {
dropdown.ClearOptions(); //現在の要素をクリアする
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("5");
dropdown.AddOptions(list); //新しく要素のリストを設定する
dropdown.value = 1; //デフォルトを設定(0~n-1)
}
}
}

(要素の取得のみのクラス「ButtonTest」)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ButtonTest : MonoBehaviour {
//OnClick のオーバーロード
public void OnClick(Dropdown dropdown) {
Debug.Log("dropdown.value = " + dropdown.value); //値を取得(先頭から連番(0~n-1))
Debug.Log("text(options) = " + dropdown.options[dropdown.value].text); //リストからテキストを取得
Debug.Log("text(captionText) = " + dropdown.captionText.text); //Labelからテキストを取得
}
}

 この「ButtonTest」を例えばボタンにアタッチし、要素を取得するドロップダウンをインスペクタで設定すれば、スクリプトはかなり簡潔になる。ドロップダウンの状態は「Dropdown」オブジェクトが保持しているわけだから、これを引数にして直接渡してしまえば良いわけだ。もちろん「Label」オブジェクトを引数にするのもアリだろう。





●プレイ中にボタンを押すと…




■uGUI の見た目を変更する(簡易的な外観のカスタマイズ)

 一番簡単な方法として、各オブジェクトの「Image>Source Image」を入れ替えれば良い。試しに Asset Store (Ctrl-9) から「Cartoon UI Pack」をインポートして使ってみよう。キーワードに「Cartoon UI」を入れて「無料のみ」のボタンを押せば簡単に見つかる。


 インポートし終わったら、あとは適当なものを選ぶだけだ。好きなものを設定してみよう。見た目が変わったハズだ。




 また、ボタンをハイライトしたときの色などの設定は「Highlighted Color(ハイライト)」「Pressed Color(押したとき)」「Disabled Color(無効化のとき)」を変更すれば良い。好きな色を設定してみよう。



 他にも細かい設定ができるようなので参考URLを載せておこう。色々試してみると良いだろう。

(参考)
【Unity】uGUIドロップダウンメニュー(コンボボックス)の使い方
【Unity開発】uGUIのDropDownの使い方【ひよこエッセンス】


■uGUI 関連参考書籍






(関連記事)
【Unity】【C#】【JavaScript】4.6.x の Canvas~UI Text のテキストをコードで変更する
【Unity】【C#】【JavaScript】GUIText をコードで生成する


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: C#  サンプル  Unityリファレンス 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop