Godot 4基于RigidBody3D的3D赛车Context Steering AI修复需求
Godot 4 3D赛车AI修复(情境转向+Path3D跟随)
问题概述
使用Phind生成的脚本控制RigidBody3D赛车时,仅能移动但无法正确旋转,且Path3D跟随逻辑未正确实现。节点结构为:父节点RigidBody3D,子节点包含MeshInstance3D、CollisionShape3D。
修复后的完整脚本
extends RigidBody3D @export var max_speed: float = 350.0 @export var steer_force: float = 0.1 @export var look_ahead_distance: float = 100.0 @export var num_rays: int = 8 @export var path: Path3D # 关联场景中的Path3D节点 @export var ray_length: float = 50.0 # 危险检测射线长度 var ray_directions: Array[Vector3] = [] var interest: Array[float] = [] var danger: Array[float] = [] var chosen_dir: Vector3 = Vector3.ZERO func _ready(): # 初始化射线方向:围绕Y轴(竖直轴)生成均匀分布的射线 ray_directions.resize(num_rays) interest.resize(num_rays) danger.resize(num_rays) for i in range(num_rays): var angle = i * 2 * PI / num_rays # 基于本地Z轴(前进方向)生成射线,覆盖360度 ray_directions[i] = Vector3.FORWARD.rotated(Vector3.UP, angle) func set_interest(): if path: # 计算Path3D上的前进方向 var path_position = path.curve.get_closest_point(global_position) var progress = path.curve.get_closest_offset(path_position) var look_ahead_point = path.curve.interpolate_baked(progress + look_ahead_distance) var path_direction = (look_ahead_point - global_position).normalized() path_direction.y = 0 # 保持水平方向,忽略Y轴高度变化 # 计算每条射线与路径方向的匹配度(兴趣值) for i in range(num_rays): # 将射线方向转换为全局空间 var global_ray_dir = global_transform.basis * ray_directions[i] global_ray_dir.y = 0 # 点积越大,兴趣值越高(仅保留正向匹配) var dot_product = global_ray_dir.dot(path_direction) interest[i] = max(0.0, dot_product) else: # 默认兴趣:保持当前前进方向 for i in range(num_rays): var global_ray_dir = global_transform.basis * ray_directions[i] global_ray_dir.y = 0 var dot_product = global_ray_dir.dot(global_transform.basis.z) interest[i] = max(0.0, dot_product) func set_danger(): var space_state = get_world_3d().direct_space_state # 排除自身碰撞,避免射线检测到自己 var exclude = [self] for i in range(num_rays): var global_ray_dir = global_transform.basis * ray_directions[i] global_ray_dir.y = 0 # 水平射线,只检测障碍物 var ray_start = global_position + Vector3.UP * 0.5 # 从车身中部发射射线 var ray_end = ray_start + global_ray_dir.normalized() * ray_length var query = PhysicsRayQueryParameters3D.create(ray_start, ray_end, exclude) var result = space_state.intersect_ray(query) # 根据障碍物距离计算危险值:越近危险值越高 if result: var distance = (ray_start - result["position"]).length() danger[i] = 1.0 - (distance / ray_length) else: danger[i] = 0.0 func choose_direction(): # 危险射线对应的兴趣值清零 for i in range(num_rays): if danger[i] > 0.1: # 阈值过滤轻微碰撞 interest[i] = 0.0 # 计算最终选择的方向 chosen_dir = Vector3.ZERO for i in range(num_rays): chosen_dir += ray_directions[i] * interest[i] # 确保方向向量有效 if chosen_dir.length_squared() > 0: chosen_dir = chosen_dir.normalized() else: # 无有效方向时保持当前前进方向 chosen_dir = Vector3.FORWARD func _physics_process(delta): set_interest() set_danger() choose_direction() # 将选择的方向转换为全局空间 var global_chosen_dir = global_transform.basis * chosen_dir global_chosen_dir.y = 0 # 保持水平移动 # 计算期望速度 var desired_velocity = global_chosen_dir * max_speed # 平滑过渡到期望速度 var target_velocity = linear_velocity.lerp(desired_velocity, steer_force * delta) target_velocity.y = linear_velocity.y # 保留Y轴速度(比如重力影响) # 应用速度到刚体 linear_velocity = target_velocity # 平滑旋转到前进方向 if global_chosen_dir.length_squared() > 0: # 计算目标朝向 var target_basis = Basis.looking_at(-global_chosen_dir, Vector3.UP) # 平滑插值旋转 global_transform.basis = global_transform.basis.slerp(target_basis, steer_force * delta * 5.0)
关键修复说明
- 旋转逻辑修正:放弃错误的
look_at用法,改用slerp平滑插值刚体的Basis属性,确保旋转符合物理规则,同时保持水平方向避免倾斜。 - 射线检测修复:修正射线起点/终点计算,排除自身碰撞,根据障碍物距离动态计算危险值,而非简单的0/1判断。
- Path3D跟随实现:直接关联Path3D节点,通过路径上的前瞻点计算前进方向,确保赛车沿路径行驶。
- 物理运动修正:将速度变量直接应用到RigidBody3D的
linear_velocity属性,符合Godot物理引擎的控制逻辑,同时保留Y轴速度以响应重力。 - 兴趣方向优化:基于全局空间计算射线与目标方向的点积,确保兴趣值计算准确,避免本地坐标系的方向混淆。
内容的提问来源于stack exchange,提问作者hayaaah
相关产品推荐
相关产品推荐

