287 lines
7.9 KiB
Markdown
287 lines
7.9 KiB
Markdown
|
|
# 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**:
|
||
|
|
|
||
|
|
```sui
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```sui
|
||
|
|
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
|
||
|
|
|
||
|
|
```sui
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```sui
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```sui
|
||
|
|
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:
|
||
|
|
|
||
|
|
```sui
|
||
|
|
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()`:
|
||
|
|
|
||
|
|
```c
|
||
|
|
contact[i].surface.bounce = 0.5; // 0 = no bounce, 1 = perfect bounce
|
||
|
|
contact[i].surface.bounce_vel = 0.1;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Applying Forces
|
||
|
|
|
||
|
|
```sui
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```sui
|
||
|
|
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):
|
||
|
|
|
||
|
|
```sui
|
||
|
|
# 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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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! 🎮
|