Building a Marble Game in Godot 4.4.1

Creating physics-based games in Godot 4 opens up exciting new possibilities โ€” and I’ve taken full advantage of them in my latest YouTube video: Godot 4 Marble Game.

This project started as an experiment to explore the upgraded physics engine in Godot 4.4.1. I wanted to build something simple, visually appealing, and mechanically satisfying โ€” a marble game was the perfect fit. Rolling physics, precise control, camera smoothing, and interactive level elements all come together to create a polished, responsive experience.

๐ŸŽฎ Whatโ€™s in the Game?

The marble is controlled using input-driven forces applied to a RigidBody3D, allowing realistic rolling movement and interactions with obstacles. The camera dynamically follows the marble, with rotation and zoom controls for better visibility and player feedback.

The level design takes inspiration from classic marble games, with ramps, platforms, and triggers that demonstrate what Godot 4โ€™s physics system can handle out of the box. No third-party plugins were used โ€” just native Godot tools and a lot of testing.

๐Ÿง  Why Godot?

Godot 4 is more than just a game engine โ€” itโ€™s a free, open-source, and lightweight tool that gives developers full control without the overhead. This game was built to showcase how powerful and accessible Godot 4 really is.

  • ๐Ÿ’ธ Free & Open-Source: No licenses, no royalties โ€” ever.
  • ๐Ÿชถ Lightweight & Efficient: Fast to load, easy on system resources, and works great even on modest hardware.
  • ๐Ÿ”Œ GDExtension System: Extend engine functionality with C++ without touching the core engine code.
  • ๐ŸŽฎ Improved Input Handling: Responsive and consistent controls across mouse, keyboard, gamepad, and touch.
  • โš™๏ธ Smoother Physics & Interpolation: Realistic motion, better feel, and a more polished game experience.
  • ๐Ÿ› ๏ธ Enhanced 3D Editor Tools: Powerful scene tools, gizmos, and debugging make 3D development intuitive.
  • ๐ŸŒ Cross-Platform Export: Build once, deploy to PC, mobile, web, and consoles.

These made the development process smoother and more enjoyable than ever.

๐Ÿ“น Watch the Video

I break down the key gameplay mechanics and show the game in action in our latest YouTube video. Whether you’re here for inspiration, education, or just love marble games โ€” you’ll want to check it out.

๐Ÿงฐ Want the Project Files?

Here’s the full project code and a breakdown of the code on the channel. Subscribe and stay tuned for more in-depth tutorials, devlogs, and tips for building your own Godot 4 games.

ball.gd

extends RigidBody3D

@export var camera_3d : Camera3D

@export var movement_speed : float = 385.0
@export var max_velocity : float = 7.5
@export var jump_power : float = 6.0

var grounded = false

func _physics_process(delta):
	movement(delta)
	if linear_velocity.x > max_velocity:
		linear_velocity.x = max_velocity
	if linear_velocity.x < -max_velocity:
		linear_velocity.x = -max_velocity
	if linear_velocity.z > max_velocity:
		linear_velocity.z = max_velocity
	if linear_velocity.z < -max_velocity:
		linear_velocity.z = -max_velocity
	
	if Input.is_action_just_pressed("jump") and grounded:
		jump()

func movement(delta):
	var f_input = Input.get_action_raw_strength("backward") - Input.get_action_raw_strength("forward")
	var h_input = Input.get_action_raw_strength("right") - Input.get_action_raw_strength("left")
	
	var camera_tranform = camera_3d.get_camera_transform()
	
	var relative_camera_direction_z = camera_tranform.basis.z.normalized()
	var relative_camera_direction_x = camera_tranform.basis.x.normalized()
	
	var direction_f = f_input * relative_camera_direction_z
	var direction_h = h_input * relative_camera_direction_x
	
	apply_central_force(direction_f * movement_speed * delta)
	apply_central_force(direction_h * movement_speed * delta)

func jump():
	apply_central_impulse(Vector3.UP * jump_power)

camera.gd

extends Node3D

@export_category("Configurables")
@export var cam_v_max : float = 110.0
@export var cam_v_min : float = -75.0
@export var h_sensitivity : float = 0.1
@export var v_sensitivity : float = 0.1
@export var h_acceleration : float = 15.0
@export var v_acceleration : float = 15.0
@export var smooth_camera_tolerance : float = .3

var camrot_h : float = 0.0
var camrot_v : float = 0.0

@onready var bano = $"../Bano"
@onready var h_rotation = $HRotation
@onready var v_rotation = $HRotation/VRotation

# Called when the node enters the scene tree for the first time.
func _ready():
	#hide mouse at start
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _physics_process(delta):
	global_position = lerp(global_position, bano.get_node("MeshInstance3D").global_position,smooth_camera_tolerance)
	
	camrot_v = clamp(camrot_v, cam_v_min, cam_v_max)
	
	h_rotation.rotation_degrees.y = lerp(h_rotation.rotation_degrees.y, camrot_h, delta * h_acceleration)
	v_rotation.rotation_degrees.x = lerp(v_rotation.rotation_degrees.x, camrot_v, delta * v_acceleration)
	rotation_degrees.z = 0
	
func _input(event):
	if event is InputEventMouseMotion:
		camrot_h += -event.relative.x * h_sensitivity
		camrot_v += -event.relative.y * v_sensitivity
	elif event is InputEventJoypadMotion:
		if event.axis == 2:
			camrot_h += -event.axis_value * h_sensitivity * 100
		elif event.axis == 3:
			camrot_v += -event.axis_value * v_sensitivity * 100

ground_ray.gd

extends RayCast3D

@export var ball : RigidBody3D
@export var ball_mesh : MeshInstance3D

func _physics_process(_delta):
	global_position = ball_mesh.global_position
	
	if is_colliding():
		ball.grounded = true
	else:
		ball.grounded = false

Follow along, learn with us, and letโ€™s build amazing games in Godot 4.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *