Skip to content

Commit

Permalink
transitions between any air state and wall run fixed to not stay on i…
Browse files Browse the repository at this point in the history
…nfinite loop between states
  • Loading branch information
ninetailsrabbit committed Dec 22, 2024
1 parent 8d0b450 commit 6dfebce
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func run_transition(from: MachineState, to: MachineState, parameters: Dictionary
transition.to_state = to
transition.parameters = parameters

if transition.should_transition():
if transition.should_transition():
transition.on_transition()
state_changed.emit(from, to)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,10 @@ class_name FirstPersonController extends CharacterBody3D
## stuttering movement on irregular vertical surfaces
@onready var front_close_wall_checker: RayCast3D = %FrontCloseWallChecker
@onready var back_close_wall_checker: RayCast3D = %BackCloseWallChecker
@onready var bottom_right_wall_checker: RayCast3D = %BottomRightWallChecker
@onready var bottom_right_wall_checker_2: RayCast3D = %BottomRightWallChecker2
@onready var top_right_wall_checker: RayCast3D = %TopRightWallChecker
@onready var top_right_wall_checker_2: RayCast3D = %TopRightWallChecker2
@onready var bottom_left_wall_checker: RayCast3D = %BottomLeftWallChecker
@onready var bottom_left_wall_checker_2: RayCast3D = %BottomLeftWallChecker2
@onready var top_left_wall_checker: RayCast3D = %TopLeftWallChecker
@onready var top_left_wall_checker_2: RayCast3D = %TopLeftWallChecker2
@onready var right_wall_checker: RayCast3D = %RightWallChecker
@onready var right_wall_checker_2: RayCast3D = %RightWallChecker2
@onready var left_wall_checker: RayCast3D = %LeftWallChecker
@onready var left_wall_checker_2: RayCast3D = %LeftWallChecker2

@onready var ceil_shape_cast: ShapeCast3D = $CeilShapeCast

Expand Down Expand Up @@ -87,8 +83,9 @@ func _ready() -> void:
finite_state_machine.register_transitions([
WalkToRunTransition.new(),
RunToWalkTransition.new(),
JumpToWallRunTransition.new(),
FallToWallRunTransition.new()
AnyToWallRunTransition.new(),
WallRunToFallTransition.new(),
WallRunToWallJumpTransition.new()
])

finite_state_machine.state_changed.connect(on_state_changed)
Expand Down Expand Up @@ -119,39 +116,21 @@ func is_falling() -> bool:


func wall_detected() -> bool:
var top_right_wall: bool = top_right_wall_checker.is_colliding() or top_right_wall_checker_2.is_colliding()
var bottom_right_wall: bool = bottom_right_wall_checker.is_colliding() or bottom_right_wall_checker_2.is_colliding()
var top_left_wall: bool = top_left_wall_checker.is_colliding() or top_left_wall_checker_2.is_colliding()
var bottom_left_wall: bool = bottom_left_wall_checker.is_colliding() or bottom_left_wall_checker_2.is_colliding()
var right_wall: bool = right_wall_checker.is_colliding() or right_wall_checker_2.is_colliding()
var left_wall: bool = left_wall_checker.is_colliding() or left_wall_checker_2.is_colliding()

return wall_jump and ((top_right_wall and bottom_right_wall) or (top_left_wall and bottom_left_wall))
return wall_jump and (right_wall or left_wall)


func get_current_wall_detected_normal() -> Vector3:
if top_left_wall_checker.is_colliding():
return top_left_wall_checker.get_collision_normal()

elif top_left_wall_checker_2.is_colliding():
return top_left_wall_checker_2.get_collision_normal()

elif bottom_left_wall_checker.is_colliding():
return bottom_left_wall_checker.get_collision_normal()

elif bottom_left_wall_checker_2.is_colliding():
return bottom_left_wall_checker_2.get_collision_normal()

elif top_right_wall_checker.is_colliding():
return top_right_wall_checker.get_collision_normal()

elif top_right_wall_checker_2.is_colliding():
return top_right_wall_checker_2.get_collision_normal()

elif bottom_right_wall_checker.is_colliding():
return bottom_right_wall_checker.get_collision_normal()

elif bottom_right_wall_checker_2.is_colliding():
return bottom_right_wall_checker_2.get_collision_normal()

if right_wall_checker.is_colliding():
return right_wall_checker.get_collision_normal()
elif right_wall_checker_2.is_colliding():
return right_wall_checker_2.get_collision_normal()
elif left_wall_checker.is_colliding():
return left_wall_checker.get_collision_normal()
elif left_wall_checker_2.is_colliding():
return left_wall_checker_2.get_collision_normal()
else:
return Vector3.ZERO

Expand All @@ -175,14 +154,10 @@ func switch_mouse_capture_mode() -> void:

func _update_wall_checkers() -> void:
if is_inside_tree():
top_left_wall_checker.enabled = wall_jump or wall_run
top_left_wall_checker_2.enabled = wall_jump or wall_run
top_right_wall_checker.enabled = wall_jump or wall_run
top_right_wall_checker_2.enabled = wall_jump or wall_run
bottom_left_wall_checker.enabled = wall_jump or wall_run
bottom_left_wall_checker_2.enabled = wall_jump or wall_run
bottom_right_wall_checker.enabled = wall_jump or wall_run
bottom_right_wall_checker_2.enabled = wall_jump or wall_run
right_wall_checker.enabled = wall_jump or wall_run
right_wall_checker_2.enabled = wall_jump or wall_run
left_wall_checker.enabled = wall_jump or wall_run
left_wall_checker_2.enabled = wall_jump or wall_run

front_close_wall_checker.enabled = stairs
back_close_wall_checker.enabled = stairs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,51 +249,27 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.05)
target_position = Vector3(0, 0, 1)
debug_shape_custom_color = Color(0.613951, 0.141829, 0.230894, 1)

