Files
godot-demo-projects/3d/ik/fps/example_player.gd
Hugo Locurcio 5f4a9e409f Use InputEventMouseMotion.screen_relative where relevant in all demos (#1232)
This provides mouse sensitivity that is independent of the viewport
size, without needing to query for project settings.

This also inverts the mouse motion direction in the 3D navigation demo
to better match expectations, and increases mouse sensitivity in the
Window Management demo to be closer to other demos.
2025-10-01 18:54:19 -07:00

225 lines
6.1 KiB
GDScript

extends CharacterBody3D
# Walking variables.
const norm_grav = -38.8
const MAX_SPEED = 22
const JUMP_SPEED = 26
const ACCEL = 8.5
# Sprinting variables. Similar to the variables above, just allowing for quicker movement
const MAX_SPRINT_SPEED = 34
const SPRINT_ACCEL = 18
# How fast we slow down, and the steepest angle we can climb.
const DEACCEL = 28
const MAX_SLOPE_ANGLE = 40
# How fast the bullets launch
const LEFT_MOUSE_FIRE_TIME = 0.15
const BULLET_SPEED = 100
var vel = Vector3()
# A vector for storing the direction the player intends to walk towards.
var dir = Vector3()
# A boolean to track whether or not we are sprinting
var is_sprinting = false
# You may need to adjust depending on the sensitivity of your mouse
var MOUSE_SENSITIVITY = 0.08
# A boolean for tracking whether the jump button is down
var jump_button_down = false
# The current lean value (our position on the lean track) and the path follow node
var lean_value = 0.5
# A variable for tracking if the right mouse button is down.
var right_mouse_down = false
# A variable for tracking if we can fire using the left mouse button
var left_mouse_timer = 0
# A boolean for tracking whether we can change animations or not
var anim_done = true
# The current animation name
var current_anim = "Starter"
# The simple bullet rigidbody
var simple_bullet = preload("res://fps/simple_bullet.tscn")
# We need the camera for getting directional vectors. We rotate ourselves on the Y-axis using
# the camera_holder to avoid rotating on more than one axis at a time.
@onready var camera_holder = $CameraHolder
@onready var camera = $CameraHolder/LeanPath/PathFollow3D/IK_LookAt_Chest/Camera3D
@onready var path_follow_node = $CameraHolder/LeanPath/PathFollow3D
# The animation player for aiming down the sights.
@onready var anim_player = $CameraHolder/AnimationPlayer
# The end of the pistol.
@onready var pistol_end = $CameraHolder/Weapon/Pistol/PistolEnd
func _ready():
anim_player.animation_finished.connect(animation_finished)
set_physics_process(true)
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
set_process_input(true)
func _physics_process(delta):
process_input(delta)
process_movement(delta)
func process_input(delta):
# Reset dir, so our previous movement does not effect us
dir = Vector3()
# Get the camera's global transform so we can use its directional vectors
var cam_xform = camera.get_global_transform()
# ----------------------------------
# Walking
if Input.is_key_pressed(KEY_UP) or Input.is_key_pressed(KEY_W):
dir += -cam_xform.basis[2]
if Input.is_key_pressed(KEY_DOWN) or Input.is_key_pressed(KEY_S):
dir += cam_xform.basis[2]
if Input.is_key_pressed(KEY_LEFT) or Input.is_key_pressed(KEY_A):
dir += -cam_xform.basis[0]
if Input.is_key_pressed(KEY_RIGHT) or Input.is_key_pressed(KEY_D):
dir += cam_xform.basis[0]
if Input.is_action_just_pressed(&"ui_cancel"):
if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if Input.is_mouse_button_pressed(2):
if not right_mouse_down:
right_mouse_down = true
if anim_done:
if current_anim != "Aiming":
anim_player.play("Aiming")
current_anim = "Aiming"
else:
anim_player.play("Idle")
current_anim = "Idle"
anim_done = false
else:
right_mouse_down = false
if Input.is_mouse_button_pressed(1):
if left_mouse_timer <= 0:
left_mouse_timer = LEFT_MOUSE_FIRE_TIME
# Create a bullet
var new_bullet = simple_bullet.instantiate()
get_tree().root.add_child(new_bullet)
new_bullet.global_transform = pistol_end.global_transform
new_bullet.linear_velocity = new_bullet.global_transform.basis.z * BULLET_SPEED
if left_mouse_timer > 0:
left_mouse_timer -= delta
# ----------------------------------
# ----------------------------------
# Sprinting
if Input.is_key_pressed(KEY_SHIFT):
is_sprinting = true
else:
is_sprinting = false
# ----------------------------------
# ----------------------------------
# Jumping
if Input.is_key_pressed(KEY_SPACE):
if not jump_button_down:
jump_button_down = true
if is_on_floor():
vel.y = JUMP_SPEED
else:
jump_button_down = false
# ----------------------------------
# ----------------------------------
# Leaning
if Input.is_key_pressed(KEY_Q):
lean_value += 1.2 * delta
elif Input.is_key_pressed(KEY_E):
lean_value -= 1.2 * delta
else:
if lean_value > 0.5:
lean_value -= 1 * delta
if lean_value < 0.5:
lean_value = 0.5
elif lean_value < 0.5:
lean_value += 1 * delta
if lean_value > 0.5:
lean_value = 0.5
lean_value = clamp(lean_value, 0, 1)
path_follow_node.h_offset = lean_value
if lean_value < 0.5:
var lerp_value = lean_value * 2
path_follow_node.rotation_degrees.z = (20 * (1 - lerp_value))
else:
var lerp_value = (lean_value - 0.5) * 2
path_follow_node.rotation_degrees.z = (-20 * lerp_value)
# ----------------------------------
func process_movement(delta):
var grav = norm_grav
dir.y = 0
dir = dir.normalized()
vel.y += delta * grav
var hvel = vel
hvel.y = 0
var target = dir
if is_sprinting:
target *= MAX_SPRINT_SPEED
else:
target *= MAX_SPEED
var accel
if dir.dot(hvel) > 0:
if not is_sprinting:
accel = ACCEL
else:
accel = SPRINT_ACCEL
else:
accel = DEACCEL
hvel = hvel.lerp(target, accel * delta)
vel.x = hvel.x
vel.z = hvel.z
# TODO: This information should be set to the CharacterBody properties instead of arguments.
velocity = vel
move_and_slide()
# Mouse based camera movement
func _input(event):
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
rotate_y(deg_to_rad(event.screen_relative.x * MOUSE_SENSITIVITY * -1))
camera_holder.rotate_x(deg_to_rad(event.screen_relative.y * MOUSE_SENSITIVITY))
# We need to clamp the camera's rotation so we cannot rotate ourselves upside down
var camera_rot = camera_holder.rotation_degrees
if camera_rot.x < -40:
camera_rot.x = -40
elif camera_rot.x > 60:
camera_rot.x = 60
camera_holder.rotation_degrees = camera_rot
else:
pass
func animation_finished(_anim):
anim_done = true