← Go back to the home page

3D Godot character script that can handle reversing gravity

Just wanted to share this with you, because I've never seen anything about flipping a CharacterBody3D including its physics.
I don't think I have to explain what the references are, but just in case you don't know, watch any character tutorial and it'll come clear.
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
const SENSITIVITY = 0.01

# Declare a "mock" velocity to use before rotating axis.
var mvelocity = Vector3.ZERO

# Important references
@onready var head: Node3D = $Head
@onready var camera: Camera3D = $Head/Camera3D

# Mouse stuff
func _ready() -> void:
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		head.rotate_y(-event.relative.x * SENSITIVITY)
		camera.rotate_x(-event.relative.y * SENSITIVITY)
		camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-45), deg_to_rad(45))
	if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _physics_process(delta: float) -> void:
	# Adjust the up direction to match the rotation.
	up_direction = (transform.basis * Vector3(0, 1, 0)).normalized()

	# Add the gravity.
	if not is_on_floor():
		mvelocity += get_gravity() * delta

	# Release the mouse when escape is hit.
	if Input.is_action_just_pressed("ui_cancel"):
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		mvelocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
	var direction := (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

	if direction:
		mvelocity.x = direction.x * SPEED
		mvelocity.z = direction.z * SPEED
	else:
		mvelocity.x = 0
		mvelocity.z = 0

	# Rotate the "mock" velocity to create the true velocity,
	velocity = transform.basis * mvelocity
	# and apply the velocity to actually move.
	move_and_slide()