非日常デコレーション

一般 web developer の雑記です

Unityでチュートリアルシーン

仕上がりはこんなかんじ

f:id:mokotixfemilar:20151215162745g:plain

gif化がうまくいかずに4倍速になってます。 よく見たら竜巻⇨雷のシーンがバグってますね…。 余裕がないので修正しません。

動機

元々LeapMotionとUnityを組み合わせたゲームを開発しようとしていて、そのときにチュートリアルシーンを作りたくて結構迷ってしまったので記録のために書いてみました。 とても簡単なので他のゲームを作るときにも流用できると思います。汎用性はあまりないテキトーなコードですみません。というか個人的な備忘録です。

イベントの認識は元々はLeapMotionでやってたのでテキストはそのままですが、このコードではSpaceキーが押された時を認識してイベントを進めることにします。

//TutorialEvent.cs

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class TutorialEvent : MonoBehaviour
{
   //宣言したゲームオブジェクトにPrefabをInspectorで設定しておく
    public GameObject fire;
    public GameObject bomb;
    public GameObject thunder;
    public GameObject tornade;
  //イベントが進むフラグ管理
    public int EventFlag = 0;
    public bool ai;

    void Update ()
    {

        //円を描いてみよう
        if (EventFlag == 0) {
            if (Input.GetKeyDown ("space")) {
                Instantiate (fire);
                EventFlag = 1;
            }
        }
            
        //スワイプしよう
        if (EventFlag == 2) {
            if (Input.GetKeyDown ("space")) {
                Instantiate (bomb);
                EventFlag = 3;
            }
        }

        //オーケーサインを作ろう
        if (EventFlag == 4) {
            if (Input.GetKeyDown ("space")) {
                Instantiate (tornade);
                EventFlag = 5;
            }
        }

        //両手で輪っかを作ろう
        if (EventFlag == 6) {
            if (Input.GetKeyDown ("space")) {
                Instantiate (thunder);
                EventFlag = 7;
            }
        }

        if (EventFlag == 1 && ai == false) { 
            StartCoroutine (Wait ());
            ai = true;
        }

        if (EventFlag == 3 && ai == false) {
            StartCoroutine (Wait2 ());
            ai = true;
        }
        if (EventFlag == 5 && ai == false) {
            StartCoroutine (Wait3 ());
            ai = true;
        }
        if (EventFlag == 7 && ai == false) {
            StartCoroutine (Wait4 ());
            ai = true;
        }

    }
 //イベント発生までの間隔を管理するコルーチン
 //今回は3秒に設定してます
 //3秒経つと、effectタグをつけたPrefabが破壊されてイベントが進みます
    IEnumerator Wait ()
    {
        yield return new WaitForSeconds (3.0f);
        var effectobj = GameObject.FindGameObjectsWithTag ("effect");
        foreach (GameObject objs in effectobj)
            Destroy (objs);
        ai = false;
        EventFlag = 2;
    }

    IEnumerator Wait2 ()
    {
        yield return new WaitForSeconds (3.0f);
        var effectobj = GameObject.FindGameObjectsWithTag ("effect");
        foreach (GameObject objs in effectobj)
            Destroy (objs);
        ai = false;
        EventFlag = 4;
    }

    IEnumerator Wait3 ()
    {
        yield return new WaitForSeconds (3.0f);
        var effectobj = GameObject.FindGameObjectsWithTag ("effect");
        foreach (GameObject objs in effectobj)
            Destroy (objs);
        ai = false;
        EventFlag = 6;
    }

    IEnumerator Wait4 ()
    {
        yield return new WaitForSeconds (5.0f);
        var effectobj = GameObject.FindGameObjectsWithTag ("effect");
        foreach (GameObject objs in effectobj)
            Destroy (objs);
        ai = false;
    }
}

今度はシーンのUITextを動的に生成するコード。

//TutorialText.cs

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class TutorialText : MonoBehaviour {

    public TutorialEvent tutorial;

    void Start () {
            StartCoroutine("Process");
    }

    IEnumerator  Process(){
        yield return StartCoroutine(TheFirst());
        yield return StartCoroutine(TheSecond());
        yield return StartCoroutine(TheThird());
        yield return StartCoroutine(TheFourth());
        yield return StartCoroutine(TheEnd());
        yield return StartCoroutine(GoToMain());
    }

    IEnumerator TheFirst(){
    
        while (!(tutorial.GetComponent<TutorialEvent>().EventFlag==1)){

            this.GetComponent<Text>().text = "まずは円を描いてみましょう";
            yield return null;
        }

        yield return new WaitForSeconds(3.0f);
    }

    IEnumerator TheSecond(){
        
        while (!(tutorial.GetComponent<TutorialEvent>().EventFlag==3)){
            
            this.GetComponent<Text>().text = "次に手を横にスワイプさせてみましょう";
            yield return null;
        }
        
        yield return new WaitForSeconds(3.0f);
    }

    IEnumerator TheThird(){
        
        while (!(tutorial.GetComponent<TutorialEvent>().EventFlag==5)){
            
            this.GetComponent<Text>().text = "次に右指でOKサインを作りましょう";
            yield return null;
        }
        
        yield return new WaitForSeconds(3.0f);
    }

    IEnumerator TheFourth(){
        
        while (!(tutorial.GetComponent<TutorialEvent>().EventFlag==7)){
            
            this.GetComponent<Text>().text = "最後に両手の親指と人差し指で\n大きな輪っかを作ってみましょう";
            yield return null;
        }
        
        yield return new WaitForSeconds(3.0f);
    }
    
    IEnumerator TheEnd(){

        this.GetComponent<Text>().text = "チュートリアルは以上になります";
        yield return new WaitForSeconds(5.0f);

        this.GetComponent<Text>().text = "それでは間もなくゲームが始まります";
        yield return new WaitForSeconds(5.0f);
            
        this.GetComponent<Text>().text = "今のジェスチャーを駆使して、\n制限時間内にできるだけ\n多くのオブジェクトを破壊しましょう";
        yield return new WaitForSeconds(5.0f);
        
    }

 //チュートリアルシーンを抜けてゲームへ
    IEnumerator GoToMain(){
        Application.LoadLevel("MainScene");
        yield return null;
    }
}

Update()関数を使用するのが普通だと思ってたんですが、 どうやらこういった類のコードはコルーチンオンリーで管理したほうがやりやすいらしいです。 ちなみに上のコードは常にジェスチャの認識を見張っていないといけないため止むを得ずUpdate()を使用しています。なので苦労しました。