suicmez/GAMEDEV_GUIDE.md
2025-12-17 13:18:12 +05:30

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:

  1. A body - represents mass and motion
  2. A geometry - collision shape
  3. 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 ODE
  • ode_close() - Shutdown ODE
  • ode_world_create() -> *() - Create a world
  • ode_world_destroy(world) - Destroy world
  • ode_world_set_gravity(world, x, y, z) - Set gravity vector
  • ode_world_step(world, timestep) - Advance simulation

Body Management

  • ode_body_create(world) -> *() - Create a dynamic body
  • ode_body_destroy(body)
  • ode_body_set_position(body, x, y, z)
  • ode_body_get_position(body, &x, &y, &z) - Retrieve position via pointers
  • ode_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 rotation
  • ode_body_get_rotation(body, &w, &x, &y, &z) - Get quaternion rotation
  • ode_body_set_box_mass(body, density, width, height, length)

Collision Shapes (Geometries)

  • ode_create_box_geom(space, width, height, length) -> *() - Create box shape
  • ode_create_plane_geom(space, nx, ny, nz, d) -> *() - Create plane shape
  • ode_geom_set_body(geom, body) - Attach geometry to body
  • ode_geom_destroy(geom)

Collision Spaces

  • ode_simple_space_create(parent) -> *() - Create simple space
  • ode_space_destroy(space)
  • ode_space_collide(world, space, contactgroup) - Important: Detect collisions
  • ode_joint_group_create(max_size) -> *() - Create contact joint group
  • ode_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:

  1. Use dBVHSpaceCreate() (not yet exposed) instead of simple space
  2. Reduce collision pairs by using collision filtering
  3. Use larger timesteps (but beware stability)
  4. 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 before ode_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! 🎮