おもちゃラボ

Unityで遊びを作ってます

【Unity】Input Managerの使い方まとめ。PS3やPS4のコントローラの入力を取得しよう

ゲームを作った場合、PS3やPS4などのコントローラ(その他ゲームパッド)を使って検証したいことがあります。ここではUnityでPS3のコントローラの入力を取る方法を紹介します。コントローラとPCはUSBで接続しておきましょう(Bluetoothでも大丈夫です)

ワイヤレスコントローラー(DUALSHOCK 4) ジェット・ブラック(CUH-ZCT2J)

ワイヤレスコントローラー(DUALSHOCK 4) ジェット・ブラック(CUH-ZCT2J)

  • ソニー・インタラクティブエンタテインメント
Amazon

今回の記事の内容は次のようになります。

手っ取り早くコントローラの入力を取る

まずは一番簡単な方法から紹介します。普通にコントローラのキーが押されたかを調べるだけであれば、次のコードだけで十分です。

// 左十字キーが押された場合
Input.GetButtonDown("joystick button 7");

マウスやキー入力とほぼ同じコードでPS3/4のコントローラ入力を取得出来るのがわかりますね。引数にはコントローラに割り振られたボタン番号を渡します。

PS3/4のコントローラのボタン番号は、Unity Asset Storeで配布されている「Controller Tester」というアセットで簡単に調べられます。

このアセットをインポートして、「Controller Tester」→「Scene」→「Controller Test」のシーンを開いて下さい。ゲームを実行すると次のような画面が表示されるので、コントローラのボタンを押すと、対応するボタン番号のところの表示が変化します。

f:id:nn_hokuson:20171120200246g:plain

ちなみに・・・手元にあるPS3のコントローラでは次のような割当になっていました。

f:id:nn_hokuson:20171120202638j:plain:w350

Input Managerを使ってコントローラの入力を取得する

とりあえず、Input.GetButton系のメソッドを使うことで、簡単にPS3やPS4のコントローラの入力が取れることはわかりました。

ただ、デバッグのときにはキーボードのASWDキーでプレイヤを動かしたいのだけど・・・となると入力部分を全てif文で分けるか置換するか・・・これは考えたくないですね(笑)

そこで、Input Managerの登場です。Input Managerは簡単に言えばキー入力やゲームパッドの入力、タッチパネルからの入力などに名前をつける仕組みです。そして、スクリプト側ではその名前を使って入力を取得します。

f:id:nn_hokuson:20171120224625j:plain:w450

つまり、入力デバイスとスクリプトの間にInput Managerというレイヤを一層噛ますことで、異なる入力でも同じ名前で扱うことが出来るようになるのです。

ここではPS3コントローラの左十字キーと、キーボードのAキーに「Left」という名前をつけてみましょう。

Unityのメニューバーから「Edit」→「Project Settings」→「Input」を選択し、インスペクタを開いて下さい。次のように入力が並んでいるのでSizeの欄の値を一つ増やして「19」と入力し、エンターを押して下さい。

f:id:nn_hokuson:20171120224645j:plain:w300

一番下に新たな入力(Cancel)が複製されるので、次のように設定して下さい。

項目名
Name Left
Positive Button joystick button 7
Alt Positive Button a
Type Key or Mouse Button

これでPS3の左十字キー(joystic button 7)とAキーに「Left」という名前をつけることができました。

最後にスクリプトで次のように記述することで左十字キーが押されたときとAキーが押されたとき、いずれも「Left Key Pressed」という文字が表示されます。

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

public class Test : MonoBehaviour {

	void Update () {
		if(Input.GetButtonDown("Left")){
			Debug.Log("Left Key Pressed");
		}
	}
}

アナログスティックの値を取得する

アナログスティックの値も、ボタン入力とほぼ同じ方法で取得することができます。アナログスティックに関してはもともと用意されている「Horizontal」と「Vertical」の値を流用しましょう。

