Setup
-
Install Odin :
-
-
There are no releases.
-
The code has to be compiled from source by running the
.\build.bat releaseinx64 Native Tools Command Prompt for VS 2022command shell.
-
-
-
Get the latest RayLib release :-
Not needed, as Raylib is already part of the Odin vendor, located at:
<odin_installation_folder>/vendor/raylib/.
-
-
The file system :
. └── main.odin-
Inside the
<odin_installation_folder>/vendor/raylib: . ├── README.md ├── raymath.odin ├── raylib.odin ├── raygui.odin ├── LICENSE ├── easings.odin ├── windows │ ├── raylibdll.lib │ ├── raylib.lib │ ├── raylib.dll │ ├── rayguidll.lib │ ├── raygui.lib │ └── raygui.dll ├── wasm // not needed for Windows │ ├── libraylib.a │ └── libraygui.a ├── rlgl // standalone module, not required for raylib "A multi-OpenGL abstraction layer with an immediate-mode style API" │ └── rlgl.odin ├── macos-arm64 // not needed for Windows │ ├── libraygui.dylib │ └── libraygui.a ├── macos // not needed for Windows │ ├── libraylib.dylib │ ├── libraylib.a │ ├── libraylib.550.dylib │ ├── libraylib.5.5.0.dylib │ ├── libraygui.dylib │ └── libraygui.a └── linux // not needed for Windows ├── libraylib.so.550 ├── libraylib.so.5.5.0 ├── libraylib.so ├── libraylib.a ├── libraygui.so └── libraygui.a
-
-
Compile and execute :
-
odin run .
-
Moving Ball
/*
odin run .
*/
import rl "vendor:raylib"
main :: proc() {
SCREEN_WIDTH :: 800
SCREEN_HEIGHT :: 450
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Odin-Caio-Fork | 1_moving_ball")
defer rl.CloseWindow()
rl.SetTargetFPS(60)
// Initialize ball
ball_position := [2]f32{ SCREEN_WIDTH/2.0, SCREEN_HEIGHT/2.0 }
for !rl.WindowShouldClose() {
// Update ball position
if rl.IsKeyDown(.RIGHT) { ball_position.x += 2.0 }
if rl.IsKeyDown(.LEFT) { ball_position.x -= 2.0 }
if rl.IsKeyDown(.UP) { ball_position.y -= 2.0 }
if rl.IsKeyDown(.DOWN) { ball_position.y += 2.0 }
// Draw
{
rl.BeginDrawing()
defer rl.EndDrawing()
rl.ClearBackground(rl.WHITE)
rl.DrawText("move the ball with arrow keys", 10, 10, 20, rl.DARKGRAY)
rl.DrawCircleV(ball_position, 50, rl.MAROON)
}
}
}
Collect The Coins
/*
odin run .
*/
import rl "vendor:raylib"
MAX_COINS :: 20
PLAYER_SPEED :: 4.0
Player :: struct {
position: [2]f32,
radius: f32,
score: int,
}
Coin :: struct {
position: [2]f32,
radius: f32,
active: bool,
}
main :: proc() {
SCREEN_WIDTH :: 800
SCREEN_HEIGHT :: 600
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Odin-Caio-Fork | 2_collect_the_coins")
defer rl.CloseWindow()
rl.SetTargetFPS(60)
// Initialize player
player := Player{
position = { 400, 300 },
radius = 20,
score = 0,
}
// Initialize coins
coins: [MAX_COINS]Coin
for i in 0..<MAX_COINS {
coins[i].position = {
f32(rl.GetRandomValue(20, SCREEN_WIDTH - 20)),
f32(rl.GetRandomValue(20, SCREEN_HEIGHT - 20)),
}
coins[i].radius = 8
coins[i].active = true
}
for !rl.WindowShouldClose() {
// Update player
if rl.IsKeyDown(.W) { player.position.y -= PLAYER_SPEED }
if rl.IsKeyDown(.S) { player.position.y += PLAYER_SPEED }
if rl.IsKeyDown(.A) { player.position.x -= PLAYER_SPEED }
if rl.IsKeyDown(.D) { player.position.x += PLAYER_SPEED }
// Check collisions
for i in 0..<MAX_COINS {
if coins[i].active {
dist := rl.Vector2Distance(player.position, coins[i].position)
if dist < player.radius + coins[i].radius {
coins[i].active = false
player.score += 1
}
}
}
// Draw
{
rl.BeginDrawing()
defer rl.EndDrawing()
rl.ClearBackground(rl.DARKGREEN)
rl.DrawCircleV(player.position, player.radius, rl.BLUE)
for i in 0..<MAX_COINS {
if coins[i].active {
rl.DrawCircleV(coins[i].position, coins[i].radius, rl.GOLD)
}
}
rl.DrawText(rl.TextFormat("Score: %d", player.score), 10, 10, 20, rl.WHITE)
}
}
}
Space Invaders
import "base:mem"
import "base:mem/allocators"
import "base:container/slice"
import "core:os"
import "core:fmt"
import rl "vendor:raylib"
MAX_BULLETS :: 64
BULLET_SPEED :: 6.0
Player :: struct {
position: [2]f32,
radius: f32,
}
Bullet :: struct {
position: [2]f32,
velocity: [2]f32,
radius: f32,
active: bool,
}
Enemy :: struct {
position: [2]f32,
radius: f32,
active: bool,
color: rl.Color,
}
main :: proc() {
allocator := allocators.heap_allocator()
os.init_std_files()
allocators.temp_allocator_init(0, allocator)
defer allocators.temp_allocator_destroy()
SCREEN_WIDTH :: 800
SCREEN_HEIGHT :: 600
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Odin-Caio-Fork | 3_space_invaders")
defer rl.CloseWindow()
rl.SetTargetFPS(60)
// Initialize player
player := Player{
{ 400, 500 },
20,
}
bullets: [MAX_BULLETS]Bullet // Stack allocation
// Initialize enemies
enemies, slice_err := slice.create(Enemy, 20, allocator) // Heap allocation, using context.allocator implicitly
fmt.assertf(slice_err == nil, "Error '%v' when creating the slice", slice_err)
defer _ = slice.delete(enemies, allocator)
enemies_destroyed_count: uint
spawn_inactive_enemies(enemies)
for !rl.WindowShouldClose() {
// Update player
if rl.IsKeyDown(.A) { player.position.x -= 4 }
if rl.IsKeyDown(.D) { player.position.x += 4 }
if rl.IsKeyDown(.W) { player.position.y -= 4 }
if rl.IsKeyDown(.S) { player.position.y += 4 }
if rl.IsKeyPressed(.SPACE) {
spawn_bullet(bullets[:], player.position)
}
// Update bullets
for i in 0..<len(bullets) {
if bullets[i].active {
bullets[i].position.x += bullets[i].velocity.x
bullets[i].position.y += bullets[i].velocity.y
if bullets[i].position.y < 0 {
bullets[i].active = false
}
}
}
// Update enemies
active_enemies := 0
for i in 0..<len(enemies) {
if enemies[i].active {
enemies[i].position.y += 0.5
active_enemies += 1
}
}
// Respawn inactive enemies
if active_enemies < 5 {
spawn_inactive_enemies(enemies)
}
// Check Collisions
for i in 0..<len(bullets) {
if !bullets[i].active { continue }
for j in 0..<len(enemies) {
if !enemies[j].active { continue }
d := rl.Vector2Distance(bullets[i].position, enemies[j].position)
r := bullets[i].radius + enemies[j].radius
if d < r {
bullets[i].active = false
enemies[j].active = false
enemies_destroyed_count += 1
}
}
}
// Draw
{
rl.BeginDrawing()
defer rl.EndDrawing()
rl.ClearBackground(rl.BLACK)
rl.DrawCircleV(player.position, player.radius, rl.BLUE)
for i in 0..<len(bullets) {
if bullets[i].active {
rl.DrawCircleV(bullets[i].position, bullets[i].radius, rl.YELLOW)
}
}
for i in 0..<len(enemies) {
if enemies[i].active {
rl.DrawCircleV(enemies[i].position, enemies[i].radius, enemies[i].color)
}
}
rl.DrawText("Space Invaders: WASD move | SPACE shoot", 10, 10, 20, rl.WHITE)
rl.DrawText(fmt.ctprintf("Enemies destroyed: %v", enemies_destroyed_count), 10, 40, 20, rl.YELLOW)
// Prints while doing a temporary allocation.
}
free_all_err := mem.free_all(allocators.temp_allocator)
fmt.assertf(free_all_err == nil, "Error '%v' when freeing the temp_allocator", free_all_err)
}
}
spawn_bullet :: proc(bullets: []Bullet, pos: [2]f32) {
for i in 0..<len(bullets) {
if !bullets[i].active {
bullets[i].active = true
bullets[i].position = pos
bullets[i].velocity = {0, -BULLET_SPEED}
bullets[i].radius = 5
return
}
}
}
spawn_inactive_enemies :: proc(enemies: []Enemy) {
for i in 0..<len(enemies) {
if !enemies[i].active {
enemies[i].position = {
f32(rl.GetRandomValue(50, 750)),
f32(rl.GetRandomValue(50, 300)),
}
enemies[i].radius = 15
enemies[i].active = true
enemies[i].color = {
u8(rl.GetRandomValue(100, 255)),
u8(rl.GetRandomValue(100, 255)),
u8(rl.GetRandomValue(100, 255)),
255,
}
}
}
}