LoginSignup
2
0

【Unity】FPSゲームのリコイルの実装

Last updated at Posted at 2024-03-06

概要

こんな感じのリコイルを作ります。
Recoil.gif
どんなリコイルパターンになるか自由に設定でき、滑らかな視点移動にしました。

前提として以前に書いた記事の方法2の視点移動Scriptを使っています。

仕組み

回転ベクトルについて

以前のコードに新しく変数を追加します。

POVController.cs
[SerializeField] private float _returnSpeed = 1;
[SerializeField] private float _snappiness = 6;
private Vector3 _recoilTargetRotation;
private Vector3 _currentRecoilRotation;
private Vector3 _returnTarget;

リコイルが入力されたとき_recoilTargetRotationの値を変え、それに伴って_currentRecoilRotationが変動します。プレイヤーの視点に反映されるのは_currentRecoilRotationです。
_returnTargetはリコイルによって跳ね上がった視点が戻ってくる場所です。
この3つのベクトルによりリコイルの入力で瞬間的に_recoilTargetRotationの値が変わるが、_snappinessの速度で跳ね上がり、_returnSpeedのスピードで元の視点に戻ります。

POVController.cs
    /// <summary>指定したリコイルを設定する</summary>
    public void Recoil(Vector2 recoil)
    {
        _recoilTargetRotation += new Vector3(-recoil.y, recoil.x, 0);
    }

    /// <summary>リコイルを反映させる</summary>
    void ReflectsRecoil()
    {
        _recoilTargetRotation = Vector3.Slerp(_recoilTargetRotation, _returnTarget,
            _returnSpeed * Time.fixedDeltaTime);
        _currentRecoilRotation = Vector3.Slerp(_currentRecoilRotation, _recoilTargetRotation,
            _snappiness * Time.fixedDeltaTime);
    }

Recoilメソッドでリコイルの入力を受け付けています。入力時に直感的にわかりやすいので引数をVector2、XY反転でやっていますが好みで。
ReflectsRecoilで各ベクトルの動きを計算しています。Vectr3.Slerpを使うことで2つのベクトルの差が大きいほど速い視点移動となります。つまり一定の縦リコイルを入力し続けても、次第にリコイルは小さくなるということです。
加減速の計算について詳しくないのでReflectsRecoilはFixedUpdateで実行しています。

ベクトルを反映させる

以前の記事のコードのLookの一部を書き換えます

        // 頭、体の向きの適用
-        _head.transform.localRotation = Quaternion.Euler(_xRotation, 0, 0);
-        _body.transform.localRotation = Quaternion.Euler(0, _yRotation, 0);
+        _head.transform.localRotation = 
+            Quaternion.Euler(_xRotation + _currentRecoilRotation.x, 0, 0);
+        _body.transform.localRotation = 
+            Quaternion.Euler(0, _yRotation + _currentRecoilRotation.y, 0);

ベクトルの入力にリコイルのベクトルを追加しているだけです。
次は_returnTargetについて
このままだとリコイル制御でマウスを動かしたとき、その分だけリコイルが終わったときに視点が動いてしまいます。なので、リコイル制御したときに戻ってくる場所を更新する必要がある。

+    if (mouseInput.magnitude != 0)
+    {
+        _returnTarget = _currentRecoilRotation;
+    }

リコイルの入力

一定間隔で射撃でき、最初は一定のリコイルパターンがあるが最後のほうはランダムなリコイルになる、よくあるようなリコイルを再現します。射撃をやめればリコイルパターンはリセットされます。

リコイルテストコード
RecoilTest.cs
using UnityEngine;

public class RecoilTest : MonoBehaviour
{
    [SerializeField] private POVController _povController;
    [SerializeField] private GameObject _bulletMarkObject;
    [SerializeField] private float _fireRate = 0.12f;
    [SerializeField] private Vector2[] _recoilPattern;
    [SerializeField] private Vector2 _randomRecoil;

    private float _fireTimer;
    private int _recoilPatternIndex;

    private void Update()
    {
        if (_fireTimer < _fireRate)
        {
            _fireTimer += Time.deltaTime;
        }

        if (Input.GetButton("Fire1"))
        {
            if (_fireTimer >= _fireRate)
            {
                _fireTimer = 0;

                if (_recoilPatternIndex < _recoilPattern.Length) // パターンが設定されていればそれに従う
                {
                    _povController.Recoil(_recoilPattern[_recoilPatternIndex]);
                    _recoilPatternIndex++;
                }
                else // 一定以降はランダムとか
                {
                    _povController.Recoil(new Vector2(Random.Range(-_randomRecoil.x, _randomRecoil.x), _randomRecoil.y));
                }
                
                // 着弾地点が分かるようにオブジェクト生成
                Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out RaycastHit hit);
                Destroy(Instantiate(_bulletMarkObject, hit.point, Quaternion.identity), 5);
            }
        }
        else
        {
            _recoilPatternIndex = 0;
        }
    }
}

Inspector

image.png

もっとこだわれば既存のゲームを再現できるでしょう。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0