【Unity】ゲームの進行を管理する

投稿者: | 2020-09-26

ストーリーに沿ってゲームを進行させる方法を考えてみました。

上の動画では、プレイヤーがコライダーに入ると秒読みが始まり、3秒後に球を出現させて、プレイヤーが球の方を向くと別のコライダーが出現し、そのコライダーに入るとゲーム終了のテキストが出て、2秒後にシーンが再読み込みされます。

まず、シーンにプレイヤーとSphere、2つのCube、テキストを置きました。

Cubeにはタグを付けて、コライダーのIs Triggerにチェックを入れます。

Sphereにも別のタグを付けます。

そして、空のゲームオブジェクトにスクリプトとAudioSourceコンポーネントを追加しました。

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameScript : MonoBehaviour
{
    [SerializeField] GameObject[] colliders; // Cube
    [SerializeField] Text text; // テキスト
    [SerializeField] Transform target; // Sphere

    public static bool onTriggerEnter;

    int chapter;
    int state;  
    float sec;
    Transform playerHead;
    AudioSource audioSource;

    // Start is called before the first frame update
    void Start()
    {
        chapter = 1;
        state = 1;
        text.text = "";
        playerHead = Camera.main.transform;
        target.gameObject.SetActive(false);
        audioSource = GetComponent();

        // コライダーを非アクティブ
        foreach (GameObject col in colliders)
        {
            col.SetActive(false);
        }

        switch (chapter)
        {
            case 1:

                colliders[0].SetActive(true);

                break;

            case 2:

                break;

        }
    }

    // Update is called once per frame
    void Update()
    {
        switch (chapter)
        {
            case 1:

                switch (state)
                {
                    case 1:

                        if(onTriggerEnter)
                        {
                            onTriggerEnter = false;
                            audioSource.Play();

                            colliders[0].SetActive(false); // 赤いのコライダーを非アクティブ
                            state = 2; // 次のステートへ
                        }
                        break;
                    case 2:

                        sec += Time.deltaTime;
                        text.text = sec + "";

                        
                        if (sec >= 3f)
                        {
                            sec = 0f;
                            text.text = "";
                            target.gameObject.SetActive(true);
                            state = 3; // 次のステートへ
                        }
                        break;

                    case 3:

                        RaycastHit hit;
                        Vector3 dir = target.position - playerHead.position;
                        if (Physics.Raycast(playerHead.position, dir, out hit, 20f))
                        {
                            if (hit.collider.tag == "Target")
                            {
                                float dot = Vector3.Dot(playerHead.forward, Vector3.Normalize(dir));

                                // プレイヤーがターゲットの方を見たら
                                if (dot > 0.9f)
                                {
                                    target.gameObject.SetActive(false);
                                    colliders[1].SetActive(true); // 緑のコライダーをアクティブ
                                    chapter = 2;// 次のチャプターへ
                                    state = 1;
                                }
                            }
                        }                      
                        break;
                }

                break;

            case 2:

                switch (state)
                {
                    case 1:

                        if (onTriggerEnter)
                        {
                            onTriggerEnter = false;
                            audioSource.Play();
                            colliders[1].SetActive(false); // 緑のコライダーを非アクティブ
                            state = 2;
                            text.text = "ゲーム終了";
                        }
                        break;

                    case 2:

                        sec += Time.deltaTime;

                        if (sec >= 2f)
                        {
                            SceneManager.LoadScene("New Scene");
                        }
                        break;
                }
                break;
        }
    }
}

SphereやCube、テキストはスクリプトにアタッチします。Start()でそれらを全て非アクティブにしておいて、チャプター1のときは赤いCubeだけアクティブにします。

target.gameObject.SetActive(false);

// コライダーを非アクティブ
foreach (GameObject col in colliders)
{
    col.SetActive(false);
}

switch (chapter)
{
    case 1:

        colliders[0].SetActive(true);

        break;

    case 2:

        break;

}

Update()でもチャプターとステートによって別の処理をします。まず、アクティブになっている赤いCubeに入ったときに音を鳴らして次のステートに行くようにしています。

switch (chapter)
{
    case 1:

        switch (state)
        {
            case 1:

                if(onTriggerEnter)
                {
                    onTriggerEnter = false;
                    audioSource.Play();

                    colliders[0].SetActive(false); // 赤いのコライダーを非アクティブ
                    state = 2; // 次のステートへ
                }
                break;

コライダーに入ったときにFirstPersonControllerクラスで、このスクリプトの静的メンバ変数をtrueにします。

// FirstPersonController
private void OnTriggerEnter(Collider other)
{
    if (other.tag == "Collider")
    {
        GameScript.onTriggerEnter = true;
    }
}

ステート2の間、時間をカウントしてテキストに表示します。3秒立つとSphereをアクティブにして次のステートへ行きます。

case 2:

    sec += Time.deltaTime;
    text.text = sec + "";

                        
    if (sec >= 3f)
    {
        sec = 0f;
        text.text = "";
        target.gameObject.SetActive(true);
        state = 3; // 次のステートへ
    }
    break;

ステート3では、プレイヤーの頭からSphereへレイがあたっているときに、プレイヤーの視線と、頭からSphereまでのベクトルの内積を計算して、プレイヤーがSphereの方を見たら次のチャプターへ遷移します。

case 3:

    RaycastHit hit;
    Vector3 dir = target.position - playerHead.position;
    if (Physics.Raycast(playerHead.position, dir, out hit, 20f))
    {
        if (hit.collider.tag == "Target")
        {
            float dot = Vector3.Dot(playerHead.forward, Vector3.Normalize(dir));

            // プレイヤーがターゲットの方を見たら
            if (dot > 0.9f)
            {
                target.gameObject.SetActive(false);
                colliders[1].SetActive(true); // 緑のコライダーをアクティブ
                chapter = 2;// 次のチャプターへ
                state = 1;
            }
        }
    }                      
    break;

同様に次のチャプターでも各ステートの中の処理と、遷移の条件、遷移するときの処理を書いていくとストーリーに沿って順にゲームの進行管理ができました。

case 2:

    switch (state)
    {
        case 1:

            if (onTriggerEnter)
            {
                onTriggerEnter = false;
                audioSource.Play();
                colliders[1].SetActive(false); // 緑のコライダーを非アクティブ
                state = 2;
                text.text = "ゲーム終了";
            }
            break;

        case 2:

            sec += Time.deltaTime;

            if (sec >= 2f)
            {
                SceneManager.LoadScene("New Scene");
            }
            break;
    }
    break;

コメントを残す

メールアドレスが公開されることはありません。