Unity で独自のコルーチンを定義する
Unity では、IEnumerator
を StartCoroutine
に渡すことで、コルーチンを実行できる。
標準で定義されている WWW
や WaitForSeconds
などを使うことが多いが、独自のコルーチンを定義することもできる。
Unity 5.3 から追加された CustomYieldInstruction
クラスを使えば、ちょっとしたコルーチンを定義するのが楽になる。
概要
- 非同期で作成されるファイルを待つコルーチンを定義する
環境
- Unity 5.3.1p3
基本的なこと
Custom Coroutines – Unity Blog に書いてあること。
- Unity 5.3 から
CustomYieldInstruction
が使えるようになった keepWaiting
プロパティを override して、コルーチンがもう終了すべきか・まだすべきでないかを定義するWaitForSecondsRealtime
という実例がある
非同期で作成されるファイルを待つコルーチンを定義する
ファイルの作成を待ってから、そのファイルに対して何かしたいときがある。例えば、スクリーンショットを撮影して Twitter に投稿したい場合など。
参考: Unity でスクリーンショットを撮る | KAKELOG
そこで、ファイルが作成されることを待つ WaitForFile
を定義してみる。
WaitForFile を定義する
WaitForFile.cs
using UnityEngine;
using System.IO;
public class WaitForFile : CustomYieldInstruction
{
public string FilePath { get; private set; }
public bool IsCompleted { get; private set; }
private float timeoutTime;
private bool existance;
public override bool keepWaiting
{
get
{
IsCompleted = File.Exists(FilePath) == existance;
if (IsCompleted)
{
return false;
}
if (Time.realtimeSinceStartup >= timeoutTime)
{
return false;
}
return true;
}
}
public WaitForFile(string filePath, bool existance, float timeout)
{
this.timeoutTime = Time.realtimeSinceStartup + timeout;
this.existance = existance;
FilePath = filePath;
}
}
CustomYieldInstruction
を継承した場合、keepWaiting
を override するだけでコルーチンが定義できる。
今回の様に、何か特定の条件が成立するまで待つルーチンを書くのに便利。
これを使って、スクリーンショットを撮って何かするコルーチンを書いてみると、以下の様になる。
IEnumerator CaptureScreenshot(string fileName)
{
var filePath = Path.Combine(Application.persistentDataPath, fileName);
if (File.Exists(filePath))
{
File.Delete(filePath);
var deletion = new WaitForFile(filePath, false, 1.0f);
yield return deletion;
if (!deletion.IsCompleted)
{
// Timeout.
yield break;
}
}
Application.CaptureScreenshot(fileName);
var creation = new WaitForFile(filePath, true, 1.0f);
yield return creation;
if (!creation.IsCompleted)
{
// Timeout.
yield break;
}
// Do something with the file.
}
WaitWhile / WaitUntil
Custom Coroutines – Unity Blog の文末に補足があるように、
WaitUntil
や WaitWhile
を使うと「何か特定の条件が成立するまで待つルーチン」はもっと簡単に書ける。
yield return new WaitUntil(() => File.Exists(filePath));
でも、今回の様にタイムアウトを指定したい場合など、終了条件が複雑になる場合は、クラス化すると良いと思う。
もうちょっと複雑なコルーチンを定義する
これまで(Unity 5.2 まで)通り、IEnumerator
を実装したクラスを定義する必要がある。
コルーチンの内部で別のコルーチンを使用する場合などは、IEnumerator
の各メソッドを内部のコルーチンに移譲するような実装が必要になる(たぶん)。
まとめ・感想
CustomYieldInstruction
を使えば、特定の条件が成立するまで待つルーチンを簡単に定義できる- 特定の条件がラムダ式一発で書けるほど単純なら、
WaitUntil
やWaitWhile
を使えば、もっと簡単に定義できる - もうちょっと複雑なコルーチンを定義しようと思ったら
IEnumerator
を実装する必要がある
コルーチン楽しい。楽しいけど使いどころは考えた方が良い気がする。特に、キャンセルしたいときがつらいといつも思う。