Unityで三人称視点のキャラクターコントローラを作ってみよう(第7弾)

空中移動

キャラクターのジャンプ移動を実装した時に、キャラクターが地面に立っている条件で移動するようにスクリプトを作りました。これは現実世界と変わらない動作ですが、アスレチック要素が多いゲームの場合、力学のルールに反して空中でも多少移動を許すとより正確にジャンプをしたりして、ゲーム性がよくなります。

まず、空中移動のパラメータをCharacterクラスに追加しましょう。

public float runSpeed = 3f; // 走る速度
public float acceleration = 10f; // 加速度
public float maxGroundAngle = 45; // 登れる坂の傾斜の閾値
public float turnSmoothing = 0.3f;

public float airAcceleration = 10f; // 空中の加速度
public float maxAirSpeed = 2f; // 空中の最高速度

これで地面に立っている時と空中で移動している時に加速度と最高速度を別々に指定する事ができます。

次は、FixedUpdateの以下のコードを修正します。

if (!isJumping && isOnGround)
{
    ApplyMotion();
}

条件を無くして単純にこれにします。

 ApplyMotion();

ApplyMotionの中に地面上の移動と空中移動の分岐を追加します。

    // ...
    
    if (!isOnGround)
    {

    }
    else
    {
        Vector3 velocity = rb.velocity;
        if (groundRigidbody != null)
        {
            velocity -= groundRigidbody.velocity;
        }
        groundVelocity = ProjectOnPlane(velocity, groundNormal);

        float groundAngle = 90f - Mathf.Asin(groundNormal.y) * 180f / Mathf.PI;
        bool movingDownhill = movement.y <= 0f;
        if (groundAngle <= maxGroundAngle || movingDownhill)
        {
            if (groundVelocity.magnitude < runSpeed)
            {
                // 加速度を設定する
                rb.AddForce(movement * acceleration, ForceMode.Acceleration);
            }
        }
    }
}

最高速度を確認せずにとりあえず動かしてみましょう。

if (!isOnGround)
{
    rb.AddForce(movement * airAcceleration, ForceMode.Acceleration);
}

これで動作を確認しましょう。

動きは極端ですが、一応空中でも移動できるのが確認できます。次は速度を制限したいですが、空中移動の場合、最高速度の計算が少しややこしくなります。なぜかというと、走ってジャンプした時に空中で移動方向にさらに加速してしまうと不自然です。(そもそも空中で移動するのが不自然ですが、違和感が感じる時と感じない時があります。)パラメータとして設定した空中の最高速度よりXZの平面に対して移動速度が低ければ加速させますが、この速度を超えても加速をさせる場合もあります。走ってジャンプすると助走によって空中移動の最高速度を超える事もありますが、左右または反対方向への移動を許したいので、離陸した時の速度を記憶する必要があります。幸いにすでにgroundVelocityで必要な情報を記憶しています。これを使って速度制限のロジックを追加します。

if (!isOnGround)
{
    // 加速後の速度を計算する
    Vector3 newVelocity = rb.velocity + movement * airAcceleration * Time.fixedDeltaTime;
    Vector3 groundNewVelocity = ProjectOnPlane(newVelocity, Vector3.up);
    // 新しい速度をXYの平面に射影する
    float groundNewSpeed = groundNewVelocity.magnitude;
    // 空中の速度制限と離陸時の速度を超えた場合...
    float maxSpeed = Mathf.Max(maxAirSpeed, groundVelocity.magnitude);
    if (groundNewSpeed > maxSpeed)
    {
        // 最高速度を超えないように速度を調整する
        groundNewVelocity = groundNewVelocity.normalized * maxSpeed;
        // この速度を到達するための加速度を計算する
        Vector3 acceleration = (groundNewVelocity - ProjectOnPlane(rb.velocity, Vector3.up)) / Time.fixedDeltaTime;
        // Rigidbodyにこの加速度を適用する
        rb.AddForce(acceleration, ForceMode.Acceleration);
    }
    if (groundNewSpeed <= maxAirSpeed || groundNewSpeed <= groundVelocity.magnitude)
    {
        // 速度制限を超えていないのでこのまま加速度を適用する
        rb.AddForce(movement * airAcceleration, ForceMode.Acceleration);
    }
}

これで空中移動の動きがだいぶ良くなります。あとは速度制限と加速度を調整して操作性を調整します。

Leave a Reply

Your email address will not be published. Required fields are marked *

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください