Skip to content

Commit

Permalink
syntax changes when change state on fsm inside the first person contr…
Browse files Browse the repository at this point in the history
…oller and shaping the wall jump behaviour
  • Loading branch information
ninetailsrabbit committed Dec 22, 2024
1 parent 9ecbb4f commit cbebfbb
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,37 @@ class_name FirstPersonController extends CharacterBody3D
@export var crawl: bool = false
@export var slide: bool = true
@export var wall_run: bool = false
@export var wall_jump: bool = false
@export var wall_jump: bool = false:
set(value):
if value != wall_jump:
wall_jump = value
_update_wall_checkers()

@export var wall_climb: bool = false
@export var surf: bool = false
@export var swim: bool = false
@export var stairs: bool = true
@export var stairs: bool = true:
set(value):
if value != stairs:
stairs = value
_update_wall_checkers()

@onready var debug_ui: CanvasLayer = $DebugUI
@onready var finite_state_machine: FiniteStateMachine = $FiniteStateMachine
@onready var camera: CameraShake3D = $CameraController/Head/CameraShake3D
@onready var camera_controller: CameraController3D = $CameraController

## This raycast detects walls so the stair up and stair down is not applied to avoid a weird
## 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 top_right_wall_checker: RayCast3D = %TopRightWallChecker
@onready var bottom_left_wall_checker: RayCast3D = %BottomLeftWallChecker
@onready var top_left_wall_checker: RayCast3D = %TopLeftWallChecker

@onready var ceil_shape_cast: ShapeCast3D = $CeilShapeCast

@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var stand_collision_shape: CollisionShape3D = $StandCollisionShape
@onready var crouch_collision_shape: CollisionShape3D = $CrouchCollisionShape
Expand All @@ -51,6 +70,8 @@ func _enter_tree() -> void:


func _ready() -> void:
_update_wall_checkers()

collision_layer = GameGlobals.player_collision_layer
debug_ui.visible = OS.is_debug_build()

Expand Down Expand Up @@ -89,6 +110,12 @@ func is_falling() -> bool:
return not is_grounded and opposite_to_gravity_vector


func can_wall_jump() -> bool:
return wall_jump \
and ( (top_left_wall_checker.is_colliding() and bottom_left_wall_checker.is_colliding()) \
or (top_right_wall_checker.is_colliding() and bottom_right_wall_checker.is_colliding()) )


func lock_movement() -> void:
finite_state_machine.lock_state_machine()
camera_controller.lock()
Expand All @@ -98,14 +125,25 @@ func unlock_movement() -> void:
finite_state_machine.unlock_state_machine()
camera_controller.unlock()


func switch_mouse_capture_mode() -> void:
if InputHelper.is_mouse_visible():
InputHelper.capture_mouse()
else:
InputHelper.show_mouse_cursor()


func _update_wall_checkers() -> void:
if is_inside_tree():
top_left_wall_checker.enabled = wall_jump or wall_run
top_right_wall_checker.enabled = wall_jump or wall_run
bottom_left_wall_checker.enabled = wall_jump or wall_run
bottom_right_wall_checker.enabled = wall_jump or wall_run

front_close_wall_checker.enabled = stairs
back_close_wall_checker.enabled = stairs


func _update_collisions_based_on_state(current_state: MachineState) -> void:
match current_state.name:
"Idle", "Walk", "Run":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=31 format=3 uid="uid://bx3bh475g3jjf"]
[gd_scene load_steps=32 format=3 uid="uid://bx3bh475g3jjf"]

[ext_resource type="Script" path="res://components/motion/3D/first-person/controller/first_person_controller.gd" id="1_v7v7g"]
[ext_resource type="PackedScene" uid="uid://bcj2w63oj13e5" path="res://components/motion/3D/first-person/controller/debug_ui/first_person_debug_ui.tscn" id="2_ml2dd"]
Expand All @@ -13,6 +13,7 @@
[ext_resource type="Script" path="res://components/motion/3D/first-person/controller/states/air/jump.gd" id="10_ynwvi"]
[ext_resource type="Script" path="res://components/motion/3D/first-person/controller/states/ground/slide.gd" id="11_jpv7j"]
[ext_resource type="Script" path="res://components/motion/3D/first-person/controller/states/special/swim.gd" id="12_vyybp"]
[ext_resource type="Script" path="res://components/motion/3D/first-person/controller/states/air/wall_jump.gd" id="13_uw3wc"]
[ext_resource type="Script" path="res://components/cards/camera/3D/shake/camera_shake_3d.gd" id="14_smxor"]
[ext_resource type="Script" path="res://components/motion/3D/first-person/shooter/weapons/firearm_weapon_holder.gd" id="15_cjftc"]
[ext_resource type="Script" path="res://components/motion/3D/first-person/shooter/weapons/motion/weapon_sway.gd" id="16_2a3la"]
Expand Down Expand Up @@ -209,6 +210,10 @@ script = ExtResource("12_vyybp")
eyes = NodePath("../../../CameraController/Head")
actor = NodePath("../../..")

