feat: implemented ledge hang

This commit is contained in:
Sara Gerretsen 2026-05-05 17:08:09 +02:00
parent aa579d0118
commit bd082ccb95
6 changed files with 126 additions and 33 deletions

View file

@ -11,6 +11,7 @@ void PlayerBehaviourTree::_bind_methods() {
BIND_HPROPERTY(Variant::OBJECT, player_body, PROPERTY_HINT_NODE_TYPE, "PlayerBody", PROPERTY_USAGE_NO_EDITOR);
BIND_HPROPERTY(Variant::OBJECT, character, PROPERTY_HINT_NODE_TYPE, "Node3D");
BIND_HPROPERTY(Variant::OBJECT, anim, PROPERTY_HINT_NODE_TYPE, "AnimationPlayer", PROPERTY_USAGE_READ_ONLY);
BIND_HPROPERTY(Variant::OBJECT, hang_ray, PROPERTY_HINT_NODE_TYPE, "RayCast3D", PROPERTY_USAGE_READ_ONLY);
ClassDB::bind_method(D_METHOD("get_world_move_input"), &self_type::get_world_move_input);
}

View file

@ -31,4 +31,5 @@ public:
GET_SET_FNS(PlayerBody *, player_body);
GET_SET_FNS(Node3D *, character);
GET_SET_FNS(AnimationTree *, anim);
GET_SET_FNS(RayCast3D *, hang_ray);
};

Binary file not shown.

View file

