Tôi đang xây dựng một trò chơi đua xe bằng đá cẩm thạch khá đơn giản trong Unity3D. Quả bóng là một vật thể vật lý 3D chỉ di chuyển trên trục X và Y. Nó có khả năng lăn trái và phải, và nhảy. Những thứ khá cơ bản, ngoại trừ việc tôi gặp phải một vấn đề phá vỡ trò chơi: Khi ngã và chạm đất, cường độ nảy của quả bóng có thể được kết hợp với lực nhảy của nó để tạo ra một cú nhảy cực cao. Điều này có nghĩa là, với các lần nhấn nút đúng lúc, người chơi có thể khiến bóng nảy cao hơn theo cấp số nhân, đạt đến độ cao ngoài ý muốn. Tôi không thể thiết kế đúng mức cho đến khi trục trặc này được khắc phục. Tôi đã minh họa ví dụ này:
Nhảy, tuy nhiên, không đơn giản như chỉ bắn bóng thẳng lên trên. Để tạo điều kiện cho sự phức tạp hơn trong thiết kế cấp độ, tôi đã lập trình góc nhảy tương đối với bề mặt mà quả bóng đang lăn.
Hình 3 , trong hình minh họa đó, là cách trò chơi của tôi hoạt động cho đến nay; không phải hình 4 . Điều này làm cho việc giải quyết vấn đề nảy + nhảy trở nên khó khăn hơn nhiều, vì tôi không thể đơn giản đo và đặt một lực hoặc vận tốc chính xác trên trục Y. Làm như vậy dẫn đến hành vi kỳ lạ, điều này trở nên đáng chú ý hơn khi quả bóng di chuyển trên các sườn dốc hơn.
Cho đến nay, tôi đã có thể nghĩ ra một giải pháp cho tất cả các vấn đề thiết kế khác trong trò chơi này và sau đó tìm ra cách lập trình chúng, nhưng vấn đề này khiến tôi bế tắc. Tôi đã thử một số cách tiếp cận khác nhau, nhưng không có cách nào trong số chúng có hiệu quả.
Đây là kịch bản C # điều khiển cú nhảy của bóng:
using UnityEngine;
using System.Collections;
public class BallJumping : MonoBehaviour {
public System.Action onJump;
public Rigidbody objRigidbody; // Set this to the player
public bool isGrounded; // Determines whether or not the ball is on the ground
public Transform groundChecker; // A child object that's slightly larger than the ball
public float groundRadius = 0.6f;
public LayerMask whatIsGround; // Determines what layers qualify as ground
public AudioClip jumpSFX;
public AudioClip stickyJumpSFX;
private float p_WillJumpTimeRemaining; // Grace periods before/after hitting the ground to trigger jump
private float p_CanJumpTimeRemaining;
public float earlyJumpToleranceDuration = 0.2f;
public float lateJumpToleranceDuration = 0.2f;
public float jump = 500f; // Jumping power
private float halfJump = 250f; // Used for the sticky puddles
public bool stuck = false; // Used for sticky materials
private float contactX;
private float contactY;
// Input for jumping
void Update () {
if (Input.GetButtonDown ("Jump") && isGrounded == true) {
ProcessJump();
}
}
// Continuously checks whether or not the ball is on the ground
void FixedUpdate () {
if (Physics.CheckSphere (groundChecker.position, groundRadius, whatIsGround) == true) {
isGrounded = true;
} else {
isGrounded = false;
}
}
// Sets a grace period for before or after the ball contacts the ground for jumping input
void ProcessJump () {
bool boolGetJump = Input.GetButtonDown("Jump");
if (boolGetJump && isGrounded == false) {
p_WillJumpTimeRemaining = earlyJumpToleranceDuration;
} else {
if (p_WillJumpTimeRemaining > 0) {
p_WillJumpTimeRemaining -= Time.fixedDeltaTime;
}
}
if (isGrounded) {
p_CanJumpTimeRemaining = lateJumpToleranceDuration;
}
if (isGrounded || p_WillJumpTimeRemaining > 0) {
Jump();
}
if (p_CanJumpTimeRemaining > 0) {
p_CanJumpTimeRemaining -= Time.fixedDeltaTime;
}
}
// Sticky puddles script -- hinders jumping while in the puddle
void OnTriggerEnter (Collider collision) {
if (collision.gameObject.tag == "Sticky") {
stuck = true;
}
}
void OnTriggerExit (Collider collision) {
if (collision.gameObject.tag == "Sticky") {
stuck = false;
}
}
// Calculates the normals for the jump angle
void OnCollisionStay (Collision collision) {
Debug.Log ("Collision.");
foreach (ContactPoint contact in collision.contacts) {
contactX = contact.normal.x;
contactY = contact.normal.y;
}
}
// Controls jumping
void Jump() {
Debug.Log ("Jump.");
p_WillJumpTimeRemaining = 0.0f;
p_CanJumpTimeRemaining = 0.0f;
halfJump = jump * 0.5f; // Cuts jumping force in half while in a sticky puddle
GetComponent<AudioSource>().volume = 1;
GetComponent<AudioSource>().pitch = Random.Range (0.9f, 1.1f);
if (stuck == false) {
objRigidbody.AddForce (contactX * jump, contactY * jump, 0);
GetComponent<AudioSource>().clip = jumpSFX;
GetComponent<AudioSource>().Play ();
}
else if (stuck == true) {
objRigidbody.AddForce (contactX * halfJump, contactY * halfJump, 0);
GetComponent<AudioSource>().clip = stickyJumpSFX;
GetComponent<AudioSource>().Play ();
}
if (onJump != null) {
onJump();
}
}
}
Nỗ lực mới nhất của tôi là thử nhảy - Rigidbody.velocity.magnitude * 50 , để giảm sức nhảy bằng tốc độ mà quả bóng đang di chuyển. Nó gần như đã giải quyết được vấn đề nảy + nhảy, bằng cách giảm tỷ lệ lực nhảy xuống 0 khi tốc độ của quả bóng đạt đến mức tương đương với vận tốc. Nó hoạt động từ bế tắc, nhưng vấn đề là, nó cũng chiếm độ lớn trong khi quả bóng được tiếp đất, ngăn không cho bóng lăn ở tốc độ tối đa và nhảy. Tôi đã gần, nhưng không hoàn toàn ở đó!
Tôi là một lập trình viên mới làm quen, và tôi bị bối rối ở đây. Bất cứ ai có thể giúp tôi tìm một giải pháp sáng tạo cho vấn đề này? Miễn là người chơi có thể liên tục nảy và nhảy cao hơn, tôi không thể thiết kế bất kỳ cấp độ nào, bởi vì tất cả họ sẽ chỉ có thể bị lừa. Tôi rất muốn tiến lên - vấn đề này đã kìm hãm tôi trong một thời gian dài, vì vậy tôi rất trân trọng một số lời khuyên!