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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 14:03:14