複数のIEnumratorを時間管理しつつ処理するCoroutine
作ったもの
作ろうと思ったきっかけ
ステージクリア型のパズルゲームのステージセレクト画面で縮小したマップを一気に3x4の12個動的に読み込みつつ、表示。みたいなことをStartCoroutineでやっていたらFPSがガタ落ちでUIの反応が悪くなってしまったので。
例えば
あるGameObjectはあるPrefabを10個Instantiate
して子要素として付けておく必要があるとする。
しかしUnityのInstantiate
は重い。 なので、StartCoroutine
を使って、yield return null
しながら1個づつInstantiate
することにした。(よくある)
public class HogeObject : MonoBehaviour
{
public GameObject prefab;
public void Start()
{
StartCoroutine(CreateIterator());
}
private IEnumrator CreateIterator()
{
for(int i = 0;i < 10;++i)
{
var go = Instantiate(prefab) as GameObject;//heavy work
go.transform.SetParent(this.transform,false);
yield return null;
}
}
}
しかし、このHogeObject
は画面上に100個あった!(よくある(?))
すると、StartCoroutine
が100回呼ばれ、1フレームに100個Instantiate
されることになってプチフリーズのような状態に。
何が悪いかって
UnityのStartCoroutine
の仕組みには横の繋がりがなく、独立して動いているので、何個StartCoroutine
されてようが知ったこっちゃないということ。
なので
冒頭に戻る。 自前StartCoroutineの作成。
上記例はこうなる。
public class HogeObject : MonoBehaviour
{
public GameObject prefab;
public void Start()
{
//StartCoroutine(CreateIterator());//この1行が
TaskScheduler.Instance.AddIterator(CreateIterator());//これになる
}
private IEnumrator CreateIterator()
{
for(int i = 0;i < 10;++i)
{
var go = Instantiate(prefab) as GameObject;//heavy work
go.transform.SetParent(this.transform,false);
yield return null;
}
}
}
StartCoroutine
だった箇所をTaskScheduler.Instance.AddIterator
に変える事で、TaskScheduler(Singletone)で1本だけ走っているStartCoroutine
が時間を計測しつつよしなにCreateIteratorを処理するので、HogeObject が100個あっても1000個あっても処理落ちはしない(はず)。
仕組み
- Unityの
StartCoroutine
はIEnumerator
を渡して実行しているが、仕組みとしてはIEnumerator#MoveNext
を呼ぶ事で処理を中断→継続させている。このMoveNext
を呼ぶ処理を自分で作れば自前StartCoroutine
みたいな事が出来る。
- そこで、複数の
IEnumrator
を受け取って、順次MoveNext
を呼んでいく+処理時間が一定時間を超えていたらyield return null;
するIEnumrator
をStartCoroutine
しておく(何言ってんだ)
- すなわち、Unityの
StartCoroutine
の拡張をUnityのStartCoroutine
を使うことで実装した感じ(何言ってんだその2)。
public void Start()
{
StartCoroutine(Iterator());
}
private IEnumerator Iterator()
{
while (true)
{
var ts = Time.realtimeSinceStartup;
do
{
if (iteratorList.Count <= 0) break;
foreach (var itr in iteratorList.ToList())
{
if (itr.Value.MoveNext() == false)
{
iteratorList.Remove(itr.Key);
}
if (Time.realtimeSinceStartup - ts > targetTime) yield return null;
}
} while (Time.realtimeSinceStartup - ts <= targetTime);
yield return null;
}
}
補足
- Action 渡せるものもついでに作ったので、
TaskScheduler.Instance.AddAction(()=>Instantiate(hogeObj));
のようにラムダ式渡したりなんかも。
-
yield return new WaitForSeconds(ms);
などには未対応。 というか、処理の負荷分散目的であって、順次処理目的でのStartCoroutineは普通にStartCoroutineでやってくれれば良いのでは。
-
WWW www;
で yield return www;
とかは価値があるけど未対応。 while(www.isDone == false){yield return null;}
にすれば使えると思う(試してない)
- 基本は独立のStartCoroutineに横のつながりをもたせたなら、Priorityの概念を入れるのが筋だとは思うが未実装。必要に駆られたら作ります。
StartCoroutine
はIEnumerator
を渡して実行しているが、仕組みとしてはIEnumerator#MoveNext
を呼ぶ事で処理を中断→継続させている。このMoveNext
を呼ぶ処理を自分で作れば自前StartCoroutine
みたいな事が出来る。IEnumrator
を受け取って、順次MoveNext
を呼んでいく+処理時間が一定時間を超えていたらyield return null;
するIEnumrator
をStartCoroutine
しておく(何言ってんだ)StartCoroutine
の拡張をUnityのStartCoroutine
を使うことで実装した感じ(何言ってんだその2)。 public void Start()
{
StartCoroutine(Iterator());
}
private IEnumerator Iterator()
{
while (true)
{
var ts = Time.realtimeSinceStartup;
do
{
if (iteratorList.Count <= 0) break;
foreach (var itr in iteratorList.ToList())
{
if (itr.Value.MoveNext() == false)
{
iteratorList.Remove(itr.Key);
}
if (Time.realtimeSinceStartup - ts > targetTime) yield return null;
}
} while (Time.realtimeSinceStartup - ts <= targetTime);
yield return null;
}
}
- Action 渡せるものもついでに作ったので、
TaskScheduler.Instance.AddAction(()=>Instantiate(hogeObj));
のようにラムダ式渡したりなんかも。 -
yield return new WaitForSeconds(ms);
などには未対応。 というか、処理の負荷分散目的であって、順次処理目的でのStartCoroutineは普通にStartCoroutineでやってくれれば良いのでは。 -
WWW www;
でyield return www;
とかは価値があるけど未対応。while(www.isDone == false){yield return null;}
にすれば使えると思う(試してない) - 基本は独立のStartCoroutineに横のつながりをもたせたなら、Priorityの概念を入れるのが筋だとは思うが未実装。必要に駆られたら作ります。
Author And Source
この問題について(複数のIEnumratorを時間管理しつつ処理するCoroutine), 我々は、より多くの情報をここで見つけました https://qiita.com/divideby_zero/items/2f5362fbb2b6dc4d8e99著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .