Hey, I’m a bit new to programming and hitting a bit of a roadblock.
I’ve set up a fixed camera system that changes the position of a camera once the player enters a certain area.
I am not using tank controls, and instead am looking to create a camera-relative movement system, so forward is always aligned to the current camera positions. i’ve messed with godot’s built in character controller script and figured out how to achieve this (multiplying the input by the camera’s basis rather than the transform basis inside the direction var), but the problem now is that this is being updated as part of the physics process, updated on every frame, so when my camera switches, my movement direction switches at the same time, creating a disorienting effect.
my desired outcome is for the player’s movement direction to be carried through the camera change, and then update once a new movement input is made. the way fixed camera moments work in some older jrpgs. the solution seems to me to be to only check for the camera’s position and rotation at the beginning of each directional button press, rather than on every frame, but i’m having trouble figuring out exactly how to configure my code to make that happen.
here’s my code:
extends CharacterBody3D
@onready var camera = get_viewport().get_camera_3d()
const SPEED = 5
const JUMP_VELOCITY = 2
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction = (camera.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
If you are trying to make it follow the player as it moves there are some pretty easy ways to do that. One is making a global variable for the players position and rotation and then applying that global variable to the camera. Here’s a video for how to create a global variable. https://www.youtube.com/watch?v=sc-tEPdLZhk&t=3s
no, i’m not having any issues with the cameras. my issue is with the player’s movement direction as it relates to the camera. I have multiple camera positions set up with triggers that switch the camera’s position when the player enters them. the cameras are fixed and are not intended to follow the player. think older silent hill / resident evil.
but instead of using tank controls, i want the player’s movement to be camera-relative, so that “forward” is always forward according to the current camera’s POV, not forward according to the 3d world’s axis.
i’ve achieved this mostly, but the problem is the transition period in which the camera switches. the movement direction switches as the camera does, whereas i want there to be a delay in applying the new camera’s pos/rot, so that the switch isn’t as disorienting to the player
The way it is done in most games, is to store the camera transform/basis when the user input starts. As long as at least one button is pressed, keep using this stored basis.
This means even if the camera changes, the player will keep moving in the same world space direction if they keep the same button pressed. Only once they start a new movement, it will be relative to the new camera.
Here is your adjusted code:
extends CharacterBody3D
@onready var camera = get_viewport().get_camera_3d()
const SPEED = 5
const JUMP_VELOCITY = 2
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
@onready var movement_basis = camera.basis
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y -= gravity * delta
# Handle jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if is_zero_approx(input_dir.length()):
movement_basis = camera.basis
var direction = (movement_basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
This will update the movement basis as long as there is no player input. When there is player input, then the movement basis won’t be updated, keeping it relative to the last known camera basis.
I have not tested the code as I’m on mobile, so you may need to adjust it a bit more.