画面上で回せるハンドルを作る
はじめに
スクリーン上でくるくると回せるハンドルを実装します。マウスで操作できます。
エディターでの作業
キャンバス上にハンドルとなるImageを作成します。
そのハンドルにスクリプトとEventTriggerを取り付けておきます。
EventTriggerのDragイベントにOnDrag関数を、
BeginDragイベントにOnBeginDrag関数をそれぞれ設定する。
これはハンドル上でドラッグしたときに呼び出されます。
スクリプト
using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.UI; public class HandleController : MonoBehaviour { const float HALF_ROUND = 180; const float ROUND = 360; float _handleUpperLimit = 360 * 8; float _lowerLimit = 0; float _aimUpperLimit = 360; // 回転を始めた角度を基準に現在ハンドルが回転した角度 float _currentAngle = 0; // 回した直前の角度と回転方向 float _beforeAngle = 0; // ハンドルがいままで回転した角度(総回転角) [SerializeField] float _totalAngle = 0; void Start() { // ハンドルの角度の初期設定。 var rot = transform.eulerAngles; rot.z = ClampTotalAngle(0) % ROUND; transform.eulerAngles = rot; } // 角度差を制限内で総回転角に加算 float ClampTotalAngle(float dt) { return Mathf.Clamp(_totalAngle + dt, _lowerLimit, _handleUpperLimit); } // ハンドルの回し始めを直前の角度にする public void OnBeginDrag() { _beforeAngle = GetMouseAngle(); } // 回した直前の角度と現在の角度との差を総回転角に代入する public void OnDrag() { var rot = transform.eulerAngles; _currentAngle = GetMouseAngle(); // 角度差を計算 float deltaAngle = _currentAngle - _beforeAngle; deltaAngle = HALF_ROUND >= Mathf.Abs(deltaAngle) ? deltaAngle : -Mathf.Sign(deltaAngle) * (ROUND - Mathf.Abs(deltaAngle)); // 角度差を加算して角度制限 _totalAngle = ClampTotalAngle(deltaAngle); // ハンドルの更新 rot.z = _totalAngle % ROUND; transform.eulerAngles = rot; // 現在の角度をキャッシュ _beforeAngle = _currentAngle; } // ハンドルからみたマウスの位置の角度を返す float GetMouseAngle() { var mousePos = Vector3.zero; // スクリーン座標からワールド座標に変換 RectTransformUtility.ScreenPointToWorldPointInRectangle( transform as RectTransform, Mouse.current.position.ReadValue(), Camera.main, out mousePos); var dt = mousePos - transform.position; // ハンドルから見たマウスの角度(値が-180~180の間になってる) var deg = Mathf.Atan2(dt.y, dt.x) * Mathf.Rad2Deg; // 360°に変換 var mouseAngle = deg < 0 ? deg + 360 : deg; return mouseAngle; } /// <summary> /// プレイヤーが狙う角度を返す /// </summary> /// <returns>角度</returns> public float AimAngle() { var angle = (_totalAngle / _handleUpperLimit) * _aimUpperLimit; return angle; } }
説明
ハンドル上でドラッグされてる間、ハンドルを基準としたマウスの角度をGetMouseAngle関数で取り続けます。
ハンドルを回しているのはOnDrag関数です。
まず現在フレームと前フレームの角度差を計算します。
// 角度差を計算 float deltaAngle = _currentAngle - _beforeAngle; deltaAngle = HALF_ROUND >= Mathf.Abs(deltaAngle) ? deltaAngle : -Mathf.Sign(deltaAngle) * (ROUND - Mathf.Abs(deltaAngle));
ここの処理で角度差は180°以下だとみなしています。それ以上の場合は逆方向に回転したと処理します。
こうすることでどちらに回転しても角度差をそのまま利用できます。基準のX軸をまたぐ時には回転方向を合わせるため符号を逆にしています。
その後は角度差をtotalAngleに加算して360で割った値をハンドルの回転に代入してます。
AimAngle関数ではハンドルの総回転角をハンドルの上限で割って狙っている角度を返します。 handleUpperLimitを変更すると狙う角度の上限までに必要なハンドルの回転角度が変わります。
おわりに
飛び道具の軌道予測のために角度を毎フレーム取得し続けています。
そこまで処理コストは高くないと思うけどもっと良い方法があったら教えていただきたい。