Unity 3D FPS游戏:WASD移动、跳跃与鼠标视角控制实现问询
3D FPS游戏PlayerController脚本优化指导
需求回顾
- WASD键控制玩家移动,空格键触发跳跃
- 鼠标固定在游戏窗口中央,移动鼠标实现视角旋转
- 启用刚体的freeze rotation功能,避免物理碰撞导致玩家旋转
现有脚本问题与优化方案
1. 鼠标锁定功能缺失
原脚本未实现鼠标固定窗口中央的核心需求,需在Start()方法中添加鼠标锁定逻辑:
Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false;
建议补充游戏暂停/窗口失焦时的鼠标恢复逻辑,避免玩家操作受影响。
2. 视角旋转逻辑修正
原脚本将头部的X/Y轴旋转混合处理,不符合FPS游戏常规逻辑(身体负责左右转向,头部负责上下俯仰),优化后:
- 玩家身体(
transform)处理鼠标X轴输入,实现左右转向 - 头部(
head)仅处理鼠标Y轴输入,实现上下俯仰,避免身体与头部旋转混乱
修正后的视角旋转代码:
// 身体左右旋转 transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivity, 0); // 头部上下俯仰 rotationY += Input.GetAxis("Mouse Y") * sensitivity; rotationY = Mathf.Clamp(rotationY, headMinY, headMaxY); head.localEulerAngles = new Vector3(-rotationY, head.localEulerAngles.y, 0);
3. 移动逻辑优化
- 原
FixedUpdate()中通过AddForce加速度限制的方式冗余,可直接设置水平速度并限制大小,更高效:
void FixedUpdate() { if (h == 0 && v == 0) return; // 计算移动方向并转换为世界空间 Vector3 moveDir = new Vector3(h, 0, v).normalized; moveDir = transform.TransformDirection(moveDir); // 设置目标速度,保留Y轴速度(重力/跳跃) Vector3 targetVelocity = new Vector3(moveDir.x * speed, body.velocity.y, moveDir.z * speed); body.velocity = targetVelocity; }
- 原代码使用
head.TransformDirection会导致移动方向跟随头部上下俯仰,改为transform.TransformDirection保证移动方向与身体朝向一致,符合FPS操作习惯。
4. 跳跃逻辑修复
原脚本跳跃时直接清零水平速度(new Vector2(0, jumpForce)),导致空中无法移动,修正为保留水平速度:
if (Input.GetKeyDown(jumpButton) && GetJump()) { body.velocity = new Vector3(body.velocity.x, jumpForce, body.velocity.z); }
同时优化GetJump()的射线检测起点,避免因玩家模型原点位置误判地面:
bool GetJump() { // 从玩家脚底附近发射射线,避免穿模 Ray ray = new Ray(transform.position + Vector3.up * 0.1f, Vector3.down); return Physics.Raycast(ray, out RaycastHit hit, jumpDistance, layerMask); }
5. LayerMask逻辑修正
原LayerMask设置可能导致射线检测忽略必要层,建议明确指定地面层,例如:
// 假设地面层名为"Ground" layerMask = 1 << LayerMask.NameToLayer("Ground"); // 或者排除玩家层,避免检测自身 layerMask = ~(1 << LayerMask.NameToLayer("Player"));
优化后的完整脚本
using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class PlayerController : MonoBehaviour { public float speed = 5f; public Transform head; public float sensitivity = 2f; public float headMinY = -40f; public float headMaxY = 40f; public KeyCode jumpButton = KeyCode.Space; public float jumpForce = 7f; public float jumpDistance = 1.2f; private float h, v; private int layerMask; private Rigidbody body; private float rotationY; void Start() { body = GetComponent<Rigidbody>(); body.freezeRotation = true; // 锁定鼠标到窗口中央 Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; // 设置LayerMask,仅检测地面层(根据实际层名调整) layerMask = 1 << LayerMask.NameToLayer("Ground"); } void FixedUpdate() { if (h == 0 && v == 0) return; // 计算移动方向并转换为世界空间 Vector3 moveDir = new Vector3(h, 0, v).normalized; moveDir = transform.TransformDirection(moveDir); // 设置目标速度,保留Y轴速度 Vector3 targetVelocity = new Vector3(moveDir.x * speed, body.velocity.y, moveDir.z * speed); body.velocity = targetVelocity; } bool GetJump() { Ray ray = new Ray(transform.position + Vector3.up * 0.1f, Vector3.down); return Physics.Raycast(ray, jumpDistance, layerMask); } void Update() { h = Input.GetAxis("Horizontal"); v = Input.GetAxis("Vertical"); // 身体左右旋转 transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivity, 0); // 头部上下俯仰 rotationY += Input.GetAxis("Mouse Y") * sensitivity; rotationY = Mathf.Clamp(rotationY, headMinY, headMaxY); head.localEulerAngles = new Vector3(-rotationY, head.localEulerAngles.y, 0); // 跳跃逻辑 if (Input.GetKeyDown(jumpButton) && GetJump()) { body.velocity = new Vector3(body.velocity.x, jumpForce, body.velocity.z); } } void OnDrawGizmosSelected() { Gizmos.color = Color.red; Gizmos.DrawRay(transform.position + Vector3.up * 0.1f, Vector3.down * jumpDistance); } // 窗口失焦时恢复鼠标,避免操作异常 void OnApplicationFocus(bool hasFocus) { if (hasFocus) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } else { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } } }
内容的提问来源于stack exchange,提问作者Hackproger
相关产品推荐
相关产品推荐

