Any Idea how to program loops like in Sonic?

Godot Version

Godot 4.5.1

Question

I am making a 3D Sonic inspired game. I will need a system for loops however I may have underestimated how hard making this is going to be.

I have ramp physics in the game. It’s not great and needs debugging for sure, but it works. However I am completely lost on how to program a loop.

I know about states, however how do I incorporate that for going up a wall for the character to well, be on the wall?

Here is my code for reference:
extends CharacterBody3D
class_name mainChar
#Define Variables
@export_group(“Camera”)
var _camera_input_direction := Vector2.ZERO
var fall_speed = 1500
var coyote_timer = 0.0
var coyote_time = 0.1
var gravity = -800
var Speed = 0
var ground_Speed = Speed
var jump_time = 1.0
var jump_timer = 0.0
var jump_velocity = 500
var can_Jump = false
var momen = 1
var acc = 10
var dec = 10
var rot = 0.0
var slope_angle = 0.0
var direction = 0
var motion = Vector3(0,0,0)
var grounded = false
var fall_off_wall = false
var slope_factor = 0.0
var control_lock = false
var stuck = false
@onready var Player: MeshInstance3D = %Model
@onready var FloorCast: RayCast3D = %FloorCast
@onready var IdleCollisionShape: CollisionShape3D = %“Hitbox-Idle”
var angle = 0
var friction = gravity * $“.”.rotation.z
var last_movement_direction := Vector3.BACK
var Max_Speed = 200
var Left_Stick_Sensitivity := 0.1
var Right_Stick_Sensitivity := 0.1
var Jump_Velocity = 250
var count = 0
var rotationSpeed = 12.0
@onready var camera: Node3D = %CameraPivot
@onready var _camera: Camera3D = %Camera3D
var moving = 0
var mouse_sensitivity := 0.25
var x_input = 0
var y_input = 0
#func _slopeDetection():
#if %FloorCast.is_colliding() or not %SlopeCast.is_colliding():
#angle = 0
#elif %SlopeCast.is_colliding():
#angle = 1
#gravity
func _ready() → void:
Autoload.player = self
#print(FloorCast)
add_to_group(“Player”)
add_to_group(“Bad Guys”)
func _gravity(delta: float) → void:
if not is_on_floor() and rot == 0:
count == 1
velocity.y = move_toward(velocity.y, fall_speed, gravity * delta)
else:
if abs(slope_factor) == 1: #gets the slope factor to prepare for physics
velocity.y = 0

	else:
		velocity.y = 80

func _input(event: InputEvent) → void:
if event.is_action_pressed(“left_click”): #K&M input
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if event.is_action_pressed(“ui_cancel”):
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _physics_process(delta: float) → void:
_jump(1)
#print(FloorCast)
if FloorCast.is_colliding():
can_Jump = true#Jumping failsafe, bandaid if anything but as long as it works
if Input.is_action_just_pressed(“Jump”) and can_Jump:
velocity.y += jump_velocity
if not is_on_floor():
_gravity(delta)
camera.rotation.x += _camera_input_direction.y * delta #camera script
camera.rotation.x = clamp(camera.rotation.x, -PI / 6.0, PI / 3.0) #Restricts movement of the camera to help player maintain control
camera.rotation.y -= _camera_input_direction.x * delta #rotates the camera
_camera_input_direction = Vector2.ZERO #gets the camera input
#var camera_direction := Input.get_action_strength(“Look_Left”) - Input.get_action_strength(“Look_Right”);
#var character_direction := Input.get_action_strength(“Left”) - Input.get_action_strength(“Right”);
#camera.rotation.y += camera_direction * Right_Stick_Sensitivity;
#Player.rotation.y += character_direction * Right_Stick_Sensitivity;
#print(Speed)
if Input.is_action_pressed(“Up”) or Input.is_action_pressed(“Down”) or Input.is_action_pressed(“Left”) or Input.is_action_pressed(“Right”):
moving = true
if Speed != Max_Speed: #builds the momemtum for the player
Speed += momen
else:
moving = false
if Speed > 0:
Speed -= dec #decelerates the Speed so the player slows down
#var look_direction = Vector2(velocity.z, velocity.x)
#Player.rotation.y = lerp_angle(Player.rotation.y, look_direction.angle(), delta * 12)

