ジャンプと、それを成立させる接地判定
キャラクターをジャンプさせ、空中での再ジャンプを止めるには?
ジャンプ自体は 1 行。難しいのは「足が何かに付いている瞬間」を知ることです。キャラクターの真下にある小さな球を Ground 層に対して検査し、true を返したときだけジャンプを許可します。垂直速度が 0 かで判定するのは絶対にやめてください——頂点でも 0 になります。
キャラクターをジャンプさせるだけなら一行です。ただ「何かの上に立っているときだけジャンプさせる」のが半日かかる作業で、理由は単純に Unity が足元の接地を教えてくれず —— 自分で行って聞いてくるしかないからです。
足元の下の球が毎フレーム答えを返し、その隣の二つの時間窓こそが、操作を過酷ではなくフェアに感じさせるものです。
ジャンプそのもの
// CharacterController: you own gravity, so you own the jump too.
verticalSpeed = Mathf.Sqrt(jumpHeight * -2f * gravity);
// Rigidbody: hand it an impulse and let physics do the arc.
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);接地チェック
ありがちな間違いは、縦方向の速度がゼロ近いかどうかで接地を判定することです。速度はジャンプの頂点で必ずゼロに近くなるため、まさにその頂点で二回目のジャンプができてしまいます —— 誰も求めていない定番のふわふわ二段ジャンプです。
- 1
Ground レイヤーを作成します: で layer を追加し、床とすべてのプラットフォームをそこに割り当てます。
- 2
キャラクターの空の子オブジェクトを一つ作り、GroundCheck と名付けて、足元の真下 —— 足にぴったりではなく数センチ下 —— に置きます。
- 3
毎フレーム、その点を中心とした小さな球と Ground レイヤーの当たりを調べます。何かの上に立っているとは、その球が何かと重なっているということです。
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class Jump : MonoBehaviour
{
public Transform groundCheck;
public LayerMask groundLayer;
public float checkRadius = 0.25f;
public float jumpHeight = 2f;
public float gravity = -20f;
// A jump the player asked for a moment too early still counts.
public float bufferTime = 0.12f;
// So does one asked for a moment after walking off an edge.
public float coyoteTime = 0.12f;
CharacterController cc;
float verticalSpeed, lastGrounded, lastPressed;
void Awake() => cc = GetComponent<CharacterController>();
void Update()
{
bool grounded = Physics.CheckSphere(
groundCheck.position, checkRadius, groundLayer, QueryTriggerInteraction.Ignore);
if (grounded) lastGrounded = Time.time;
if (Input.GetButtonDown("Jump")) lastPressed = Time.time;
if (grounded && verticalSpeed < 0f) verticalSpeed = -2f;
if (Time.time - lastPressed < bufferTime && Time.time - lastGrounded < coyoteTime)
{
verticalSpeed = Mathf.Sqrt(jumpHeight * -2f * gravity);
lastPressed = lastGrounded = -99f; // spend both, so it fires once
}
verticalSpeed += gravity * Time.deltaTime;
cc.Move(Vector3.up * verticalSpeed * Time.deltaTime);
}
// Draw the check in the Scene view, so you can see what it is testing.
void OnDrawGizmosSelected()
{
if (groundCheck == null) return;
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
}
}二つのタイマーは省略可能な飾りではない
Coyote time —— 段差の端から歩き出した 2 フレーム後に Jump を押したプレイヤーは、まさにジャンプするつもりだった人です。これがなければ、ゲームに無視されている気がしてなりません。
Jump buffer —— 着地の直前に Jump を押したなら、着地と同時にジャンプするつもりです。これがなければその一押しは無駄に捨てられ、プレイヤーは操作系のせいにします。
どちらも 0.1 秒前後が目安です。意識されることはなくても、この二つが「ピシッと決まる操作」と「壊れた操作」の違いの大半を埋めています。
これを作るレッスン
移動、ジャンプ、ブロックへの着地
この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — キャラクターと戦闘 のレッスン 02。