Godot Version
4.7
Question
I'm trying to make a Sonic-style fangame and this is my current roadblock. I'm not using a framework because they tend to be too complicated to edit and too bulky for lower-end systems to run, which it's my goal to have it run on those. I have tried CharacterBody3D, RigidBody3D, and Kinematic Frozen RigidBody3D to make this happen, to make the player rotate with the loop, stay stuck to it all the way around including upside-down, and rotate the controls with the character so you don't have to change from holding forward at the top of the loop to backward in order to go all the way around. I've been stuck on this for a couple of days, trying whatever I could think of and looking for any solutions and nothing has worked.
I kind of found a way to rotate the controls with .slide(), but when I do that, gravity becomes basically a glide instead of gradually increasing as you go down. I know that’s what’s causing it because if I comment out the .slide(), it works again. Here’s what I have for movement, minus the rotation itself:
extends RigidBody3D
@onready var groundHitAll = $ShapeCast3D
@export var maxRunSpeed = 100.0
@export var accelSpeed = 5.0
@export var skidSpeed = 15
@export var jumpSpeed = 10
@export var rotSpeed = 10
@export var manualGravity = 5
var velocity = Vector3.ZERO
var rotatedVel = Vector3.ZERO
var characterUp = Vector3.UP
var currentSpeed = 0
var lastDir = Vector3.ZERO
var isGrounded = false
var input
var direction
func _integrate_forces(state):
# Find ground
if groundHitAll.is_colliding():
characterUp = groundHitAll.get_collision_normal(0)
isGrounded = true
else:
characterUp = Vector3.UP
isGrounded = false
# Get directional input
input = Input.get_vector("MoveLeft","MoveRight","MoveForward","MoveBackward")
direction = (transform.basis * Vector3(input.x, 0, input.y)).normalized()
# Get speed
if direction:
# Find input near backwards to movement direction
var shouldSkid = false
if currentSpeed > 0.5:
var angle = direction.signed_angle_to(velocity, characterUp)
if abs(rad_to_deg(angle)) > 90:
shouldSkid = true
# Skid decelerate
if shouldSkid:
currentSpeed = max(currentSpeed - skidSpeed * state.step, 0)
velocity.x = lastDir.x * currentSpeed
velocity.z = lastDir.z * currentSpeed
# Accelerate
else:
currentSpeed = min(currentSpeed + accelSpeed * state.step, maxRunSpeed)
velocity.x = direction.x * currentSpeed
velocity.z = direction.z * currentSpeed
lastDir = direction
# Natural decelerate or stop
else:
currentSpeed = max(currentSpeed - accelSpeed * state.step, 0)
if currentSpeed <= 0.1:
currentSpeed = 0
velocity.x = 0
velocity.z = 0
else:
velocity.x = lastDir.x * currentSpeed
velocity.z = lastDir.z * currentSpeed
if isGrounded:
velocity.y -= manualGravity * state.step
velocity = velocity.slide(characterUp)
# Get gravity
velocity.y -= manualGravity * state.step
# Jump
if Input.is_action_just_pressed("Jump") and isGrounded:
velocity.y = jumpSpeed
isGrounded = false
# Move
state.linear_velocity = velocity