てぃってぃの楽しい副業日記!

ワイと嫁(てぃってぃ)夫婦の、副業、育児、プログラミング、筋トレ、ゲームを綴ったブログです☆

【ゲームプログラミング】その4. Unityで仮想コントローラーを再現する①

こんばんは、てぃってぃ(嫁)の夫です。

「スマホでローグライク開発」第4回です。
今回の記事は「Unityで仮想コントローラーを再現する①」です。①にしているのは、コントローラー実装も作業が多くて、段階的に実装していくからでございます。
今までの記事はここからどうぞ!

www.kt2525family.com


方針

第1回の「ポリシー」で書いた、こだわりポイント、操作性。
今までプレイしてきたすべてのゲームを振り返ってみても、一番操作しやすかったと思うのは、ズバ抜けて

「ニンテンドー64のコントローラー」

なんですよね。あれほど操作性が快適だったコントローラーは他に知らないです。シンプルにして最強という感じ。あの心地よい操作性をスマホで再現するのが目標です。うまくいけばローグライクだけじゃなくていろいろ使えそうな気もしますしね。

まずは十字キー。ローグライクの場合、上下左右と斜め方向の計8方向に進める必要があります。そしてそれが「快適」でないとゲームが楽しめません。ただし、8方向を同時に全て表示するのは、スマホで触る以上、押し間違えによるリスクがありそうです。なので、以下のようなGUIを目標にします。

  1. 基本は十字キー(上下左右)を表示
  2. 「R」ボタンを押している間は斜めのみ表示
  3. 慎重さが必要ないとき用のアナログスティックも用意

上記踏まえたうえで、開発途中ですが、現状こんな感じですね。アナログスティックは移動キーの真ん中に配置する予定です。
(ほかにも色々突っ込みたいところがあるかもしれませんが、左下のコントローラーのみ見てくださいw)

Unity仮想コントローラー
Unity仮想コントローラー

実装メモ

以下、実装方法のメモ書きです。

① 8方向の画像を用意する

こちらは最終的にきれいなフリーの素材を使用するか、自作で頑張るかはわからないですが、ひとまずてきとーに作成したこちらのボタンを使用して、それぞれを回転させて8方向のボタンとして使用します。とりあえずね。 (あらためて見ると、ひどいクオリティw なんか上のほう欠けとるし…)

② 画像をスプライトに登録する

画像をスプライト用フォルダにコピペすればOK。詳細は割愛。

③ UIボタンを作成して、画像を登録する

UIボタンを作成して、画像を登録(スプライト用フォルダからUIボタンの画像として登録)します。
まずはUIボタンの作成から。Hierarchy上を右クリックして、「UI」→「Button」を選びます。

f:id:kktt0312:20200219143325p:plain

作成したUIボタンのImageソースを作成したボタン画像にします。

Unity 仮想コントローラー

これで、1ボタン完成です。この手順をそれぞれ上下左右の4枚、斜めの4枚用意して、合計8枚にします。
イメージはこんな感じ。


④ 全画像に共通するクラスを作成する

ここからがオブジェクト指向が絡んできます。
ボタンに共通するクラスを先に作成しておき、画面上の8個のボタン(インスタンス)は、ボタン毎の役割(左とか右上とか)を設定して、その設定に応じた動きをさせるという感じ。

