Much better state machine, crouch ceiling detection and GamePad support
This commit is contained in:
@ -18,6 +18,7 @@ Move with WASD, space to jump, shift to sprint, C to crouch.
|
|||||||
- Crouching
|
- Crouching
|
||||||
- Sprinting
|
- Sprinting
|
||||||
- 2 crosshairs/reticles, one is animated (more to come?)
|
- 2 crosshairs/reticles, one is animated (more to come?)
|
||||||
|
- Controller/GamePad support (enabled through code, see wiki)
|
||||||
|
|
||||||
If you make a cool game with this addon, I would love to hear about it!
|
If you make a cool game with this addon, I would love to hear about it!
|
||||||
|
|
||||||
@ -26,7 +27,12 @@ If you make a cool game with this addon, I would love to hear about it!
|
|||||||
|
|
||||||
You can make this a super basic controller by just disabling everything.
|
You can make this a super basic controller by just disabling everything.
|
||||||
|
|
||||||
**How to change settings:**
|
**How to add controller/GamePad support**
|
||||||
|
In the controls export group, there is a commented section at the end that says "Uncomment this if you want full controller support". Uncomment that block.
|
||||||
|
Make a key map for each direction (left, right, up, down) and map them to your joystick.
|
||||||
|
Write in these keymaps in the controls section of the player settings.
|
||||||
|
In the `_process` function, there is another block of commented code at the end that says the same thing. Uncomment that too.
|
||||||
|
You should now be able to look around with the joystick. Make sure you add the other controls to the input map. (movement, jumping, crouching, sprinting, etc.)
|
||||||
|
|
||||||
**How to change settings:**
|
**How to change settings:**
|
||||||
Click on the character node and there should be settings in the "Feature Settings" group.
|
Click on the character node and there should be settings in the "Feature Settings" group.
|
||||||
|
@ -15,6 +15,7 @@ extends CharacterBody3D
|
|||||||
@export var HEAD : Node3D
|
@export var HEAD : Node3D
|
||||||
@export var CAMERA : Camera3D
|
@export var CAMERA : Camera3D
|
||||||
@export var CAMERA_ANIMATION : AnimationPlayer
|
@export var CAMERA_ANIMATION : AnimationPlayer
|
||||||
|
@export var COLLISION_MESH : CollisionShape3D
|
||||||
|
|
||||||
@export_group("Controls")
|
@export_group("Controls")
|
||||||
# We are using UI controls because they are built into Godot Engine so they can be used right away
|
# We are using UI controls because they are built into Godot Engine so they can be used right away
|
||||||
@ -27,6 +28,12 @@ extends CharacterBody3D
|
|||||||
@export var CROUCH : String
|
@export var CROUCH : String
|
||||||
@export var SPRINT : String
|
@export var SPRINT : String
|
||||||
|
|
||||||
|
# Uncomment if you want full controller support
|
||||||
|
#@export var LOOK_LEFT : String
|
||||||
|
#@export var LOOK_RIGHT : String
|
||||||
|
#@export var LOOK_UP : String
|
||||||
|
#@export var LOOK_DOWN : String
|
||||||
|
|
||||||
@export_group("Feature Settings")
|
@export_group("Feature Settings")
|
||||||
@export var immobile : bool = false
|
@export var immobile : bool = false
|
||||||
@export var jumping_enabled : bool = true
|
@export var jumping_enabled : bool = true
|
||||||
@ -42,8 +49,9 @@ extends CharacterBody3D
|
|||||||
|
|
||||||
# Member variables
|
# Member variables
|
||||||
var speed : float = base_speed
|
var speed : float = base_speed
|
||||||
var is_crouching : bool = false
|
# States: normal, crouching, sprinting
|
||||||
var is_sprinting : bool = false
|
var state : String = "normal"
|
||||||
|
var low_ceiling : bool = false # This is for when the cieling is too low and the player needs to crouch.
|
||||||
|
|
||||||
# Get the gravity from the project settings to be synced with RigidBody nodes
|
# Get the gravity from the project settings to be synced with RigidBody nodes
|
||||||
var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity") # Don't set this as a const, see the gravity section in _physics_process
|
var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity") # Don't set this as a const, see the gravity section in _physics_process
|
||||||
@ -52,6 +60,7 @@ var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
|||||||
func _ready():
|
func _ready():
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
|
|
||||||
# Add some debug data
|
# Add some debug data
|
||||||
@ -68,19 +77,13 @@ func _physics_process(delta):
|
|||||||
var input_dir = Vector2.ZERO
|
var input_dir = Vector2.ZERO
|
||||||
if !immobile:
|
if !immobile:
|
||||||
input_dir = Input.get_vector(LEFT, RIGHT, FORWARD, BACKWARD)
|
input_dir = Input.get_vector(LEFT, RIGHT, FORWARD, BACKWARD)
|
||||||
|
|
||||||
handle_movement(delta, input_dir)
|
handle_movement(delta, input_dir)
|
||||||
|
|
||||||
toggle_crouch()
|
low_ceiling = $CrouchCeilingDetection.is_colliding()
|
||||||
toggle_sprint(input_dir)
|
|
||||||
|
|
||||||
if is_crouching:
|
|
||||||
speed = crouch_speed
|
|
||||||
elif is_sprinting:
|
|
||||||
speed = sprint_speed
|
|
||||||
else:
|
|
||||||
speed = base_speed
|
|
||||||
|
|
||||||
|
handle_state(input_dir)
|
||||||
|
update_camera_fov()
|
||||||
|
update_collision_scale()
|
||||||
|
|
||||||
if view_bobbing:
|
if view_bobbing:
|
||||||
headbob_animation(input_dir)
|
headbob_animation(input_dir)
|
||||||
@ -95,15 +98,14 @@ func handle_jumping():
|
|||||||
if Input.is_action_just_pressed(JUMP) and is_on_floor():
|
if Input.is_action_just_pressed(JUMP) and is_on_floor():
|
||||||
velocity.y += jump_velocity
|
velocity.y += jump_velocity
|
||||||
|
|
||||||
|
|
||||||
func handle_movement(delta, input_dir):
|
func handle_movement(delta, input_dir):
|
||||||
|
|
||||||
var direction = input_dir.rotated(-HEAD.rotation.y)
|
var direction = input_dir.rotated(-HEAD.rotation.y)
|
||||||
direction = Vector3(direction.x, 0, direction.y)
|
direction = Vector3(direction.x, 0, direction.y)
|
||||||
|
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
|
|
||||||
if in_air_momentum:
|
if in_air_momentum:
|
||||||
if is_on_floor(): # Don't lerp y movement
|
if is_on_floor():
|
||||||
if motion_smoothing:
|
if motion_smoothing:
|
||||||
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
|
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
|
||||||
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
|
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
|
||||||
@ -119,58 +121,78 @@ func handle_movement(delta, input_dir):
|
|||||||
velocity.z = direction.z * speed
|
velocity.z = direction.z * speed
|
||||||
|
|
||||||
|
|
||||||
func _process(delta):
|
func handle_state(moving):
|
||||||
|
|
||||||
$UserInterface/DebugPanel.add_property("FPS", 1.0/delta, 0)
|
|
||||||
|
|
||||||
if Input.is_action_just_pressed(PAUSE):
|
|
||||||
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
||||||
elif Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
|
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
||||||
|
|
||||||
|
|
||||||
func _unhandled_input(event):
|
|
||||||
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
|
||||||
HEAD.rotation_degrees.y -= event.relative.x * mouse_sensitivity
|
|
||||||
HEAD.rotation_degrees.x -= event.relative.y * mouse_sensitivity
|
|
||||||
HEAD.rotation.x = clamp(HEAD.rotation.x, deg_to_rad(-90), deg_to_rad(90))
|
|
||||||
|
|
||||||
|
|
||||||
func toggle_crouch():
|
|
||||||
if crouch_enabled:
|
|
||||||
if crouch_mode == 0:
|
|
||||||
is_crouching = Input.is_action_pressed(CROUCH)
|
|
||||||
elif crouch_mode == 1:
|
|
||||||
if Input.is_action_just_pressed(CROUCH):
|
|
||||||
is_crouching = !is_crouching
|
|
||||||
|
|
||||||
# Replace with your own crouch animation code
|
|
||||||
if is_crouching:
|
|
||||||
$Collision.scale.y = lerp($Collision.scale.y, 0.75, 0.2)
|
|
||||||
else:
|
|
||||||
$Collision.scale.y = lerp($Collision.scale.y, 1.0, 0.2)
|
|
||||||
|
|
||||||
|
|
||||||
func toggle_sprint(moving):
|
|
||||||
if sprint_enabled:
|
if sprint_enabled:
|
||||||
if sprint_mode == 0:
|
if sprint_mode == 0:
|
||||||
if !is_crouching: # Crouching takes priority over sprinting
|
if Input.is_action_pressed("sprint") and !Input.is_action_pressed("crouch"):
|
||||||
is_sprinting = Input.is_action_pressed(SPRINT)
|
if moving:
|
||||||
else:
|
if state != "sprinting":
|
||||||
is_sprinting = false # Fix a bug where if you are sprinting and then crouch then let go of the sprinting button you keep sprinting
|
enter_sprint_state()
|
||||||
elif sprint_mode == 1:
|
|
||||||
if Input.is_action_just_pressed(SPRINT):
|
|
||||||
if !is_crouching:
|
|
||||||
is_sprinting = !is_sprinting
|
|
||||||
else:
|
else:
|
||||||
is_sprinting = false
|
if state == "sprinting":
|
||||||
|
enter_normal_state()
|
||||||
|
elif state == "sprinting":
|
||||||
|
enter_normal_state()
|
||||||
|
elif sprint_mode == 1:
|
||||||
|
if moving:
|
||||||
|
if Input.is_action_just_pressed("sprint"):
|
||||||
|
match state:
|
||||||
|
"normal":
|
||||||
|
enter_sprint_state()
|
||||||
|
"sprinting":
|
||||||
|
enter_normal_state()
|
||||||
|
elif state == "sprinting":
|
||||||
|
enter_normal_state()
|
||||||
|
|
||||||
if dynamic_fov:
|
if crouch_enabled:
|
||||||
if is_sprinting and moving:
|
if crouch_mode == 0:
|
||||||
CAMERA.fov = lerp(CAMERA.fov, 85.0, 0.3)
|
if Input.is_action_pressed("crouch") and !Input.is_action_pressed("sprint"):
|
||||||
else:
|
if state != "crouching":
|
||||||
CAMERA.fov = lerp(CAMERA.fov, 75.0, 0.3)
|
enter_crouch_state()
|
||||||
|
elif state == "crouching" and !$CrouchCeilingDetection.is_colliding():
|
||||||
|
enter_normal_state()
|
||||||
|
elif crouch_mode == 1:
|
||||||
|
if Input.is_action_just_pressed("crouch"):
|
||||||
|
match state:
|
||||||
|
"normal":
|
||||||
|
enter_crouch_state()
|
||||||
|
"crouching":
|
||||||
|
if !$CrouchCeilingDetection.is_colliding():
|
||||||
|
enter_normal_state()
|
||||||
|
|
||||||
|
|
||||||
|
func enter_normal_state():
|
||||||
|
#print("entering normal state")
|
||||||
|
var prev_state = state
|
||||||
|
state = "normal"
|
||||||
|
speed = base_speed
|
||||||
|
|
||||||
|
func enter_crouch_state():
|
||||||
|
#print("entering crouch state")
|
||||||
|
var prev_state = state
|
||||||
|
state = "crouching"
|
||||||
|
speed = crouch_speed
|
||||||
|
|
||||||
|
|
||||||
|
func enter_sprint_state():
|
||||||
|
#print("entering sprint state")
|
||||||
|
var prev_state = state
|
||||||
|
state = "sprinting"
|
||||||
|
speed = sprint_speed
|
||||||
|
|
||||||
|
|
||||||
|
func update_camera_fov():
|
||||||
|
if state == "sprinting":
|
||||||
|
CAMERA.fov = lerp(CAMERA.fov, 85.0, 0.3)
|
||||||
|
else:
|
||||||
|
CAMERA.fov = lerp(CAMERA.fov, 75.0, 0.3)
|
||||||
|
|
||||||
|
|
||||||
|
func update_collision_scale():
|
||||||
|
if state == "crouching": # Add your own crouch animation code
|
||||||
|
COLLISION_MESH.scale.y = lerp(COLLISION_MESH.scale.y, 0.75, 0.2)
|
||||||
|
else:
|
||||||
|
COLLISION_MESH.scale.y = lerp(COLLISION_MESH.scale.y, 1.0, 0.2)
|
||||||
|
|
||||||
|
|
||||||
func headbob_animation(moving):
|
func headbob_animation(moving):
|
||||||
@ -179,3 +201,27 @@ func headbob_animation(moving):
|
|||||||
CAMERA_ANIMATION.speed_scale = speed / base_speed
|
CAMERA_ANIMATION.speed_scale = speed / base_speed
|
||||||
else:
|
else:
|
||||||
CAMERA_ANIMATION.play("RESET")
|
CAMERA_ANIMATION.play("RESET")
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
$UserInterface/DebugPanel.add_property("FPS", 1.0/delta, 0)
|
||||||
|
$UserInterface/DebugPanel.add_property("State", state, 0)
|
||||||
|
|
||||||
|
if Input.is_action_just_pressed(PAUSE):
|
||||||
|
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||||
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
|
elif Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
|
||||||
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||||
|
|
||||||
|
HEAD.rotation.x = clamp(HEAD.rotation.x, deg_to_rad(-90), deg_to_rad(90))
|
||||||
|
|
||||||
|
# Uncomment if you want full controller support
|
||||||
|
#var controller_view_rotation = Input.get_vector(LOOK_LEFT, LOOK_RIGHT, LOOK_UP, LOOK_DOWN)
|
||||||
|
#HEAD.rotation_degrees.y -= controller_view_rotation.x * 1.5
|
||||||
|
#HEAD.rotation_degrees.x -= controller_view_rotation.y * 1.5
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event):
|
||||||
|
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||||
|
HEAD.rotation_degrees.y -= event.relative.x * mouse_sensitivity
|
||||||
|
HEAD.rotation_degrees.x -= event.relative.y * mouse_sensitivity
|
||||||
|
@ -59,11 +59,12 @@ MarginContainer/constants/margin_left = 10
|
|||||||
MarginContainer/constants/margin_right = 10
|
MarginContainer/constants/margin_right = 10
|
||||||
MarginContainer/constants/margin_top = 10
|
MarginContainer/constants/margin_top = 10
|
||||||
|
|
||||||
[node name="Character" type="CharacterBody3D" node_paths=PackedStringArray("HEAD", "CAMERA", "CAMERA_ANIMATION")]
|
[node name="Character" type="CharacterBody3D" node_paths=PackedStringArray("HEAD", "CAMERA", "CAMERA_ANIMATION", "COLLISION_MESH")]
|
||||||
script = ExtResource("1_0t4e8")
|
script = ExtResource("1_0t4e8")
|
||||||
HEAD = NodePath("Head")
|
HEAD = NodePath("Head")
|
||||||
CAMERA = NodePath("Head/Camera")
|
CAMERA = NodePath("Head/Camera")
|
||||||
CAMERA_ANIMATION = NodePath("Head/camera_animation")
|
CAMERA_ANIMATION = NodePath("Head/camera_animation")
|
||||||
|
COLLISION_MESH = NodePath("Collision")
|
||||||
CROUCH = "crouch"
|
CROUCH = "crouch"
|
||||||
SPRINT = "sprint"
|
SPRINT = "sprint"
|
||||||
|
|
||||||
@ -117,4 +118,8 @@ layout_mode = 2
|
|||||||
[node name="VBoxContainer" type="VBoxContainer" parent="UserInterface/DebugPanel/MarginContainer"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="UserInterface/DebugPanel/MarginContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="CrouchCeilingDetection" type="RayCast3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||||
|
target_position = Vector3(0, 1, 0)
|
||||||
|
|
||||||
[editable path="UserInterface/Reticle_1"]
|
[editable path="UserInterface/Reticle_1"]
|
||||||
|
Reference in New Issue
Block a user