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

Godot实现Ledge grab抓边功能时RayCast2D反复启停异常问题

Godot 2D抓边功能RayCast检测器反复启停问题修复

问题相关实现代码

if $LedgeDetectorh.is_colliding()&&$LedgeDetectorh.get_collider() is Ledge:
    if !$LedgeDetectorv.is_colliding():
        isLedgeGrabbed = true
elif !$LedgeDetectorh2.is_colliding():
    isLedgeGrabbed = false
if isLedgeGrabbed:
    
    Motion = Vector2.ZERO
    
    applyGravity = false
    $LedgeDetectorv.enabled = false
    $LedgeDetectorh2.enabled = false
    
    if immediateInputLogic:
        onLedgeGrabbedIdle = false
        $LedgeTimer.start(5)
    
    else:
        onLedgeGrabbedIdle = true
        
    if onLedgeGrabbedIdle:
        Motion.y =0
        Motion.x = 0
        
        if $LedgeTimer.time_left ==0:
            
            if $LedgeDetectorh2.enabled == true:
                
                self.position.direction_to($LedgeDetectorh2.get_collision_point())* 50
            
    elif $LedgeTimer.time_left > 0:
        print("works")
        Motion.y = lerp(Motion.y, -400,1)
        Motion.x = lerp(Motion.x, 100 * direction,0.2)
elif !$LedgeDetectorh2.is_colliding():
    $LedgeDetectorh.enabled = true
    $LedgeDetectorv.enabled = true
    $LedgeDetectorh2.enabled = true
    applyGravity = true

问题根因

检测器反复启停是逻辑闭环导致的死循环:

  • 进入抓边状态时你主动将$LedgeDetectorh2设为禁用,Godot中禁用状态的RayCast2D调用is_colliding()固定返回false
  • 下一帧运行到外层elif !$LedgeDetectorh2.is_colliding()判断时条件成立,直接执行三个检测器全启用、开启重力的逻辑,退出抓边状态
  • 检测器重启后立刻检测到Ledge碰撞,再次进入抓边状态、禁用$LedgeDetectorh2,无限循环就出现了

附带的逻辑bug:

  • onLedgeGrabbedIdle分支下的位置偏移计算是死代码:计算得到的方向向量没有赋值给Motion或position直接丢弃,且进入该分支前$LedgeDetectorh2已经被禁用,分支内$LedgeDetectorh2.enabled == true的判断永远不成立
  • 抓边状态进出没有加前置校验,同一帧内可能多次修改isLedgeGrabbed值,进一步加剧状态抖动

修复方案

核心修改原则:不要在抓边流程中频繁开关RayCast(RayCast启用后至少需要1帧才能返回正确碰撞结果,频繁开关会导致检测结果完全失效),给状态切换加互斥判断,避免循环触发。
修复后的参考代码:

# 仅在非抓边状态下检测抓边进入条件,避免帧内状态反复跳变
if not isLedgeGrabbed:
    if $LedgeDetectorh.is_colliding() && $LedgeDetectorh.get_collider() is Ledge:
        if !$LedgeDetectorv.is_colliding():
            isLedgeGrabbed = true
else:
    # 抓边状态下用玩家输入作为退出条件,不要依赖被禁用的射线检测
    # 可根据自己的输入映射调整,比如按跳跃上爬、按下落松手
    if Input.is_action_just_pressed("jump") || Input.is_action_just_pressed("drop"):
        isLedgeGrabbed = false

if isLedgeGrabbed:
    Motion = Vector2.ZERO
    applyGravity = false
    # 移除频繁开关检测器的逻辑,保持检测器常开即可
    # $LedgeDetectorv.enabled = false
    # $LedgeDetectorh2.enabled = false

    if immediateInputLogic:
        onLedgeGrabbedIdle = false
        $LedgeTimer.start(5)
    else:
        onLedgeGrabbedIdle = true

    if onLedgeGrabbedIdle:
        Motion = Vector2.ZERO
        if $LedgeTimer.time_left == 0:
            # 修复无效的位置计算,抓边对齐推荐直接读取Ledge上的预放置标记点,比射线计算稳定
            if $LedgeDetectorh.get_collider() is Ledge:
                global_position = $LedgeDetectorh.get_collider().get_node("GrabPoint").global_position
    elif $LedgeTimer.time_left > 0:
        Motion.y = lerp(Motion.y, -400, 1)
        Motion.x = lerp(Motion.x, 100 * direction, 0.2)
else:
    # 非抓边状态统一重置参数,不要加多余的碰撞判断避免漏重置
    applyGravity = true
    onLedgeGrabbedIdle = false

优化建议

  • 所有Ledge碰撞体场景里提前加一个Marker2D标记抓边后角色应该对齐的位置,抓边时直接对齐到该点,比实时算射线偏移稳定很多,不会出现位置抖动
  • 如果一定要在抓边时关闭多余射线,就不要用被关闭的射线做退出判断,单独写退出逻辑
  • 抓边状态可以加一个冷却时间,退出抓边后0.2秒内不允许再次触发抓边,避免快速操作导致的状态跳变

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:51:24