odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

snake.odin (4082B)


      1 package snake
      2 
      3 import rl "../raylib"
      4 import "core:math"
      5 import "core:fmt"
      6 
      7 WINDOW_SIZE :: 1000
      8 GRID_WIDTH :: 20
      9 CELL_SIZE :: 16
     10 CANVAS_SIZE :: GRID_WIDTH*CELL_SIZE
     11 TICK_RATE :: 0.13
     12 Vec2i :: [2]int
     13 MAX_SNAKE_LENGTH :: GRID_WIDTH*GRID_WIDTH
     14 
     15 snake: [MAX_SNAKE_LENGTH]Vec2i
     16 snake_length: int
     17 tick_timer: f32 = TICK_RATE
     18 move_direction: Vec2i
     19 game_over: bool
     20 food_pos: Vec2i
     21 
     22 place_food :: proc() {
     23 	occupied: [GRID_WIDTH][GRID_WIDTH]bool
     24 
     25 	for i in 0..<snake_length {
     26 		occupied[snake[i].x][snake[i].y] = true
     27 	}
     28 
     29 	free_cells := make([dynamic]Vec2i, context.temp_allocator)
     30 
     31 	for x in 0..<GRID_WIDTH {
     32 		for y in 0..<GRID_WIDTH {
     33 			if !occupied[x][y] {
     34 				append(&free_cells, Vec2i {x, y})
     35 			}
     36 		}
     37 	}
     38 
     39 	if len(free_cells) > 0 {
     40 		random_cell_index := rl.GetRandomValue(0, i32(len(free_cells) - 1))
     41 		food_pos = free_cells[random_cell_index]
     42 	}
     43 }
     44 
     45 restart :: proc() {
     46 	start_head_pos := Vec2i { GRID_WIDTH / 2, GRID_WIDTH / 2 }
     47 	snake[0] = start_head_pos
     48 	snake[1] = start_head_pos - {0, 1}
     49 	snake[2] = start_head_pos - {0, 2}
     50 	snake_length = 3
     51 	move_direction = {0, 1}
     52 	game_over = false
     53 	place_food()
     54 }
     55 
     56 main :: proc() {
     57 	rl.SetConfigFlags({.VSYNC_HINT})
     58 	rl.InitWindow(WINDOW_SIZE, WINDOW_SIZE, "Snake")
     59 	rl.InitAudioDevice()
     60 
     61 	restart()
     62 
     63 	food_sprite := rl.LoadTexture("food.png")
     64 	head_sprite := rl.LoadTexture("head.png")
     65 	body_sprite := rl.LoadTexture("body.png")
     66 	tail_sprite := rl.LoadTexture("tail.png")
     67 
     68 	eat_sound := rl.LoadSound("eat.wav")
     69 	crash_sound := rl.LoadSound("crash.wav")
     70 
     71 	for !rl.WindowShouldClose() {
     72 		if rl.IsKeyDown(.UP) {
     73 			move_direction = {0, -1}
     74 		}
     75 
     76 		if rl.IsKeyDown(.DOWN) {
     77 			move_direction = {0, 1}
     78 		}
     79 
     80 		if rl.IsKeyDown(.LEFT) {
     81 			move_direction = {-1, 0}
     82 		}
     83 
     84 		if rl.IsKeyDown(.RIGHT) {
     85 			move_direction = {1, 0}
     86 		}
     87 
     88 		if game_over {
     89 			if rl.IsKeyPressed(.ENTER) {
     90 				restart()
     91 			}
     92 		} else {
     93 			tick_timer -= rl.GetFrameTime()
     94 		}
     95 
     96 		if tick_timer <= 0 {
     97 			next_part_pos := snake[0]
     98 			snake[0] += move_direction
     99 			head_pos := snake[0]
    100 
    101 			if head_pos.x < 0 || head_pos.y < 0 || head_pos.x >= GRID_WIDTH || head_pos.y >= GRID_WIDTH {
    102 				game_over = true
    103 				rl.PlaySound(crash_sound)
    104 			}
    105 
    106 			for i in 1..<snake_length {
    107 				cur_pos := snake[i]
    108 
    109 				if cur_pos == head_pos {
    110 					game_over = true
    111 					rl.PlaySound(crash_sound)
    112 				}
    113 
    114 				snake[i] = next_part_pos
    115 				next_part_pos = cur_pos
    116 			}
    117 
    118 			if head_pos == food_pos {
    119 				snake_length += 1
    120 				snake[snake_length - 1] = next_part_pos
    121 				place_food()
    122 				rl.PlaySound(eat_sound)
    123 			}
    124 
    125 			tick_timer = TICK_RATE + tick_timer
    126 		}
    127 
    128 		rl.BeginDrawing()
    129 		rl.ClearBackground({76, 53, 83, 255})
    130 
    131 		camera := rl.Camera2D {
    132 			zoom = f32(WINDOW_SIZE) / CANVAS_SIZE,
    133 		}
    134 
    135 		rl.BeginMode2D(camera)
    136 
    137 		rl.DrawTextureV(food_sprite, {f32(food_pos.x), f32(food_pos.y)}*CELL_SIZE, rl.WHITE)
    138 
    139 		for i in 0..<snake_length {
    140 			part_sprite := body_sprite
    141 			dir: Vec2i
    142 
    143 			if i == 0 {
    144 				part_sprite = head_sprite
    145 				dir = snake[i] - snake[i + 1]
    146 			} else if i == snake_length - 1 {
    147 				part_sprite = tail_sprite
    148 				dir = snake[i - 1] - snake[i]
    149 			} else {
    150 				dir = snake[i - 1] - snake[i]
    151 			}
    152 
    153 			rot := math.atan2(f32(dir.y), f32(dir.x)) * math.DEG_PER_RAD
    154 
    155 			source := rl.Rectangle {
    156 				0, 0,
    157 				f32(part_sprite.width), f32(part_sprite.height),
    158 			}
    159 
    160 			dest := rl.Rectangle {
    161 				f32(snake[i].x)*CELL_SIZE + 0.5*CELL_SIZE,
    162 				f32(snake[i].y)*CELL_SIZE + 0.5*CELL_SIZE,
    163 				CELL_SIZE,
    164 				CELL_SIZE,
    165 			}
    166 
    167 			rl.DrawTexturePro(part_sprite, source, dest, {CELL_SIZE, CELL_SIZE}*0.5, rot, rl.WHITE)
    168 		}
    169 
    170 		if game_over {
    171 			rl.DrawText("Game Over!", 4, 4, 25, rl.RED)
    172 			rl.DrawText("Press Enter to play again", 4, 30, 15, rl.BLACK)
    173 		}
    174 
    175 		score := snake_length - 3
    176 		score_str := fmt.ctprintf("Score: %v", score)
    177 		rl.DrawText(score_str, 4, CANVAS_SIZE - 14, 10, rl.GRAY)
    178 		
    179 		rl.EndMode2D()
    180 		rl.EndDrawing()
    181 
    182 		free_all(context.temp_allocator)
    183 	}
    184 
    185 	rl.UnloadTexture(head_sprite)
    186 	rl.UnloadTexture(food_sprite)
    187 	rl.UnloadTexture(body_sprite)
    188 	rl.UnloadTexture(tail_sprite)
    189 
    190 	rl.UnloadSound(eat_sound)
    191 	rl.UnloadSound(crash_sound)
    192 
    193 	rl.CloseAudioDevice()
    194 	rl.CloseWindow()
    195 }