Slide 1

Slide 1 text

いまさらだけど確認しておこう Coroutine @RyotaMurohoshi 2018/09/15(土) Unity非同期完全に理解した勉強会

Slide 2

Slide 2 text

2015年3月21日

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

いやぁ~~っ! 未来、ついに来ましたね!

Slide 5

Slide 5 text

async/await、最高ですね! ガンガン使っていきましょう!

Slide 6

Slide 6 text

ところでちょっと 現実に、そして日常に戻りましょう

Slide 7

Slide 7 text

Unityのバージョン何ですか? 2018系?2017系?5系?もしかして4系?

Slide 8

Slide 8 text

『最新作を作っているバージョン』だけじゃないですよ! 『利益を生み出しているゲームのバージョン』は何ですか!?

Slide 9

Slide 9 text

世の中のすべてのUnityプロジェクトが (特になんの工夫もせず) async/awaitが使えるわけじゃない・・・ 使えるけれど、大人のあれで使わないってこともあるよね

Slide 10

Slide 10 text

ある程度利益を出していて大きく変更予定のないヤツ 大人の事情でUnityをアップデートできないヤツ こういうプロジェクト、まれによくある

Slide 11

Slide 11 text

世の中の全てのUnityプロジェクトで async/awaitが使えるわけじゃない・・・

Slide 12

Slide 12 text

というわけで・・・

Slide 13

Slide 13 text

いまさらだけど確認しておこう Coroutine @RyotaMurohoshi 2018/09/15(土) Unity非同期完全に理解した勉強会

Slide 14

Slide 14 text

※ 私の勤め先や個人ではUnity 2018やasync/await 使えますよ!

Slide 15

Slide 15 text

Coroutineがasync/awaitの代わりになるわけじゃない async/awaitが使える場所ではそっち使おう! Rxが適切な時もあるよ!

Slide 16

Slide 16 text

けれど、それでも・・・

Slide 17

Slide 17 text

古いプロジェクトをメンテ・改善していると Coroutineが助けてくれること、ある! Coroutineのデバックで嵌ることも、あるかも! 今更だけど、このタイミングで確認しておきましょう!

Slide 18

Slide 18 text

基本的なCoroutineの使い方は 知っている前提で いくつか(みんなが知っているかもしれないけど)Tipsを話します

Slide 19

Slide 19 text

