Untitled
unknown
plain_text
6 days ago
4.6 kB
2
Indexable
Never
package main import "core:math/linalg" import "core:fmt" import "core:time" import "core:math/rand" import rl "vendor:raylib" // MARK: Consts WINDOW_SIZE :: 500 BLOCK_SIZE :: 25 UPDATE_INTERVAL_MS :: 100 // MARK: Structs GameState :: struct { // Snake head: Point, body: [dynamic]Point, direction: Direction, food: Point, last_update_ts: time.Time, is_game_over: bool } Direction :: enum { LEFT, RIGHT, DOWN, UP } Point :: [2]int // MARK: Globals g_game_state: ^GameState // MARK: Main update :: proc() { if g_game_state.is_game_over { if rl.IsKeyPressed(.R) { g_game_state.is_game_over = false mem_free() reset_state() } return } // Input handling dir := g_game_state.direction if rl.IsKeyDown(.W) && dir != .DOWN do g_game_state.direction = .UP if rl.IsKeyDown(.A) && dir != .RIGHT do g_game_state.direction = .LEFT if rl.IsKeyDown(.S) && dir != .UP do g_game_state.direction = .DOWN if rl.IsKeyDown(.D) && dir != .LEFT do g_game_state.direction = .RIGHT // Snake update elapsed_ts := time.duration_milliseconds(time.since(g_game_state.last_update_ts)) if elapsed_ts <= UPDATE_INTERVAL_MS { return } g_game_state.last_update_ts = time.now() prev_pos := g_game_state.head new_head := g_game_state.head + dir_to_point(g_game_state.direction) // Wall collision grid_size := WINDOW_SIZE / BLOCK_SIZE is_x_valid := new_head.x < 0 || new_head.x >= grid_size is_y_valid := new_head.y < 0 || new_head.y >= grid_size if is_x_valid || is_y_valid { g_game_state.is_game_over = true return } // Snake collision for pt in g_game_state.body { if new_head == pt { g_game_state.is_game_over = true return } } // Snake movement g_game_state.head = new_head for &pt in g_game_state.body { temp := pt pt = prev_pos prev_pos = temp } // Food collision if g_game_state.head == g_game_state.food { append(&g_game_state.body, g_game_state.food) g_game_state.food = get_rand_pos() } } draw :: proc() { rl.BeginDrawing() defer rl.EndDrawing() bg_color := rl.DARKGRAY if g_game_state.is_game_over else rl.DARKBROWN rl.ClearBackground(bg_color) // Draw grid num_rows := WINDOW_SIZE / BLOCK_SIZE grid_color := rl.ColorAlpha(rl.GRAY, 0.5) for i in 0..<num_rows { x := f32(i * BLOCK_SIZE) rl.DrawLineV({x, 0}, {x, WINDOW_SIZE}, grid_color) rl.DrawLineV({0, x}, {WINDOW_SIZE, x}, grid_color) } // Draw score score := fmt.caprint(len(g_game_state.body)) SCORE_FONT_SIZE :: 150 score_width := rl.MeasureText(score, SCORE_FONT_SIZE) score_color := rl.ColorAlpha(rl.BEIGE, 0.8) rl.DrawText(score, (WINDOW_SIZE / 2) - score_width / 2, (WINDOW_SIZE / 2) - 70, SCORE_FONT_SIZE, score_color) // Draw snake { // Head snake_head := point_to_vec2(g_game_state.head * BLOCK_SIZE) rl.DrawRectangleV(snake_head, {BLOCK_SIZE, BLOCK_SIZE}, rl.PINK) // Body for pt in g_game_state.body { point := point_to_vec2(pt * BLOCK_SIZE) rl.DrawRectangleV(point, {BLOCK_SIZE, BLOCK_SIZE}, rl.WHITE) } } // Draw Food food_pos := point_to_vec2(g_game_state.food * BLOCK_SIZE) rl.DrawRectangleV(food_pos, {BLOCK_SIZE, BLOCK_SIZE}, rl.SKYBLUE) // Draw restart text if g_game_state.is_game_over { rl.DrawText("Press 'R' to restart", 10, 10, 30, rl.WHITE) } } // MARK: Utils point_to_vec2 :: proc(point: Point) -> rl.Vector2 { return { f32(point.x), f32(point.y) } } dir_to_point :: proc(dir: Direction) -> (ret: Point) { switch(dir) { case .LEFT: ret = {-1, 0} case .RIGHT: ret = {1, 0} case .DOWN: ret = {0, 1} case .UP: ret = {0, -1} } return ret } get_rand_pos :: proc() -> Point { range := i32(WINDOW_SIZE / BLOCK_SIZE) return { int(rand.int31_max(range)), int(rand.int31_max(range)), } } // MARK: Exports @(export) init_window :: proc() { rl.SetConfigFlags({.WINDOW_RESIZABLE}) rl.InitWindow(WINDOW_SIZE, WINDOW_SIZE, "snake") rl.SetTargetFPS(120) } @(export) tick :: proc() { update() draw() } @(export) mem_free :: proc() { free(g_game_state) } @(export) reset_state :: proc() { half_screen := (WINDOW_SIZE / BLOCK_SIZE) / 2 g_game_state = new(GameState) g_game_state^ = GameState { head = {half_screen - 1, half_screen - 1}, food = get_rand_pos() } } @(export) state :: proc() -> rawptr { return g_game_state } @(export) reload_state :: proc(mem: rawptr) { g_game_state = (^GameState)(mem) } @(export) is_restart :: proc(mem: rawptr) -> bool { return rl.IsKeyDown(.LEFT_SHIFT) && rl.IsKeyPressed(.R) } @(export) is_close_window :: proc() -> bool { return rl.WindowShouldClose() } @(export) close_window :: proc() { rl.CloseWindow() }
Leave a Comment