[node name="WallJump" type="Node" parent="FiniteStateMachine/Special" node_paths=PackedStringArray("actor")]
script = ExtResource("13_uw3wc")
actor = NodePath("../../..")

[node name="StandCollisionShape" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_1od8w")

Expand Down Expand Up @@ -264,3 +269,43 @@ script = ExtResource("20_iio1q")
[node name="FireArmWeapon" type="Node3D" parent="CameraController/Head/CameraShake3D/FireArmWeaponHolder/Sway/Noise/Tilt/Impulse/Recoil" node_paths=PackedStringArray("camera")]
script = ExtResource("21_q3kyc")
camera = NodePath("../../../../../../..")

[node name="WallCheckers" type="Node3D" parent="."]

[node name="FrontCloseWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
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="BackCloseWallChecker" type="RayCast3D" parent="WallCheckers"]
unique_name_in_owner = true
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"]
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)
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)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="BottomLeftWallChecker" 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)
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(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
target_position = Vector3(-1.3, 0, 0)
debug_shape_custom_color = Color(0.78019, 0.402978, 0.179512, 1)

[node name="Node3D" type="Node3D" parent="."]
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class_name AirState extends MachineState
@export var air_friction: float = 10.0
@export var maximum_fall_velocity: float = 25.0
@export_group("Input actions")
@export var jump_input_action: String = "jump"
@export var jump_input_action: String = InputControls.JumpAction

var current_air_speed: float = 0.0

Expand All @@ -26,7 +26,7 @@ func apply_gravity(force: float = gravity_force, delta: float = get_physics_proc

func air_move(delta: float = get_physics_process_delta_time()) -> void:
if not actor.motion_input.input_direction.is_zero_approx():
accelerate(delta)
accelerate(delta)


func accelerate(delta: float = get_physics_process_delta_time()) -> void:
Expand Down Expand Up @@ -59,15 +59,20 @@ func get_speed() -> float:

func detect_jump() -> void:
if actor.jump and InputMap.has_action(jump_input_action) and Input.is_action_just_pressed(jump_input_action):
FSM.change_state_to("Jump")
FSM.change_state_to(Jump)


func detect_wall_jump() -> void:
if actor.can_wall_jump() and InputMap.has_action(jump_input_action) and Input.is_action_just_pressed(jump_input_action):
FSM.change_state_to(WallJump)


func detect_swim() -> void:
if FSM.states.has("Swim") and actor.swim:
var swim_state: Swim = FSM.states["Swim"] as Swim

if swim_state.eyes.global_position.y <= swim_state.water_height:
FSM.change_state_to("Swim")
FSM.change_state_to(Swim)


func limit_fall_velocity() -> void:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func physics_update(delta: float):
FSM.change_state_to("Walk")

detect_swim()
detect_wall_jump()

actor.move_and_slide()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,13 @@ func physics_update(delta: float):

if actor.is_grounded:
if actor.motion_input.input_direction.is_zero_approx():
FSM.change_state_to("Idle")
FSM.change_state_to(Idle)
else:
FSM.change_state_to("Walk")
FSM.change_state_to(Walk)


detect_fall_after_jump_fall_time_passed()
detect_wall_jump()

actor.move_and_slide()

Expand Down Expand Up @@ -138,7 +139,7 @@ func detect_fall_after_jump_fall_time_passed() -> void:
or up_direction_opposite.is_equal_approx(Vector3.UP) and actor.position.y > last_jumped_position.y \
or up_direction_opposite.is_equal_approx(Vector3.RIGHT) and actor.position.x > last_jumped_position.x \
or up_direction_opposite.is_equal_approx(Vector3.LEFT) and actor.position.x < last_jumped_position.x:
FSM.change_state_to("Fall")
FSM.change_state_to(Fall)



Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
class_name WallJump extends AirState

@export var wall_jump_horizontal_force: float = 5.0
@export var wall_jump_vertical_force: float = 7.0
@export var wall_jump_times: int = 3


var current_wall_jump_times: int = 0
var current_wall_normal: Vector3 = Vector3.ZERO


func enter() -> void:
apply_wall_jump()


func exit(_next_state: MachineState):
current_wall_jump_times = 0
current_wall_normal = Vector3.ZERO


func physics_update(delta: float):
super.physics_update(delta)

if actor.is_grounded:
if actor.motion_input.input_direction.is_zero_approx():
FSM.change_state_to(Idle)
else:
FSM.change_state_to(Walk)

if actor.can_wall_jump() and current_wall_jump_times < wall_jump_times and InputMap.has_action(jump_input_action) and Input.is_action_just_pressed(jump_input_action):
apply_wall_jump()

actor.move_and_slide()


func apply_wall_jump() -> void:
current_wall_normal = get_current_wall_normal()

if current_wall_normal.is_zero_approx():
FSM.change_state_to(Fall)
return

current_wall_jump_times += 1
actor.velocity = wall_jump_horizontal_force * current_wall_normal
actor.velocity.y = wall_jump_vertical_force


func get_current_wall_normal() -> Vector3:
if actor.top_left_wall_checker.is_colliding():
return actor.top_left_wall_checker.get_collision_normal()
elif actor.bottom_left_wall_checker.is_colliding():
return actor.bottom_left_wall_checker.get_collision_normal()
elif actor.top_right_wall_checker.is_colliding():
return actor.top_right_wall_checker.get_collision_normal()
elif actor.bottom_right_wall_checker.is_colliding():
return actor.bottom_right_wall_checker_wall_checker.get_collision_normal()
else:
return Vector3.ZERO
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func physics_update(delta):
super.physics_update(delta)

if not Input.is_action_pressed(crawl_input_action) and not actor.ceil_shape_cast.is_colliding():
FSM.change_state_to("Crouch")
FSM.change_state_to(Crouch)

accelerate(delta)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ func physics_update(delta):

if not Input.is_action_pressed(crouch_input_action) and not actor.ceil_shape_cast.is_colliding():
if actor.motion_input.input_direction.is_zero_approx():
FSM.change_state_to("Idle")
FSM.change_state_to(Idle)
else:
FSM.change_state_to("Walk")
FSM.change_state_to(Walk)

accelerate(delta)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class_name GroundState extends MachineState
@export var crouch_input_action: StringName = InputControls.CrouchAction
@export var crawl_input_action: StringName = InputControls.CrawlAction
@export_group("Animation")
@export var crouch_animation: StringName = "crouch"
@export var crawl_animation: StringName = "crawl"
@export var crouch_animation: StringName = InputControls.CrouchAction
@export var crawl_animation: StringName = InputControls.CrawlAction

var current_speed: float = 0
var stair_stepping := false
Expand Down Expand Up @@ -65,9 +65,11 @@ func get_speed() -> float:


func stair_step_up():
if not actor.stairs or not stair_stepping_enabled:
if not actor.stairs or not stair_stepping_enabled \
or actor.front_close_wall_checker.is_colliding() or actor.back_close_wall_checker.is_colliding():
return


stair_stepping = false

if actor.motion_input.world_coordinate_space_direction.is_zero_approx():
Expand Down Expand Up @@ -146,7 +148,8 @@ func stair_step_up():


func stair_step_down():
if not actor.stairs or not stair_stepping_enabled:
if not actor.stairs or not stair_stepping_enabled \
or actor.front_close_wall_checker.is_colliding() or actor.back_close_wall_checker.is_colliding():
return

stair_stepping = false
Expand All @@ -171,25 +174,25 @@ func stair_step_down():
#region State Detectors
func detect_run() -> void:
if actor.run and InputMap.has_action(run_input_action) and Input.is_action_pressed(run_input_action):
FSM.change_state_to("Run")
FSM.change_state_to(Run)


func detect_slide() -> void:
if actor.crouch and actor.slide and InputMap.has_action(crouch_input_action) and Input.is_action_pressed(crouch_input_action):
FSM.change_state_to("Slide")
FSM.change_state_to(Slide)


func detect_crouch() -> void:
if actor.crouch and InputMap.has_action(crouch_input_action) and Input.is_action_pressed(crouch_input_action):
FSM.change_state_to("Crouch")
FSM.change_state_to(Crouch)


func detect_crawl() -> void:
if actor.crawl and InputMap.has_action(crawl_input_action) and Input.is_action_pressed(crawl_input_action):
FSM.change_state_to("Crawl")
FSM.change_state_to(Crawl)


func detect_jump() -> void:
if actor.jump and InputMap.has_action(jump_input_action) and Input.is_action_just_pressed(jump_input_action):
FSM.change_state_to("Jump")
FSM.change_state_to(Jump)
#endregion
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func physics_update(delta):
decelerate(delta)

if not actor.motion_input.input_direction.is_zero_approx():
FSM.change_state_to("Walk")
FSM.change_state_to(Walk)

detect_jump()
detect_crouch()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func physics_update(delta):
super.physics_update(delta)

if actor.motion_input.input_direction.is_zero_approx() or not Input.is_action_pressed(run_input_action):
FSM.change_state_to("Walk")
FSM.change_state_to(Walk)

accelerate(delta)

Expand Down
Loading

0 comments on commit cbebfbb

Please sign in to comment.