@ -35,6 +35,9 @@ adjustment_enabled = true
[node name="TestRoom" type="Node3D" unique_id=112638301]
script = SubResource("GDScript_x4mbn")
[node name="Player" parent="." unique_id=591139793 instance=ExtResource("1_ctiq7")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.029971719, 1, 2.8870416)
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1908495879]
transform = Transform3D(0.5391505, 0.72937495, -0.42110476, 2.780595e-08, 0.5, 0.86602557, 0.84220964, -0.4669181, 0.26957524, 0, 0, 0)
shadow_enabled = true
@ -57,16 +60,20 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.985878, -0.106285095, -28.1
size = Vector3(11.271973, 18.138779, 52.59424)
[node name="CSGBox3D10" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1786873333]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.7057834, 0.35496426, 3.489521)
size = Vector3(0.73794556, 1.0221081, 10.836697)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.7057834, 1.2590272, 3.489521)
size = Vector3(0.73794556, 2.830234, 10.836697)
[node name="CSGBox3D11" type="CSGBox3D" parent="CSGCombiner3D" unique_id=394932180]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.6592455, 2.3439102, -43.909866)
size = Vector3(0.73794556, 5, 21.215439)
[node name="CSGBox3D4" type="CSGBox3D" parent="CSGCombiner3D" unique_id=2098568400]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.4827657, 0.3491907, -23.528507)
size = Vector3(4.4109497, 17.305145, 4.536865)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.078620434, 0.3491907, -23.528507)
size = Vector3(7.533722, 17.305145, 4.536865)
[node name="CSGBox3D9" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1023898064]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.9423108, 0.3491907, -36.697388)
size = Vector3(1.481575, 17.305145, 2.1267395)
[node name="CSGBox3D14" type="CSGBox3D" parent="CSGCombiner3D" unique_id=306043989]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 8.961569, 8.730846, -28.135025)
@ -74,18 +81,18 @@ size = Vector3(11.131348, 0.59587574, 52.558292)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D30" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1234145919]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.858661, -1.0997419, -23.528507)
size = Vector3(1.864624, 14.40728, 0.8496094)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.858661, -1.9499207, -23.528507)
size = Vector3(1.864624, 12.706923, 0.8496094)
[node name="CSGBox3D12" type="CSGBox3D" parent="CSGCombiner3D" unique_id=2123158945]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.2749672, -0.47547364, -23.528507)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.2428513, -0.40600038, -23.528507)
operation = 2
size = Vector3(5.0676727, 15.293793, 5.795166)
size = Vector3(5.003441, 15.432739, 5.795166)
[node name="CSGBox3D20" type="CSGBox3D" parent="CSGCombiner3D" unique_id=332092224]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.2835846, -2.68553, -23.528507)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.6967564, -2.68553, -23.528507)
operation = 2
size = Vector3(7.050438, 10.87368, 5.795166)
size = Vector3(5.8767815, 10.87368, 5.795166)
[node name="CSGBox3D19" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1874001058]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.234858, 6.584762, -37.56486)
@ -93,8 +100,8 @@ operation = 2
size = Vector3(7, 7.8739157, 33.867874)
[node name="CSGBox3D17" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1138974560]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.01117754, 8.081971, -23.529545)
size = Vector3(7.354126, 1.8395844, 4.5408936)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.01117754, 8.460324, -31.645954)
size = Vector3(7.354126, 1.0828781, 12.311127)
[node name="CSGBox3D3" type="CSGBox3D" parent="CSGCombiner3D" unique_id=2113952448]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -1.2953725, -4.5720077, -1.8097906)
@ -116,8 +123,8 @@ size = Vector3(5.218512, 0.6420107, 4.210966)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D27" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1573045647]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -6.266988, 3.3423727, -35.185455)
size = Vector3(7, 0.49568796, 11.825884)
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -5.9904985, 3.3423727, -35.185455)
size = Vector3(7.5529785, 0.49568796, 11.825884)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D13" type="CSGBox3D" parent="CSGCombiner3D" unique_id=2006282059]
@ -126,8 +133,8 @@ size = Vector3(14.360085, 1.0296273, 23.871582)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D18" type="CSGBox3D" parent="CSGCombiner3D" unique_id=2075586533]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 9.257527, 2.224138, -37.198185)
size = Vector3(6.8769503, 0.99967694, 34.419334)
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 9.2342415, 2.224138, -37.198185)
size = Vector3(6.923522, 0.99967694, 34.419334)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D7" type="CSGBox3D" parent="CSGCombiner3D" unique_id=439598012]
@ -136,8 +143,8 @@ size = Vector3(11.229938, 0.59587574, 31.750126)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D8" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1838759176]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0.033503532, 8.860284, -23.523607)
size = Vector3(7.36936, 0.33700037, 4.361328)
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0.033503007, 8.860284, -29.54027)
size = Vector3(7.36936, 0.33700037, 16.394653)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D6" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1564599820]
@ -149,8 +156,8 @@ transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -3.8
size = Vector3(5.4628997, 8.825525, 4.630616)
[node name="CSGBox3D26" type="CSGBox3D" parent="CSGCombiner3D" unique_id=780570957]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -6.0997567, 3.331369, -35.19776)
size = Vector3(6.9240575, 0.499228, 12)
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -5.8714023, 3.331369, -35.19422)
size = Vector3(7.380766, 0.499228, 11.920166)
[node name="CSGBox3D28" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1486590753]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -1.6073493, 6.314084, -8.71521)
@ -158,8 +165,5 @@ size = Vector3(10.05127, 0.49568796, 13.489602)
material = ExtResource("2_x4mbn")
[node name="CSGBox3D29" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1161233408]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -1.6312003, 6.30308, -8.717578)
size = Vector3(10.311796, 0.499228, 13.683594)
[node name="Player" parent="." unique_id=591139793 instance=ExtResource("1_ctiq7")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.029971719, 1, 2.8870416)
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -1.6312003, 6.0177565, -8.717578)
size = Vector3(10.311796, 1.0698754, 13.683594)

View file

