Unity玩家移动碰撞添加求助:水下场景Y轴移动受限问题
Unity水下游戏:碰撞检测与全轴移动兼容问题
问题描述
开发水下游戏时,现有移动脚本实现了玩家沿相机方向移动,但存在两个核心问题:
- 玩家会直接穿过墙体,添加Box Collider无效果;
- 添加Rigidbody后碰撞生效,但Y轴(上下)移动被限制,无法满足水下游动需求。
新编写的移动脚本解决了碰撞问题,但仍无法实现Y轴移动,需要同时兼顾碰撞检测与全轴自由移动。
原有移动脚本(Movement1)
using UnityEngine; using System.Collections; public class Movement1 : MonoBehaviour { /* wasd : basic movement shift : Makes camera accelerate */ public float sensX; public float sensY; public Transform orientation; float xRotation; float yRotation; float mainSpeed = 5.0f; //regular speed float shiftAdd = 8.0f; //multiplied by how long shift is held. Running float maxShift = 12.0f; //Maximum speed when holding shift float camSens = 0.25f; //How sensitive the mouse is private Vector3 lastMouse = new Vector3(255, 255, 255); //sets mouse near the middle private float totalRun= 1.0f; private void Start() { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } void Update () { float mouseX = Input.GetAxisRaw("Mouse X") * Time.deltaTime * sensX; float mouseY = Input.GetAxisRaw("Mouse Y") * Time.deltaTime * sensY; yRotation += mouseX; xRotation -= mouseY; xRotation = Mathf.Clamp(xRotation, -90f, 90f); transform.rotation = Quaternion.Euler(xRotation, yRotation, 0); orientation.rotation = Quaternion.Euler(0, yRotation, 0); //Keyboard commands float f = 0.0f; Vector3 p = GetBaseInput(); if (p.sqrMagnitude > 0){ // only move while a direction key is pressed if (Input.GetKey (KeyCode.LeftShift)){ totalRun += Time.deltaTime; p = p * totalRun * shiftAdd; p.x = Mathf.Clamp(p.x, -maxShift, maxShift); p.y = Mathf.Clamp(p.y, -maxShift, maxShift); p.z = Mathf.Clamp(p.z, -maxShift, maxShift); } else { totalRun = Mathf.Clamp(totalRun * 0.5f, 1f, 1000f); p = p * mainSpeed; } p = p * Time.deltaTime; Vector3 newPosition = transform.position; if (Input.GetKey(KeyCode.Space)){ //If player wants to move on X and Z axis only transform.Translate(p); newPosition.x = transform.position.x; newPosition.z = transform.position.z; transform.position = newPosition; } else { transform.Translate(p); } } } private Vector3 GetBaseInput() { //returns the basic values, if it's 0 than it's not active. Vector3 p_Velocity = new Vector3(); if (Input.GetKey (KeyCode.W)){ p_Velocity += new Vector3(0, 0 , 1); } if (Input.GetKey (KeyCode.S)){ p_Velocity += new Vector3(0, 0, -1); } if (Input.GetKey (KeyCode.A)){ p_Velocity += new Vector3(-1, 0, 0); } if (Input.GetKey (KeyCode.D)){ p_Velocity += new Vector3(1, 0, 0); } return p_Velocity; } }
新移动脚本(NewMovement)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewMovement : MonoBehaviour { [Header("Movement")] public float moveSpeed; public Transform orientation; float horizontalInput; float verticalInput; Vector3 moveDirection; Rigidbody rb; // Start is called before the first frame update private void Start() { rb = GetComponent<Rigidbody>(); rb.freezeRotation = true; } private void MyInput() { horizontalInput = Input.GetAxisRaw("Horizontal"); verticalInput = Input.GetAxisRaw("Vertical"); } private void MovePlayer() { moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput; rb.AddForce(moveDirection.normalized * moveSpeed * 8f, ForceMode.Force); } // Update is called once per frame private void Update() { MyInput(); SpeedControl(); } private void FixedUpdate() { MovePlayer(); } private void SpeedControl() { Vector3 flatVel = new Vector3(rb.velocity.x, rb.velocity.y, rb.velocity.z); if(flatVel.magnitude > moveSpeed) { Vector3 limitedVel = flatVel.normalized * moveSpeed; rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z); } } }
相机控制脚本(NewCamMovement)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewCamMovement : MonoBehaviour { public float sensX; public float sensY; public Transform orientation; float xRotation; float yRotation; private void Start() { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } // Update is called once per frame void Update() { float mouseX = Input.GetAxisRaw("Mouse X") * Time.deltaTime * sensX; float mouseY = Input.GetAxisRaw("Mouse Y") * Time.deltaTime * sensY; yRotation += mouseX; xRotation -= mouseY; xRotation = Mathf.Clamp(xRotation, -90f, 90f); transform.rotation = Quaternion.Euler(xRotation, yRotation, 0); orientation.rotation = Quaternion.Euler(0, yRotation, 0); } }
解决方案
核心问题分析
- 原有脚本穿墙原因:
Movement1使用transform.Translate直接修改位置,完全绕过Unity物理引擎,因此Box Collider无法触发碰撞检测。 - 新脚本Y轴移动失效原因:
NewMovement未处理Y轴输入,且未将Y轴方向加入移动向量;同时需确保Rigidbody设置正确。
修改后的完整移动脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewMovement : MonoBehaviour { [Header("Movement")] public float moveSpeed; public float verticalMoveSpeed; // 可单独设置上下移动速度,适配水下体验 public Transform orientation; float horizontalInput; float verticalInput; float verticalYInput; // 上下移动输入 Vector3 moveDirection; Rigidbody rb; private void Start() { rb = GetComponent<Rigidbody>(); rb.freezeRotation = true; rb.useGravity = false; // 水下关闭默认重力,可自定义浮力逻辑 } private void MyInput() { horizontalInput = Input.GetAxisRaw("Horizontal"); verticalInput = Input.GetAxisRaw("Vertical"); // 检测上下移动输入:Space上浮,Left Ctrl下潜 verticalYInput = 0; if (Input.GetKey(KeyCode.Space)) verticalYInput += 1; if (Input.GetKey(KeyCode.LeftControl)) verticalYInput -= 1; } private void MovePlayer() { // 构建XZ平面移动方向 Vector3 horizontalMove = orientation.forward * verticalInput + orientation.right * horizontalInput; // 加入Y轴移动方向 moveDirection = horizontalMove.normalized + Vector3.up * verticalYInput; // 应用移动力:XZ用moveSpeed,Y轴用verticalMoveSpeed(可选分开设置) rb.AddForce(horizontalMove.normalized * moveSpeed * 8f + Vector3.up * verticalYInput * verticalMoveSpeed * 8f, ForceMode.Force); } private void Update() { MyInput(); SpeedControl(); } private void FixedUpdate() { MovePlayer(); } private void SpeedControl() { // 分离XZ和Y轴速度限制,避免上下移动影响平面速度 Vector3 flatVel = new Vector3(rb.velocity.x, 0, rb.velocity.z); float yVel = rb.velocity.y; // 限制XZ平面速度 if(flatVel.magnitude > moveSpeed) { Vector3 limitedFlatVel = flatVel.normalized * moveSpeed; rb.velocity = new Vector3(limitedFlatVel.x, yVel, limitedFlatVel.z); } // 限制Y轴速度 if(Mathf.Abs(yVel) > verticalMoveSpeed) { rb.velocity = new Vector3(rb.velocity.x, Mathf.Sign(yVel) * verticalMoveSpeed, rb.velocity.z); } } }
额外设置说明
- Rigidbody配置:确保Rigidbody的
Use Gravity关闭,Freeze Rotation勾选(避免玩家旋转),不冻结任何轴; - Collider配置:给Player添加Box Collider或Capsule Collider,并确保墙体等障碍物也有Collider(且标记为Static);
- 浮力扩展:若需要模拟水下浮力,可在脚本中添加额外逻辑,比如当玩家未主动上下移动时,缓慢上浮或下沉。
内容的提问来源于stack exchange,提问作者Dawid
相关产品推荐
相关产品推荐

