Godot中KinematicBody2D站在RigidBody上无法跳跃的问题求助
问题原因分析
- Push函数调用时机错误:当前代码在
move_and_slide()之前调用Push(),但get_slide_count()仅在move_and_slide()执行后才会返回有效碰撞次数,这会导致碰撞逻辑异常,包括站在刚体上时的地面检测失效。 - 站在刚体上时地面检测失效:即使
infinite_inertia设为false,KinematicBody2D的is_on_floor()可能无法正确识别动态RigidBody为地面,或者因对刚体施加向下冲量导致刚体下沉,玩家失去地面接触。
解决方案
步骤1:修正Push函数调用时机
将Push()移到move_and_slide()之后,确保能获取到当前帧的碰撞数据:
func _physics_process(delta) -> void: # 处理输入、动画、跳跃逻辑 if Input.is_action_pressed("right"): $Sprite.flip_h=false velocity.x += speed animation.play("Walk") elif Input.is_action_pressed("left"): $Sprite.flip_h=true velocity.x -= speed animation.play("Walk") else: animation.play("Idle") if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y =- jumpforce animation.play("Idle") velocity.y += gravity velocity=move_and_slide(velocity,Vector2.UP, false, 4, PI/4, false) velocity.x= lerp(velocity.x,0,0.2) # 移动后再处理碰撞推动逻辑 Push()
步骤2:优化地面检测(针对RigidBody)
如果调整调用时机后仍无法跳跃,需要手动检测是否站在RigidBody上,补充is_on_floor()的判断:
func is_on_valid_floor() -> bool: # 先调用原生的is_on_floor() if is_on_floor(): return true # 手动检测是否与RigidBody发生地面方向的碰撞 for index in get_slide_count(): var collision = get_slide_collision(index) # 检查碰撞对象是否是RigidBody,且碰撞法线接近向上(角度小于45度) if collision.collider is RigidBody2D and collision.normal.angle_to(Vector2.UP) < PI/4: return true return false
然后将跳跃逻辑中的is_on_floor()替换为is_on_valid_floor():
if Input.is_action_just_pressed("jump") and is_on_valid_floor(): velocity.y =- jumpforce animation.play("Idle")
步骤3:优化刚体推动逻辑(避免向下冲量导致下沉)
当玩家站在刚体上时,碰撞法线是向上的,此时施加向下的冲量会让刚体下沉,导致玩家失去地面接触。修改Push函数,只在水平方向碰撞时施加冲量:
func Push(): for index in get_slide_count(): var collision = get_slide_collision(index) if collision.collider.is_in_group("bodies"): # 只提取水平方向的法线分量,避免对垂直方向的碰撞施加冲量 var horizontal_normal = Vector2(collision.normal.x, 0).normalized() if horizontal_normal.length_squared() > 0: collision.collider.apply_central_impulse(-horizontal_normal * push)
完整修正后代码
extends KinematicBody2D onready var animation = $AnimationPlayer export (int, 0, 200) var push = 30 var velocity :=Vector2.ZERO var gravity := 30 var speed := 50 var jumpforce = 300 func _physics_process(delta) -> void: if Input.is_action_pressed("right"): $Sprite.flip_h=false velocity.x += speed animation.play("Walk") elif Input.is_action_pressed("left"): $Sprite.flip_h=true velocity.x -= speed animation.play("Walk") else: animation.play("Idle") if Input.is_action_just_pressed("jump") and is_on_valid_floor(): velocity.y =- jumpforce animation.play("Idle") velocity.y += gravity velocity=move_and_slide(velocity,Vector2.UP, false, 4, PI/4, false) velocity.x= lerp(velocity.x,0,0.2) Push() func Push(): for index in get_slide_count(): var collision = get_slide_collision(index) if collision.collider.is_in_group("bodies"): var horizontal_normal = Vector2(collision.normal.x, 0).normalized() if horizontal_normal.length_squared() > 0: collision.collider.apply_central_impulse(-horizontal_normal * push) func is_on_valid_floor() -> bool: if is_on_floor(): return true for index in get_slide_count(): var collision = get_slide_collision(index) if collision.collider is RigidBody2D and collision.normal.angle_to(Vector2.UP) < PI/4: return true return false
内容的提问来源于stack exchange,提问作者nicktehboy meny
相关产品推荐
相关产品推荐

