Nhảy, và bước kiểm tra chạm đất giúp nó chạy đúng
Làm sao cho nhân vật nhảy, và không cho nó nhảy tiếp khi đang ở trên không?
Lệnh nhảy chỉ một dòng; phần khó là biết lúc nào chân đang chạm đất. Kiểm tra một hình cầu nhỏ ngay dưới chân nhân vật với layer Ground, và chỉ cho nhảy khi phép kiểm tra đó trả về true. Đừng kiểm tra vận tốc dọc có bằng 0 hay không — ở đỉnh cú nhảy nó cũng bằng 0.
Cho nhân vật nhảy chỉ mất một dòng. Cho nó chỉ nhảy khi đang đứng trên cái gì đó mới là phần ngốn cả buổi chiều, lý do là Unity không tự nói cho bạn biết chân đang chạm gì — bạn phải tự đi hỏi.
Hình cầu nằm dưới chân và trả lời mỗi khung hình. Hai khoảng thời gian bên cạnh là thứ khiến việc điều khiển có cảm giác công bằng thay vì khắt khe.
Bản thân cú nhảy
// CharacterController: bạn tự quản trọng lực, nên cũng tự quản cú nhảy.
verticalSpeed = Mathf.Sqrt(jumpHeight * -2f * gravity);
// Rigidbody: đưa cho nó một xung lực rồi để vật lý lo đường bay.
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);Phép kiểm chạm đất
Cách sai là kiểm tra xem vận tốc dọc có gần bằng 0 không. Nó gần bằng 0 ở đỉnh mọi cú nhảy, nên nhân vật được nhảy thêm lần nữa đúng ngay đỉnh — cú nhảy đôi lửng lơ kinh điển mà chẳng ai yêu cầu.
- 1
Tạo layer Ground: , rồi gán sàn và mọi bệ đỡ vào layer đó.
- 2
Tạo một object con rỗng của nhân vật, đặt tên GroundCheck, và đưa nó xuống ngay dưới bàn chân — thấp hơn vài centimet, đừng đặt đúng ngay chân.
- 3
Mỗi khung hình, kiểm tra một hình cầu nhỏ tại điểm đó với layer Ground. Đang đứng trên thứ gì đó nghĩa là hình cầu chồng lên thứ gì đó.
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;
// Cú nhảy người chơi bấm sớm một chút vẫn được tính.
public float bufferTime = 0.12f;
// Cú nhảy bấm ngay sau khi vừa rời mép cũng vậy.
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; // dùng hết cả hai, để chỉ nhảy một lần
}
verticalSpeed += gravity * Time.deltaTime;
cc.Move(Vector3.up * verticalSpeed * Time.deltaTime);
}
// Vẽ vùng kiểm trong Scene view, để bạn nhìn thấy nó đang kiểm cái gì.
void OnDrawGizmosSelected()
{
if (groundCheck == null) return;
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(groundCheck.position, checkRadius);
}
}Hai bộ đếm giờ này không phải đồ trang trí
Coyote time: người chơi bấm Jump hai khung hình sau khi vừa bước khỏi mép là họ đang muốn nhảy. Thiếu nó, game cho cảm giác đang phớt lờ bạn.
Jump buffer: người chơi bấm Jump ngay trước lúc tiếp đất là họ muốn nhảy tiếp khi chạm đất. Thiếu nó, cú bấm bị vứt đi và họ đổ lỗi cho điều khiển.
Cả hai đều quanh 0.1 giây. Không ai nhận ra chúng, mà gộp lại chúng chiếm phần lớn khác biệt giữa điều khiển đã tay và điều khiển như hỏng.
bài học làm ra thứ này
Di chuyển, nhảy và đáp lên khối
Bài này là một công thức đứng riêng. Trong khoá học, cùng thứ đó được dựng như một phần của dự án xuyên suốt năm tab — Nhân vật & Chiến đấu, bài 02.