public void StartLaunchLoop() { StartCoroutine(LaunchCoroutine()); } private IEnumerator LaunchCoroutine() { // 1秒に一回 Launch が呼び出される while (true) { Launch(); yield return new WaitForSeconds(1.0F); } } private void Launch() { // 略 }

Slide 20

Slide 20 text

「当たり前」って大切 確認していきましょう

Slide 21

Slide 21 text

その1

Slide 22

Slide 22 text

void Start() { // 入れ子のコルーチンを呼び出す StartCoroutine(ParentRoutine()); } IEnumerator ParentRoutine() { Debug.Log("start parent"); // 子コルーチンの呼び出し yield return ChildRoutine(); Debug.Log("end parent"); } IEnumerator ChildRoutine() { Debug.Log("start child"); yield return new WaitForSeconds(1.0F); Debug.Log("end child"); } 実行結果 start parent start child end child end parent ※ Unity 5.3 以降

Slide 23

Slide 23 text

その1 Coroutineの入れ子でStartCoroutineがいらないのはUnity 5.3から! Unity 5.2とそれより前ではStartCoroutineが必要! コンパイルエラーにならないからこそ、気を付けよう!

Slide 24

Slide 24 text

void Start() { // 入れ子のコルーチンを呼び出す StartCoroutine(ParentRoutine()); } IEnumerator ParentRoutine() { Debug.Log("start parent"); // 子コルーチンの呼び出し yield return ChildRoutine(); Debug.Log("end parent"); } IEnumerator ChildRoutine() { Debug.Log("start child"); yield return new WaitForSeconds(1.0F); Debug.Log("end child"); } 実行結果 Unity 5.3 以降 start parent start child end child end parent 実行結果 Unity 5.2 以前 start parent end parent

Slide 25

Slide 25 text

void Start() { // 入れ子のコルーチンを呼び出す StartCoroutine(ParentRoutine()); } IEnumerator ParentRoutine() { Debug.Log("start parent"); // Unity 5.2以前で子コルーチンの呼び出し // StartCoroutineが必要! yield return StartCoroutine(ChildRoutine()); Debug.Log("end parent"); } IEnumerator ChildRoutine() { Debug.Log("start child"); yield return new WaitForSeconds(1.0F); Debug.Log("end child"); } 実行結果 start parent start child end child end parent

Slide 26

Slide 26 text

コンパイルエラーにならないからこそたちが悪い! Unity 5.3以上になれている人が、古いプロジェクトを触って これに気が付かず、コンパイルは通るのに 「あれ、なんか思ってる挙動と違う!?」ってなるorz

Slide 27

Slide 27 text

その2

Slide 28

Slide 28 text

その2 WaitForSecondsはTime.timeScaleの影響を受ける 影響を受けたくないならば、WaitForSecondsRealtimeを使う! 注) WaitForSecondsRealtimeはUnity 5.3から利用可能

Slide 29

Slide 29 text

IEnumerator WaitRoutine() { Debug.Log("start"); // ↓Time.timescaleの影響を受ける yield return new WaitForSeconds(1.0F); // ↓Time.timescaleの影響を受けない yield return new WaitForSecondsRealtime(1.0F); Debug.Log("end"); }

Slide 30

Slide 30 text

その3

Slide 31

Slide 31 text

その3 Unity 5.3からはWaitUntil、WaitWhile、 そしてCustomYieldInstructionを活用しよう!

Slide 32

Slide 32 text

IEnumerator BattleRoutine() { SetUiBeforeBattle(); yield return new WaitUntil(() => onReadyBattle); // onReadyBattleがtrueになったらここに到達 StartBattle(); // 略 }

Slide 33

Slide 33 text

CustomYieldInstructionを使えば、 自分でWaitUntil・WaitWhileみたいなクラスを 柔軟に作れる

Slide 34

Slide 34 text

その4

Slide 35

Slide 35 text

その4 Unity 5.2とそれより古いバージョンでも WaitUntil、WaitWhile、CustomYieldInstructionは作れる・使える ただしStartCoroutineとの併用が必要!

Slide 36

Slide 36 text

WaitUntil、WaitWhile、CustomYieldInstructionは Unity 5.3から導入された型。けどUnity 5.2以前でも自分で作れる。

Slide 37

Slide 37 text

public abstract class CustomYieldInstruction : IEnumerator { public abstract bool keepWaiting { get; } public object Current { get { return null; } } public bool MoveNext() { return keepWaiting; } public void Reset() { } } public sealed class WaitUntil : CustomYieldInstruction { Func m_Predicate; public override bool keepWaiting { get { return !m_Predicate(); } } public WaitUntil(Func predicate) { m_Predicate = predicate; } }

Slide 38

Slide 38 text

IEnumerator BattleRoutine() { SetUiBeforeBattle(); // StartCoroutineが必要なことに注意! yield return new StartCoroutine(WaitUntil(() => onReadyBattle)); // onReadyBattleがtrueになったらここに到達 StartBattle(); // 略 }

Slide 39

Slide 39 text

その5

Slide 40

Slide 40 text

その5 StopCoroutine(Coroutine routine)が使えるのは、 Unity 2017.1から!

Slide 41

Slide 41 text

その6

Slide 42

Slide 42 text

その6 StartCoroutineしたコンポーネントがdisableになっても Coroutineは動き続ける

Slide 43

Slide 43 text

その7

Slide 44

Slide 44 text

その7 StartCoroutineしたコンポーネントが破壊されると Coroutineは止まる

Slide 45

Slide 45 text

その8

Slide 46

Slide 46 text

その8 StartCoroutineしたコンポーネントを 持つGameObjectが破壊されるとCoroutineは止まる

Slide 47

Slide 47 text

その9

Slide 48

Slide 48 text

その9 StartCoroutineしたコンポーネントを 持つGameObjectがSetActivate(false) されると Coroutineは止まる。再開もしない。

Slide 49

Slide 49 text

「あれ何でCoroutine動かないの?」って思ったら、 コンポーネントが破壊されてないかも確認しましょう

Slide 50

Slide 50 text

その10

Slide 51

Slide 51 text

その10 StartCoroutine(IEnumerator)したCoroutineは、 StopCoroutine(string)じゃ止められない。

Slide 52

Slide 52 text

void Start() { StartCoroutine(SpawnRoutine()); } void StopSpawn() { // StartCoroutine(IEnumerator)で始めたCoroutineは、 // StopCoroutine(string)では止められない StopCoroutine("SpawnRoutine"); } IEnumerator SpawnRoutine() { // 略 }

Slide 53

Slide 53 text

その11

Slide 54

Slide 54 text

その11 あるコンポーネントにおいて StartCoroutine(string)したCoroutineが複数あって、 StopCoroutine(string) すると そのstring(メソッド名)のすべてのCoroutineが止まる

Slide 55

Slide 55 text

void Start() { // Coroutineは複数走っている StartCoroutine("SpawnRoutine"); StartCoroutine("SpawnRoutine"); } void StopSpawn() { // このコンポーネントのSpawnRoutine(文字列)がすべて止まる StopCoroutine("SpawnRoutine"); } IEnumerator SpawnRoutine() { // 略 }

Slide 56

Slide 56 text

※ 文字列でのStartCoroutine・StopCoroutineの利用 おすすめしません

Slide 57

Slide 57 text

その12

Slide 58

Slide 58 text

その12 StopAllCoroutinesはそのコンポーネントがStartした Coroutineをすべて止める StartCoroutine(string)・StartCoroutine(IEnumerator)で始めたやつどちらもすべて。

Slide 59

Slide 59 text

その13

Slide 60

Slide 60 text

その13 StartやOnCollisionEnterなどのイベント関数も Coroutineライクに書ける

Slide 61

Slide 61 text

void OnCollisionEnter(Collision collision) { Debug.Log(collision.gameObject.name); // すぐに殺す Destroy(collision.gameObject); }

Slide 62

Slide 62 text

// voidじゃなくて、IEnumerator IEnumerator OnCollisionEnter(Collision collision) { Debug.Log(collision.gameObject.name); // 1秒待ってから殺す yield return new WaitForSeconds(1.0F); Destroy(collision.gameObject); }

Slide 63

Slide 63 text

その14

Slide 64

Slide 64 text

その14 Coroutineはコンポーネントのインスタンスに紐づく

Slide 65

Slide 65 text

その15

Slide 66

Slide 66 text

その15 Coroutineはメインスレッドで動く

Slide 67

Slide 67 text

まとめ

Slide 68

Slide 68 text

Coroutineがasync/awaitの代わりになるわけじゃない async/awaitが使える場所ではそっち使おう! Rxが適切な時もあるよ!

Slide 69

Slide 69 text

けれど、それでも・・・

Slide 70

Slide 70 text

古いプロジェクトをメンテ・改善していると Coroutineが助けてくれること、ある! Coroutineのデバックで嵌ることも、あるかも! 今更だけど、このタイミングで確認、大切ですね!

Slide 71

Slide 71 text

async / awaitはいいぞ! Rxもいいぞ! Coroutineもプロジェクトによっては、使う時があるよね!

Slide 72

Slide 72 text

いまさらだけど確認しておこう Coroutine @RyotaMurohoshi 2018/09/15(土) Unity非同期完全に理解した勉強会