Godot Version
4.5.1
Question
Hello I want to recreate a game like “Kula Word” (PS1)
There is a ball that has 3 commands:
- go forward 1 block
- rotate 90 degrees left or right
- jump forward 2 blocks
The ball moves on 2x2x2 cubes, with ortogonal movements (so it should be simple)
when the ball comes to an edge, the ball can wrap around that cube and the gravity changes
I am strugling to change gravity and rotation of the ball.
here is the script I’ve made…
extends CharacterBody3D
enum STATE {IDLE, MOVING, DEAD, WRAP}
@export var roll_speed = 0.5 # Secondi per completare un movimento
@export var jump_height = 4.0 # Altezza del salto
@export var grid_size = 2.0 # Dimensione di un blocco
@onready var ball: CSGSphere3D = $CSGSphere3D
@onready var ray_next_block: RayCast3D = $RayNextBlock
@onready var animation_player: AnimationPlayer = $AnimationPlayer
var state: STATE = STATE.IDLE
var initial_pos = .0
var target_pos = .0
var gravity_dir = Vector3.DOWN
func _physics_process(delta):
# Solo gravità
if not is_on_floor() and state not in [STATE.DEAD, STATE.WRAP]:
velocity += gravity_dir * 9.8
else:
velocity = Vector3.ZERO
if state != STATE.WRAP:
state = STATE.IDLE
move_and_slide()
if state != STATE.IDLE:
return
# Input movimento - SOLO UI_UP per avanzare di 1 blocco
if Input.is_action_just_pressed("ui_up"):
move_forward_one_block()
# Input salto - SOLO UI_ACCEPT per saltare di 2 blocchi
elif Input.is_action_just_pressed("ui_accept"):
jump_forward_two_blocks()
if Input.is_action_just_pressed("ui_left"):
dorotate(90)
elif Input.is_action_just_pressed("ui_right"):
dorotate(-90)
func dorotate(degrees: int):
if state != STATE.IDLE:
return
state = STATE.MOVING
var rotate_tween = create_tween()
var current_rotation = rotation_degrees.y
var target_rotation = current_rotation + degrees
#change this to rotate around transform up direction
rotate_tween.tween_property(self, "rotation_degrees:y", target_rotation, roll_speed)
rotate_tween.finished.connect(_on_move_finished)
func move_forward_one_block():
if state != STATE.IDLE:
return
state = STATE.MOVING
# Direzione avanti (basata sulla rotazione della palla o globale)
if not ray_next_block.is_colliding():
wrap_block()
return
var forward_direction = -transform.basis.z
var target_position = position + (forward_direction * grid_size)
# Animazione di rotolamento
var tween = create_tween()
tween.set_parallel()
tween.tween_property(self, "position", target_position, roll_speed)
animation_player.play("rotate")
tween.finished.connect(_on_move_finished)
func wrap_block():
var forward_direction = -transform.basis.z
var target_position = position + (forward_direction * grid_size)
var target2 = target_position + (Vector3.RIGHT.cross(forward_direction) * grid_size)
gravity_dir = -forward_direction.normalized()
var tween = create_tween()
tween.tween_property(self, "position", target_position, roll_speed)
tween.tween_property(self, "rotation:x", rotation.x + deg_to_rad(-90), roll_speed)
tween.tween_property(self, "position", target2, roll_speed)
tween.finished.connect(_on_move_finished)
#TODO fix to grid position?
func jump_forward_two_blocks():
if state != STATE.IDLE:
return
state = STATE.MOVING
var forward_direction = -transform.basis.z
target_pos = position + (forward_direction * grid_size * 2)
initial_pos = position
# Animazione di salto con arco parabolico
var tween = create_tween()
tween.set_parallel(true)
# Movimento orizzontale + arco verticale
tween.tween_method(_update_jump_position, 0.0, 1.0, roll_speed * 1.5)
animation_player.play("rotate")
#tween.finished.connect(_on_move_finished)
func _update_jump_position(t: float):
# Interpolazione lineare per il movimento orizzontale
var horizontal_pos = initial_pos.lerp(target_pos, t)
# Arco parabolico per il movimento verticale
var arc_height = jump_height * sin(t * PI)
position = Vector3(horizontal_pos.x, initial_pos.y + arc_height, horizontal_pos.z)
func _on_move_finished():
state = STATE.IDLE
func die():
state = STATE.DEAD
animation_player.play("die")
func goto_gameover():
get_tree().change_scene_to_file("res://levels/GameOver.tscn")