項目名
Name Horizontal
Type Joystick Axis
Axis X Axis
Joy Num Get Motion from all Joysticks
項目名
Name Vertical
Type Joystick Axis
Axis Y Axis
Joy Num Get Motion from all Joysticks

アナログスティックからの入力はGetAxisメソッドを使って取得できます。

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

public class NewBehaviourScript : MonoBehaviour {

	void Update () {
		float h = Input.GetAxis("Horizontal");
		float v = Input.GetAxis("Vertical");
		Debug.Log(h+","+v);
	}
}

アナログスティックを左に倒したときはGetAxisメソッドの戻り値は「-1」になり、アナログスティックを右に倒したときの戻り値は「1」になります。通常の状態では「0」が戻ってきます。

docs.unity3d.com

まとめ

InputManagerの使い方を紹介しました。InputManagerを使うことで、入力デバイスとスクリプトの間にレイヤを一層噛ますことができ、異なる入力でも同じ名前で扱うことが出来ました。

【Unity】JoyConを使ってSurvival ShooterをVR化する

f:id:nn_hokuson:20171114203601j:plain

色々な方が記事にされていますが、Nintendo SWITCHのJoyConはBLE通信を使っているのでPCやAndroidと簡単に接続することが出来ます。

www.taguchikun.com

そこで、今回はJoy-Con(ジョイコン)とGoogle Cardboardを使ってUnityの「Survival Shooter」をVR化する方法を紹介します。動画にするとこんな感じです。

f:id:nn_hokuson:20171114204640g:plain

Survival ShooterをVR化するまでの流れは次のようになります。

Joy-Conの入力をUnityで取得する

ジョイコンのデータをUnityで取得するにはInput Managerを使用します。メニューバーから「Edit」→「Project Settings」→「Input」を選択して下さい。

インスペクタの部分に入力軸が表示されるので、左右のジョイコン用に「Horizontal」と「Vertical」のデータを1組ずつ作ります。それぞれの項目を選択してから「Ctrl-D」でコピーして、それぞれ「Horizontal1」「Horizontal2」「Vertical1」「Vertical2」とリネームしてください。

f:id:nn_hokuson:20171110224457p:plain:w320

また、それぞれの軸の設定項目(Name, Axis, Joy Num)は次のように設定します。

Name Axis Joy Num
Horizontal1 6th axis(Joysticks) Joystick 1
Horizontal2 6th axis(Joysticks) Joystick 2
Vertical1 5th axis(Joysticks) Joystick 1
Vertical2 5th axis(Joysticks) Joystick 2


これでスクリプトからJoy-Conのアナログスティックの値をInput.GetAxisメソッドで受け取ることができるようになりました。このサンプルは左Joy-Conのアナログスティックの値を取得しています。

 float h = Input.GetAxis("Horizontal1");
 float v = Input.GetAxis("Vertical1");

ちなみに・・・

今回はJoy-ConのAxisを5thと6thに設定しましたが、環境によっては異なる値になることがあるようです。この場合はアセット「Controller Tester」を使うことで、Joy-ConのどのボタンがUnityのどの軸に割り振られているかを、簡単に確認できますよ。


Survival ShooterでJoy-conの入力を受け取る

Androidで実行したときにプレイヤの移動は、右ジョイコンのアナログスティック、プレイヤの回転は左ジョイコンのアナログスティックで操作できるようにしましょう。また、銃を撃つにはAボタンを使用します。

まずは、Asset Storeから「Survival Shooter」をダウンロードしましょう。

アセットがダウンロードできたら、Assets/_Complete-Game/Scripts/Player/PlayeMovement.csを開きFixedUpdateメソッドを次のように修正してください。

void FixedUpdate ()
{
        // Store the input axes.
        float h = Input.GetAxis("Horizontal1");
        float v = Input.GetAxis("Vertical1");
        float h2 = Input.GetAxis("Horizontal2");

        // Move the player around the scene.
        Move (-h, -v);

        // Turn the player to face the mouse cursor.
        //Turning ();
        transform.Rotate (0, h2*4, 0);

        // Animate the player.
        Animating (h, v);
}

