Sorry about that, here’s the full code for the player:
extends CharacterBody3D
const MAX_SPEED = 15000
const TOP_SPEED = 10000
const BASE_MOVE_SPEED = 300
const GRAVITY = 50
var maxSpeed = MAX_SPEED
var topSpeed = TOP_SPEED
var baseMoveSpeed = BASE_MOVE_SPEED
var gravity = GRAVITY
var mouse_sensitivity := 0.18
var camera_input_direction := Vector2.ZERO
var last_movement_direction = Vector3.BACK
@onready var camera_pivot: Node3D = %Camera_Controller
@onready var camera: Camera3D = %Camera
@onready var model = %PlayerModel
var move_speed = BASE_MOVE_SPEED
var acceleration = 10.0
var rotation_speed = 30
var jump_impulse := 40
var hasJumped = false
var hasJustLeftSlope = false
var realVelocityYLastPosValue
var realVelocityYLastNegValue = 0
var lastRawInputVector:Vector2
var isStopping = false
var noInputDeceleration = 400
var lastMoveDirectionX = 0
var lastMoveDirectionZ = 0
var lastMoveDirectionXSlide = 0
var lastMoveDirectionZSlide = 0
var isPlayerStomping = false
var isPlayerStompingFirst = false
var canPlayerFullStomp = false
var isPlayerBoosting = false
var moveSpeedY = 0
var SuperPeelOutTimer = 0
var SpindashTimer = 0
var AirdashTimer = 0
var speedPreSpindash
var extraDelta
var xform: Transform3D
func _ready():
%SpringArm3D.add_excluded_object($".")
%PlayerModelRotateXY.position = position
floor_snap_length = 0.1
floor_constant_speed = false
slide_on_ceiling = false
func _input(event: InputEvent) -> void:
if event.is_action_pressed("useLeftMouse"):
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
elif event.is_action_pressed("useEsc"):
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _unhandled_input(event: InputEvent) -> void:
var is_camera_motion := (
event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED
)
if is_camera_motion:
camera_input_direction = event.screen_relative * mouse_sensitivity * 4
%CameraTimer.start()
func _physics_process(delta):
extraDelta = delta
#Camera movement
camera_pivot.rotation.x -= camera_input_direction.y * delta
camera_pivot.rotation.x = clamp(camera_pivot.rotation.x, -PI/3.0, PI/3.0)
camera_pivot.rotation.y -= camera_input_direction.x * delta
camera_input_direction = Vector2.ZERO
#Get player imput direction
var raw_input := Input.get_vector("useA","useD","useW","useS")
var forward = camera.global_basis.z
var right = camera.global_basis.x
#Get raycast collision normal
var collisionNormal = %RayCast3D.get_collision_normal()
GlobalVariables.overlayFloorNormal = collisionNormal
GlobalVariables.overlayRealMoveSpeed = get_real_velocity()
#Use camera and player imput to get move direction
var move_direction = forward * raw_input.y + right * raw_input.x
GlobalVariables.overlayMoveDirection = move_direction
if AirdashTimer == 0:
#move_direction.y = 0
pass
move_direction = move_direction.normalized()
#Stop player
if raw_input == lastRawInputVector*(-1) and lastRawInputVector != Vector2(0,0):
isStopping = true
noInputDeceleration = 700
else:
isStopping = false
noInputDeceleration = 400
if raw_input != Vector2(0,0) and isStopping == false:
lastRawInputVector = raw_input
var y_velocity := velocity.y
velocity.y = 0
#Determine if the player can go up walls
var floorFrontAngle = snappedi(rad_to_deg(get_floor_angle()),1)
if move_speed >= 10000:
if floorFrontAngle+45 < 180:
floor_max_angle = deg_to_rad(floorFrontAngle+45)
else:
floor_max_angle = deg_to_rad(180)
slide_on_ceiling = false
floor_block_on_wall = false
elif move_speed < 10000:
floor_max_angle = deg_to_rad(45)
slide_on_ceiling = true
floor_block_on_wall = true
if floorFrontAngle >= 70 and is_on_floor():
up_direction = collisionNormal
set_up_direction(up_direction)
else:
up_direction = Vector3(0,1,0)
set_up_direction(up_direction)
if Input.is_action_pressed("useShift") and is_on_floor(): #SUPER PEEL OUT
SuperPeelOutTimer += delta
%CapsuleMeshInstance3D.visible = false
%PeeloutMeshInstance3D.visible = true
if move_speed > 800:
velocity.x = lastMoveDirectionX * move_speed * delta
velocity.z = lastMoveDirectionZ * move_speed * delta
move_speed = move_speed - 800
elif move_speed <= 800:
move_speed = 0
elif Input.is_action_just_released("useShift") and is_on_floor():
%CapsuleMeshInstance3D.visible = true
%PeeloutMeshInstance3D.visible = false
if SuperPeelOutTimer > 3:
SuperPeelOutTimer = 3
move_speed = snappedi(((SuperPeelOutTimer*3.5) * 1000),1)
SuperPeelOutTimer = 0
if Input.is_action_pressed("useRightMouse") and is_on_floor(): #SPINDASH
if SpindashTimer == 0:
speedPreSpindash = move_speed
if AirdashTimer == 0:
lastMoveDirectionXSlide = lastMoveDirectionX
lastMoveDirectionZSlide = lastMoveDirectionZ
SpindashTimer += delta
%CapsuleMeshInstance3D.visible = false
%SphereMeshInstance3D.visible = true
if move_speed > 0:
lastMoveDirectionXSlide = lerp(lastMoveDirectionXSlide, move_direction.x, 1 * delta)
lastMoveDirectionZSlide = lerp(lastMoveDirectionZSlide, move_direction.z, 1 * delta)
velocity.x = lastMoveDirectionXSlide * move_speed * delta
velocity.z = lastMoveDirectionZSlide * move_speed * delta
move_speed = snappedi(speedPreSpindash/1.5,1)
elif Input.is_action_just_released("useRightMouse") and is_on_floor():
%CapsuleMeshInstance3D.visible = true
%SphereMeshInstance3D.visible = false
if SpindashTimer > 1:
SpindashTimer = 1
move_speed = speedPreSpindash + snappedi(((SpindashTimer) * 1000),1)
SpindashTimer = 0
if Input.is_action_pressed("useRightMouse") and !is_on_floor(): #AIRDASH
if AirdashTimer == 0:
speedPreSpindash = move_speed
y_velocity = y_velocity / 3
if SpindashTimer == 0:
lastMoveDirectionXSlide = lastMoveDirectionX
lastMoveDirectionZSlide = lastMoveDirectionZ
gravity = 1
AirdashTimer += delta
%CapsuleMeshInstance3D.visible = false
%SphereMeshInstance3D.visible = true
if move_speed > 0:
lastMoveDirectionXSlide = lerp(lastMoveDirectionXSlide, move_direction.x, 1 * delta)
lastMoveDirectionZSlide = lerp(lastMoveDirectionZSlide, move_direction.z, 1 * delta)
velocity.x = lastMoveDirectionXSlide * move_speed * delta
velocity.z = lastMoveDirectionZSlide * move_speed * delta
move_speed = snappedi(speedPreSpindash/3,1)
elif Input.is_action_just_released("useRightMouse") and !is_on_floor():
%CapsuleMeshInstance3D.visible = true
%SphereMeshInstance3D.visible = false
if AirdashTimer > 1:
AirdashTimer = 1
move_speed = speedPreSpindash + snappedi(((AirdashTimer) * 1000),1)
y_velocity = (snappedi(((AirdashTimer*2) * 30),1) * move_direction.y)
gravity = 1
AirdashTimer = 0
#Reset ability timers
if !is_on_floor():
if SuperPeelOutTimer != 0:
%CapsuleMeshInstance3D.visible = true
%PeeloutMeshInstance3D.visible = false
SuperPeelOutTimer = 0
if SpindashTimer != 0:
%CapsuleMeshInstance3D.visible = true
%SphereMeshInstance3D.visible = false
SpindashTimer = 0
elif is_on_floor():
if AirdashTimer != 0:
%CapsuleMeshInstance3D.visible = true
%SphereMeshInstance3D.visible = false
AirdashTimer = 0
#Add speed when landing, based on how fast you were moving down
if get_real_velocity().y < 0 and is_on_floor() and get_real_velocity().y < -2:
move_speed += snappedi((get_real_velocity().y*-1)/2,1)
#Player movement
if isStopping == false and SpindashTimer == 0 and AirdashTimer == 0:
if raw_input != Vector2(0,0) and is_on_floor(): #is moving and on floor
%MoveTimer.start()
if move_speed < baseMoveSpeed and SuperPeelOutTimer == 0 and SpindashTimer == 0:
move_speed = baseMoveSpeed
velocity = move_direction * move_speed * delta
lastMoveDirectionX = move_direction.x
lastMoveDirectionZ = move_direction.z
if move_speed < topSpeed:
move_speed = move_speed + 10
elif move_speed > topSpeed:
if move_speed-5 < topSpeed:
move_speed = topSpeed
else:
move_speed = move_speed -5
print(move_direction)
elif raw_input == Vector2(0,0) and is_on_floor(): #is not moving and on floor
if move_speed > baseMoveSpeed:
velocity.x = lastMoveDirectionX * move_speed * delta
velocity.z = lastMoveDirectionZ * move_speed * delta
move_speed = move_speed - noInputDeceleration
elif move_speed <= baseMoveSpeed:
velocity.x = 0
velocity.z = 0
move_speed = 0
elif raw_input != Vector2(0,0) and !is_on_floor(): #is moving and not on floor
velocity = move_direction * move_speed * delta
lastMoveDirectionX = move_direction.x
lastMoveDirectionZ = move_direction.z
if move_speed < 400:
move_speed = move_speed + 10
elif move_speed >= 400:
move_speed = move_speed - 1
elif raw_input == Vector2(0,0) and !is_on_floor(): #is not moving and not on floor
if move_speed > 0:
velocity.x = lastMoveDirectionX * move_speed * delta
velocity.z = lastMoveDirectionZ * move_speed * delta
move_speed = move_speed - 20
elif move_speed <= 0:
velocity.x = lastMoveDirectionX * move_speed * delta
velocity.z = lastMoveDirectionZ * move_speed * delta
move_speed = 0
elif isStopping == true:
move_speed = move_speed - noInputDeceleration
if move_speed < 0:
move_speed = 0
velocity.x = lastMoveDirectionX * move_speed * delta
velocity.z = lastMoveDirectionZ * move_speed * delta
if velocity.x == 0 and velocity.z == 0:
isStopping = false
lastRawInputVector = raw_input
if move_speed > maxSpeed:
move_speed = maxSpeed
#Debug overlay variables
GlobalVariables.overlayMoveSpeed = move_speed
GlobalVariables.playerMoveSpeed = move_speed
GlobalVariables.playerRealVelocity = get_real_velocity()
GlobalVariables.playerIsOnFloor = is_on_floor()
GlobalVariables.overlayVelocity = velocity
GlobalVariables.overlayVelocity = is_on_floor()
#Debug tool
if Input.is_action_just_pressed("useE"):
move_speed = 15000
#ABILITIES
#Boost
if Input.is_action_just_pressed("useR"):
if GlobalVariables.playerSpeedOMeter >= 3334:
isPlayerBoosting = true
Signals.emit_signal("playerBoost")
move_speed = maxSpeed
elif Input.is_action_pressed("useR"):
if GlobalVariables.playerSpeedOMeter >= 1 and isPlayerBoosting == true:
move_speed = maxSpeed
Signals.emit_signal("playerBoosting")
if GlobalVariables.playerSpeedOMeter <= 0:
isPlayerBoosting = false
elif Input.is_action_just_released("useR"):
isPlayerBoosting = false
if get_real_velocity().y < 0 and get_real_velocity().y < realVelocityYLastNegValue:
realVelocityYLastNegValue = get_real_velocity().y
#Stomp
if Input.is_action_just_pressed("useCtrl") and !is_on_floor():
if isPlayerStomping == false:
canPlayerFullStomp = true
isPlayerStomping = true
isPlayerStompingFirst = true
if get_real_velocity().y > 0:
y_velocity = 0
velocity.y = 0
moveSpeedY = -500
%CapsuleMeshInstance3D.visible = false
%SphereMeshInstance3D.visible = true
elif isPlayerStomping == true and canPlayerFullStomp == true:
canPlayerFullStomp = false
isPlayerStompingFirst = false
move_speed = 0
y_velocity = 0
velocity.y = 0
moveSpeedY = 0
await get_tree().create_timer(0.05).timeout
moveSpeedY = -1000
%CapsuleMeshInstance3D.visible = false
%SphereMeshInstance3D.visible = true
elif Input.is_action_just_pressed("useCtrl") and is_on_floor():
GlobalVariables.playerFallInWaterCTRL = true
pass
if isPlayerStomping == true and is_on_floor():
isPlayerStomping = false
if isPlayerStompingFirst == true and realVelocityYLastNegValue < -150:
move_speed = move_speed + snappedi((-realVelocityYLastNegValue*2),1)
isPlayerStompingFirst = false
realVelocityYLastNegValue = 0
moveSpeedY = 0
%CapsuleMeshInstance3D.visible = true
%SphereMeshInstance3D.visible = false
#Gravity for different states
if !is_on_floor():
if GlobalVariables.playerInWater == false and get_real_velocity().y >= 0 and AirdashTimer == 0:
gravity = 50
elif GlobalVariables.playerInWater == false and get_real_velocity().y < 0 and AirdashTimer == 0:
gravity = 70
elif GlobalVariables.playerInWater == true and get_real_velocity().y >= 0 and AirdashTimer == 0:
gravity = 20
elif GlobalVariables.playerInWater == true and get_real_velocity().y < 0 and AirdashTimer == 0:
gravity = 30
elif is_on_floor():
gravity = 0
GlobalVariables.overlayFloorAngle = rad_to_deg($".".get_floor_angle())
#Water movement
if GlobalVariables.playerInWater == false:
maxSpeed = MAX_SPEED
topSpeed = TOP_SPEED
baseMoveSpeed = BASE_MOVE_SPEED
elif GlobalVariables.playerInWater == true:
maxSpeed = MAX_SPEED * 0.8
topSpeed = TOP_SPEED * 0.8
baseMoveSpeed = BASE_MOVE_SPEED * 0.8
if isPlayerStomping == false:
velocity.y = y_velocity + -gravity * delta
elif isPlayerStomping == true:
velocity.y = y_velocity + moveSpeedY * delta
#Change floor snap length based on speed
if move_speed < 10000 and move_speed/200 > 0.1 and hasJumped == false:
floor_snap_length = 0.1 * (move_speed/100)
elif move_speed >= 10000 and move_speed/200 > 0.1 and hasJumped == false:
floor_snap_length = 0.1 * (move_speed/300)
else:
floor_snap_length = 0.1
#Jump
if Input.is_action_just_pressed("useSpace") and %IsOnFloorTimer.time_left > 0 and hasJumped == false:
hasJumped = true
floor_snap_length = 0
velocity.y += jump_impulse + get_real_velocity().y
elif Input.is_action_just_released("useSpace") and !is_on_floor() and velocity.y > (jump_impulse/2):
velocity.y = velocity.y - (jump_impulse/2)
#Attempt at fixing loop de loop issue
#up_direction.y = 1 - (get_floor_angle()/180)*2
#set_up_direction(up_direction)
move_and_slide()
if get_real_velocity().y > 0:
realVelocityYLastPosValue = get_real_velocity().y
#Get a collision normal to align player even if raycast isn't touching something
if %RayCast3D.is_colliding() == false and is_on_floor():
collisionNormal = get_floor_normal()
align_with_floor(collisionNormal)
#Handle player rotation in different states
if is_on_floor():
%IsOnFloorTimer.start()
hasJustLeftSlope = true
hasJumped = false
align_with_floor(collisionNormal)
global_transform = global_transform.interpolate_with(xform, 10 * delta)
if collisionNormal == Vector3(0,1,0):
rotation = lerp(rotation, Vector3(0,0,0), 10 * delta)
elif !is_on_floor():
global_transform.basis.x = lerp(global_transform.basis.x, Vector3(1,0,0), 2 * delta)
global_transform.basis.y = lerp(global_transform.basis.y, Vector3(0,1,0), 2 * delta)
global_transform.basis.z = lerp(global_transform.basis.z, Vector3(0,0,1), 2 * delta)
if hasJumped == false and hasJustLeftSlope == true: #simulate momentum from running off ramp
velocity.y += realVelocityYLastPosValue
hasJustLeftSlope = false
GlobalVariables.overlayGravity = gravity
%PlayerModelRotateXY.position = position
%PlayerModelRotateXY.rotation = global_rotation
GlobalVariables.overlayPlayerRotation = rotation
if move_direction.length() > 0.2:
last_movement_direction = move_direction
var target_angle = Vector3.BACK.signed_angle_to(last_movement_direction,Vector3.UP)
model.rotation.y = lerp_angle(model.rotation.y, target_angle, rotation_speed * delta)
func align_with_floor(collisionNormal):
xform = global_transform
xform.basis.y = collisionNormal
xform.basis.x = -xform.basis.z.cross(collisionNormal)
xform.basis = xform.basis.orthonormalized()
It has a lot of code related to special abilities that probably aren’t related to the main issue, but I kept them in to help other parts of the code make more sense, and just in case they may be interfering with the movement code too.
I am also aware that basing the player movement on the direction the camera is pointing could cause unwanted issues when looking up or down, but I still haven’t fixed it to help test the wall running mechanic, although I am working on a patching it.