7.9 KiB
Sui Language Game Development Guide
Overview
Sui now has integrated support for game development with Raylib (graphics) and ODE (physics). This guide explains how to build games with physics simulation and rendering.
Key Concepts
1. Physics Engine (ODE)
ODE (Open Dynamics Engine) provides rigid body dynamics, collision detection, and constraint solving. The key components are:
- World: The container for all physical objects and gravity
- Bodies: Dynamic rigid bodies with mass, velocity, and position
- Geometries: Collision shapes (boxes, planes, spheres, etc.)
- Space: Collision detection space that organizes geometries
- Contacts: Joint constraints created when objects collide
2. Graphics Engine (Raylib)
Raylib provides 2D and 3D graphics, input handling, and audio support.
3. Integration Pattern
The key to proper physics is the collision detection loop:
# Initialize
ode_init()
ode_space_collide(world, space, contactgroup)
ode_world_step(world, timestep)
ode_joint_group_empty(contactgroup)
Important: You must call collision detection before world step, and clear the contact group after the step.
Step-by-Step Tutorial
Step 1: Initialize the World
fn main() -> int do
# Initialize graphics
init_window(800, 600, "My Game")
set_target_fps(60)
# Initialize physics
ode_init()
let world = ode_world_create()
let space = ode_simple_space_create(0 as *())
let contactgroup = ode_joint_group_create(0)
# Set gravity (9.81 m/s² downward)
ode_world_set_gravity(world, 0.0, -9.81, 0.0)
# ... rest of code
end
Step 2: Create Physics Objects
Each dynamic object needs:
- A body - represents mass and motion
- A geometry - collision shape
- Mass configuration
# Create a cube that falls
let body = ode_body_create(world)
ode_body_set_position(body, 0.0, 5.0, 0.0) # x, y, z
ode_body_set_box_mass(body, 1.0, 1.0, 1.0, 1.0) # density, width, height, length
# Create collision geometry
let geom = ode_create_box_geom(space, 1.0, 1.0, 1.0)
ode_geom_set_body(geom, body)
Step 3: Create Static Objects (Ground)
Static objects are created without a body - they use infinite mass:
# Create a ground plane at y = 0
let ground_geom = ode_create_plane_geom(space, 0.0, 1.0, 0.0, 0.0)
# Parameters: space, normal_x, normal_y, normal_z, distance_d
Step 4: Main Game Loop
while window_should_close() == false do
# === PHYSICS STEP ===
# 1. Detect collisions between all geometries
ode_space_collide(world, space, contactgroup)
# 2. Simulate physics
ode_world_step(world, 1.0 / 60.0) # 60 FPS timestep
# 3. Clear contact group to avoid double-application
ode_joint_group_empty(contactgroup)
# === RENDERING STEP ===
# Get current position
let mut x = 0.0
let mut y = 0.0
let mut z = 0.0
ode_body_get_position(body, &x, &y, &z)
# Draw
begin_drawing()
clear_background(135, 206, 235, 255) # Sky blue
begin_mode3d(0.0, 2.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 45.0, 0)
draw_cube(x, y, z, 1.0, 1.0, 1.0, 255, 0, 0, 255) # Red cube
draw_cube(0.0, -1.0, 0.0, 20.0, 0.1, 20.0, 34, 139, 34, 255) # Green ground
end_mode3d()
end_drawing()
end
Step 5: Cleanup
Always destroy objects in reverse order of creation:
ode_joint_group_destroy(contactgroup)
ode_geom_destroy(geom)
ode_body_destroy(body)
ode_space_destroy(space)
ode_world_destroy(world)
ode_close()
close_window()
Available Physics Functions
World Management
ode_init()- Initialize ODEode_close()- Shutdown ODEode_world_create() -> *()- Create a worldode_world_destroy(world)- Destroy worldode_world_set_gravity(world, x, y, z)- Set gravity vectorode_world_step(world, timestep)- Advance simulation
Body Management
ode_body_create(world) -> *()- Create a dynamic bodyode_body_destroy(body)ode_body_set_position(body, x, y, z)ode_body_get_position(body, &x, &y, &z)- Retrieve position via pointersode_body_set_linear_vel(body, vx, vy, vz)ode_body_get_linear_vel(body, &vx, &vy, &vz)ode_body_set_rotation(body, w, x, y, z)- Set quaternion rotationode_body_get_rotation(body, &w, &x, &y, &z)- Get quaternion rotationode_body_set_box_mass(body, density, width, height, length)
Collision Shapes (Geometries)
ode_create_box_geom(space, width, height, length) -> *()- Create box shapeode_create_plane_geom(space, nx, ny, nz, d) -> *()- Create plane shapeode_geom_set_body(geom, body)- Attach geometry to bodyode_geom_destroy(geom)
Collision Spaces
ode_simple_space_create(parent) -> *()- Create simple spaceode_space_destroy(space)ode_space_collide(world, space, contactgroup)- Important: Detect collisionsode_joint_group_create(max_size) -> *()- Create contact joint groupode_joint_group_destroy(group)ode_joint_group_empty(group)- Important: Clear contacts after step
Common Patterns
Adding Bounce/Restitution
Modify the contact properties in libsuicmez.c near_callback():
contact[i].surface.bounce = 0.5; // 0 = no bounce, 1 = perfect bounce
contact[i].surface.bounce_vel = 0.1;
Applying Forces
# Set velocity directly
ode_body_set_linear_vel(body, 10.0, 0.0, 0.0)
Note: ODE also supports force/torque application through lower-level APIs.
Multiple Objects
Create arrays of bodies and simulate them in a loop:
let bodies_count = 5
# In main loop:
let mut i = 0
while i < bodies_count do
ode_body_get_position(bodies[i], &x, &y, &z)
draw_cube(x, y, z, 1.0, 1.0, 1.0, 255, 0, 0, 255)
i = i + 1
end
Advanced Topics
Custom Contact Properties
Edit suic_ode_set_contact_erp() and suic_ode_set_contact_cfm() to tune physics:
- ERP (Error Reduction Parameter): 0-1, controls how fast constraint violations are fixed
- CFM (Constraint Force Mixing): Soft constraint parameter, adds damping
Quaternion Rotations
ODE uses quaternions for 3D rotations. They're represented as (w, x, y, z):
# Identity rotation
ode_body_set_rotation(body, 1.0, 0.0, 0.0, 0.0)
# Get current rotation
let mut w = 0.0
let mut x = 0.0
let mut y = 0.0
let mut z = 0.0
ode_body_get_rotation(body, &w, &x, &y, &z)
Performance Optimization
For large numbers of objects:
- Use
dBVHSpaceCreate()(not yet exposed) instead of simple space - Reduce collision pairs by using collision filtering
- Use larger timesteps (but beware stability)
- Implement sleeping/deactivation for static objects
Example: Bouncing Ball Game
See tests/game_3d.sui for a complete working example of:
- Physics world setup
- Dynamic falling cube
- Ground plane
- Collision detection
- 3D rendering
Compile and run:
cargo run tests/game_3d.sui
gcc tests/game_3d.c libsuicmez/libsuicmez.c $(pkg-config --cflags --libs raylib ode) -o game
./game
Troubleshooting
"Cube falls through ground"
- Ensure
ode_space_collide()is called beforeode_world_step() - Ensure contact group is created with
ode_joint_group_create() - Call
ode_joint_group_empty()after each step
Jittery Physics
- Reduce timestep (smaller value in
ode_world_step()) - Increase ERP with
suic_ode_set_contact_erp() - Reduce CFM with
suic_ode_set_contact_cfm()
Objects moving too slow/fast
- Check gravity:
ode_world_set_gravity(world, 0.0, -9.81, 0.0) - Check body mass:
ode_body_set_box_mass(body, density, w, h, l) - Check initial velocity:
ode_body_set_linear_vel(body, vx, vy, vz)
Next Steps
The Sui language is now ready for 3D game development! Future enhancements could include:
- Higher-level GameObject system wrapping physics + rendering
- Additional collision shapes (spheres, cylinders, meshes)
- Particle systems
- Audio integration
- Input handling improvements
- Scripting for game logic
Happy game developing! 🎮