ここでは、先程InputManagerで設定したHorizontalとVerticalの入力軸を使用して、Joy-Conのアナログスティックの入力を取得しています。
また、プレイヤの回転は左ジョイコンのアナログスティックの傾きに応じて、Rotateメソッドで回転しています。従来使用しているTurnメソッドはコメントアウトしています。

Androidの実機で動作確認

Androidで動作確認するためには、まずJoy-conとAndroidをBluetoothで接続する必要があります。Androidの「設定」→「Bluetooth」を選択してください。

f:id:nn_hokuson:20171111081650j:plain:w200

この状態で、ジョイコンのペアリングボタンを3秒程度押してください。Joy-conのゲージが点滅し始めると、Androidの端末にJoy-con(R)と候補が表示されるので、タッチして接続します。同様の手順で左Joy-Conも接続してください。

f:id:nn_hokuson:20171111081742j:plain:w200

現在はJoy-conとAndroidが接続が完了しても、Joy-Con側のインジゲータの点滅は消えないようです・・・これでジョイコンとAndroidがBLEで接続されたので、Survival Shooterのアプリをインストールして動くか試してみましょう!

f:id:nn_hokuson:20171114200042j:plain

ちなみに・・・現在(2017/11)の段階では、iPhone(iOS)とジョイコンはBLEで接続できない仕様になっているようです。

Google Cardboardを使ってSurvival ShooterをVR化する

せっかくSurvival ShooterがJoy-conで遊べるようになったことですので、最後にVRでも遊べるようにしてみましょう。

今回はGoogle Cardboardを使ってVR化してみましょう。Unity2017付近からVRの開発環境がUnityと統合されたため、簡単にVRのゲームを作る事ができるようになっています。

nn-hokuson.hatenablog.com

ここで変更する項目は、入力とカメラのみになります。Androidで遊ぶ場合、プレイヤの回転はJoy-Conの右アナログスティックで行っていましたが、VRの場合はジャイロで自動的に計算されるので、不要になります。PlayerMovement.csの次の行をコメントアウトしておきましょう。

void FixedUpdate ()
{
        // float h2 = Input.GetAxis("Horizontal2");
           ・・・・・
       // transform.Rotate (0, h2*4, 0);
}


いったんMain Cameraの座標と姿勢をリセットしてから、カメラがプレイヤの視点に来るように移動させて下さい。

f:id:nn_hokuson:20171114203524j:plain

また、Projectionを「Perspective」、Field of Viewを「70」に設定します。

f:id:nn_hokuson:20171114203253p:plain:w400

Survival Shooterではプレイヤの向いている方向に銃を撃ちます。VRの場合はカメラの向いている方向に銃を撃つように、プレイヤをカメラの子要素にします。ヒエラルキーウインドウでMain CameraにPlayerをドラッグ&ドロップして下さい。

f:id:nn_hokuson:20171113070312p:plain:w300

最後にUnityでAndroidビルドします。いまのままでは通常の画面で出力されてしまうので、Player SettingsでVR映像が出力されるように設定します。

メニューバーから「FIle」→「Build Settings」を選択して下さい。PlattformをAndroidに変更後、Build Settingsの画面でPlayer Settingsボタンを押します。

f:id:nn_hokuson:20171113070815j:plain:w400

Unity2017.2以降はインスペクタのXR Settingsを選択し、Virtual Reality Supportedにチェックを入れ、Virtual Reality SDKからGoogle Cardboardを選択します。 (Unity2017.1以前はOther Settingsの中にVirtual Reality Supportedの項目があります)

f:id:nn_hokuson:20171113071055p:plain:w400

最後に「Bundle identifier」を任意の文字列に修正してから、「Build Settings」ウインドウで「Build & Run」をクリックして実機にインストールしてください

