当我跳跃时,玩家会失去之前的速度并产生撞到无形墙壁的感觉。
我尝试过改变阻力、坡度控制以及跳跃的方式,但没有成功。我也尝试过使用 chatgpt 但它没有做任何有帮助的事情。如果有任何更有效的方法可以让玩家按照我的方式进行操作,我也将不胜感激。
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour
{
[Header("Movement Settings")]
public float walkSpeed = 5f;
public float sprintSpeed = 10f;
public float crouchSpeed = 2f;
public Transform orientation;
public float groundDrag = 5f;
public float airDrag = 0f;
public float moveSmoothing = 0.1f;
public float maxSpeed = 10f;
[Header("Ground Check")]
public float groundCheckRadius = 0.5f;
public LayerMask groundLayer;
private bool grounded;
private bool wasGrounded;
[Header("Slope Handling")]
public float maxSlopeAngle = 45f;
private bool exitingSlope;
private bool onSlope;
private RaycastHit slopeHit;
private Vector3 slopeNormal;
private float slopeRayLength = 2f; // Longer ray length for better accuracy
private float groundRayOffset = 0.5f; // Height offset for ray origin
private int groundRayCount = 3; // Number of ground check rays
[Header("Player Jumping")]
public float jumpForce = 10f;
public float jumpCooldown = 1f;
public float airMultiplier = 0.25f;
private Vector3 storedVelocity;
[Header("Crouching")]
public float crouchYScale = 0.5f;
private float startYScale;
[Header("Keybinds")]
public KeyCode jumpKey = KeyCode.Space;
public KeyCode sprintKey = KeyCode.LeftShift;
public KeyCode crouchKey = KeyCode.LeftControl;
private Rigidbody rb;
private float moveSpeed;
private float horizontalInput;
private float verticalInput;
private bool readyToJump = true;
private Vector3 moveDirection;
public MovementState state;
public enum MovementState
{
walking,
sprinting,
crouching,
air
}
private void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
rb.constraints = RigidbodyConstraints.FreezeRotationX |
RigidbodyConstraints.FreezeRotationY |
RigidbodyConstraints.FreezeRotationZ;
startYScale = transform.localScale.y;
rb.drag = groundDrag;
}
private bool IsGrounded()
{
// Ground check using multiple raycasts to ensure accuracy
bool isGrounded = false;
float rayLength = slopeRayLength;
Vector3 rayOrigin = transform.position + Vector3.up * groundRayOffset;
for (int i = -1; i <= 1; i++)
{
Vector3 offset = Vector3.right * i * groundCheckRadius;
Vector3 rayPosition = rayOrigin + offset;
if (Physics.Raycast(rayPosition, Vector3.down, out RaycastHit hit, rayLength, groundLayer))
{
isGrounded = true;
slopeNormal = hit.normal;
float angle = Vector3.Angle(Vector3.up, slopeNormal);
onSlope = angle > 0 && angle <= maxSlopeAngle; // Within allowed slope range
}
}
return isGrounded;
}
private void Update()
{
wasGrounded = grounded;
grounded = IsGrounded();
MyInput();
StateHandler();
if (grounded && !wasGrounded)
{
OnLanding();
}
if (Input.GetKey(crouchKey))
{
moveSpeed = crouchSpeed;
}
else if (Input.GetKey(sprintKey))
{
moveSpeed = sprintSpeed;
}
else
{
moveSpeed = walkSpeed;
}
}
private void MyInput()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown(jumpKey) && readyToJump && grounded)
{
readyToJump = false;
Jump();
StartCoroutine(JumpCooldown());
}
if (Input.GetKey(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, crouchYScale, transform.localScale.z);
}
if (Input.GetKeyUp(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, startYScale, transform.localScale.z);
}
}
private void FixedUpdate()
{
MovePlayer();
CapSpeed();
}
private void StateHandler()
{
// Mode - Crouching
if (Input.GetKey(crouchKey))
{
state = MovementState.crouching;
moveSpeed = crouchSpeed;
}
// Mode - Sprinting
else if(grounded && Input.GetKey(sprintKey))
{
state = MovementState.sprinting;
moveSpeed = sprintSpeed;
}
// Mode - Walking
else if (grounded)
{
state = MovementState.walking;
moveSpeed = walkSpeed;
}
// Mode - Air
else
{
state = MovementState.air;
}
}
private void MovePlayer()
{
moveDirection = (orientation.forward * verticalInput + orientation.right * horizontalInput).normalized;
if (onSlope && !exitingSlope) // No parentheses because 'onSlope' is a boolean, not a method
{
rb.AddForce(GetSlopeMoveDirection() * moveSpeed * 20f, ForceMode.Force);
if (rb.velocity.y > 0)
rb.AddForce(Vector3.down * 80f, ForceMode.Force);
}
else if (grounded)
{
Vector3 targetPosition = transform.position + moveDirection * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(Vector3.Lerp(transform.position, targetPosition, moveSmoothing));
}
else
{
rb.AddForce(moveDirection * moveSpeed * airMultiplier, ForceMode.Force); // Airborne movement
}
}
private void CapSpeed()
{
float currentSpeed = rb.velocity.magnitude;
if (currentSpeed > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed; // Limit speed to avoid excessive acceleration
}
}
private void Jump() {
// Keep horizontal velocity, reset only vertical component to 0 before jump
Vector3 horizontalVelocity = new Vector3(rb.velocity.x, rb.velocity.z); // Remove Y component
// Set the new velocity with preserved horizontal components
rb.velocity = new Vector3(horizontalVelocity.x, 0, horizontalVelocity.y);
// Apply jump force
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
Debug.Log("jump");
}
private IEnumerator JumpCooldown()
{
yield return new WaitForSeconds(jumpCooldown);
readyToJump = true;
}
private IEnumerator SmoothDragChange(float newDrag, float duration)
{
float startDrag = rb.drag;
float time = 0f;
while (time < duration)
{
rb.drag = Mathf.Lerp(startDrag, newDrag, time / duration);
time += Time.fixedDeltaTime;
yield return new WaitForSeconds(Time.fixedDeltaTime);
}
rb.drag = newDrag;
}
private void OnLanding()
{
storedVelocity = rb.velocity;
Invoke("RestoreVelocity", 0.1f);
rb.drag = groundDrag; // Restore drag upon landing
Invoke(nameof(ResetGroundDrag), 0.1f); // Slight delay to smooth landing
}
private void RestoreVelocity()
{
rb.velocity = storedVelocity;
}
private void ResetGroundDrag()
{
rb.drag = groundDrag;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Vector3 rayOrigin = transform.position + Vector3.up * groundRayOffset;
float rayLength = slopeRayLength;
for (int i = -1; i <= 1; i++)
{
Vector3 offset = Vector3.right * i * groundCheckRadius;
Gizmos.DrawRay(rayOrigin + offset, Vector3.down * rayLength);
}
}
private Vector3 GetSlopeMoveDirection()
{
return Vector3.ProjectOnPlane(moveDirection, slopeHit.normal).normalized;
}
}
有很多代码需要理解,所以我不能 100% 确定为什么会出现问题。但我怀疑这是因为你根据状态不同地移动刚体。当接地时,您可以直接操纵位置,当在空中时,您可以增加力量:
else if (grounded)
{
Vector3 targetPosition = transform.position + moveDirection * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(Vector3.Lerp(transform.position, targetPosition, moveSmoothing));
}
else
{
rb.AddForce(moveDirection * moveSpeed * airMultiplier, ForceMode.Force); // Airborne movement
}
因此,当您开始跳跃时,您会失去初始速度,因为 AddForce 必须加速您的玩家,而且它可能会缓慢加速。最好保持一致,并在处理一种移动时以一种方式移动你的球员。使用力进行向上/向下移动是可以的,但对于水平移动,要么切换到力,要么仅使用位置操纵。您可以通过将空气中的水平运动更改为操纵来测试它,如下所示:
else if (grounded)
{
Vector3 targetPosition = transform.position + moveDirection * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(Vector3.Lerp(transform.position, targetPosition, moveSmoothing));
}
else
{
Vector3 targetPosition = transform.position + moveDirection * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(Vector3.Lerp(transform.position, targetPosition, moveSmoothing)); //AirMovement
}
您可以使用最后一个 else 块来根据您的需要进行调整。如果你不需要调整,你可以将其合并为一段代码,因为这是相同的操作。
你的上/下运动不应该受此影响,因为无论如何你都单独添加跳跃力