var raw_input := Input.get_vector("Left", "Right", "Up", "Down") #gets the inputs
var forward := _camera.global_basis.z #gets the forwars inputs
var right := _camera.global_basis.x #gets the x inputs
var move_direction = forward * raw_input.y + right * raw_input.x #calculates the movement direction based on the input from they player 
move_direction.y = 0.0 #Failsafe
move_direction = move_direction.normalized()
var target_velocity = move_direction * Speed #Sets the velocity the game hopes to acheive
var smoothing_factor = 0.1 #adds a bit of force so the speed can bee maintained
if angle == 0:
	target_velocity = move_direction * Speed
	velocity = velocity.lerp(target_velocity, smoothing_factor)
else:
	target_velocity = move_direction * Speed * friction #Alternate movement for when on slopes
#var move_acc = acc * 0.9
#if moving:
	#velocity = velocity.move_toward(move_direction * Speed, move_acc * delta)
move_and_slide()
if is_on_floor():
	if count == 0:
		print("On floor")
		count = 1
	can_Jump = true
	slope_angle = get_floor_angle() + (PI / 2) #gets the slope of the ground
	slope_factor = get_floor_normal().z
else:
	slope_angle = 0.0 #sets angle mode back to default when in the air
	#Player.rotation.y = rot
	Player.rotation.x = rot
	Player.rotation.z = rot
if is_on_floor():
	if not grounded:
		if abs(slope_angle) >= abs(velocity.y) and abs(velocity.y) > abs(velocity.z):
			velocity.z = (velocity * slope_factor) #Changes the movement according to the slope
		grounded = true
	up_direction.normalized() #Normalises the direction
	rot = slope_angle
else:
	if not FloorCast.is_colliding() and grounded: #gets the velocity for when not on ground
		grounded = false
		velocity = get_real_velocity()
		rot = 0
		up_direction = Vector3(0, -1, 0)
if FloorCast.is_colliding(): #Self explanatory
	can_Jump = true
if move_direction.length() > 0.2:
	last_movement_direction = move_direction #gets the last direction the player moved in
var targetAngle := Vector3.BACK.signed_angle_to(last_movement_direction, Vector3.UP) #sets a tarhet for the angle
Player.global_rotation.y = lerp(Player.rotation.y, targetAngle, rotationSpeed * delta) #rotates the player accordinlgy

func _unhandled_input(event: InputEvent) → void: #mouse controls
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
func _coyote_time(delta):#coyote timer for jumping
if coyote_time > 0:
coyote_time -= delta
else:
coyote_time = coyote_timer
if coyote_timer > 0:
can_Jump = true
else:
if not is_on_floor():
can_Jump = false
func _slopeDetection():
var slopeMode = 0
if $WallCast.is_colliding():
if not %SlopeCast.is_colliding() and %FloorCast.is_colliding():
slopeMode = 1
if slopeMode == 1:
print(slopeMode)

func _jump(delta: float) → void: #on floor detector really. Misleading name on my part but long story there
if is_on_floor():
can_Jump = true
else:
can_Jump = false
if Input.is_action_just_pressed(“check”): #tester for key variables
print(“Speed:”, Speed)
print(“can_Jump:”, can_Jump)
print(“coyote_time:”, coyote_time)
print(“is_on_floor():”, is_on_floor())
print(“jump_timer:”, jump_timer, “/”, jump_time)
print(“Friction:”, friction)
print(“Angle:”, angle)
print(“is_on_wall:”, is_on_wall())
print(“Slope_Angle:”, slope_angle)
print(“Rotation:”, rotation.z)
func _on_area_3d_body_shape_entered(body_rid: RID, body: Node3D, body_shape_index: int, local_shape_index: int) → void:
if body in get_tree().get_nodes_in_group(“Speed Boost”):
Speed = 300

Thank you

Search.

1 Like

If people searched before instinctively firing off questions, the new question load would go down about 95%. The same questions are asked over and over again, with only trivial differences.