Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | WalterBennet |
Hello,
I’m new to Godot and I’ve been trying to figure out a way to implement a leaning mechanic in my 3D character controller.
I’ve recently gotten stuck trying to figure out how to move the camera x and z position based on the current y rotation of the camera.
The current system that I’ve got only allows the camera’s position to be moved either positively or negatively from a base point along the x axis. This is obviously insufficient as the player can turn the camera 90° on the y axis and render the whole camera position movement incoherent with its current direction. Basically, no matter where the camera is currently looking at, its position is always gonna be moved on the x axis when leaning rendering the whole mechanic pointless.
I’ve also tried to make it so that the x axis of the camera would always point toward the current position where the player is looking at (rotating the axis along the camera basically) but I couldn’t figure out a way of doing it.
I’ve run out of ideas and I’m asking for you guys help to hopefully guide me toward a solution. I’ve linked both the script for the character controller (the leaning method is at the bottom, _leaning) and the player scene. Feel free to ask for more clarification if needed, I’m not certain I’m explaining the whole issue properly.
Hope you all have a good day, cheer!
Character controller (player.gd)
class_name Player extends CharacterBody3D
@export_category("Player Variables")
@export_range(1, 35, 1) var walking_speed: float = 4 # m/s
@export_range(1, 35, 1) var running_speed: float = 3 # m/s
@export_range(10, 400, 1) var acceleration: float = 40 # m/s^2
@export_range(0.1, 3.0, 0.1) var jump_height: float = 1 # m
@export_category("Player Controls")
@export var always_run: bool = false
@export var enable_camera_z_rotation: bool = true
@export_category("Camera Variables")
@export_range(0.1, 9.25, 0.05, "or_greater") var camera_sensitivity: float = 6.2
@export_range(75, 120, 1) var camera_fov: float = 90
@export_range(0, 0.1, 0.01) var rotation_amount: float = 0.06
var jumping: bool = false
var running: bool = false
var leaning: bool = false
var mouse_captured: bool = false
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
var move_dir: Vector2 # Input direction for movement
var look_dir: Vector2 # Input direction for look/aim
var walk_vel: Vector3 # Walking velocity
var grav_vel: Vector3 # Gravity velocity
var jump_vel: Vector3 # Jumping velocity
var base_position_x: float
@onready var camera: Camera3D = $Camera
func _ready() -> void:
capture_mouse()
func _input(event: InputEvent) -> void:
running = always_run != Input.is_action_pressed("run")
leaning = Input.is_action_pressed("lean_left") or Input.is_action_pressed("lean_right")
if event is InputEventMouseMotion: look_dir = event.relative * 0.01
if Input.is_action_just_pressed("jump"): jumping = true
if Input.is_action_just_pressed("exit"):
capture_mouse() if (Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE) else release_mouse()
func _physics_process(delta: float) -> void:
if mouse_captured: _rotate_camera(delta)
velocity = _walk(delta) + _gravity(delta) + _jump(delta)
_running(delta)
_leaning(delta)
move_and_slide()
func capture_mouse() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
mouse_captured = true
func release_mouse() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
mouse_captured = false
func _rotate_camera(delta: float, sens_mod: float = 1.0) -> void:
look_dir += Input.get_vector("look_left", "look_right", "look_up", "look_down")
camera.rotation.z = lerp(camera.rotation.z, (-move_dir.x * (rotation_amount if (enable_camera_z_rotation && !leaning) else 0.0)), delta * 10)
camera.rotation.y -= look_dir.x * camera_sensitivity * sens_mod * delta
camera.rotation.x = clamp(camera.rotation.x - look_dir.y * camera_sensitivity * sens_mod * delta, -1.5, 1.5)
look_dir = Vector2.ZERO
func _walk(delta: float) -> Vector3:
move_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backwards")
var _forward: Vector3 = camera.transform.basis * Vector3(move_dir.x, 0, move_dir.y)
var walk_dir: Vector3 = Vector3(_forward.x, 0, _forward.z).normalized()
walk_vel = walk_vel.move_toward(walk_dir * (walking_speed + (running_speed if running else 0.0)) * move_dir.length(), acceleration * delta)
return walk_vel
func _gravity(delta: float) -> Vector3:
grav_vel = Vector3.ZERO if is_on_floor() else grav_vel.move_toward(Vector3(0, velocity.y - gravity, 0), gravity * delta)
return grav_vel
func _jump(delta: float) -> Vector3:
if jumping:
if is_on_floor(): jump_vel = Vector3(0, sqrt(4 * jump_height * gravity), 0)
jumping = false
return jump_vel
jump_vel = Vector3.ZERO if is_on_floor() else jump_vel.move_toward(Vector3.ZERO, gravity * delta)
return jump_vel
func _running(delta: float) -> void:
if running:
camera.fov = lerp(camera.fov, camera_fov + 15, delta * 10) if running else lerp(camera.fov, camera_fov, delta * 10)
# This is the method that I've been struggling with
func _leaning(delta: float) -> void:
if leaning:
camera.rotation.z = clamp(lerp(camera.rotation.z, 0.7 if Input.is_action_pressed("lean_left") else -0.7, delta * 3), -0.25, 0.25)
camera.position.x = clamp(lerp(camera.position.x, -0.30 if Input.is_action_pressed("lean_left") else 0.30, delta * 6), base_position_x - 1.0, base_position_x + 1.0)
else:
camera.position.x = lerp(camera.position.x, base_position_x, delta * 4)
if camera.position.x == base_position_x: base_position_x = camera.position.x
**Player scene (player.tscn)**
[gd_scene load_steps=3 format=3 uid="uid://cvq4dvoyms4vw"]
[ext_resource type="Script" path="res://player/player.gd" id="1_2f8j2"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3rsb5"]
radius = 0.45
height = 1.8
[node name="Player" type="CharacterBody3D"]
script = ExtResource("1_2f8j2")
[node name="CShape" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
shape = SubResource("CapsuleShape3D_3rsb5")
[node name="Camera" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0)
fov = 90.0