[node name="BottomRightWallChecker" type="RayCast3D" parent="WallCheckers"]
[node name="RightWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.4, 0)
target_position = Vector3(1.3, 0, 0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="TopRightWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
target_position = Vector3(1.3, 0, -0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="BottomRightWallChecker2" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.4, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.258439, 0)
target_position = Vector3(1.3, 0, -0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="TopRightWallChecker2" type="RayCast3D" parent="WallCheckers"]
[node name="RightWallChecker2" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.258439, 0)
target_position = Vector3(1.3, 0, 0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="BottomLeftWallChecker" type="RayCast3D" parent="WallCheckers"]
[node name="LeftWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(0.999982, 0.00539066, -0.00255976, -0.00539032, 0.999985, 0.000138447, 0.00256046, -0.000124641, 0.999997, -0.00269533, -0.399993, 6.23205e-05)
transform = Transform3D(0.999982, 0.00539066, -0.00255976, -0.00539032, 0.999985, 0.000138447, 0.00256046, -0.000124641, 0.999997, 0.00269533, 0.258432, -6.23205e-05)
target_position = Vector3(-1.3, 0, -0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="TopLeftWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(0.999982, 0.00539066, -0.00255976, -0.00539032, 0.999985, 0.000138447, 0.00256046, -0.000124641, 0.999997, 0.00269533, 0.599993, -6.23205e-05)
target_position = Vector3(-1.3, 0, -0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="BottomLeftWallChecker2" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.4, 0)
target_position = Vector3(-1.3, 0, 0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="TopLeftWallChecker2" type="RayCast3D" parent="WallCheckers"]
[node name="LeftWallChecker2" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.258439, 0)
target_position = Vector3(-1.3, 0, 0.8)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var current_jump_input_buffer_time_frames: int = 0:


func enter():
print("entro fall")
jump_requested = false
current_coyote_time_frames = coyote_time_frames
current_jump_input_buffer_time_frames = jump_input_buffer_time_frames
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ func physics_update(delta: float):
else:
FSM.change_state_to(Walk)


detect_fall_after_jump_fall_time_passed()
detect_wall_jump()
detect_wall_run()
Expand Down
49 changes: 35 additions & 14 deletions components/motion/3D/first-person/controller/states/air/wall_run.gd
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class_name WallRun extends AirState

@export_category("Camera")
@export_range(0, 360.0, 0.01) var camera_rotation_angle: float = 5.0
@export_range(0, 360.0, 0.01) var camera_lerp_rotation_factor: float = 8.0
@export_range(0, 360.0, 0.01) var camera_tilt_angle: float = 5.0
@export_range(0, 360.0, 0.01) var camera_lerp_tilt_factor: float = 8.0
@export var camera_tilt_comeback_time: float = 0.35
@export_category("Parameters")
@export var wall_speed: float = 5.0
@export var reduce_speed_gradually: bool = true
Expand All @@ -12,21 +13,27 @@ class_name WallRun extends AirState
@export var wall_friction: float = 0.2
## Set to zero to not have a limited time on wall running.
## It can be further interrupted by the termination of the wall itself or by an external force that interrupts the action.
@export var wall_run_time: float = 0.0
@export var wall_run_time: float = 0.7
## When the wall run ends, the player cannot wall run again until this time passed
@export var wall_run_cooldown: float = 0.5
@export var wall_run_cooldown: float = 1.0

var current_wall_direction: Vector3 = Vector3.ZERO
var current_wall_normal: Vector3 = Vector3.ZERO
var wall_run_timer: Timer
var wall_run_cooldown_timer: Timer
var decrease_rate: float = 0.0

var original_camera_rotation: Vector3 = Vector3.ZERO


func ready() -> void:
_create_wall_run_timers()


func enter() -> void:
print("entro wall run")
original_camera_rotation = actor.camera.rotation

current_wall_normal = actor.get_current_wall_detected_normal()
current_wall_direction = calculate_wall_run_direction()

Expand All @@ -36,29 +43,42 @@ func enter() -> void:

if wall_run_time > 0 and is_instance_valid(wall_run_timer):
wall_run_timer.start(wall_run_time)


if is_instance_valid(wall_run_cooldown_timer):
wall_run_cooldown_timer.stop()


func physics_update(delta: float) -> void:
actor.camera.rotation.z = lerp_angle(
actor.camera.rotation.z,
camera_rotation_angle * 1 if current_wall_normal.is_equal_approx(Vector3.LEFT) else -1,
delta * camera_lerp_rotation_factor
)
actor.camera.rotation.z = lerp_angle(actor.camera.rotation.z, calculate_camera_angle(), delta * camera_lerp_tilt_factor)

detect_wall_jump()

actor.move_and_slide()


func exit(_next_state: MachineState) -> void:
if actor.camera.rotation.z != original_camera_rotation.z:
var tween: Tween = create_tween()
tween.tween_property(actor.camera, "rotation:z", original_camera_rotation.z, camera_tilt_comeback_time)\
.set_ease(Tween.EASE_OUT)

wall_run_timer.stop()

if wall_run_cooldown > 0 and is_instance_valid(wall_run_cooldown_timer):
wall_run_cooldown_timer.start(wall_run_cooldown)

func calculate_camera_angle() -> float:
var angle: float = deg_to_rad(camera_tilt_angle)

if current_wall_normal.is_equal_approx(Vector3.RIGHT) or current_wall_normal.is_equal_approx(Vector3.FORWARD):
angle *= -1

return angle


func calculate_wall_run_direction() -> Vector3:
var wall_direction = current_wall_normal.cross(Vector3.UP)

if (-actor.global_transform.basis.z - wall_direction).length() > (-actor.global_transform.basis.z - -wall_direction).length():
var player_direction: Vector3 = actor.global_transform.basis.z

if (-player_direction - wall_direction).length() > (-player_direction - -wall_direction).length():
wall_direction *= -1

return wall_direction
Expand Down Expand Up @@ -90,4 +110,5 @@ func _create_wall_run_timers() -> void:


func on_wall_run_timer_timeout() -> void:
wall_run_cooldown_timer.start(wall_run_cooldown)
FSM.change_state_to(Fall)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class_name AnyToWallRunTransition extends MachineTransition


func should_transition() -> bool:
if from_state is AirState:
return from_state.wall_run_start_cooldown_timer.is_stopped()

return false

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class_name WallRunToFallTransition extends MachineTransition



func on_transition() -> void:
if from_state is WallRun and to_state is Fall:
if from_state.wall_run_cooldown > 0 and is_instance_valid(from_state.wall_run_cooldown_timer):
from_state.wall_run_cooldown_timer.start(from_state.wall_run_cooldown)

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class_name WallRunToWallJumpTransition extends MachineTransition


func on_transition() -> void:
if from_state is WallRun and to_state is WallJump:
if from_state.wall_run_cooldown > 0 and is_instance_valid(from_state.wall_run_cooldown_timer):
from_state.wall_run_cooldown_timer.start(from_state.wall_run_cooldown)

0 comments on commit 6dfebce

Please sign in to comment.