Files
shoal/examples/bouncing_balls.odin
T
2026-10-27 21:13:12 -07:00

90 lines
2.4 KiB
Odin

package main
import "core:math/rand"
import "core:strconv"
import "core:strings"
import ecs "../src"
import rl "vendor:raylib"
app: ecs.App
ball_count: int = 10000
BallComponent :: struct {
radius: f32,
speed: f32,
}
main :: proc() {
ecs.add_plugin(&app, ecs.DefaultPlugin)
ecs.add_system(&app, ecs.STARTUP, setup_window)
ecs.add_system(&app, ecs.STARTUP, spawn_balls)
ecs.add_system(&app, ecs.UPDATE, update_balls)
ecs.add_system(&app, ecs.DRAW, draw_balls)
ecs.add_system(&app, ecs.DRAW, show_framerate)
ecs.run(&app)
}
// STARTUP system taking the world: the window lives in it, so adjusting the
// default WindowComponent is a world lookup now
setup_window :: proc(world: ^ecs.World) {
window, _ := ecs.single(world, ecs.WindowComponent)
window.background = rl.BLACK
}
// STARTUP system taking the world: spawn_entity wants it, and the balls'
// components live inside it, no global storage to declare
spawn_balls :: proc(world: ^ecs.World) {
for i in 0 ..< ball_count {
ball := ecs.spawn_entity(world)
ecs.add_component(world, ball, BallComponent{radius = 5, speed = rand.float32_range(4, 6)})
ecs.add_component(
world,
ball,
ecs.TransformComponent {
translation = {400, 300},
rotation = rand.float32_range(0, 360),
},
)
}
}
update_balls :: proc(world: ^ecs.World) {
window, _ := ecs.single(world, ecs.WindowComponent)
balls := ecs.slice(world, BallComponent)
entities := ecs.slice_entity(world, BallComponent)
for ball, i in balls {
transform := ecs.get_component(world, entities[i], ecs.TransformComponent)
pos := transform.translation
if pos.x <= 0 || pos.x >= f32(window.width) || pos.y <= 0 || pos.y >= f32(window.height) {
transform.rotation -= 180
}
dir := rl.Vector2Rotate(rl.Vector2{0, ball.speed}, transform.rotation)
transform.translation += dir
}
}
draw_balls :: proc(world: ^ecs.World) {
balls := ecs.slice(world, BallComponent)
entities := ecs.slice_entity(world, BallComponent)
for ball, i in balls {
transform := ecs.get_component(world, entities[i], ecs.TransformComponent)
rl.DrawCircle(
i32(transform.translation.x),
i32(transform.translation.y),
ball.radius,
rl.PURPLE,
)
}
}
show_framerate :: proc() {
fps_buf: [19]byte
framerate := strconv.write_int(fps_buf[:], i64(1 / rl.GetFrameTime()), 10)
rl.DrawText(strings.clone_to_cstring(framerate), 0, 0, 24, rl.WHITE)
rl.DrawText(" fps", 0, 24, 24, rl.WHITE)
rl.DrawText("10000 balls", 0, 24, 24, rl.WHITE)
}