f:id:nn_hokuson:20171114202957j:plain

これで、ようやくGoogle CardobardとJoy-Conを使ってSurvival ShooterをVR化することができました。長かったですね〜^^。ぜひ一度実際に遊んでみて下さい!

【Unity】Arduinoを使ってiOS・Android用の無線コントローラを作る

iOSやAndroidと通信するコントローラをつくるにはBLEを使うのが定番のようですが、Unityを使う場合、下回りがややこしくなりがちです。また、BLEモジュールも安くはない(4〜5000円)ので気軽に実験、というわけにはいきません。

www.switch-science.com

そこで、この記事ではBLEではなくインターネット無線通信(UDP通信)を使ってスマートフォンとコントローラを接続する方法を紹介したいと思います。

f:id:nn_hokuson:20171106195221j:plain

UDP通信をするにはArduinoと無線通信モジュールであるESP8266(EPS-WROOM-02)が必要になります。また、コントローラからUDP通信を使って送られてくるデータの受け側(サーバ側)はUnityを使います。構成としては次のような感じです。

f:id:nn_hokuson:20171104211453p:plain:w450

今回のコンテンツは次のとおりです。まずは無線モジュールのESP-WROOM-02のセットアップから始めましょう。

ESP-WROOM-02のセットアップ

ESP-WROOM-02を動かすためには、通信速度の設定など、いくつかの作業が必要になります。前回の記事にまとめているので、こちらを参考にして下さい。

nn-hokuson.hatenablog.com

手順としては「ESP8266のライブラリをインストール」のステップまで終わらせて下さい。

上の記事ではTCPを使ってPCとESP8266を接続しています。ただ、Unityなどで使うゲームコントローラを作る場合、シビアな通信速度が求められるため、TCPではなくUDPを使うことになります。TCPとUDPの違いは、次のサイトが参考になります。

gihyo.jp

そこで、今回はESP8266とUnityの間でUDP通信するようなシステムを作っていきます。

無線の送信回路を作る

今回はコントローラ側(Arduino側)のボタンを押すと、その情報が無線経由で送信され、Unityでその情報を受け取るという構成です。

ESP8266を使った回路図は次のようになります。前回の回路に追加する形で4番ポートにボタンを接続しています。プルアップ抵抗はArduino側でソフトから指定出来るので不要です。

f:id:nn_hokuson:20171104203931j:plain:w500

ちなみに・・・この回路図はTinkercadというサイトを使って作っています。もともとはAutodeskの「123d circuits」だったものが進化(?)してTinkercadになったようです。回路図作成だけでなく、シミュレーションもできるのがすごい。

www.tinkercad.com

ArduinoでUDP送信プログラムを作る

ESP-WROOM-02を使った基本的な無線通信プログラムは前回と同じです。ESP8266用ライブラリのインストールなどは、前回の記事を参考にして下さい。

次のプログラムを作成し、Arduinoに書き込んで下さい。

#include "ESP8266.h"
#include <SoftwareSerial.h>

// Wi-Fi SSID
#define SSID "Buffalo-G-xxxx"
#define PASSWORD "xxxxxxxxxxxxxx"

#define HOST_NAME "192.168.11.23"
#define HOST_PORT   5432
 
SoftwareSerial mySerial(2, 3);
ESP8266 wifi(mySerial);

/**
 * 初期設定
 */
void setup(void)
{
  pinMode(4, INPUT_PULLUP);
  Serial.begin(9600);

  if (wifi.setOprToStationSoftAP()) {
    Serial.println("to station ok");
  } else {
    Serial.println("to station error");
  }

  if (wifi.joinAP(SSID, PASSWORD)) {
    Serial.println("connect success");
  } else {
    Serial.println("connect error");
  }

  if (wifi.disableMUX()) {
    Serial.println("disable mux success");
  } else {
    Serial.println("disable mux error");
  } 

  if (wifi.registerUDP(HOST_NAME, HOST_PORT)) {
      Serial.print("register udp ok\r\n");
  } else {
      Serial.print("register udp err\r\n");
  }   
}

