この記事は「Unity アセット真夏のアドベントカレンダー 2019 Summer!」10日目の記事です。
はじめに
今回はuGUIベースのバーチャルパッド(Joystick)を実現できる「Joystick Pack」の使い方と応用を紹介しようと思います。
Joystick Packの紹介
Joystickの種類
4種類の挙動をするJoystickが用意されています
Fixed Joystick
Floating Joystick
設定されたRectTransformの範囲内ならどこからでも使えるJoystickです。
Dynamic Joystick
Floating Joystickの機能に加え、動かした方向に基準が動いていくJoystickです。
Variable Joystick
上記3つのJoystickを動的に切り替えることができるJoyStickです。ユーザー設定に応じて切り替えるような使い方が出来そうです。(ScriptからはVariableJoystick.SetMode(JoystickType)
で切替可能)
デザインの種類
計24種類のデザインが予め用意されています。
全方位用背景(6種類)
ハンドル(6種類)
横方向用背景(6種類)
縦方向用背景(6種類)
設定項目
共通で動作に関する設定が出来ます。
- Handle Range
- ハンドルが動く範囲
- 背景画像の半径を1としたスケール
- Dead Zone
- ハンドルが動くまでの閾値
- 背景画像の半径を1としたスケール
- Axis Options
-
Both
: 全方位 -
Horizontal
: 縦方向 -
Vertical
: 横方向
-
- Snap X
-
true
の場合、0か1を返す
-
- Snap Y
-
true
の場合、0か1を返す
-
使い方
参照を取得してfloat
かVector2
として受け取る。
using UnityEngine;
public class Test : MonoBehaviour
{
[SerializeField] Joystick joystick = null;
void Start()
{
float horizontal = joystick.Horizontal;
float vertical = joystick.Vertical;
Vector2 direction = joystick.Direction;
}
}
応用①: 固定っぽいのに実際はどこからでも操作できるJoystick
画面下部に表示されているが、実際にはタップした座標が基準になっています。
「アーチャー伝説」などで使われている方式です。特徴として、ユーザーに操作しやすい領域を示しつつも、実際の操作時は方向だけ意識すれば思ったとおりの操作ができます。
実装
全てのJoystickの親クラスになっているJoystick
を継承して新たなクラスを作ります。
using UnityEngine;
using UnityEngine.EventSystems;
public class FixedFloatingJoystick : Joystick
{
Vector2 InitPos = Vector2.zero;
protected override void Start()
{
InitPos = background.anchoredPosition;
base.Start();
}
public override void OnPointerDown(PointerEventData eventData)
{
background.anchoredPosition = ScreenPointToAnchoredPosition(eventData.position);
background.gameObject.SetActive(true);
base.OnPointerDown(eventData);
}
public override void OnPointerUp(PointerEventData eventData)
{
background.anchoredPosition = InitPos;
base.OnPointerUp(eventData);
}
}
応用②: Joystickの位置をRectTransformで設定しやすくする
とても便利な「Joystick Pack」ですが、少しだけ不満がありました。それはJoystickの位置の調整が左下のアンカー基準でしか出来ないことです。(そういう実装になっているため)
左下アンカーでどういう時に困るかというと、例えば下記のように画面下部中央に設置した場合です。左はiPhone XSに合わせたサイズですが、iPhone 4のCanvasサイズに変えると基準が中央からずれてしまうことが分かります。
対策として、継承先クラスで実行時にアンカー位置を修正する処理を入れてやりました。
protected override void Start()
{
background.SetAnchorWithKeepingPosition(0, 0);
base.Start();
}
SetAnchorWithKeepingPosition
は下記の記事で紹介している拡張メソッドです。これによって、エディタ上では任意のアンカーで位置の調整ができるようになりました。
uGUIで座標を変えずにPivotとAnchorの値を変えるための拡張メソッド(RectTransform) - Qiita
応用③: 入力開始・終了のタイミングを通知する
入力開始と終了のタイミングで処理を挟みたい場合があったので、通知機能を実装してみました。
使い方
Joystickが押されたタイミングと離されたタイミングで、以下の様に通知が飛んできます。
[SerializeField] NotifableJoystick joystick = null;
void Start()
{
joystick.PressedDown.Subscribe(_ =>
{
Debug.Log("押された");
}).AddTo(gameObject);
joystick.PressedUp.Subscribe(_ =>
{
Debug.Log("離された");
}).AddTo(gameObject);
}
実装
「UniRx」を使ってイベントを実装してみました。同様にJoyStick
を継承して拡張しています。
using UnityEngine;
using UnityEngine.EventSystems;
using UniRx;
using System;
public class NotifableJoystick : Joystick
{
public IObservable<Unit> PressedDown { get { return PressedDownSubject.AsObservable(); } }
Subject<Unit> PressedDownSubject = new Subject<Unit>();
public IObservable<bool> PressedUp { get { return PressedUpSubject.AsObservable(); } }
Subject<bool> PressedUpSubject = new Subject<bool>();
bool isPressing = false;
public override void OnPointerDown(PointerEventData eventData)
{
PressedDownSubject.OnNext(Unit.Default);
isPressing = true;
base.OnPointerDown(eventData);
}
public override void OnPointerUp(PointerEventData eventData)
{
if (isPressing)
{
PressedUpSubject.OnNext(true);
isPressing = false;
}
base.OnPointerUp(eventData);
}
protected override void HandleInput(float magnitude, Vector2 normalised, Vector2 radius, Camera cam)
{
base.HandleInput(magnitude, normalised, radius, cam);
if (magnitude >= DeadZone && isPressing)
{
PressedUpSubject.OnNext(false);
isPressing = false;
}
}
}
最後に
「Joystick Pack」はuGUIベースで使えて、しかも無料の素晴らしいアセットです。この記事がぜひ参考になると嬉しいです。
今回の記事で紹介したコードのライセンスは、特に記載がない限りCC0です。