【Unity2D】選択肢の実装

Unity2D

Unity2D ARPG

メッセージ送りを実装できたので今度は応用として選択肢を実装してみましょう。

選択肢はウィンドウとは別に新たなウィンドウを開いて「はい」「いいえ」の選択肢を、
プレイヤーに選ばせるといった仕組みです。

UIのカーソル操作の仕組みも一緒に覚えていきましょう。

選択肢ウィンドウを作る(メッセージウィンドウ使いまわしOK

まずは選択肢ウィンドウを作ります。

メッセージウィンドウを選択肢ウィンドウに使ってもいいですし、
ドラクエのように専用ウィンドウを出しても大丈夫です。

今回は時間の都合上メッセージウィンドウを活用していきます。

つまりメッセージが出た後、メッセージをクリアして選択肢を出します。

この時、はいといいえだけだと意味が分からなくなるので、
選択肢を見て何をしようとするかわかる文章にします。

メッセージイベントが5だったので、選択肢は6にしましょう。

メッセージイベントの後に6で始まるイベント文字列を追加します。

0個目の6は選択肢イベントで、1個目は選択文字列にします。

選択文字列は「"選択肢1\n選択肢2\n選択肢3"」とすれば自動的に改行してくれます。

ここで注意してほしいのが、ブログ上だと「円マーク記号」になっているかもしれません。

調べているとバックスラッシュじゃないと改行されないという情報もありました。

意味合い的には同じなんですけど、Unityでは違う文字列扱いされてるのかもしれません。

私の環境ではバックスラッシュで記載されるので確認が取れませんが、
もし改行されない場合は円マークかバックスラッシュかの確認をしてください。

それでは選択肢イベントを作りましょう。

選択肢用のカーソルはスプライトで用意してもいいですし、Unityのデフォルトイメージを利用しても構いません。

私は面倒だったので一旦UnityのImageを利用して8x8のサイズを45度回転させてひし形にしてみました。

ハンマー用イベントのState_Eventスクリプトの配列数を増やし「6,2,0」を配列1個目にいれます。

文字列データベースは以下のようになりました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Message_db : MonoBehaviour
{
    private static List<string> _message = new List<string> {
    // 0個目はダミーメッセージ
    "ダミーです",
    "ハンマーが壁に掛けられている",
    " ハンマーを取る\n ハンマーを取らない",
  };

    public List<string> Message
    {
        get
        {
            return Message_db._message;
        }
    }
}

文字列の先頭に全角空白をいれてますが、カーソルがくるのでずらしています。

選択肢の時だけテキストのX座標をずらしてもいいんですが、このやり方で問題が出たら考えようと思います。

次に6で発動するイベントを用意しましょう。

イベント部分だけ抽出してみます。

// 選択肢イベント
case 6:
    // ウィンドウを開く
    _State.setSystemStatus(1f, 1);
    // メッセージを更新
    _State.setSystemStatus(ev[1], 5);
    // 選択肢モードオン
    _State.setSystemStatus(1f, 6);
    _isSelectMode = true;
    // セレクト数を決める
    _selectCount = ev[2];
    // 選択肢を1にする
    _State.setSystemStatus(1f, 7);
    // 選択肢は問答無用で待機フラグをON
    _isPressKey = true;
    break;

ウィンドウを開きメッセージを更新(選択肢メッセージを表示)して、選択モードをONにします。

そしてこのスクリプトで使うフラグをわかりやすくローカル変数のフラグをONにしました。

次にセレクト数を決めます。

ウィンドウサイズ的に1~3の選択肢しかできませんね。

スクロールを作れば増やせますが、面倒なので割愛です。

次に選択肢の位置を0から1にすることで1つ目の選択肢にカーソルを合わせるようにしています。

選択肢イベントが終わるときは0に戻しておくとよいでしょう。

最後にメッセージ送りの待機フラグを付けましょう。

そして待機中の処理のところに選択肢の処理を入れます。

// イベント待機フラグがONであれば

// 選択肢であれば
if (_isSelectMode)
{
    // カーソル移動
    if (_Controller.getPushedKeyCount(10) == 1)
    {
        switch (_State.getSystemStatus[7])
        {
            case 1:
                if (_selectCount >= 1)
                {
                    _State.setSystemStatus(2, 7);
                }
                break;
            case 2:
                if (_selectCount <= 2)
                {
                    _State.setSystemStatus(1, 7);

                }
                else
                {
                    _State.setSystemStatus(3, 7);
                }
                break;
            case 3:
                _State.setSystemStatus(1, 7);
                break;
        }

    }
    else if (_Controller.getPushedKeyCount(11) == 1)
    {
        switch (_State.getSystemStatus[7])
        {
            case 1:
                if (_selectCount == 3)
                {
                    _State.setSystemStatus(3, 7);
                }
                else
                {
                    _State.setSystemStatus(2, 7);
                }
                break;
            case 2:
                if (_selectCount <= 2)
                {
                    _State.setSystemStatus(1, 7);
                }
                break;
            case 3:
                _State.setSystemStatus(2, 7);
                break;
        }
    }
}
// Aボタンカウントが1であればイベント待機フラグを折る
if (_Controller.getPushedKeyCount(0) == 1)
{
    _isPressKey = false;
    Debug.Log("メッセージ送り");
}

選択肢の数でカーソルの位置を分岐させる処理を書きました。

これを動かすために十字キーの処理を追加します。

Controll_Managerはこうなりました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class Controller_Manager : MonoBehaviour
{
    // キー入力順番配列
    protected static List<KeyCode> _PushedKeyList = new List<KeyCode>();
    // _PushedKeyList用Getter
    public List<KeyCode> PushedKeyList
    {
        get
        {
            return Controller_Manager._PushedKeyList;
        }  //取得用
    }


    protected static List<KeyCode> _PushedArrowKeyList = new List<KeyCode>();
    public List<KeyCode> PushedArrowKeyList
    {
        get
        {
            return Controller_Manager._PushedArrowKeyList;
        }  //取得用
    }




    // キーカウント配列取得
    public int[] PushedKeyCount
    {
        get { return Controller_Manager._pushedKeyCount; }
    }
    // キーカウント配列の1つを取得
    public int getPushedKeyCount(int i)
    {
        return Controller_Manager._pushedKeyCount[i];
    }

    // キーカウント配列インクリメント
    protected void pushedKeyCountInc(int i)
    {
        if (i > -1)
        {
            if (Controller_Manager._pushedKeyCount[i] < 256)
            {
                // カウントは255まで
                Controller_Manager._pushedKeyCount[i]++;
            }
        }
    }
    // キーカウント配列リセット
    protected void pushedKeyCountReset(int i)
    {
        if (i <= -1)
        {
            // マイナスの値であれば全キーカウントリセット
            for (int j = 0; j < Controller_Manager._pushedKeyCount.Length; j++)
            {
                Controller_Manager._pushedKeyCount[j] = 0;
            }
        }
        else
        {
            // -1以下でなければ指定配列のみリセット
            Controller_Manager._pushedKeyCount[i] = 0;
        }
    }

    // キーカウント処理
    protected void selectPushedKeyCount(List<KeyCode> keyList)
    {
        for (int i = 0; i < keyList.Count; i++)
        {
            switch (keyList[i])
            {
                case KeyCode.A:
                    pushedKeyCountInc(0);
                    break;
                case KeyCode.S:
                    pushedKeyCountInc(1);
                    break;
                case KeyCode.D:
                    pushedKeyCountInc(2);
                    break;
                case KeyCode.F:
                    pushedKeyCountInc(3);
                    break;
                case KeyCode.G:
                    pushedKeyCountInc(4);
                    break;
                case KeyCode.Z:
                    pushedKeyCountInc(5);
                    break;
                case KeyCode.X:
                    pushedKeyCountInc(6);
                    break;
                case KeyCode.C:
                    pushedKeyCountInc(7);
                    break;
                case KeyCode.V:
                    pushedKeyCountInc(8);
                    break;
                case KeyCode.B:
                    pushedKeyCountInc(9);
                    break;
                case KeyCode.DownArrow:
                    pushedKeyCountInc(10);
                    break;
                case KeyCode.UpArrow:
                    pushedKeyCountInc(11);
                    break;
                case KeyCode.LeftArrow:
                    pushedKeyCountInc(12);
                    break;
                case KeyCode.RightArrow:
                    pushedKeyCountInc(13);
                    break;
                default:
                    break;
            }
        }
    }

    // キーカウントリセット処理
    protected void selectPushedKeyCountReset(KeyCode keyCode)
    {
        switch (keyCode)
        {
            case KeyCode.A:
                pushedKeyCountReset(0);
                break;
            case KeyCode.S:
                pushedKeyCountReset(1);
                break;
            case KeyCode.D:
                pushedKeyCountReset(2);
                break;
            case KeyCode.F:
                pushedKeyCountReset(3);
                break;
            case KeyCode.G:
                pushedKeyCountReset(4);
                break;
            case KeyCode.Z:
                pushedKeyCountReset(5);
                break;
            case KeyCode.X:
                pushedKeyCountReset(6);
                break;
            case KeyCode.C:
                pushedKeyCountReset(7);
                break;
            case KeyCode.V:
                pushedKeyCountReset(8);
                break;
            case KeyCode.B:
                pushedKeyCountReset(9);
                break;
            case KeyCode.DownArrow:
                pushedKeyCountReset(10);
                break;
            case KeyCode.UpArrow:
                pushedKeyCountReset(11);
                break;
            case KeyCode.LeftArrow:
                pushedKeyCountReset(12);
                break;
            case KeyCode.RightArrow:
                pushedKeyCountReset(13);
                break;
            default:
                break;
        }
    }
    protected virtual void Update()
    {
    }


}

あとはState_PlayerのStart関数にシステムステートの要素を追加しておきましょう。

        // システムステータス設定
        _systemStatus.Add(0); // ウィンドウ可視状態
        _systemStatus.Add(0f); // ウィンドウアルファ値
        _systemStatus.Add(1f); // ウィンドウR値
        _systemStatus.Add(1f); // ウィンドウG値
        _systemStatus.Add(1f); // ウィンドウB値
        _systemStatus.Add(0f); // メッセージ
        _systemStatus.Add(0f); // カーソルの可視状態
        _systemStatus.Add(0f); // 選択肢状態 0は選択肢モードではない 1~3選択肢状態

これ、forを使ってきれいに書き直せそうですが面倒なので最適化は後回しです。

あとは_objectStateSystemスクリプトも追記しましょう。

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

public class _ObjectStateSystem : MonoBehaviour
{

    // オブジェクトタイプ
    [SerializeField]
    private string systemObjectType = "";

    // 0 ウィンドウの可視状態
    private bool _windowVisible = false;
    // 1 ウィンドウの透明度
    private float _windowAlpha = 0f;

    // 2 ウィンドウカラーR
    private float _windowRed = 0f;
    // 3 ウィンドウカラーG
    private float _windowGreen = 0f;
    // 4 ウィンドウカラーB
    private float _windowBlue = 0f;

    // 5メッセージ
    private float _message = 0f;

    // 6 選択肢の状態
    private bool _cursorVisible = false;


    // ウィンドウのカラー
    private Vector3 _windowColor = new Vector3();

    // 

    // ステータスオブジェクト
    private State_Player _State;
    // 自身
    private SpriteRenderer _SelfSprite;
    private Text _SelfText;
    private GameObject _SelfCursor;

    // メッセージDBオブジェクト
    private Message_db msgDB;

    void Start()
    {

        _State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();
        _SelfSprite = this.GetComponent<SpriteRenderer>();
        _SelfText = this.GetComponentInChildren<Text>();
        _SelfText.text = "";
        _SelfCursor = GameObject.FindGameObjectsWithTag("Cursor")[0];

        // メッセージDB取得
        msgDB = GameObject.FindGameObjectsWithTag("MSGDB")[0].GetComponent<Message_db>();

    }
    void Update()
    {
        switch (systemObjectType)
        {
            case "window":
                _windowUpdate();
                _selectUpdate();
                break;
            default:
                break;
        }


    }

    // ウィンドウ
    private void _windowUpdate()
    {
        // ウィンドウの可視を監視
        if (_State.getSystemStatus[0] <= 0f)
        {
            _windowVisible = false;
        }
        else
        {
            _windowVisible = true;
        }

        // ウィンドウの透明度を監視
        if (_windowAlpha != _State.getSystemStatus[1])
        {
            _windowAlpha = _State.getSystemStatus[1];
            _SelfSprite.color = new Color(_SelfSprite.color.r, _SelfSprite.color.g, _SelfSprite.color.b, _windowAlpha);
        }


        // カーソルの可視を監視
        if (_State.getSystemStatus[6] <= 0f)
        {
            _cursorVisible = false;
        }
        else
        {
            _cursorVisible = true;
        }

        // カーソルの状態変更
        if (_cursorVisible)
        {
            switch ((int)_State.getSystemStatus[7])
            {
                case 1:
                    _SelfCursor.transform.localPosition = new Vector3(_SelfCursor.transform.localPosition.x, 44f, 0);
                    break;
                case 2:
                    _SelfCursor.transform.localPosition = new Vector3(_SelfCursor.transform.localPosition.x, 26f, 0);
                    break;
                case 3:
                    _SelfCursor.transform.localPosition = new Vector3(_SelfCursor.transform.localPosition.x, 9f, 0);
                    break;
            }
            _windowAlpha = _State.getSystemStatus[1];
            _SelfSprite.color = new Color(_SelfSprite.color.r, _SelfSprite.color.g, _SelfSprite.color.b, _windowAlpha);
        }



        // メッセージを更新
        if (_message != _State.getSystemStatus[5])
        {
            _message = _State.getSystemStatus[5];
            _SelfText.text = msgDB.Message[(int)_message];
        }


    }

}

このあたりで混乱して1日使い果たしてしまいました。

おそらく当ブログのこの連載記事を見ている人、ちゃんとついていけてないと思います。

私も自分の書いたコードについていくのが必至ですから(笑)

もともとWeb系のスクリプトをメインに書いていたので、
スコープのきっちりした言語は、値渡しの点でかなり混乱してきますね。

役割別にソースコードを分けてきてはいるのですが、時間の関係上少しずつ乱れが生じてきている気がします。

とりあえず体験版を作り終えたらソースコード整理していきます。

そしてこの間にブルスクが多発してUnityのエディタ設定が破壊されてしまって、
一瞬ゲームもぶっ壊れたのかと思ってかなり焦りました。

バックアップは常に取っておかないといけませんね……

これで基本的なシステムはできました。

あとは謎解きと戦闘作りが主軸になってきます。

見た目的なものは後にしてとりあえず反転とかを駆使し手それらしく見えるようにします。

それでは!