@ -20,6 +20,11 @@ animation = &"RESET"
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_3nfmf"]
animation = &"jump"
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_i825w"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_42cpl"]
animation = &"hang"
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_uxov2"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_i825w"]
@ -69,8 +74,7 @@ animation = &"wallrun_right"
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_eqqp1"]
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_uxov2"]
graph_offset = Vector2(-708.59985, 313.57794)
nodes/output/position = Vector2(520, 140)
nodes/output/position = Vector2(720, 140)
nodes/RunStates/node = SubResource("AnimationNodeStateMachine_3nfmf")
nodes/RunStates/position = Vector2(-800, 320)
nodes/Decelerating/node = SubResource("AnimationNodeBlend2_ykyjo")
@ -103,7 +107,11 @@ nodes/Jumping/node = SubResource("AnimationNodeAnimation_3nfmf")
nodes/Jumping/position = Vector2(-340, 580)
nodes/Falling/node = SubResource("AnimationNodeAnimation_4vq5b")
nodes/Falling/position = Vector2(-340, 760)
node_connections = [&"output", 0, &"Wall", &"Decelerating", 0, &"Speed", &"Decelerating", 1, &"SkidStopLoop", &"Air", 0, &"Moving", &"Air", 1, &"AirTime", &"Speed", 0, &"RunStates", &"Wall", 0, &"Air", &"Wall", 1, &"WallAction", &"WallAction", 0, &"WallGripLoop", &"WallAction", 1, &"WallRunSide", &"WallRunSide", 0, &"WallRunLeftLoop", &"WallRunSide", 1, &"WallRunRightLoop", &"Moving", 0, &"IdleLoop", &"Moving", 1, &"Decelerating", &"AirTime", 0, &"Jumping", &"AirTime", 1, &"Falling"]
nodes/Ledge/node = SubResource("AnimationNodeBlend2_i825w")
nodes/Ledge/position = Vector2(540, 140)
nodes/LedgeLoop/node = SubResource("AnimationNodeAnimation_42cpl")
nodes/LedgeLoop/position = Vector2(300, 400)
node_connections = [&"output", 0, &"Ledge", &"Decelerating", 0, &"Speed", &"Decelerating", 1, &"SkidStopLoop", &"Air", 0, &"Moving", &"Air", 1, &"AirTime", &"Speed", 0, &"RunStates", &"Wall", 0, &"Air", &"Wall", 1, &"WallAction", &"WallAction", 0, &"WallGripLoop", &"WallAction", 1, &"WallRunSide", &"WallRunSide", 0, &"WallRunLeftLoop", &"WallRunSide", 1, &"WallRunRightLoop", &"Moving", 0, &"IdleLoop", &"Moving", 1, &"Decelerating", &"AirTime", 0, &"Jumping", &"AirTime", 1, &"Falling", &"Ledge", 0, &"Wall", &"Ledge", 1, &"LedgeLoop"]
[sub_resource type="GDScript" id="GDScript_i825w"]
resource_name = "PlayerAnimationPlayer"
@ -147,6 +155,72 @@ func _process(delta: float) -> void:
set(\"parameters/Wall/blend_amount\", 0.0)
"
[sub_resource type="GDScript" id="GDScript_f46kd"]
resource_name = "HangRay"
script/source = "@tool extends RayCast3D
@onready var body : PlayerBody = get_owner() as Node3D
@export var dist_mul : float
var last_direction : Vector3 = Vector3.BACK
func _ready() -> void:
add_exception(body)
func _process(_delta : float):
if Engine.is_editor_hint() and body:
global_position = body.global_position + Vector3.UP + Vector3.BACK * dist_mul
elif body:
var direction : Vector3 = -body.velocity
if body.is_on_wall():
direction = body.get_wall_normal()
if not direction.is_zero_approx():
last_direction = Vector3(direction.x, 0, direction.z).normalized()
global_position = body.global_position + Vector3.UP + last_direction * dist_mul
"
[sub_resource type="GDScript" id="GDScript_3rerk"]
resource_name = "BelowLedgePrerequisite"
script/source = "extends PlayerAction
var timeout : bool = false
func _ready():
$\"../Timer\".timeout.connect(set.bind(\"timeout\", false))
func _enter() -> int:
if not timeout and not body.is_on_floor() and blackboard.hang_ray.is_colliding() and blackboard.hang_ray.get_collision_normal().y > 0.9 and body.is_on_wall():
print(blackboard.hang_ray.get_collider().get_path())
timeout = true
return Success
else:
return Fail
"
[sub_resource type="GDScript" id="GDScript_uxov2"]
resource_name = "PlayerLedgeHangAction"
script/source = "extends PlayerAction
func _enter() -> int:
return Running
func _execute() -> int:
blackboard.anim.set(\"parameters/Ledge/blend_amount\", 1.0)
var diff : Vector3 = blackboard.hang_ray.get_collision_point() - blackboard.hang_ray.global_position
var direction : Vector3 = -blackboard.character.global_basis.z
if body.is_on_wall():
direction = -body.get_wall_normal()
blackboard.character.look_at(blackboard.character.global_position + Vector3(direction.x, 0, direction.z))
body.velocity = Vector3(body.velocity.x * 0.7, diff.y * 15., body.velocity.z * 0.7)
if Input.is_action_just_pressed(\"jump\"):
body.velocity += Vector3(0, 6, 0)
return Success
return Running
func _exit():
blackboard.anim.set(\"parameters/Ledge/blend_amount\", 0.0)
$\"../Timer\".start()
"
[sub_resource type="GDScript" id="GDScript_nmc1l"]
resource_name = "WallGripAction"
script/source = "extends PlayerAction
@ -275,6 +349,7 @@ parameters/WallAction/blend_amount = 0.0
parameters/WallRunSide/blend_amount = 0.0
parameters/Moving/blend_amount = 0.0
parameters/AirTime/blend_amount = 0.0
parameters/Ledge/blend_amount = 0.0
script = SubResource("GDScript_i825w")
default_blend_time = 30.0
full_speed = 7.0
@ -284,10 +359,11 @@ jump_to_fall_speed = 3.0
air_blend_speed = 6.0
[node name="HangRay" type="RayCast3D" parent="PlayerModel" unique_id=183285060]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.0015578, -0.4715811)
target_position = Vector3(0, -0.2, 0)
hit_from_inside = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, -0.508)
target_position = Vector3(0, -1.792, 0)
hit_back_faces = false
script = SubResource("GDScript_f46kd")
dist_mul = -0.508
[node name="Camera3D" type="PlayerCamera" parent="." unique_id=709217818]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.3841858e-07, 0.81082594, 1.2089427)
@ -301,6 +377,17 @@ character = NodePath("../PlayerModel")
[node name="ActionSelector" type="BehaviourSelector" parent="PlayerBehaviourTree/BehaviourRepeater" unique_id=433229675]
[node name="LedgeHangSequence" type="BehaviourSequence" parent="PlayerBehaviourTree/BehaviourRepeater/ActionSelector" unique_id=285489367]
[node name="Timer" type="Timer" parent="PlayerBehaviourTree/BehaviourRepeater/ActionSelector/LedgeHangSequence" unique_id=465275658]
process_mode = 1
[node name="CanLedgeHang" type="PlayerAction" parent="PlayerBehaviourTree/BehaviourRepeater/ActionSelector/LedgeHangSequence" unique_id=1704888964]
script = SubResource("GDScript_3rerk")
[node name="LedgeHang" type="PlayerAction" parent="PlayerBehaviourTree/BehaviourRepeater/ActionSelector/LedgeHangSequence" unique_id=189327718]
script = SubResource("GDScript_uxov2")
[node name="WallGrip" type="PlayerAction" parent="PlayerBehaviourTree/BehaviourRepeater/ActionSelector" unique_id=85039173]
script = SubResource("GDScript_nmc1l")
wall_drag = 15.0