【Unity】スマホ用のFPSコントローラ(PCデバッグ可能)。縦方向のカメラ回転制限を実装しました
どうも、だらはです。
今回はコピペで動作するスマホ用のFPSコントローラの作り方を紹介します。
PCでも動かせるのでデバッグもしやすくお勧めです。
実装方法
早速ですが実装方法を記載していきます。
- [UI -> Panel]からPanelを作成してDragLeftとDragRightと命名。DragHandlerスクリプトをアタッチする。
- [Create Empty]からGameObjectを作成してPlayerと命名。Playerスクリプトをアタッチする。
- 2のPlayerスクリプトに対して、MoveControllerにDragLeftを、LookControllerにDragRightを、Cameraにカメラを、ObjPlayerModelにプレイヤー用モデルを設定する。(図1参考)
以上です。
◆図1
◆DragHandler.csスクリプト
using System;
using UnityEngine;
using UnityEngine.EventSystems;
public class DragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public Action<PointerEventData> OnBeginDragEvent;
public Action<PointerEventData> OnDragEvent;
public Action<PointerEventData> OnEndDragEvent;
//自身が所属してるキャンバス
private Canvas _belongedCanvas;
public void OnBeginDrag(PointerEventData eventData)
{
OnBeginDragEvent?.Invoke(eventData);
}
public void OnDrag(PointerEventData eventData)
{
OnDragEvent?.Invoke(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
OnEndDragEvent?.Invoke(eventData);
}
//スクリーン座標を自身が所属してるキャンバス上の座標に変換
public Vector2 GetPositionOnCanvas(Vector2 pointerPos)
{
if (_belongedCanvas == null)
{
_belongedCanvas = GetBelongedCanvas(transform);
}
RectTransformUtility.ScreenPointToLocalPointInRectangle(_belongedCanvas.transform as RectTransform, pointerPos, _belongedCanvas.worldCamera, out Vector2 localPointerPos);
return localPointerPos;
}
//所属するCanvasを取得
private Canvas GetBelongedCanvas(Transform t)
{
if (t == null)
{
return null;
}
var canvas = t.GetComponent<Canvas>();
if (canvas != null)
{
return canvas;
}
return GetBelongedCanvas(t.parent);
}
}
◆Player.csスクリプト
using UnityEngine;
using UnityEngine.EventSystems;
public class Player : MonoBehaviour
{
//移動操作を受け付けるタッチエリア
[SerializeField]
private DragHandler _moveController;
//移動速度(m/秒)
[SerializeField]
private float _movePerSecond;
//移動操作としてタッチ開始したスクリーン座標
private Vector2 _movePointerPosBegin;
private Vector3 _moveVector;
[SerializeField]
private Camera _camera;
//カメラ操作を受け付けるタッチエリア
[SerializeField]
private DragHandler _lookController;
//カメラ速度(°/px)
[SerializeField]
private float _angularPerPixel = 1f;
//カメラ操作として前フレームにタッチしたキャンバス上の座標
private Vector2 _lookPointerPosPre;
//アニメーター制御用
[SerializeField]
GameObject objPlayerModel;
Animator animPlayer;
private void Awake()
{
_moveController.OnBeginDragEvent += OnBeginDragMove;
_moveController.OnDragEvent += OnDragMove;
_moveController.OnEndDragEvent += OnEndDragMove;
_lookController.OnBeginDragEvent += OnBeginDragLook;
_lookController.OnDragEvent += OnDragLook;
}
void Start()
{
animPlayer = objPlayerModel.GetComponent<Animator>();
}
private void Update()
{
UpdateMove(_moveVector);
}
// 移動操作
#region Move
//ドラッグ操作開始(移動用)
private void OnBeginDragMove(PointerEventData eventData)
{
//タッチ開始位置を保持
_movePointerPosBegin = eventData.position;
}
//ドラッグ操作中(移動用)
private void OnDragMove(PointerEventData eventData)
{
//タッチ開始位置からのスワイプ量を移動ベクトルにする
var vector = eventData.position - _movePointerPosBegin;
_moveVector = new Vector3(vector.x, 0f, vector.y);
}
private void UpdateMove(Vector3 vector)
{
//現在向きを基準に、入力されたベクトルに向かって移動
Vector3 nextPos = transform.rotation * vector.normalized * _movePerSecond * Time.deltaTime;
float tagP = Mathf.Pow(transform.position.x + nextPos.x, 2) + Mathf.Pow(transform.position.y + nextPos.y, 2) + Mathf.Pow(transform.position.z + nextPos.z, 2);
float nowP = Mathf.Pow(transform.position.x, 2) + Mathf.Pow(transform.position.y, 2) + Mathf.Pow(transform.position.z, 2);
//アニメーションにSpeedを渡す(平方根の計算が重いかも)
animPlayer.SetFloat("Speed", Mathf.Sqrt((Mathf.Abs(tagP - nowP))));
transform.position += nextPos;
}
//ドラッグ操作終了(移動用)
private void OnEndDragMove(PointerEventData eventData)
{
//移動ベクトルを解消
_moveVector = Vector3.zero;
}
#endregion
//カメラ操作
#region Look
private void OnBeginDragLook(PointerEventData eventData)
{
_lookPointerPosPre = _lookController.GetPositionOnCanvas(eventData.position);
}
private void OnDragLook(PointerEventData eventData)
{
var pointerPosOnCanvas = _lookController.GetPositionOnCanvas(eventData.position);
//キャンバス上で前フレームから何px操作したかを計算
var vector = pointerPosOnCanvas - _lookPointerPosPre;
//操作量に応じてカメラを回転
LookRotate(new Vector2(-vector.y, vector.x));
_lookPointerPosPre = pointerPosOnCanvas;
}
private void LookRotate(Vector2 angles)
{
Vector2 deltaAngles = angles * _angularPerPixel;
transform.eulerAngles += new Vector3(0f, deltaAngles.y);
//縦方向の回転角度制限
Vector3 mXAxiz = _camera.transform.localEulerAngles;
var x = mXAxiz.x + deltaAngles.x;
if (x >= 180)
{
x = x - 360;
}
if (x >= -60 && x <= 60)
{
mXAxiz.x = x;
_camera.transform.localEulerAngles = mXAxiz;
}
}
#endregion
}
補足
先ず、今回の方法は以下のHPで紹介されているスクリプトに対して、以下の機能を追加しました。
- アニメーター制御機能。(移動すると歩くようにアニメーションさせるため)
- 縦方向のカメラ回転制限。(制限しないとストレスフルのため)
◆参考とさせて頂きました神HP
もしプレイヤー用モデルを使わない場合はスクリプトのアニメーターに関する記述をコメントアウトしてください。
縦方向のカメラ回転制限は60度としています。適宜、値を変更して頂けたらと思います。
最後に
いかがでしたでしょうか。
今回紹介したスクリプトを使うことで、アセットを使うことなくコピペでFPSコントローラが作れます。
今後は、コントローラのジョイスティックなどを作れたら作っていきたいと思います!
以上、だらはでした。
追記(2022.3.31)
画面にジョイスティックを追加する方法についてまとめました。
超簡単に実装可能なので、参考にして頂けたら嬉しいです:)
◆スマホ用のFPSコントローラとジョイスティックを連携する方法
追記(2023.4.15)
スワイプ量に比例して移動量を調整できるよう機能追加しました。
ディスカッション
コメント一覧
まだ、コメントがありません