├── logo.png ├── odinfmt.json ├── ols.json ├── axen.odin ├── README.md └── axen_raylib.odin /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sunling472/axen/HEAD/logo.png -------------------------------------------------------------------------------- /odinfmt.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/odinfmt.schema.json", 3 | "brace_style": "_1TBS", 4 | "character_width": 80, 5 | "tabs": true 6 | } 7 | -------------------------------------------------------------------------------- /ols.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json", 3 | "enable_semantic_tokens": false, 4 | "enable_document_symbols": true, 5 | "enable_hover": true, 6 | "enable_snippets": true, 7 | "enable_format": true, 8 | "enable_fake_methods": true, 9 | "enable_inlay_hints": true, 10 | "enable_procedure_context": true, 11 | "enable_inlay_hints_default_params": false, 12 | "enable_references": true 13 | } -------------------------------------------------------------------------------- /axen.odin: -------------------------------------------------------------------------------- 1 | package axen 2 | 3 | import its "base:intrinsics" 4 | import rl "vendor:raylib" 5 | 6 | WindowSettings :: struct { 7 | width: i32, 8 | height: i32, 9 | title: cstring, 10 | fps: i32, 11 | flags: rl.ConfigFlags, 12 | } 13 | 14 | run :: proc( 15 | model: ^$M, 16 | init: proc(model: ^M), 17 | update: proc(model: ^M, dt: f32), 18 | render: proc(model: M), 19 | settings: WindowSettings, 20 | ) where its.type_is_struct(M) { 21 | 22 | rl.SetConfigFlags(settings.flags) 23 | rl.InitWindow( 24 | settings.width, 25 | settings.height, 26 | settings.title, 27 | );defer rl.CloseWindow() 28 | rl.InitAudioDevice();defer rl.CloseAudioDevice() 29 | rl.SetTargetFPS(settings.fps) 30 | 31 | init(model) 32 | 33 | for !rl.WindowShouldClose() { 34 | dt := rl.GetFrameTime() 35 | update(model, dt) 36 | 37 | rl.BeginDrawing() 38 | render(model^) 39 | rl.EndDrawing() 40 | 41 | free_all(context.temp_allocator) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](logo.png) 2 | 3 | ### This is a fork of the Nexa library, the repository of which is deleted, apparently. 4 | 5 | The library serves as a wrapper for Raylib, making it even easier to use. 6 | 7 | It uses the `init -> update -> render` architecture, and each procedure is passed the general structure of the model, which can contain any fields. This allows you to transfer data between procedures. 8 | Here is a minimal example: 9 | 10 | 11 | ```odin 12 | 13 | package axen_example 14 | 15 | import ax "libs/axen" 16 | 17 | Model :: struct { 18 | settings: ax.WindowSettings 19 | // ... 20 | } 21 | 22 | init :: proc(m: ^Model) {} 23 | 24 | update :: proc(m: ^Model, dt: f32) {} 25 | 26 | render :: proc(m: Model) {} 27 | 28 | main :: proc() { 29 | m: Model 30 | 31 | m.settings = ax.WindowSettings { 32 | width = 1280, 33 | height = 720, 34 | title = "Axen Example", 35 | fps = 60, 36 | flags = {.VSYNC_HINT, .WINDOW_RESIZABLE, .MSAA_4X_HINT}, 37 | } 38 | 39 | ax.run(&m, init, update, render, m.settings) 40 | } 41 | 42 | ``` 43 | 44 | ### Counter 45 | ```odin 46 | 47 | package axen_counter 48 | 49 | import "core:fmt" 50 | import st "core:strings" 51 | import ax "libs/axen" 52 | 53 | Model :: struct { 54 | settings: ax.WindowSettings, 55 | counter: Counter 56 | } 57 | 58 | Counter :: struct { 59 | value: int, 60 | size: i32, 61 | pos: [2]i32, 62 | format: string, 63 | color: ax.Color 64 | } 65 | 66 | init_counter :: proc(c: ^Counter) { 67 | c.value = 0 68 | c.size = 30 69 | c.pos = {100, 100} 70 | c.format = "Count: %d" 71 | c.color = ax.WHITE 72 | } 73 | 74 | update_counter :: proc(c: ^Counter) { 75 | if ax.is_key_pressed(.ENTER) { 76 | c.value += 1 77 | } 78 | if ax.is_key_pressed(.BACKSPACE) { 79 | c.value -= 1 80 | } 81 | } 82 | 83 | render_counter :: proc(c: Counter) { 84 | ax.render_text( 85 | text = st.clone_to_cstring(fmt.aprintf(c.format, c.value)), 86 | x = c.pos.x, 87 | y = c.pos.y, 88 | font_size = c.size, 89 | color = c.color 90 | ) 91 | ax.render_text( 92 | text = "Press ENTER for add count\nPress BACSPACE for dec count", 93 | x = 100, 94 | y = 200, 95 | font_size = c.size, 96 | color = c.color 97 | ) 98 | } 99 | 100 | init :: proc(m: ^Model) { 101 | init_counter(&m.counter) 102 | } 103 | 104 | update :: proc(m: ^Model, dt: f32) { 105 | update_counter(&m.counter) 106 | } 107 | 108 | render :: proc(m: Model) { 109 | ax.clear_screen(ax.BLACK) 110 | render_counter(m.counter) 111 | } 112 | 113 | main :: proc() { 114 | m: Model 115 | 116 | m.settings = ax.WindowSettings { 117 | width = 1280, 118 | height = 720, 119 | title = "Axen Example", 120 | fps = 60, 121 | flags = {.VSYNC_HINT, .WINDOW_RESIZABLE, .MSAA_4X_HINT} 122 | } 123 | 124 | ax.run(&m, init, update, render, m.settings) 125 | } 126 | ``` 127 | -------------------------------------------------------------------------------- /axen_raylib.odin: -------------------------------------------------------------------------------- 1 | package axen 2 | 3 | import json "core:encoding/json" 4 | import fmt "core:fmt" 5 | import math "core:math" 6 | import linalg "core:math/linalg" 7 | import mem "core:mem" 8 | import os "core:os" 9 | import rl "vendor:raylib" 10 | 11 | 12 | // Constants 13 | 14 | // Color Definitions 15 | WHITE: Color : {255, 255, 255, 255} // White 16 | BLACK: Color : {0, 0, 0, 255} // Black 17 | RED: Color : {255, 0, 0, 255} // Red 18 | GREEN: Color : {0, 255, 0, 255} // Green 19 | BLUE: Color : {0, 0, 255, 255} // Blue 20 | YELLOW: Color : {255, 255, 0, 255} // Yellow 21 | CYAN: Color : {0, 255, 255, 255} // Cyan 22 | MAGENTA: Color : {255, 0, 255, 255} // Magenta 23 | ORANGE: Color : {255, 165, 0, 255} // Orange 24 | PURPLE: Color : {128, 0, 128, 255} // Purple 25 | BROWN: Color : {165, 42, 42, 255} // Brown 26 | GRAY: Color : {128, 128, 128, 255} // Gray 27 | LIGHTGRAY: Color : {211, 211, 211, 255} // Light Gray 28 | DARKGRAY: Color : {169, 169, 169, 255} // Dark Gray 29 | PINK: Color : {255, 192, 203, 255} // Pink 30 | GOLD: Color : {255, 215, 0, 255} // Gold 31 | SILVER: Color : {192, 192, 192, 255} // Silver 32 | MAROON: Color : {128, 0, 0, 255} // Maroon 33 | OLIVE: Color : {128, 128, 0, 255} // Olive 34 | LIME: Color : {50, 205, 50, 255} // Lime 35 | TEAL: Color : {0, 128, 128, 255} // Teal 36 | NAVY: Color : {0, 0, 128, 255} // Navy 37 | VIOLET: Color : {238, 130, 238, 255} // Violet 38 | INDIGO: Color : {75, 0, 130, 255} // Indigo 39 | TURQUOISE: Color : {64, 224, 208, 255} // Turquoise 40 | PEACH: Color : {255, 218, 185, 255} // Peach 41 | MINT: Color : {189, 252, 201, 255} // Mint 42 | CORAL: Color : {255, 127, 80, 255} // Coral 43 | LIGHTPINK: Color : {255, 182, 193, 255} // Light Pink 44 | DARKORANGE: Color : {255, 140, 0, 255} // Dark Orange 45 | DARKGREEN: Color : {0, 100, 0, 255} // Dark Green 46 | DARKBLUE: Color : {0, 0, 139, 255} // Dark Blue 47 | LAVENDER: Color : {230, 230, 250, 255} // Lavender 48 | CRIMSON: Color : {220, 20, 60, 255} // Crimson 49 | FUCHSIA: Color : {255, 0, 255, 255} // Fuchsia 50 | LIGHTYELLOW: Color : {255, 255, 224, 255} // Light Yellow 51 | LIGHTGREEN: Color : {144, 238, 144, 255} // Light Green 52 | LIGHTBLUE: Color : {173, 216, 230, 255} // Light Blue 53 | LIGHTCORAL: Color : {240, 128, 128, 255} // Light Coral 54 | TOMATO: Color : {255, 99, 71, 255} // Tomato 55 | SEASHELL: Color : {255, 228, 196, 255} // Seashell 56 | HONEYDEW: Color : {240, 255, 240, 255} // Honeydew 57 | MISTYROSE: Color : {255, 228, 225, 255} // Misty Rose 58 | WHITESMOKE: Color : {245, 245, 245, 255} // White Smoke 59 | AZURE: Color : {240, 255, 255, 255} // Azure 60 | SANDYBROWN: Color : {244, 164, 96, 255} // Sandy Brown 61 | SLATEGRAY: Color : {112, 128, 144, 255} // Slate Gray 62 | KHAKI: Color : {240, 230, 140, 255} // Khaki 63 | LIMEGREEN: Color : {50, 205, 50, 255} // Lime Green 64 | MEDIUMSLATEBLUE: Color : {123, 104, 238, 255} // Medium Slate Blue 65 | SADDLEBROWN: Color : {139, 69, 19, 255} // Saddle Brown 66 | SLATEBLUE: Color : {106, 90, 205, 255} // Slate Blue 67 | DODGERBLUE: Color : {30, 144, 255, 255} // Dodger Blue 68 | DEEPSKYBLUE: Color : {0, 191, 255, 255} // Deep Sky Blue 69 | MEDIUMPURPLE: Color : {147, 112, 219, 255} // Medium Purple 70 | PALEVIOLETRED: Color : {219, 112, 147, 255} // Pale Violet Red 71 | NAVAJOWHITE: Color : {255, 222, 173, 255} // Navajo White 72 | GAINSBORO: Color : {220, 220, 220, 255} // Gainsboro 73 | CHARTREUSE: Color : {127, 255, 0, 255} // Chartreuse 74 | PLUM: Color : {221, 160, 221, 255} // Plum 75 | ORCHID: Color : {218, 112, 214, 255} // Orchid 76 | PEA: Color : {197, 227, 132, 255} // Pea 77 | DARKSALMON: Color : {233, 150, 122, 255} // Dark Salmon 78 | LIGHTSEAGREEN: Color : {32, 178, 170, 255} // Light Sea Green 79 | MEDIUMAQUAMARINE: Color : {102, 205, 170, 255} // Medium Aquamarine 80 | THISTLE: Color : {216, 191, 216, 255} // Thistle 81 | LIGHTSTEELBLUE: Color : {176, 196, 222, 255} // Light Steel Blue 82 | CADETBLUE: Color : {95, 158, 160, 255} // Cadet Blue 83 | DARKCYAN: Color : {0, 139, 139, 255} // Dark Cyan 84 | LAVENDERBLUSH: Color : {255, 240, 245, 255} // Lavender Blush 85 | HOTPINK: Color : {255, 105, 180, 255} // Hot Pink 86 | SILVERCHALICE: Color : {192, 192, 192, 255} // Silver Chalice 87 | DARKOLIVEGREEN: Color : {85, 107, 47, 255} // Dark Olive Green 88 | OLIVEGREEN: Color : {128, 128, 0, 255} // Olive Green 89 | TAN: Color : {210, 180, 140, 255} // Tan 90 | ROSYBROWN: Color : {188, 143, 143, 255} // Rosy Brown 91 | REDVIOLET: Color : {207, 50, 119, 255} // Red Violet 92 | MIDNIGHTBLUE: Color : {25, 25, 112, 255} // Midnight Blue 93 | AQUAMARINE: Color : {127, 255, 212, 255} // Aquamarine 94 | BRIGHTRED: Color : {255, 0, 0, 255} // Bright Red 95 | DARKKHAKI: Color : {189, 183, 107, 255} // Dark Khaki 96 | FIREBRICK: Color : {178, 34, 34, 255} // Firebrick 97 | CORNFLOWERBLUE: Color : {100, 149, 237, 255} // Cornflower Blue 98 | 99 | // Structs 100 | Texture2D :: rl.Texture2D 101 | 102 | Color :: rl.Color 103 | 104 | Rectangle :: rl.Rectangle 105 | 106 | Entity2D :: struct { 107 | pos: Vector2, 108 | size: Vector2, 109 | vel: Vector2, 110 | color: Color, 111 | speed: f32, 112 | } 113 | 114 | Animation :: struct { 115 | texture: Texture2D, 116 | frame_width: f32, 117 | frame_height: f32, 118 | num_frame: f32, 119 | frame_time: f32, 120 | current_frame: f32, 121 | elapsed_time: f32, 122 | start_frame: f32, 123 | end_frame: f32, 124 | } 125 | 126 | Camera2D :: rl.Camera2D 127 | 128 | Vector2 :: rl.Vector2 129 | 130 | Font :: rl.Font 131 | 132 | Sound :: rl.Sound 133 | 134 | Music :: rl.Music 135 | 136 | // Enums 137 | Keys :: enum (u32) { 138 | NULL = u32(rl.KeyboardKey.KEY_NULL), 139 | A = u32(rl.KeyboardKey.A), 140 | B = u32(rl.KeyboardKey.B), 141 | C = u32(rl.KeyboardKey.C), 142 | D = u32(rl.KeyboardKey.D), 143 | E = u32(rl.KeyboardKey.E), 144 | F = u32(rl.KeyboardKey.F), 145 | G = u32(rl.KeyboardKey.G), 146 | H = u32(rl.KeyboardKey.H), 147 | I = u32(rl.KeyboardKey.I), 148 | J = u32(rl.KeyboardKey.J), 149 | K = u32(rl.KeyboardKey.K), 150 | L = u32(rl.KeyboardKey.L), 151 | M = u32(rl.KeyboardKey.M), 152 | N = u32(rl.KeyboardKey.N), 153 | O = u32(rl.KeyboardKey.O), 154 | P = u32(rl.KeyboardKey.P), 155 | Q = u32(rl.KeyboardKey.Q), 156 | R = u32(rl.KeyboardKey.R), 157 | S = u32(rl.KeyboardKey.S), 158 | T = u32(rl.KeyboardKey.T), 159 | U = u32(rl.KeyboardKey.U), 160 | V = u32(rl.KeyboardKey.V), 161 | W = u32(rl.KeyboardKey.W), 162 | X = u32(rl.KeyboardKey.X), 163 | Y = u32(rl.KeyboardKey.Y), 164 | Z = u32(rl.KeyboardKey.Z), 165 | LEFT_BRACKET = u32(rl.KeyboardKey.LEFT_BRACKET), 166 | RIGHT_BRACKET = u32(rl.KeyboardKey.RIGHT_BRACKET), 167 | BACKSLASH = u32(rl.KeyboardKey.BACKSLASH), 168 | BACKSPACE = u32(rl.KeyboardKey.BACKSPACE), 169 | GRAVE = u32(rl.KeyboardKey.GRAVE), 170 | SPACE = u32(rl.KeyboardKey.SPACE), 171 | ESCAPE = u32(rl.KeyboardKey.ESCAPE), 172 | LSHIFT = u32(rl.KeyboardKey.LEFT_SHIFT), 173 | ENTER = u32(rl.KeyboardKey.ENTER), 174 | UP = u32(rl.KeyboardKey.UP), 175 | DOWN = u32(rl.KeyboardKey.DOWN), 176 | LEFT = u32(rl.KeyboardKey.LEFT), 177 | RIGHT = u32(rl.KeyboardKey.RIGHT), 178 | TAB = u32(rl.KeyboardKey.TAB), 179 | F1 = u32(rl.KeyboardKey.F1), 180 | F2 = u32(rl.KeyboardKey.F2), 181 | F3 = u32(rl.KeyboardKey.F3), 182 | F4 = u32(rl.KeyboardKey.F4), 183 | F5 = u32(rl.KeyboardKey.F5), 184 | F6 = u32(rl.KeyboardKey.F6), 185 | F7 = u32(rl.KeyboardKey.F7), 186 | F8 = u32(rl.KeyboardKey.F8), 187 | F9 = u32(rl.KeyboardKey.F9), 188 | F10 = u32(rl.KeyboardKey.F10), 189 | F11 = u32(rl.KeyboardKey.F11), 190 | F12 = u32(rl.KeyboardKey.F12), 191 | } 192 | 193 | MouseButtons :: enum (u32) { 194 | LEFT = u32(rl.MouseButton.LEFT), 195 | MIDDLE = u32(rl.MouseButton.MIDDLE), 196 | RIGHT = u32(rl.MouseButton.RIGHT), 197 | } 198 | 199 | CollisionSide :: enum { 200 | None, 201 | Top, 202 | Bottom, 203 | Left, 204 | Right, 205 | } 206 | 207 | ConfigFlags :: rl.ConfigFlags 208 | 209 | // Procedures 210 | is_window_minimized :: proc() -> bool { 211 | return rl.IsWindowMinimized() 212 | } 213 | 214 | is_window_maximized :: proc() -> bool { 215 | return rl.IsWindowMaximized() 216 | } 217 | 218 | is_window_focused :: proc() -> bool { 219 | return rl.IsWindowFocused() 220 | } 221 | 222 | is_window_fullscreen :: proc() -> bool { 223 | return rl.IsWindowFullscreen() 224 | } 225 | 226 | is_window_hidden :: proc() -> bool { 227 | return rl.IsWindowHidden() 228 | } 229 | 230 | is_window_ready :: proc() -> bool { 231 | return rl.IsWindowReady() 232 | } 233 | 234 | is_window_state :: proc(flags: ConfigFlags) -> bool { 235 | return rl.IsWindowState(flags) 236 | } 237 | 238 | set_window_state :: proc(flags: ConfigFlags) { 239 | rl.SetWindowState(flags) 240 | } 241 | 242 | set_target_fps :: proc(fps: i32) { 243 | rl.SetTargetFPS(fps) 244 | } 245 | 246 | set_exit_key :: proc(key: Keys) { 247 | rl.SetExitKey(rl.KeyboardKey(key)) 248 | } 249 | 250 | get_fps :: proc() -> i32 { 251 | return rl.GetFPS() 252 | } 253 | 254 | get_delta :: proc() -> f32 { 255 | return rl.GetFrameTime() 256 | } 257 | 258 | get_time :: proc() -> f64 { 259 | return rl.GetTime() 260 | } 261 | 262 | clear_window_state :: proc(flags: ConfigFlags) { 263 | rl.ClearWindowState(flags) 264 | } 265 | 266 | toggle_fullscreen :: proc() { 267 | rl.ToggleFullscreen() 268 | } 269 | 270 | toggle_borderless :: proc() { 271 | rl.ToggleBorderlessWindowed() 272 | } 273 | 274 | text_format :: proc(format: cstring, args: ..any) -> cstring { 275 | return rl.TextFormat(format, ..args) 276 | } 277 | 278 | maximize_window :: proc() { 279 | rl.MaximizeWindow() 280 | } 281 | 282 | minimize_window :: proc() { 283 | rl.MinimizeWindow() 284 | } 285 | 286 | restore_window :: proc() { 287 | rl.RestoreWindow() 288 | } 289 | 290 | wait_time :: proc(seconds: f64) { 291 | rl.WaitTime(seconds) 292 | } 293 | 294 | get_screen_width :: proc() -> f32 { 295 | width := rl.GetScreenWidth() 296 | return f32(width) 297 | } 298 | 299 | get_screen_height :: proc() -> f32 { 300 | height := rl.GetScreenHeight() 301 | return f32(height) 302 | } 303 | 304 | get_mouse_position :: proc() -> Vector2 { 305 | return rl.GetMousePosition() 306 | } 307 | 308 | load_texture :: proc(file_path: cstring) -> Texture2D { 309 | texture := rl.LoadTexture(file_path) 310 | 311 | return texture 312 | } 313 | 314 | load_font :: proc(path: cstring, size: i32) -> Font { 315 | return rl.LoadFontEx(path, size, nil, 250) 316 | } 317 | 318 | load_font_ex :: proc( 319 | path: cstring, 320 | size: i32, 321 | codepoints: [^]rune, 322 | codepoints_count: i32, 323 | ) -> Font { 324 | return rl.LoadFontEx(path, size, codepoints, codepoints_count) 325 | } 326 | 327 | get_key_pressed :: proc() -> Keys { 328 | return Keys(rl.GetKeyPressed()) 329 | } 330 | 331 | is_key_down :: proc(key: Keys) -> bool { 332 | return rl.IsKeyDown(rl.KeyboardKey(key)) 333 | } 334 | 335 | is_key_up :: proc(key: Keys) -> bool { 336 | return rl.IsKeyUp(rl.KeyboardKey(key)) 337 | } 338 | 339 | is_key_pressed :: proc(key: Keys) -> bool { 340 | return rl.IsKeyPressed(rl.KeyboardKey(key)) 341 | } 342 | 343 | is_key_pressed_repeat :: proc(key: Keys) -> bool { 344 | return rl.IsKeyPressedRepeat(rl.KeyboardKey(key)) 345 | } 346 | 347 | is_key_released :: proc(key: Keys) -> bool { 348 | return rl.IsKeyReleased(rl.KeyboardKey(key)) 349 | } 350 | 351 | is_mouse_button_down :: proc(button: MouseButtons) -> bool { 352 | return rl.IsMouseButtonDown(rl.MouseButton(button)) 353 | } 354 | 355 | is_mouse_button_up :: proc(button: MouseButtons) -> bool { 356 | return rl.IsMouseButtonUp(rl.MouseButton(button)) 357 | } 358 | 359 | is_mouse_button_pressed :: proc(button: MouseButtons) -> bool { 360 | return rl.IsMouseButtonPressed(rl.MouseButton(button)) 361 | } 362 | 363 | is_mouse_button_released :: proc(button: MouseButtons) -> bool { 364 | return rl.IsMouseButtonReleased(rl.MouseButton(button)) 365 | } 366 | 367 | is_collision_rect :: proc(rect1, rect2: Rectangle) -> bool { 368 | return rl.CheckCollisionRecs(rect1, rect2) 369 | } 370 | 371 | is_entity_collision2d :: proc(e1, e2: Entity2D) -> bool { 372 | r1 := Rectangle { 373 | width = e1.size.x, 374 | height = e1.size.y, 375 | x = e1.pos.x, 376 | y = e1.pos.y, 377 | } 378 | r2 := Rectangle { 379 | width = e2.size.x, 380 | height = e2.size.y, 381 | x = e2.pos.x, 382 | y = e2.pos.y, 383 | } 384 | return is_collision_rect(r1, r2) 385 | } 386 | 387 | get_collision_side :: proc(e1, e2: Entity2D) -> CollisionSide { 388 | if is_entity_collision2d(e1, e2) { 389 | overlap_left := e1.pos.x + e1.size.x - e2.pos.x 390 | overlap_right := e2.pos.x + e2.size.x - e1.pos.x 391 | overlap_top := e1.pos.y + e1.size.y - e2.pos.y 392 | overlap_bottom := e2.pos.y + e2.size.y - e1.pos.y 393 | 394 | min_overlap_x := min(overlap_left, overlap_right) 395 | min_overlap_y := min(overlap_top, overlap_bottom) 396 | 397 | // Compare the smallest overlap on the X and Y axes 398 | if min_overlap_x < min_overlap_y { 399 | if overlap_left > 0 && overlap_right > e2.size.x { 400 | return .Right 401 | } else { 402 | return .Left 403 | } 404 | } else { 405 | if overlap_top > 0 && overlap_bottom > e2.size.y { 406 | return .Top 407 | } else { 408 | return .Bottom 409 | } 410 | } 411 | } 412 | return .None 413 | } 414 | 415 | get_collision_rect :: proc(rec1, rec2: Rectangle) -> Rectangle { 416 | return rl.GetCollisionRec(rec1, rec2) 417 | } 418 | 419 | get_collision_entity2d :: proc(e1, e2: Entity2D) -> Rectangle { 420 | rect1 := Rectangle { 421 | width = e1.size.x, 422 | height = e1.size.y, 423 | x = e1.pos.x, 424 | y = e1.pos.y, 425 | } 426 | rect2 := Rectangle { 427 | width = e2.size.x, 428 | height = e2.size.y, 429 | x = e2.pos.x, 430 | y = e2.pos.y, 431 | } 432 | 433 | return get_collision_rect(rect1, rect2) 434 | } 435 | 436 | load_sound :: proc(sound: cstring) -> Sound { 437 | return rl.LoadSound(sound) 438 | } 439 | 440 | play_sound :: proc(audio: Sound) { 441 | rl.PlaySound(audio) 442 | } 443 | 444 | load_music_stream :: proc(music_path: cstring) -> Music { 445 | return rl.LoadMusicStream(music_path) 446 | } 447 | 448 | unload_music_stream :: proc(music: Music) { 449 | rl.UnloadMusicStream(music) 450 | } 451 | 452 | update_music_stream :: proc(music: Music) { 453 | rl.UpdateMusicStream(music) 454 | } 455 | 456 | play_music_stream :: proc(music: Music) { 457 | rl.PlayMusicStream(music) 458 | } 459 | 460 | stop_music_stream :: proc(music: Music) { 461 | rl.StopMusicStream(music) 462 | } 463 | 464 | pause_music_stream :: proc(music: Music) { 465 | rl.PauseMusicStream(music) 466 | } 467 | 468 | seek_music_stream :: proc(music: Music, position: f32) { 469 | rl.SeekMusicStream(music, position) 470 | } 471 | 472 | is_music_stream_played :: proc(music: Music) -> bool { 473 | return rl.IsMusicStreamPlaying(music) 474 | } 475 | 476 | get_music_lenght :: proc(music: Music) -> f32 { 477 | return rl.GetMusicTimeLength(music) 478 | } 479 | 480 | get_music_played :: proc(music: Music) -> f32 { 481 | return rl.GetMusicTimePlayed(music) 482 | } 483 | 484 | set_master_volume :: proc(value: f32) { 485 | rl.SetMasterVolume(value) 486 | } 487 | 488 | get_master_volume :: proc() -> f32 { 489 | return rl.GetMasterVolume() 490 | } 491 | 492 | master_volume_up :: proc(step: f32 = 0.10) { 493 | volume := get_master_volume() 494 | set_master_volume(volume + step) 495 | } 496 | 497 | master_volume_down :: proc(step: f32 = 0.10) { 498 | volume := get_master_volume() 499 | set_master_volume(volume - step) 500 | } 501 | 502 | resume_music_track :: proc(music: Music) { 503 | rl.ResumeMusicStream(music) 504 | } 505 | 506 | stop_sound :: proc(audio: Sound) { 507 | rl.StopSound(audio) 508 | } 509 | 510 | create_animation :: proc( 511 | texture: Texture2D, 512 | frame_width, frame_height, num_frames: f32, 513 | frame_time: f32, 514 | start_frame, end_frame: f32, 515 | ) -> ^Animation { 516 | animation := new(Animation) 517 | if animation == nil { 518 | return nil 519 | } 520 | 521 | animation.texture = texture 522 | animation.frame_width = frame_width 523 | animation.frame_height = frame_height 524 | animation.num_frame = num_frames 525 | animation.frame_time = frame_time 526 | animation.current_frame = start_frame 527 | animation.elapsed_time = 0.0 528 | animation.start_frame = start_frame 529 | animation.end_frame = end_frame 530 | 531 | return animation 532 | } 533 | 534 | run_animation :: proc(anim: ^Animation, dt: f32, looped: bool) { 535 | anim.elapsed_time += dt 536 | 537 | if anim.elapsed_time >= anim.frame_time { 538 | anim.elapsed_time -= anim.frame_time 539 | anim.current_frame += 1 540 | 541 | if anim.current_frame > anim.end_frame { 542 | if looped { 543 | anim.current_frame = anim.start_frame 544 | } else { 545 | anim.current_frame = anim.end_frame 546 | } 547 | } 548 | } 549 | } 550 | 551 | render_animation :: proc( 552 | anim: ^Animation, 553 | dest_x, dest_y: f32, 554 | rotation: f32, 555 | flip_x, flip_y: bool, 556 | ) { 557 | source := Rectangle { 558 | x = anim.current_frame * anim.frame_width, 559 | y = 0, 560 | width = anim.frame_width, 561 | height = anim.frame_height, 562 | } 563 | 564 | if flip_x { 565 | source.width *= -1 566 | } else if flip_y { 567 | source.height *= -1 568 | } 569 | 570 | dest := Rectangle { 571 | x = dest_x, 572 | y = dest_y, 573 | width = anim.frame_width, 574 | height = anim.frame_height, 575 | } 576 | 577 | rl.DrawTexturePro( 578 | anim.texture, 579 | source, 580 | dest, 581 | Vector2{0, 0}, 582 | rotation, 583 | WHITE, 584 | ) 585 | } 586 | 587 | reset_animation :: proc(anim: ^Animation) { 588 | anim.current_frame = anim.start_frame 589 | anim.elapsed_time = 0.0 590 | } 591 | 592 | render_texture :: proc( 593 | tex: Texture2D, 594 | tex_x, tex_y: f32, 595 | rotation: f32, 596 | flip_x: bool, 597 | flip_y: bool, 598 | ) { 599 | origin_x: f32 = f32(tex.width) / 2 600 | origin_y: f32 = f32(tex.height) / 2 601 | 602 | scale_x: f32 603 | if flip_x { 604 | scale_x = -1.0 605 | } else { 606 | scale_x = 1.0 607 | } 608 | 609 | scale_y: f32 610 | if flip_y { 611 | scale_y = -1.0 612 | } else { 613 | scale_y = 1.0 614 | } 615 | 616 | rl.DrawTexturePro( 617 | tex, 618 | rl.Rectangle{0, 0, f32(tex.width), f32(tex.height)}, // Source rectangle (entire texture) 619 | rl.Rectangle { 620 | tex_x, 621 | tex_y, 622 | f32(tex.width) * scale_x, 623 | f32(tex.height) * scale_y, 624 | }, // Destination rectangle 625 | rl.Vector2{origin_x, origin_y}, // Rotation origin (center of the texture) 626 | rotation, 627 | WHITE, 628 | ) 629 | } 630 | 631 | render_sub_texture :: proc( 632 | tex: Texture2D, 633 | src_rect, dest_rect: Rectangle, 634 | rotation: f32, 635 | flip_x: bool, 636 | flip_y: bool, 637 | ) { 638 | origin_x: f32 = f32(tex.width) / 2 639 | origin_y: f32 = f32(tex.height) / 2 640 | 641 | scale_x: f32 642 | if flip_x { 643 | scale_x = -1.0 644 | } else { 645 | scale_x = 1.0 646 | } 647 | 648 | scale_y: f32 649 | if flip_y { 650 | scale_y = -1.0 651 | } else { 652 | scale_y = 1.0 653 | } 654 | 655 | rl.DrawTexturePro( 656 | tex, 657 | src_rect, 658 | dest_rect, 659 | Vector2{origin_x, origin_y}, 660 | rotation, 661 | WHITE, 662 | ) 663 | } 664 | 665 | render_text :: proc(text: cstring, x, y, font_size: i32, color: Color) { 666 | rl.DrawText(text, x, y, font_size, color) 667 | } 668 | 669 | render_text_ex :: proc( 670 | font: Font, 671 | text: cstring, 672 | font_size: f32, 673 | spacing: f32, 674 | color: Color, 675 | x, y: f32, 676 | ) { 677 | rl.DrawTextEx(font, text, Vector2{x, y}, font_size, spacing, color) 678 | } 679 | 680 | render_rect_filled :: proc(x, y, width, height: f32, color: Color) { 681 | rl.DrawRectangle(i32(x), i32(y), i32(width), i32(height), color) 682 | } 683 | 684 | render_rect_filled_v :: proc(pos, size: Vector2, color: Color) { 685 | rl.DrawRectangleV(pos, size, color) 686 | } 687 | 688 | render_rect_pro :: proc( 689 | rect: Rectangle, 690 | origin: Vector2, 691 | rotate: f32, 692 | color: Color, 693 | ) { 694 | rl.DrawRectanglePro(rect, origin, rotate, color) 695 | } 696 | 697 | render_rect_line :: proc(x, y, width, height: f32, color: Color) { 698 | rl.DrawRectangleLines(i32(x), i32(y), i32(width), i32(height), color) 699 | } 700 | 701 | render_entity2d :: proc(e: Entity2D) { 702 | render_rect_filled_v(e.pos, e.size, e.color) 703 | } 704 | 705 | render_circle_line :: proc(center_x, center_y, radius: f32, color: Color) { 706 | rl.DrawCircleLines(i32(center_x), i32(center_y), radius, color) 707 | } 708 | 709 | render_circle_filled :: proc(center_x, center_y, radius: f32, color: Color) { 710 | rl.DrawCircle(i32(center_x), i32(center_y), radius, color) 711 | } 712 | 713 | render_line :: proc(x1, y1, x2, y2: f32, color: Color) { 714 | rl.DrawLine(i32(x1), i32(y1), i32(x2), i32(y2), color) 715 | } 716 | 717 | clear_screen :: proc(color: Color) { 718 | rl.ClearBackground(color) 719 | } 720 | 721 | rad_to_deg :: proc(radians: f32) -> f32 { 722 | return radians * (180.0 / math.PI) 723 | } 724 | 725 | deg_to_rad :: proc(degrees: f32) -> f32 { 726 | return degrees * (math.PI / 180.0) 727 | } 728 | 729 | apply_camera :: proc(cam: Camera2D) { 730 | rl.BeginMode2D(cam) 731 | } 732 | 733 | end_camera :: proc() { 734 | rl.EndMode2D() 735 | } 736 | 737 | camera_follow :: proc(cam: ^Camera2D, target_x, target_y: f32) { 738 | cam.target = Vector2{target_x, target_y} 739 | } 740 | 741 | camera_follow_lerp :: proc( 742 | cam: ^Camera2D, 743 | target_x, target_y, lerp_amount: f32, 744 | ) { 745 | desired_target := Vector2 { 746 | target_x - (f32(rl.GetScreenWidth()) / 2) / cam.zoom, 747 | target_y - (f32(rl.GetScreenHeight()) / f32(1.5)) / cam.zoom, 748 | } 749 | 750 | cam.target.x += (desired_target.x - cam.target.x) * lerp_amount 751 | cam.target.y += (desired_target.y - cam.target.y) * lerp_amount 752 | } 753 | 754 | clamp_camera :: proc(cam: ^Camera2D, world_width, world_height: f32) { 755 | half_screen_width := (f32(rl.GetScreenWidth()) / 2) / cam.zoom 756 | half_screen_height := (f32(rl.GetScreenHeight()) / 2) / cam.zoom 757 | 758 | cam.target.x = max( 759 | half_screen_width, 760 | min(cam.target.x, world_width - half_screen_width), 761 | ) 762 | cam.target.y = max( 763 | half_screen_height, 764 | min(cam.target.y, world_height - half_screen_height), 765 | ) 766 | } 767 | 768 | clamp_camera_target :: proc( 769 | cam: ^Camera2D, 770 | target_x, target_y, world_width, world_height: f32, 771 | ) { 772 | half_screen_width := (f32(rl.GetScreenWidth()) / (2 * cam.zoom)) 773 | half_screen_height := (f32(rl.GetScreenHeight()) / (2 * cam.zoom)) 774 | 775 | cam.target.x = max( 776 | half_screen_width, 777 | min(target_x, world_width - half_screen_width), 778 | ) 779 | cam.target.y = max( 780 | half_screen_height, 781 | min(target_y, world_height - half_screen_height), 782 | ) 783 | } 784 | 785 | disable_cursor :: proc() { 786 | rl.HideCursor() 787 | } 788 | 789 | clamp_mouse :: proc() { 790 | rl.DisableCursor() 791 | } 792 | 793 | close_window :: proc() { 794 | os.exit(0) 795 | } 796 | 797 | --------------------------------------------------------------------------------