ゴマちゃんフロンティア

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

【Unity】ジャンプの高さをボタンを押す長さで調節できるようにする方法

time 2019/03/28

今回はUnityのすごく今更感のある「ジャンプ」に関するお話です。

世間一般のアクションゲームは、ジャンプボタン入力後に押した長さに応じて高度を調節できるようになっているかと思います。
それをUnityで実現しようとしたら意外と難しかったので、今回私が行った実装を紹介しようと思います。

ちなみに私はCharactorControllerを使用しています。
Rigidbodyを使用する場合でも概念は同じかと思いますので、参考になれば幸いです。

ジャンプの実装方法

「ジャンプ」と一口に言ってもいろいろな実装方法があるため、まず私が以前作っていたジャンプのロジックをご紹介します。
(水平方向への移動やキャラクターの回転は省略しています)

[SerializeField]
protected float jumpSpeed = 7f;
[SerializeField]
protected float maxFallSpeed = 20f;
[SerializeField]
protected float gravity = 15f;

// 加速度計算用の位置情報
protected Vector3 oldPosition;

// 現在の加速度
protected Vector3 velocity;

// キャラクターコントローラ
protected CharacterController characterController;

void Start()
{
    characterController = this.GetComponent<CharacterController>();
}

void Update()
{
    if (Input.GetButtonDown("Jump")) {
        inputJumpButton = true;
    }

    if (Input.GetButtonUp("Jump")) {
        inputJumpButton = false;
    }
}

void FixedUpdate()
{
    Vector3 moveDirection = Vector3.zero;

    // ジャンプ移動反映
    moveDirection += CalcJumping(moveDirection);

    // CharacterControllerで移動
    characterController.Move(moveDirection * Time.deltaTime);

    // 移動処理後の加速度を計算
    velocity = (transform.position - oldPosition) / Time.deltaTime;
}

protected Vector3 CalcJumping(Vector3 moveDirection)
{
    if (inputJumpButton && characterController.isGrounded) {
        velocity.y = jumpSpeed;
    }

    moveDirection.y = velocity.y - gravity * Time.deltaTime;
    moveDirection.y = Mathf.Max(moveDirection.y, -maxFallSpeed);

    return moveDirection;
}

垂直方向の移動処理を行う場合、移動前と移動後の位置から「どのくらい移動したか」の加速度を保持することが重要です。
これは「(現在の位置 – 1フレーム前の位置) / Time.deltaTime」で求められます。
この加速度はジャンプ後の重力落下速度を計算する際に重要になります。

ジャンプの処理は「着地時にジャンプボタンを押したらY軸に加速度を設定する」というものです。
その後で重力加速度の計算を行い、結果をCharactorController.Move()に渡します。

これでジャンプはできるようになりますが、ボタンを短く押しても長く押しても一定の高さでしかジャンプしません。
昔のファミコンゲームならともかく、最近のゲームとしては不自由ですね。

押す長さによって高度を変える実装

次に本題で、ボタンを押す長さによってジャンプの高さを調節できるようにします。
先ほどのスクリプトをあれこれ修正します。

[SerializeField]
protected float jumpSpeed = 7f;
[SerializeField]
protected float maxFallSpeed = 20f;
[SerializeField]
protected float gravity = 15f;

// 最小ジャンプフラグ
protected bool minJumpFlag = false;

// 加速度計算用の位置情報
protected Vector3 oldPosition;

// 現在の加速度
protected Vector3 velocity;

// キャラクターコントローラ
protected CharacterController characterController;

void Start()
{
    characterController = this.GetComponent<CharacterController>();
}

void Update()
{
    if (Input.GetButtonDown("Jump")) {
        inputJumpButton = true;
    }

    if (Input.GetButtonUp("Jump")) {
        inputJumpButton = false;
    }
}

void FixedUpdate()
{
    Vector3 moveDirection = Vector3.zero;

    // ジャンプ移動反映
    moveDirection += CalcJumping(moveDirection);

    // CharacterControllerで移動
    characterController.Move(moveDirection * Time.deltaTime);

    // 移動処理後の加速度を計算
    velocity = (transform.position - oldPosition) / Time.deltaTime;

    // 着地している場合は最小ジャンプフラグをfalseにする
    if (characterController.isGrounded) {
        minJumpFlag = false;
    }
}

protected Vector3 CalcJumping(Vector3 moveDirection)
{
    // 着地時にジャンプボタンが押された場合は加速度を与える
    if (inputJumpButton && characterController.isGrounded) {
        velocity.y = jumpSpeed;
    }

    // 空中でジャンプボタンが押されていない場合は加速度を0に戻す
    if (!inputJumpButton && !characterController.isGrounded && !minJumpFlag) {
        if (0 <= velocity.y) {
             velocity.y = 0;
        }
        minJumpFlag = true;
    }

    moveDirection.y = velocity.y - gravity * Time.deltaTime;
    moveDirection.y = Mathf.Max(moveDirection.y, -maxFallSpeed);

    return moveDirection;
}

ポイントは「空中でジャンプボタンを離したタイミングでY軸の加速度を0にする」ことです。Y軸に加速度を与えることでジャンプしているので、0を代入した上で重力加速度の計算を入れればOKです。
空中でジャンプボタンを離した時「だけ」処理をするため、minJumpFlagというフラグで制御しています。

また離したタイミングがジャンプ後の落下中であった場合、加速度を0にしてしまうと一瞬ふわっとしてしまいます。
なので現在の加速度が0以上 (=上昇中) の場合のみ処理を行うようにしました。

動作確認

実際に動かすと以下のような挙動になります。

ジャンプボタンを離すとスッと落下しています。
やや挙動が不自然ですが、一般的なアクションゲームであれば問題ないレベルかと思います!

down

コメントする