Godot 4中如何让CharacterBody3D对齐圆柱网格实现F-Zero X式移动?
实现CharacterBody3D绕圆柱旋转移动(仿F-Zero X)
你的思路完全可行,通过多射线加权计算目标法线的方式,能稳定获取角色所在圆柱表面的贴合方向,接下来可以通过平滑旋转对齐法线+适配表面的移动逻辑完成需求,具体实现步骤如下:
一、完善目标法线计算(避免除以0错误)
首先要处理射线未碰撞或距离为0的情况,防止计算报错:
func _physics_process(delta): # 初始化权重和法线列表 var weights: Array[float] = [] var normals: Array[Vector3] = [] # 遍历三个射线,收集有效碰撞数据 for ray in [Raycast1, Raycast2, Raycast3]: if ray.is_colliding(): var dist = ray.get_collision_point().distance_to(ray.global_position) if dist > 0.001: # 避免除以0 weights.append(1.0 / dist) normals.append(ray.get_collision_normal()) # 计算目标法线,无有效碰撞时 fallback 到默认向上 var target_normal = Vector3.UP if weights.size() > 0: var weighted_sum = Vector3.ZERO for i in range(weights.size()): weighted_sum += weights[i] * normals[i] target_normal = weighted_sum.normalized()
二、平滑旋转角色对齐目标法线
通过球面插值(slerp)让角色的basis.y(当前向上方向)逐步对齐target_normal,同时保留角色的前进方向在圆柱表面的切平面上:
# 获取当前角色的向上和前进方向 var current_up = basis.y var current_forward = basis.z # 将前进方向投影到目标法线的垂直平面(切平面),避免旋转时方向偏移 var target_forward = (current_forward - target_normal * current_forward.dot(target_normal)).normalized() # 处理投影后方向接近零的极端情况 if target_forward.length_squared() < 0.001: target_forward = Vector3.FORWARD # 创建目标坐标系:以target_normal为向上,target_forward为前进 var target_basis = Basis.looking_at(target_forward, target_normal) # 平滑旋转到目标坐标系(rotation_speed控制旋转快慢,建议设为4~6) var rotation_speed = 5.0 basis = basis.slerp_to(target_basis, rotation_speed * delta)
三、适配圆柱表面的移动逻辑
调整重力方向让角色贴紧圆柱,同时将玩家输入转换为表面切向的移动方向:
# 读取玩家输入(需提前在项目设置中绑定move_forward/move_backward/move_left/move_right动作) var input_dir = Vector2( Input.get_action_strength("move_right") - Input.get_action_strength("move_left"), Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward") ).normalized() # 将2D输入转换为角色局部坐标系的3D移动方向 var move_dir = basis.x * input_dir.x + basis.z * input_dir.y # 确保移动方向严格在切平面内(垂直于目标法线) move_dir = (move_dir - target_normal * move_dir.dot(target_normal)).normalized() # 调整重力方向:让角色被"吸附"在圆柱表面 var gravity = -target_normal * 12.0 # 数值可根据需求调整 velocity += gravity * delta # 应用移动速度(move_speed控制角色移动快慢) var move_speed = 8.0 velocity = move_dir * move_speed + gravity * delta # 执行移动(传入target_normal作为地面法线,让move_and_slide正确处理碰撞) move_and_slide(velocity, target_normal)
关键注意事项
- 射线布局:建议将三个射线分布在角色的前、左、右三个方向(或上、左、右),避免集中在一侧导致法线计算偏差。
- 参数调整:
rotation_speed、gravity、move_speed需要根据你的场景大小和手感需求反复调试。 - 边界处理:当角色离开圆柱表面时,代码会自动 fallback 到默认向上方向,可根据需求添加额外的脱离判定逻辑。
内容的提问来源于stack exchange,提问作者user2635469
相关产品推荐
相关产品推荐

