package boids import rl "vendor:raylib" import "core:math" import "core:math/rand" import "core:fmt" Vec2 :: [2]f32 Rect :: rl.Rectangle Img :: rl.Image Tex :: rl.Texture Key :: rl.KeyboardKey Color :: rl.Color SCREEN_WIDTH : i32 = 1600 SCREEN_HEIGHT : i32 = 1000 BW := f32(SCREEN_WIDTH) BH := f32(SCREEN_HEIGHT) BOID_W :f32 = 15 BOID_H :f32 = 30 Boid :: struct { pos: Vec2, angle: f32, accel: Vec2, vel: Vec2 } GameState :: struct { dt: f32, keymap: map[string]Key, follow_point: Maybe(Vec2), boids: [dynamic]Boid, } init_state :: proc() -> ^GameState { keymap := make(map[string]Key) keymap["Left"] = .A keymap["Right"] = .D keymap["Up"] = .W keymap["Down"] = .S state := GameState { keymap = keymap, } boid := Boid { pos = {100, 100}, } append(&state.boids, boid) cloned := new_clone(state) return cloned } get_rand_angle :: proc(min: i32, max: i32) -> f32 { return f32(rl.GetRandomValue(min, max)) * rl.DEG2RAD } get_boid_shape :: proc(boid: Boid, points: ^[4]Vec2) { points[0] = rl.Vector2Rotate({ BOID_H*0.5, 0}, boid.angle) + boid.pos points[1] = rl.Vector2Rotate({-BOID_H*0.5, -BOID_W*0.5}, boid.angle) + boid.pos points[2] = rl.Vector2Rotate({-BOID_H*0.5*0.5, 0}, boid.angle) + boid.pos points[3] = rl.Vector2Rotate({-BOID_H*0.5, BOID_W*0.5}, boid.angle) + boid.pos } player_input :: proc(using s: ^GameState) { horizontal: f32 vertical: f32 if rl.IsKeyDown(s.keymap["Right"]) { horizontal = 1 } if rl.IsKeyDown(s.keymap["Left"]) { horizontal = -1 } if rl.IsKeyDown(s.keymap["Up"]) { vertical = -1 } if rl.IsKeyDown(s.keymap["Down"]) { vertical = 1 } if rl.IsMouseButtonPressed(rl.MouseButton.LEFT) { follow_point = rl.GetMousePosition() } } update :: proc(using s: ^GameState) { max_speed :f32 = 15 if fpoint, ok := follow_point.?; ok { for &boid in boids { dist := rl.Vector2LengthSqr(fpoint - boid.pos) if dist < 10 { continue } desired := fpoint - boid.pos if dist < 200 { desired = rl.Vector2Normalize(desired) * (dist / 200 * max_speed) } else { desired = rl.Vector2Normalize(desired) * max_speed } steer := desired - boid.vel steer = rl.Vector2ClampValue(steer, 0, 5) boid.accel += steer boid.vel += boid.accel boid.pos += boid.vel desired_angle := math.atan2(boid.vel.y, boid.vel.x) angle := rl.Lerp(boid.angle, desired_angle, s.dt * 7) boid.angle = angle boid.accel *= 0 } } } draw :: proc(using s: ^GameState) { boid_shape := [4]Vec2{} for boid in boids { get_boid_shape(boid, &boid_shape) rl.DrawLineEx(boid_shape[0], boid_shape[1], 2, rl.BLACK) rl.DrawLineEx(boid_shape[1], boid_shape[2], 2, rl.BLACK) rl.DrawLineEx(boid_shape[2], boid_shape[3], 2, rl.BLACK) rl.DrawLineEx(boid_shape[3], boid_shape[0], 2, rl.BLACK) } } main :: proc() { rl.SetTraceLogLevel(.ERROR) rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Boids") rl.SetTargetFPS(60) state := init_state() defer { delete(state.boids) free(state) } for !rl.WindowShouldClose() { state.dt = rl.GetFrameTime() player_input(state) update(state) rl.BeginDrawing() rl.ClearBackground(rl.WHITE) draw(state) rl.EndDrawing() } rl.CloseWindow() }