ゴマちゃんフロンティア

アザラシが大好きなエンジニアの開発日記です

【開発日記】アクション「ステップ移動」の実装

time 2018/09/24

というわけで、今回は「アザラシを守るゲーム」に所謂「ステップ移動」が欲しくなったので、その実装方法について考えてみました。
画像では全く伝わりませんが、3Dアクションではよくある「短距離を高速移動するアクション」です。

ステップ移動の入力は「移動キーの2度押し」にしました。
実装方法はぐぐるとあれこれ出てくるので詳細は割愛しますが、1回目の移動入力を保持しておき、2回目の移動入力までの時間と入力方向で処理を行うか判定します。
瞬間的な移動入力を取得するのでGetAxisRaw()を使用し、取得したベクトルは正規化して使用するのがポイントです。

具体的な移動処理ですが、物理演算を使用している場合の常としてTransformから位置を操作するのはよろしくありません。プレイヤーはCharacterControllerを使用しているので、CharacterController.Move()を呼び出して移動させます。
ただ呼び出すだけでは通常移動と変わらないので、「一定時間特定の方向に大きく移動させ続ける」ようなイメージです。その実現に今回はDOTweenのTo()を使用しました。基本的な仕組みは以下の記事を参照いただければと思います。
ざっくり言うと、特定の2地点間を決められた時間内で移動するためのベクトルが返ってくるので、それをCharacterControllerで使って移動させています。
https://gomafrontier.com/unity/1510#DOTween

2つの処理を1つの関数に書くと以下のような感じになります。これをUpdate()あたりで呼び出して使用します。

// ステップ移動の判定に使用する移動入力のベクトル情報
private Vector3 inputFirstMoveVelocity = Vector3.zero;

// ステップ移動の2度押しカウント用
private int step_input_count = 0;

// ステップ移動実行中フラグ
private bool isStepping = false;

/// <summary>
/// ステップ移動の制御
/// </summary>
protected void StepMovement()
{
    // ステップが実行されていない場合のみ判定
    if (!isStepping) {
        // 水平移動入力
        if (Input.GetButtonDown("Horizontal") || Input.GetButtonDown("Vertical")) {
            // 移動入力情報を正規化して取得
            Vector3 inputMoveVelocity = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")).normalized;

            // 初回入力時・入力ベクトルが設定されていない場合は設定
            if (inputFirstMoveVelocity == Vector3.zero) {
                inputFirstMoveVelocity = inputMoveVelocity;
                step_input_count = 0;
            // 入力ベクトルが零ベクトルではない場合にステップ判定
            } else if (inputMoveVelocity != Vector3.zero) {
                float inputMoveAngle = Vector3.Angle(inputMoveVelocity, inputFirstMoveVelocity);

                // 1回目と2回目の入力差が制限角度内であればステップ
                if (inputMoveAngle < GameConstants.STEP_SECOND_INPUT_LIMIT_ANGLE) {
                    inputFirstMoveVelocity = Vector3.zero;

                    // 入力ベクトルをステップ用ベクトルとして準備
                    Vector3 destVelocity = inputMoveVelocity * 15f;

                    // ステップ移動用のシーケンス作成
                    Sequence sequence = DOTween.Sequence();
                    sequence.SetEase(Ease.OutCirc);
                    sequence.OnStart(() => {
                        isStepping = true;
                        animator.Play("Step");
                    });
                    sequence.Append(
                        DOTween.To(
                            () => transform.position,
                            v => {
                                Vector3 velocity = (v - transform.position) * Time.deltaTime;
                                characterController.Move(velocity);
                            },
                            transform.position + destVelocity,
                            GameConstants.STEP_EXECUTE_TIME
                        )
                    );
                    sequence.OnComplete(() => isStepping = false);
                    sequence.Play();
                }
            }
        }
    }

    // 初回入力されている場合の処理
    if (inputFirstMoveVelocity != null) {
        step_input_count++;

        // 2度押し入力されなかった場合はリセット
        if (GameConstants.STEP_SECOND_INPUT_INTERVAL_FRAME < step_input_count) {
            inputFirstMoveVelocity = Vector3.zero;
            step_input_count = 0;
        }
    }
}

使用している変数のうち、animatorAnimatorcharacterControllerCharacterControllerのインスタンスです。あらかじめStart()あたりで参照を取っておきましょう。

isSteppingフラグは「ステップ中に再度ステップ処理が走ることを防止」「ステップ中の移動入力による移動・回転の無効化」のために使用します。
フラグの切り替えはSequenceOnStart()OnComplete()に記述することで、ステップ移動の開始時と終了時に切り替わるようになります。

1つ気になるのは、Sequence.SetEase()でイージングを指定しても反映されない (?) ことです。
iTweenで似たようなものを実装したときはイージングが効いていたので、気になる方はそちらのValueTo()あたりを使用すると良いかもしれません。
DOTweenの方が開始時と終了時の処理を (ラムダ式で) スッキリ書けるので、最近にはDOTweenの方を使用することが多いです。

ゲーム上で動かすと下のような感じになります。

ステップ時のモーションとしてくるっと1回転させています。
全然「ステップ」ではありませんが、ゲームではよくあることなので気にしないようにしましょう。

down

コメントする