射线检测障碍物时禁止向射线方向移动的实现方案
解决方案:第三人称控制器障碍物阻挡输入与跳跃优化
看起来你遇到的核心问题有两个:一是当前的射线检测只会完全禁止移动,没法区分方向过滤输入;二是跳跃时残留的移动输入会让角色带着方向起跳。我帮你调整代码,完美解决这两个问题:
1. 重构射线检测:支持检测任意方向
先把原来的IsNotBlocked改成更灵活的方向检测函数,这样能分别检查前后左右是否有障碍物:
// 检测指定的角色局部方向是否被障碍物阻挡 bool IsDirectionBlocked(Vector3 localDirection) { // 从角色胸部位置发射射线,避免地面/脚下物体干扰检测 Vector3 rayOrigin = transform.position + Vector3.up * 0.5f; // 把角色局部方向转换成世界空间方向 Vector3 worldDirection = transform.TransformDirection(localDirection); // 执行射线检测,返回是否命中目标层 if (Physics.Raycast(rayOrigin, worldDirection, out RaycastHit hit, _CheckDistance, _RaycastCollidableLayers)) { // 调试用:命中时画红色射线,方便你看检测范围 Debug.DrawLine(rayOrigin, hit.point, Color.red); return true; } // 未命中时画绿色射线 Debug.DrawLine(rayOrigin, rayOrigin + worldDirection * _CheckDistance, Color.green); return false; }
2. 过滤输入方向:只保留无障碍物的移动分量
在获取用户输入后,我们把输入分解到角色的前后左右方向,逐个检查是否被阻挡,过滤掉无效的输入分量:
void Update() { PushStates(); // --- 修改输入处理逻辑 --- Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")); Vector2 inputDir = input.normalized; // 把输入拆解为角色局部的前后/左右分量 float forwardInput = inputDir.y; // W/S对应的前后输入 float rightInput = inputDir.x; // A/D对应的左右输入 // 逐个方向检查,被阻挡则清零对应输入 if (IsDirectionBlocked(Vector3.forward) && forwardInput > 0) forwardInput = 0; if (IsDirectionBlocked(Vector3.back) && forwardInput < 0) forwardInput = 0; if (IsDirectionBlocked(Vector3.right) && rightInput > 0) rightInput = 0; if (IsDirectionBlocked(Vector3.left) && rightInput < 0) rightInput = 0; // 重新构建过滤后的有效输入方向 Vector2 filteredInputDir = new Vector2(rightInput, forwardInput); // 如果过滤后输入几乎为0,直接设为Vector2.zero,避免微小移动触发动画 if (filteredInputDir.magnitude < 0.1f) { filteredInputDir = Vector2.zero; } else { filteredInputDir = filteredInputDir.normalized; } // --- 输入处理结束 --- bool running = Input.GetButton("Run"); // 使用过滤后的有效输入执行移动 if (filteredInputDir != Vector2.zero) { Move(filteredInputDir, running); } else { // 没有有效输入时,平滑重置移动速度,避免动画异常 currentSpeed = Mathf.SmoothDamp(currentSpeed, 0, ref speedSmoothVelocity, speedSmoothTime); } // 原有动画与跳跃检测逻辑保持不变 float animationSpeedPercent = ((running) ? currentSpeed / runSpeed : currentSpeed / walkSpeed * .5f); anim.SetFloat("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime); anim.SetFloat("speedPercentPush", animationSpeedPercent, speedSmoothTime, Time.deltaTime); // ... 原有跳跃检测代码 ... JumpCheck(); }
3. 跳跃时重置水平状态:确保只向上跳
在你的三个跳跃函数里,添加重置水平速度的代码,彻底清除输入残留,让跳跃只有垂直方向的力:
void ShortJump() { // 重置水平移动状态,确保跳跃时没有水平速度 currentSpeed = 0f; speedSmoothVelocity = 0f; // 原有跳跃逻辑 velocityY = shortJumpForce; // 替换成你实际的跳跃力变量 anim.SetTrigger("shortJump"); canJump = Time.time + jumpCooldown; } void JumpWalk() { currentSpeed = 0f; speedSmoothVelocity = 0f; velocityY = walkJumpForce; anim.SetTrigger("jumpWalk"); canJump = Time.time + jumpCooldown; } void JumpRun() { currentSpeed = 0f; speedSmoothVelocity = 0f; velocityY = runJumpForce; anim.SetTrigger("jumpRun"); canJump = Time.time + jumpCooldown; }
4. 优化Move函数:空中彻底禁止水平移动
如果你已经有跳跃期间禁止移动的逻辑,可以在Move函数开头加个判断,确保空中时不会处理任何水平移动:
void Move(Vector2 inputDir, bool running) { // 不在地面时,直接返回不处理水平移动 if (!GroundCheck()) { currentSpeed = 0f; speedSmoothVelocity = 0f; return; } // 原有移动逻辑保持不变 if (inputDir != Vector2.zero) { float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y; transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime)); } float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude; currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime)); velocityY += Time.deltaTime * gravity; Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY; controller.Move(velocity * Time.deltaTime); currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude; if (GroundCheck()) { velocityY = 0; anim.SetBool("onAir", false); } else { anim.SetBool("onAir", true); } }
这些修改的核心作用:
- 方向过滤:玩家可以向无障碍物的方向自由移动,不会被完全锁死
- 跳跃重置:彻底清除移动输入残留,顶着障碍物跳时只会垂直向上
- 空中锁死:跳跃过程中不会产生任何水平速度,完全符合你的需求
内容的提问来源于stack exchange,提问作者Quincy Norbert
相关产品推荐
相关产品推荐

