Skip to content

Instantly share code, notes, and snippets.

@Lassi-Koykka
Last active January 24, 2024 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lassi-Koykka/205844e549e37063df1729d504a5bb06 to your computer and use it in GitHub Desktop.
Save Lassi-Koykka/205844e549e37063df1729d504a5bb06 to your computer and use it in GitHub Desktop.
Simple Godot first-person character controller which also handles climbing stairs
class_name Player extends CharacterBody3D
@onready var head: Node3D = $Head
@onready var arms: Node3D = $Head/Camera3D/Arms
@onready var head_default_pos: Vector3 = head.position
@onready var arms_default_pos: Vector3 = arms.position
@export var SPEED: float = 10
@export var JUMP_VELOCITY: float = 4.5
@export var HEAD_LERP_SPEED: float = 10
@export var MAX_STEP_HEIGHT: float = 0.3
@export var MIN_STEP_WIDTH: float = 0.1
@export var STEP_UP_MARGIN: float = 0.001
@export var STEP_DOWN_MARGIN: float = 0.01
@export_range(-90, 90, 0.001, "radians_as_degrees") var HEAD_ANGLE_MAX: float = PI/2
@export_range(-90, 90, 0.001, "radians_as_degrees") var HEAD_ANGLE_MIN: float = -PI/2
var look_sensitivity = ProjectSettings.get_setting("player/look_sensitivity")
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var input_dir: Vector2 = Vector2.ZERO
var mouse_dir: Vector2 = Vector2.ZERO
var is_falling = false
func _ready():
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _input(event):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and event is InputEventMouseMotion:
mouse_dir = event.relative
rotate_y(deg_to_rad(-event.relative.x * look_sensitivity))
head.rotate_x(deg_to_rad(-event.relative.y * look_sensitivity))
head.rotation.x = clamp(head.rotation.x, HEAD_ANGLE_MIN, HEAD_ANGLE_MAX)
func _physics_process(delta: float):
head.position = lerp(head.position, head_default_pos, HEAD_LERP_SPEED * delta)
input_dir = Input.get_vector("move_left","move_right","move_forward","move_backward").normalized()
var direction: Vector3 = global_transform.basis * Vector3(input_dir.x, 0, input_dir.y).normalized()
var just_jumped = is_on_floor() and Input.is_action_just_pressed("jump")
var vel_y = JUMP_VELOCITY if just_jumped else (velocity.y - gravity * delta)
if just_jumped:
is_falling = true
elif is_on_floor():
is_falling = false
var horizontal_velocity = direction * SPEED
var horizontal_movement = horizontal_velocity * delta
var step_up = false
var coll: KinematicCollision3D = KinematicCollision3D.new()
var collides = test_move(global_transform, horizontal_movement, coll)
if collides and coll.get_position().y - global_position.y <= MAX_STEP_HEIGHT:
var step_delta = coll.get_position() - global_position
var stair_width_check_move = (direction * MIN_STEP_WIDTH)
var step_up_transform = global_transform.translated(Vector3.UP * (step_delta.y + STEP_UP_MARGIN))
step_up = MIN_STEP_WIDTH <= 0 or !test_move(step_up_transform, stair_width_check_move, coll)
if step_up:
global_position.y += step_delta.y
head.position.y -= step_delta.y
global_position += horizontal_movement
if !collides and !is_on_floor() and !is_falling:
var can_step_down = test_move(global_transform, Vector3.DOWN * (MAX_STEP_HEIGHT + STEP_DOWN_MARGIN), coll)
if(can_step_down):
var step_delta = coll.get_position() - global_position
global_position.y += step_delta.y
head.position.y -= step_delta.y
else:
is_falling = true
velocity = horizontal_velocity + Vector3.UP * vel_y
if not step_up:
move_and_slide()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment