Godot 4.3 2D平台游戏抓钩穿模碰撞问题求解
抓钩高速移动穿过多边形碰撞体的修复方案(Godot 4.3)
我正在开发一款基于Godot 4.3的2D平台游戏,核心玩法之一是抓钩机制:角色向前发射绳索,若钩住物体则向其快速移动。该机制数月前已完成开发,近期提升抓钩整体速度后,出现了抓钩偶尔穿过多边形碰撞体的问题。我怀疑当前仅检测多边形边缘,抓钩高速移动穿入内部后就停止检测了,求优化方案避免穿模。
核心代码
func perform_grapple(): home_state = HomeState.FIRE var max_distance = 3000.0 var hook_speed = 5000.0 var hook_return = 7000.0 var max_time = 0.2 var tail_hit = false var hook_point = Vector2.ZERO var hooked_object = null tail.visible = true tail_end.visible = true tail_end.global_position = global_position tail_end_start_position = global_position var timer = Timer.new() timer.wait_time = max_time timer.one_shot = true add_child(timer) timer.start() #is_home_pause = true var pause_timer := Timer.new() pause_timer.wait_time = 0.15 pause_timer.one_shot = true add_child(pause_timer) pause_timer.start() var direction = Vector2(0, 0) var target_pos = global_position var offsetY = velocity.y * 0.15 var offsetX = velocity.x * 0.15 if sprite.flip_h: tail_end.flip_h = false target_pos += Vector2(-max_distance - offsetX, offsetY) else: tail_end.flip_h = true target_pos += Vector2(max_distance - offsetX, offsetY) can_home = false while true: var delta = get_physics_process_delta_time() var step = hook_speed * delta var next_pos = tail_end.global_position.move_toward(target_pos, step) var space_state = get_world_2d().direct_space_state var query = PhysicsRayQueryParameters2D.new() query.from = tail_end.global_position query.to = next_pos query.collision_mask = 1 << 4 query.exclude = [self] query.collide_with_areas = true query.collide_with_bodies = true var result = space_state.intersect_ray(query) if result: tail_hit = true hook_point = result.position var collider = result.collider hooked_object = find_with_method(collider, "pause_movement") if hooked_object: hooked_object.pause_movement(true) else: hooked_object = collider tail_end_start_position = hook_point tail_end.global_position = hook_point tail.points = [tail.to_local(global_position), tail.to_local(tail_end.global_position)] is_home_pause = false homing_target = hook_point homing_target_object = hooked_object home_state = HomeState.HOME is_homing = true return # 以下代码永远不会执行,建议删除 # var freeze = find_with_method(hooked_object, "pause_movement") # if freeze: # freeze.pause_movement(true) # return
优化方案
1. 改用形状投射替代射线检测
射线检测仅检测两点连线的碰撞,高速移动时步长过大容易直接跳过碰撞体。改用ShapeCast2D(用小圆形/胶囊形状模拟抓钩碰撞范围),能检测整个移动路径上的碰撞:
# 提前在节点树中添加ShapeCast2D,或代码创建 var shape_cast = ShapeCast2D.new() shape_cast.shape = CircleShape2D.new() shape_cast.shape.radius = 5.0 # 根据抓钩视觉大小调整 shape_cast.collision_mask = 1 << 4 shape_cast.exclude = [self] add_child(shape_cast) # 在循环中更新投射参数 while true: var delta = get_physics_process_delta_time() var step = hook_speed * delta var next_pos = tail_end.global_position.move_toward(target_pos, step) shape_cast.global_position = tail_end.global_position shape_cast.target_position = next_pos - tail_end.global_position shape_cast.force_shapecast_update() if shape_cast.is_colliding(): var result = shape_cast.get_collision_point() var collider = shape_cast.get_collider() # 后续碰撞处理逻辑和原代码一致 break
2. 限制单帧最大移动步长
如果坚持用射线检测,可拆分大移动步长为多次小步检测,避免跳过碰撞体:
while true: var delta = get_physics_process_delta_time() var step = hook_speed * delta var max_single_step = 50.0 # 单帧最大移动距离,按需调整 var remaining_dist = tail_end.global_position.distance_to(target_pos) while remaining_dist > 0: var current_step = min(step, remaining_dist, max_single_step) var next_pos = tail_end.global_position.move_toward(target_pos, current_step) # 执行原射线检测逻辑 var space_state = get_world_2d().direct_space_state var query = PhysicsRayQueryParameters2D.new() query.from = tail_end.global_position query.to = next_pos query.collision_mask = 1 << 4 query.exclude = [self] var result = space_state.intersect_ray(query) if result: # 处理碰撞逻辑 return tail_end.global_position = next_pos remaining_dist -= current_step
3. 启用连续碰撞检测(CCD)
如果tail_end是带碰撞体的节点(比如RigidBody2D),开启其continuous_collision_detection属性,让物理引擎自动处理高速物体的碰撞检测,但纯逻辑驱动的抓钩优先用形状投射方案。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