少し悩みましたが、ボタン毎に関数を作りました。こちらが全ボタン共通のクラスです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class ButtonScript : MonoBehaviour
{
    private bool isRMode = false;
    [SerializeField] GameObject xPanel;
    [SerializeField] GameObject dPanel;
    private List<KeyCode> pressingKeyCode = new List<KeyCode>();
    :
    public void OnLeftButton()
    {
        pressingKeyCode.Add(KeyCode.LeftArrow);
    }
    : 
    :// (全8方向分作る)public void Update()
    {
        lock (this)
        {
            if (pressingKeyCode.Count > 0)
            {
                GameObject player = GameObject.Find("Player");
                if (player != null)
                {
                    ExecuteEvents.Execute<IMouseDownEvent>(player, null, (handler, eventData) => handler.OnMouseDown(pressingKeyCode));
                }
            }
        }
    }        


「OnLeftButton()」と同じ感じで、8方向それぞれのイベント用関数を自作します。
「xPanel」「dPanel」はそれぞれ上下左右、斜め方向用のパネルです。「Rボタン押下時は斜め方向のみ表示」を実現するために必要になります。後ほど詳細を記載します。
「IMouseDownEvent」は自作インターフェース(抽象クラス)です。イベントの受け側は「IMouseDownEvent」を継承して、イベントリスナーとなる「OnMouseDown()」を実装することで、ボタン押下のイベントが受け取れます。
インターフェースのソースコードはこんな感じ。

public interface IMouseDownEvent : IEventSystemHandler
{
    void OnMouseDown(List<KeyCode> keyCode);
}


⑤ 各ボタンに共通クラスをアタッチする

先ほど作ったクラスを、各8種類のボタンにアタッチする。このアタッチっていう概念がUnityの世界です。
※たぶん、オブジェクト指向でいうところの基底クラスに設定しているということに当たる(オブジェクトを先に作ってそのあとに基底を設定しているから、順番が逆だけど)。なので、先ほどのクラスはボタンの基底クラスを継承しているクラスでないといけない。
アタッチできたら、Unityの設定にて、ボタン押下時(PointerDown)・離した時(PointerUp)のイベントハンドラが設定できます。そこで、ボタン毎に、呼ぶ関数を登録できるので、先ほど共通クラスに実装した各種イベントハンドラ用関数を登録することになります。(左ボタンだったら「OnLeftButton()」、という風に)

f:id:kktt0312:20200219144012p:plain

これで、めでたく各種ボタン毎にイベントを飛ばすことができるようになります。
あとはプレイヤー側やメニュー側のオブジェクトに渡すことで、各種操作が可能になりますね。
ということで、次回は、本ボタンを使って実際にキャラクター移動をするところまで実装しようと思います。

⑥ 上下左右、斜め方向の4枚ずつでcanvasにまとめる

あとは、「Rボタンを押している間だけ斜めを表示」を実装します。上下左右、斜め方向で、2つのcanvasを作成します。Hierarchy上でcanvasを2つ作成したら、4枚ずつのボタンをまとめてドラッグ&ドロップでOKです。



⑦ 斜め方向のcanvasはデフォルト非表示、R押下時のみ表示にする

あとは、Rボタンを実装し、Rボタン押下中のみ斜め方向のcanvasを表示する用に実装するだけ。
Rボタンのイベントハンドラの実装は移動ボタンと全く同じなので割愛します。
canvas毎の表示/非表示は、以下のように「SetActive」で切り替えられます。

    public void OnRButton()
    {
        if (!isRMode)
        {
            pressingKeyCode.Clear();
            isRMode = true;
        }

        xPanel.SetActive(true);
        dPanel.SetActive(false);
    }


これでめでたく、冒頭でも乗せた、以下画像のような動きをさせることができます。 めでたしめでたし(簡単そうに見えますが、だいぶはまりました…w)。


あとがき

うん。難しいけど超楽しい。(笑)
Androidの開発環境を作るのだけで丸1日以上かかってきて、GUI系の知識がなかったからUnityがうまく使えなかったけど、だんだん慣れてきて、ようやくC#の領域に入ってきたという感じです(うまく言えないけど伝わるかな)。
ただ、C#、というかオブジェクト指向の知識がないと、たぶんUnityの世界の話なのか、Androidの世界の話なのか、C#の世界の話なのかが訳わからなくなってお手上げになっていただろうな。このあたりは日ごろの仕事に感謝。

次回あたりはいよいよ動くものをお見せする予定でございます。

ご購読、ありがとうございました。