- Edited
Hi,
I've just transitioned to Godot 4 to prototype a new project. I'm doing a 3D project now for an idea I've had for a while, and I think I've went way over my head because apparently something I thought would be trivial is way harder than I thought, and it admittedly doesn't help that I don't have much experience with 3D transformations.
Basically, I'm trying to make it so that a first person controller is aligned with a slope when it's stepped on if the angle is above a certain threshold. Ideally the axis of looking would also be altered to work with this, i.e. if the character steps on a slope going up they will be corrected so they will be looking up that slope, and same for looking down. Such that the centered view is lower for going down and higher for going up. I've tried a bunch of methods, but can't seem to get it right.
There are two constraints on this project that absolutely are needed in order for this to work in my project. First, this is for a first person game, and mouselook is necessary. Mouselook in my prototype has the mouse changing the Y rotation of the player. This changes things dramatically from it were say a first person game. I have looked through lots of forum threads and such, and actually found one tutorial for what I'm doing. However, this tutorial can't be used because it's overwriting the entire transform of the object, and I also tried things with bases as well. These methods can not be used because this constricts the player's ability to look left and right with the mouse.
I also wanted the player's view to be constant with the slope when looking around it too. So if the player looks to the left or right while looking up, they will still be flush with the ramp, but will be slanted to an outside observer.
The final important thing is that the rotation must be lerp'd. The jerkiness of such clamping would definitely make people sick if this isn't accounted for.
I have a script that accomplishes some of this, but doesn't really work too well. For some reason it doesn't seem to take into account the player's rotation.
There's 4 ramps here.
Going up this one is fine
But I rotate downwards on this one, opposite of the first.
And the side ones don't work at all
Anyways, here is my script. I'm trying to use rotations on the X and Z axes to rotate it properly based on the normal for the floor. I have also tried using trigonometry to figure out the correct values based on the player's rotation, but couldn't really find something that worked. I think that's really the best method, does anyone have any ideas? Thanks
@onready var camera = $CollisionShape3D/Camera3D
const JumpVelocity = 16
const Acceleration = 0.2
const Deceleration = 1.5
const TopSpeed = 15.0
const SmoothingTime = 4.0
var lookSensitity : float = ProjectSettings.get_setting("player/look_sensitivity")
var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity")
var targetRotation : Vector3 = Vector3(0.0, 0.0, 0.0)
var floorNormal : Vector3 = Vector3()
var floorAngle : float = 0.0
func _physics_process(delta):
var new_velocity : Vector3 = velocity
if(is_on_floor()):
if (Input.is_action_just_pressed("ui_accept")):
new_velocity.y = JumpVelocity
floorAngle = get_floor_angle()
if (floorAngle > 0.4):
floorNormal = get_floor_normal()
targetRotation = rotation.lerp(Vector3(-floorNormal.x, rotation.y, -floorNormal.z), delta * SmoothingTime )
else:
targetRotation = Vector3(0.0, rotation.y, 0.0)
floorNormal = Vector3.UP
else:
new_velocity.y -= gravity * delta
targetRotation = Vector3(0.0, rotation.y, 0.0)
floorNormal = Vector3.UP
var inputDir : Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction : Vector3 = (transform.basis * Vector3(inputDir.x, 0, inputDir.y)).normalized()
if (direction != Vector3.ZERO):
var xVelNew : float = new_velocity.x + direction.x * Acceleration
var zVelNew : float = new_velocity.z + direction.z * Acceleration
new_velocity.x = clamp(xVelNew, -TopSpeed, TopSpeed)
new_velocity.z = clamp(zVelNew, -TopSpeed, TopSpeed)
else:
new_velocity.x = move_toward(velocity.x, 0, Deceleration)
new_velocity.z = move_toward(velocity.z, 0, Deceleration)
velocity = new_velocity
move_and_slide()
rotation = Vector3(targetRotation.x, rotation.y, targetRotation.z)
func _input(event):
if(event is InputEventMouseMotion):
camera.rotate_x(-event.relative.y * lookSensitity)
var x : float = clamp(camera.rotation.x, -PI / 2, PI / 2)
camera.rotation = Vector3(x, camera.rotation.y, camera.rotation.z)
rotate_y(-event.relative.x * lookSensitity)