You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js第一人称视角Raycast碰撞检测相机穿模问题求解

Three.js第一人称Raycast碰撞穿模修复方案

问题根因

当前碰撞逻辑存在三个核心疏漏,直接导致前后移动均出现穿模:

  • 射线覆盖不全:仅检测正前方碰撞,未覆盖后退方向;且检测距离阈值小于单帧移动步长,移动时相机直接跨过检测区间穿入模型内部
  • 检测时序错误:先更新相机位置再做碰撞校验,此时相机点位已经进入模型网格内部,射线从模型内部向外发射时无法返回正确的碰撞结果
  • 无碰撞缓冲:直接以相机视点位为基准做贴脸检测,浮点计算误差会导致点位滑入网格面内部

具体修复方案

1. 补全全移动方向射线

不要仅发射单条正前方射线,需覆盖水平移动的四个基础方向,所有射线以相机当前位置为原点发射:

import * as THREE from 'three'
// 本地坐标系下的四个检测方向
const DETECT_DIRS = [
  new THREE.Vector3(0, 0, -1), // 正前方
  new THREE.Vector3(0, 0, 1),  // 正后方
  new THREE.Vector3(-1, 0, 0), // 正左方
  new THREE.Vector3(1, 0, 0)   // 正右方
]
const COLLISION_RADIUS = 0.4 // 碰撞缓冲半径,单位米,适配第一人称人模宽度
const raycaster = new THREE.Raycaster()

2. 改为移动前预检测逻辑

禁止先更新相机位置再做碰撞判断,需先计算本帧的总移动偏移量,提前判断移动后是否会触发碰撞,确认无碰撞再更新相机位置:

// 先计算本帧预期的移动偏移量moveOffset(根据键盘输入、相机朝向计算得到)
let moveValid = true

for (const localDir of DETECT_DIRS) {
  // 将本地检测方向转换为世界坐标系下的方向,匹配相机当前朝向
  const worldDir = localDir.clone().applyQuaternion(camera.quaternion).normalize()
  raycaster.set(camera.position, worldDir)
  // 检测与场景碰撞体列表的交点,collisionMeshes为提前收集的所有需要参与碰撞的场景网格
  const hits = raycaster.intersectObjects(collisionMeshes, false)
  if (hits.length && hits[0].distance < COLLISION_RADIUS) {
    moveValid = false
    break
  }
}

// 仅当所有方向检测无碰撞时,才更新相机位置
if (moveValid) {
  camera.position.add(moveOffset)
}

3. 可选优化项

  • 不要使用高精细度的展示模型做碰撞检测,给场景物体生成简化的Box、Capsule碰撞盒,可大幅提升检测稳定性和运行性能
  • 若需要支持跳跃、蹲伏逻辑,额外补充正上、正下两个方向的射线检测,避免穿地面、撞天花板
  • 碰撞检测列表提前过滤掉小型装饰、透明特效等不需要参与碰撞的物体,减少不必要的计算量

内容的提问来源于stack exchange,提问作者keno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 16:12:16