void loop(void)
{
    int val = digitalRead(4); 

    char message[8];
    message[0] = ( val == LOW ) ? '0' : '1';
    
    wifi.send((const uint8_t*)message, strlen(message));
    delay(33);
}

このプログラムではUDP通信をするためにregisterUDP関数を使っています。setup関数の中でUDP通信の準備をし、loop関数の中でHOST_NAMEで指定したIPアドレスのHOST_PORTに向けて毎回データを送信しています。

送信するデータはボタンが押されている場合は「0」、ボタンが押されていない場合は「1」を送信しています。UDP通信で送信するデータは文字列なので、ボタン数が増えたときは良い感じに圧縮して送ればOKです。

これで、ESP-WROOM-02とUnityでUDP通信する仕組みのうち、ハード側の部分が完成しました。次はUnityでデータを受け取る部分を作っていきます。

UnityでUDP通信のデータを受信する

ArduinoからUDP通信で送られてくるデータを受け取るため、Unity上にUDPサーバを作成します。

プロジェクトウィンドウで右クリックして「Create」→「C# Script」を選択し、UDPServerという名前で保存して下さい。

保存できたらUDPServerに次のプログラムを入力して下さい。

using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class UDPServer : MonoBehaviour
{
    int LOCA_LPORT = 5432;
    static UdpClient udp;
    Thread thread;
    public static string message = "";

    void Start ()
    {
        udp = new UdpClient(LOCA_LPORT);
        thread = new Thread(new ThreadStart(ThreadMethod));
        thread.Start(); 
    }

    void OnApplicationQuit()
    {
        thread.Abort();
    }

    private static void ThreadMethod()
    {
        while(true)
        {
            IPEndPoint remoteEP = null;
            byte[] data = udp.Receive(ref remoteEP);
            string text = Encoding.ASCII.GetString(data);
            message = text;
            Debug.Log(text);
        }
    } 
}

UnityでUDPサーバを作る方法はこちらの記事を参考にさせていただきました。

qiita.com

スレッドの中でUDPで送られてくるデータを監視しておき、データが来た場合は文字列型に変換してmessage変数に代入しています。

スクリプトを保存できたら、空のオブジェクトにスクリプトをアタッチしてから実行してみて下さい。

f:id:nn_hokuson:20171104212620j:plain

コンソールに「0」のデータが表示されると思います。Arduino側のボタンを押すとコンソールの表示も「1」になります。

f:id:nn_hokuson:20171104213059g:plain

無線コントローラのボタンが押されたらUIを書き換える

最後はおまけです(笑)送られてきたデータはUDPServerクラスのstatic変数(message)に代入しているので、他のクラスから使いたい場合はUDPServer.messageでアクセスできます。

例えば、Arduino側のボタンを押したときに、UnityでUIを書き換えたい場合は、次のようなプログラムを作成して下さい。

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

public class MessageController : MonoBehaviour {

    public Text message;

    void Update () 
    {        
        if (UDPTest.message.Length != 0)
        {
            message.text = (UDPTest.message [0] == '1') ? "OFF" : "ON";
        }
    }
}

Text変数にはインスペクタからUIのTextを指定します。Updateメソッドの中ではメッセージが来ているか(長さが0ではないか)を確認し、そのデータをもとに表示する文字列を切り替えています。

MessageControllerスクリプトが作成できたらcanvasオブジェクトなどにアタッチして、インスペクタからMessage変数の欄にTextオブジェクトをドラッグ&ドロップします。

f:id:nn_hokuson:20171104213847j:plain

実行すると次のようにボタンのON/OFFによってUnityの表示も変化します。

www.youtube.com

まとめ

今回はESP-WROOM-02(ESP8266)を使ってUnityとUDP通信する方法を紹介しました。Unityで使う無線コントローラを作りたい場合には、役に立つ・・・ような気がします。