I think we're going to have to poke at things generally and see what's what because I've been playtesting my controller quite a bit and it's weirdly consistent in how it breaks, one thing that did change though if slightly was when I switched the calculations from deg_to_rad to PI/4. This worked a bit better, but the breaking behaviour still happened if I wiggled the mouse around enough. I have no problem posting the player controller code just bear in mind there is a lot, it's loosely based off Garbaj's FPS tutorial and I've done some heavy tweaking. It's one of the things I want to release as open source since I've been surprised at how little fully functional FPS controllers there are out there with the features you'd expect for Godot. I'm going to throw out some open source templates with controllers so that people can have fun with the engine and not get frustrated over incomplete or broken examples.
extends CharacterBody3D
var mouseSensitivity = 0.2
var direction = Vector3()
var gravityVector = Vector3()
var horizontalVelocity = Vector3()
var movement = Vector3()
var customVelocity = Vector3()
var fall = Vector3()
var speed
var defaultMoveSpeed = 5.0
var sprintMoveSpeed = 10.0
var crouchMoveSpeed = 1.0
var gravity = 10
var jumpHeight = 6
var horizontalAcceleration = 6
var swayAmount = 30
var verticalSwayAmount = 30
var plasmaPistolMaxGunShake = 1.5
var crouchingPlasmaPistolMaxGunShake = 0.5
@onready var mouseLookNode = $MouseLookNode
@onready var playerCollisionShape = $PlayerCollisionShape
@onready var playerCamera = $MouseLookNode/PlayerCamera
#@onready var playerHand = $MouseLookNode/Hand
@onready var playerRaycast = $MouseLookNode/PlayerCamera/PlayerRaycast
@onready var playerCrosshair = $MouseLookNode/PlayerCamera/CenterContainer/PlayerCrosshair
@onready var plasmaBullet = preload("res://PlasmaBullet.tscn")
@onready var plasmaGrenade = preload("res://PlasmaGrenade.tscn")
@onready var grenadeTransform = $MouseLookNode/GrenadeTransform
@onready var flashLight = $MouseLookNode/Flashlight
@onready var raycastCeilingCheck = $RaycastCeilingCheck
@onready var groundCheck = $GroundCheck
@onready var defaultCameraHeight = $defaultCameraHeight
@onready var crouchCameraHeight = $crouchCameraHeight
@onready var playerCrouchingCollisionShape = $playerCrouchingCollisionShape
@onready var fireRateTimer = $FireRateTimer
var isHittingCeiling = false
var isCrouching = false
var isSprinting = false
var isPlayerFiring = false
var isFlashlightEnabled = false
var canEnterDriverSeat = false
var vehicleDriverArea
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
playerCamera.make_current()
isCrouching = false
playerCollisionShape.set_deferred(("disabled"), false)
playerCrouchingCollisionShape.set_deferred(("disabled"), true)
speed = defaultMoveSpeed
func _unhandled_input(event):
if event is InputEventMouseMotion:
rotate_y(deg_to_rad(-event.relative.x * mouseSensitivity))
mouseLookNode.rotate_x(deg_to_rad(mouseSensitivity * event.relative.y))
mouseLookNode.rotation.x = clamp (mouseLookNode.rotation.x, -(PI/4), PI/4)
# mouseLookNode.rotation.x = clamp(mouseLookNode.rotation.x, deg_to_rad(-80), deg_to_rad(80))
func _physics_process(delta):
direction = Vector3()
if Input.is_action_pressed("Crouch") and isCrouching == false:
isCrouching = true
playerCollisionShape.set_deferred(("disabled"), true)
playerCrouchingCollisionShape.set_deferred(("disabled"), false)
mouseLookNode.global_transform.origin = crouchCameraHeight.global_transform.origin
speed = crouchMoveSpeed
elif Input.is_action_just_released("Crouch") and isCrouching == true and raycastCeilingCheck.is_colliding():
isCrouching = true
playerCollisionShape.set_deferred("disabled", true)
playerCrouchingCollisionShape.set_deferred("disabled", false)
mouseLookNode.global_transform.origin = crouchCameraHeight.global_transform.origin
speed = crouchMoveSpeed
elif not Input.is_action_pressed("Crouch") and isCrouching == true and raycastCeilingCheck.is_colliding():
isCrouching = true
playerCollisionShape.set_deferred("disabled", true)
playerCrouchingCollisionShape.set_deferred("disabled", false)
mouseLookNode.global_transform.origin = crouchCameraHeight.global_transform.origin
speed = crouchMoveSpeed
elif not Input.is_action_pressed("Crouch") and isCrouching == true and not raycastCeilingCheck.is_colliding():
isCrouching = false
playerCollisionShape.set_deferred("disabled", false)
playerCrouchingCollisionShape.set_deferred("disabled", true)
mouseLookNode.global_transform.origin = defaultCameraHeight.global_transform.origin
speed = defaultMoveSpeed
if not groundCheck.is_colliding():
gravityVector += Vector3.DOWN * gravity * delta
else:
gravityVector = -get_floor_normal() * gravity
if Input.is_action_just_pressed("Space") and groundCheck.is_colliding() and not raycastCeilingCheck.is_colliding():
gravityVector = Vector3.UP * jumpHeight
if Input.is_action_pressed("Shift") and isCrouching == false and groundCheck.is_colliding() and not raycastCeilingCheck.is_colliding():
isSprinting = true
speed = sprintMoveSpeed
elif Input.is_action_just_released("Shift") and isCrouching == false and groundCheck.is_colliding() and not raycastCeilingCheck.is_colliding():
isSprinting = false
speed = defaultMoveSpeed
if Input.is_action_pressed("W"):
direction += transform.basis.z
elif Input.is_action_pressed("S"):
direction -= transform.basis.z
if Input.is_action_pressed("A"):
direction += transform.basis.x
elif Input.is_action_pressed("D"):
direction -= transform.basis.x
if Input.is_action_just_pressed("E") and canEnterDriverSeat == true:
vehicleDriverArea.ActivateVehicleCamera()
queue_free()
if Input.is_action_just_pressed("E") and playerRaycast.is_colliding():
var playerRaycastCollision = playerRaycast.get_collider()
if playerRaycastCollision.has_method("SecurityGateButton"):
playerRaycastCollision.call_deferred("SecurityGateButton")
if Input.is_action_just_pressed("F") and isFlashlightEnabled == false:
flashLight.visible = true
isFlashlightEnabled = true
elif Input.is_action_just_pressed("F") and isFlashlightEnabled == true:
flashLight.visible = false
isFlashlightEnabled = false
direction = direction.normalized()
horizontalVelocity = horizontalVelocity.lerp(direction * speed, horizontalAcceleration * delta)
movement.z = horizontalVelocity.z + gravityVector.z
movement.x = horizontalVelocity.x + gravityVector.x
movement.y = gravityVector.y
set_velocity(movement)
set_up_direction(Vector3.UP)
move_and_slide()
velocity = velocity
OH! I just clicked on something having a glance at my code! It might in fact be to do with my crouch code and me changing the camera origin? I'll double check that and see if it works, but here it all is in case.
Edit: Nevermind, double checked and same results.