A game engine for top-down 2D RPG games.
rpg game-engine raylib c99

Actor animations, gamepad movement, fix collision

+1
include/keraforge.h
··· 4 4 #include <keraforge/_header.h> 5 5 #include <keraforge/actor.h> 6 6 #include <keraforge/fs.h> 7 + #include <keraforge/graphics.h> 7 8 #include <keraforge/input.h> 8 9 #include <keraforge/math.h> 9 10 #include <keraforge/sprites.h>
+7 -2
include/keraforge/actor.h
··· 11 11 struct kf_actor 12 12 { 13 13 struct kf_spritesheet sprites; 14 - struct kf_vec2(f32) pos, vel, size; 14 + struct kf_vec2(f32) pos, vel, size, sizeoffset; 15 15 f32 speed, speedmod, friction; 16 - bool collide; 16 + bool collide, controlled; 17 17 enum kf_direction pointing; 18 18 void (*tick)(struct kf_world *world, struct kf_actor *self); 19 19 void (*draw)(struct kf_world *world, struct kf_actor *self); ··· 21 21 22 22 struct kf_actor *kf_actor_new(struct kf_spritesheet sprites, f32 width, f32 height, bool collides); 23 23 24 + /* Load a spritesheet, filling in the details for loading character spritesheets. */ 25 + struct kf_spritesheet kf_actor_loadspritesheet(char *filename); 26 + 24 27 /* Check if the actor can move in the given direction. */ 25 28 int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir); 26 29 /* Add the given force to the actor's velocity. */ 27 30 void kf_actor_addforce(struct kf_actor *self, struct kf_vec2(f32) force); 28 31 /* Move the actor according to their velocity. */ 29 32 void kf_actor_move(struct kf_world *world, struct kf_actor *actor, f32 deltatime); 33 + /* Draw the actor. */ 34 + void kf_actor_draw(struct kf_actor *actor); 30 35 31 36 #endif
+15
include/keraforge/graphics.h
··· 1 + #ifndef __kf_graphics__ 2 + #define __kf_graphics__ 3 + 4 + #include <keraforge/_header.h> 5 + 6 + /* Number of frames since the game opened. 7 + You should only use this for animations! 8 + It's not consistent enough for precise timing use deltatime (kf_dts/kf_dtms) for that. */ 9 + extern u64 kf_frame; 10 + /* Deltatime in milliseconds */ 11 + extern f32 kf_dtms; 12 + /* Deltatime in seconds. */ 13 + extern f32 kf_dts; 14 + 15 + #endif
+3
include/keraforge/input.h
··· 21 21 GamepadAxis axis[KF_INPUTBIND_MAX]; 22 22 }; 23 23 24 + /* Deadzone for gamepads. Default is 0.2f. */ 25 + extern f32 kf_deadzone; 26 + 24 27 extern struct _kf_inputbinds kf_inputbinds; 25 28 26 29 /* Add a new input binding. */
+1
include/keraforge/sprites.h
··· 12 12 }; 13 13 14 14 struct kf_spritesheet kf_loadspritesheet(char *filename, int spritewidth, int spriteheight); 15 + void kf_drawsprite(struct kf_spritesheet *sheet, f32 x, f32 y, int spritex, int spritey); 15 16 16 17 #endif
+4
include/keraforge/world.h
··· 40 40 /* Get a pointer to the tile ID at a given position. */ 41 41 kf_tileid_t *kf_world_gettile(struct kf_world *world, u32 x, u32 y); 42 42 43 + /* Draw visible collision rectangles. */ 44 + struct kf_actor; 45 + void kf_world_drawcolliders(struct kf_world *world, struct kf_actor *player, Camera2D camera); 46 + 43 47 /* Draw the part of the world visible to the given camera. */ 44 48 void kf_world_draw(struct kf_world *world, Camera2D camera); 45 49
+51 -6
src/actor.c
··· 13 13 14 14 actor->speed = 25; 15 15 actor->speedmod = 1; 16 - actor->friction = 1.25f; 16 + actor->friction = 1.5f; 17 17 actor->pointing = kf_north; 18 18 19 19 return actor; 20 20 } 21 21 22 + struct kf_spritesheet kf_actor_loadspritesheet(char *filename) 23 + { 24 + return kf_loadspritesheet(filename, 20, 20); 25 + } 26 + 22 27 int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir) 23 28 { 24 - Rectangle r = {actor->pos.x - actor->size.x / 2, actor->pos.y - actor->size.y / 2, actor->size.x, actor->size.y}; 29 + Rectangle r = { 30 + actor->pos.x + (actor->size.x / 2) + actor->sizeoffset.x, 31 + actor->pos.y + (actor->size.y / 2) + actor->sizeoffset.y, 32 + actor->size.x, 33 + actor->size.y 34 + }; 25 35 r.x += dir.x; 26 36 r.y += dir.y; 27 37 ··· 37 47 Rectangle tr = { 38 48 trx, 39 49 sy * KF_TILE_SIZE_PX, 40 - KF_TILE_SIZE_PX + 1, 41 - KF_TILE_SIZE_PX + 1, 50 + /* TODO: 51 + Subtracting 1 as a bandaid fix to high velocities causing the player to be stopped early in collisions. 52 + This is a very notorious problem in 3D collision and fwik there are plenty of 2D solutions. 53 + I'll research and implement one eventually:tm: */ 54 + KF_TILE_SIZE_PX - 1, 55 + KF_TILE_SIZE_PX - 1, 42 56 }; /* tile rect */ 43 57 u32 x; 44 58 kf_tileid_t *tile = kf_world_gettile(world, sx, sy); ··· 78 92 delta.y = 0; 79 93 } 80 94 81 - if (delta.y > 0) 82 - self->pointing = kf_north; 95 + if (!self->controlled) 96 + { 97 + if (delta.y > 0) 98 + self->pointing = kf_south; 99 + else if (delta.y < 0) 100 + self->pointing = kf_north; 101 + else if (delta.x < 0) 102 + self->pointing = kf_west; 103 + else if (delta.x > 0) 104 + self->pointing = kf_east; 105 + } 83 106 84 107 self->pos = kf_add_vec2(f32)(self->pos, delta); 85 108 ··· 100 123 else if (self->speedmod) 101 124 self->speedmod -= self->friction * self->friction * dt; 102 125 } 126 + 127 + void kf_actor_draw(struct kf_actor *actor) 128 + { 129 + int x = 0, y = 0; 130 + 131 + switch (actor->pointing) 132 + { 133 + case kf_south: y = 0; break; 134 + case kf_east: y = 1; break; 135 + case kf_west: y = 2; break; 136 + case kf_north: y = 3; break; 137 + } 138 + 139 + if (actor->vel.x != 0 || actor->vel.y != 0) 140 + x += 7; /* walk sprites */ 141 + 142 + x += (kf_frame / 15) % 4; 143 + 144 + /* todo: run and jump */ 145 + 146 + kf_drawsprite(&actor->sprites, actor->pos.x, actor->pos.y, x, y); 147 + }
+5
src/graphics.c
··· 1 + #include <keraforge.h> 2 + 3 + u64 kf_frame = 0; 4 + f32 kf_dtms = 0; 5 + f32 kf_dts = 0;
+2 -1
src/input.c
··· 1 - #include "keraforge/input.h" 2 1 #include <keraforge.h> 3 2 #include <raylib.h> 4 3 #include <stdio.h> 5 4 #include <string.h> 5 + 6 + f32 kf_deadzone = 0.2f; 6 7 7 8 struct _kf_inputbinds kf_inputbinds = { 8 9 .count = 1,
+94 -42
src/main.c
··· 1 1 #include "keraforge/input.h" 2 - #include "keraforge/sprites.h" 3 - #include "keraforge/ui.h" 2 + #include <keraforge.h> 4 3 #include <raylib.h> 5 4 #include <raymath.h> 6 - #include <keraforge.h> 7 5 #include <stdio.h> 8 6 #include <stdlib.h> 9 7 10 8 static Camera2D cam; 11 - static f32 dt = 0; 12 9 static struct kf_vec2(u32) select = { 0, 0 }; 13 10 static int selected_tile = 0; 14 11 ··· 23 20 inputbind_move_down, 24 21 inputbind_move_left, 25 22 inputbind_move_right, 23 + inputbind_ui_up, 24 + inputbind_ui_down, 25 + inputbind_ui_left, 26 + inputbind_ui_right, 26 27 inputbind_select, 27 28 inputbind_cancel, 28 - inputbind_palette 29 + inputbind_pause, 30 + inputbind_palette, 31 + inputbind_zoom_reset, 32 + inputbind_zoom_in, 33 + inputbind_zoom_out 29 34 ; 30 35 31 36 static void loadbinds() 32 37 { 33 - inputbind_move_up = kf_addinput("move_up", KEY_UP, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y); 34 - inputbind_move_down = kf_addinput("move_down", KEY_DOWN, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y); 35 - inputbind_move_left = kf_addinput("move_left", KEY_LEFT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X); 36 - inputbind_move_right = kf_addinput("move_right", KEY_RIGHT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X); 37 - inputbind_select = kf_addinput("select", KEY_Z, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN); 38 - inputbind_cancel = kf_addinput("cancel", KEY_X, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN); 39 - inputbind_palette = kf_addinput("palette", KEY_TAB, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_AXIS_UNKNOWN); 38 + inputbind_move_up = kf_addinput("move_up", KEY_W, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y); 39 + inputbind_move_down = kf_addinput("move_down", KEY_S, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y); 40 + inputbind_move_left = kf_addinput("move_left", KEY_A, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X); 41 + inputbind_move_right = kf_addinput("move_right", KEY_D, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X); 42 + 43 + inputbind_ui_up = kf_addinput("ui_up", KEY_W, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_UP, GAMEPAD_AXIS_UNKNOWN); 44 + inputbind_ui_down = kf_addinput("ui_down", KEY_S, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN); 45 + inputbind_ui_left = kf_addinput("ui_left", KEY_A, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_LEFT, GAMEPAD_AXIS_UNKNOWN); 46 + inputbind_ui_right = kf_addinput("ui_right", KEY_D, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN); 47 + 48 + inputbind_select = kf_addinput("select", KEY_F, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN); 49 + inputbind_cancel = kf_addinput("cancel", KEY_Q, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN); 50 + 51 + inputbind_pause = kf_addinput("pause", KEY_ESCAPE, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_MIDDLE_RIGHT, GAMEPAD_AXIS_UNKNOWN); 52 + inputbind_palette = kf_addinput("palette", KEY_TAB, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_AXIS_UNKNOWN); 53 + 54 + inputbind_zoom_reset = kf_addinput("zoom_reset", KEY_ZERO, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN); 55 + inputbind_zoom_in = kf_addinput("zoom_in", KEY_EQUAL, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN); 56 + inputbind_zoom_out = kf_addinput("zoom_out", KEY_MINUS, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN); 40 57 } 41 58 42 59 static void _player_tick_move(struct kf_actor *self) 43 60 { 61 + struct kf_vec2(f32) v = {0, 0}; 62 + 63 + /* gamepad axis movement */ 64 + f32 gpx = kf_getgamepadaxis(inputbind_move_left); 65 + f32 gpy = kf_getgamepadaxis(inputbind_move_up); 66 + // f32 gpx = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X); 67 + // f32 gpy = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y); 68 + if (gpx > kf_deadzone || gpx < -kf_deadzone || gpy > kf_deadzone || gpy < -kf_deadzone) 69 + { 70 + v.y = gpy; 71 + v.x = gpx; 72 + 73 + f32 angle = Vector2LineAngle(Vector2Zero(), (Vector2){gpx, gpy}) * RAD2DEG; 74 + angle /= 90; 75 + switch ((int)roundf(angle)) 76 + { 77 + case 0: self->pointing = kf_east; break; 78 + case 1: self->pointing = kf_north; break; 79 + case -2: 80 + case 2: self->pointing = kf_west; break; 81 + case -1: self->pointing = kf_south; break; 82 + } 83 + 84 + goto done; 85 + } 86 + 87 + /* non-axis movement */ 44 88 bool w = kf_checkinputdown(inputbind_move_up); 45 89 bool s = kf_checkinputdown(inputbind_move_down); 46 90 bool a = kf_checkinputdown(inputbind_move_left); 47 91 bool d = kf_checkinputdown(inputbind_move_right); 48 92 49 - struct kf_vec2(f32) v = {0, 0}; 93 + if (a && d) { v.x = 0; } 94 + else if (a) { v.x = -1; self->pointing = kf_west; } 95 + else if (d) { v.x = 1; self->pointing = kf_east; } 50 96 51 - if (w && s) 52 - v.y = 0; 53 - else if (w) 54 - v.y = -1; 55 - else if (s) 56 - v.y = 1; 97 + if (w && s) { v.y = 0; } 98 + else if (w) { v.y = -1; self->pointing = kf_north; } 99 + else if (s) { v.y = 1; self->pointing = kf_south; } 57 100 58 - if (a && d) 59 - v.x = 0; 60 - else if (a) 61 - v.x = -1; 62 - else if (d) 63 - v.x = 1; 101 + v = kf_normalize_vec2(f32)(v); 64 102 103 + done: 65 104 if (v.x || v.y) 66 - kf_actor_addforce(self, kf_normalize_vec2(f32)(v)); 67 - 68 - if (IsKeyPressed(KEY_SPACE) && self->speedmod <= 1.0f) 69 - self->speedmod = 2.0f; 105 + kf_actor_addforce(self, v); 70 106 } 71 107 72 108 static void _player_tick(struct kf_world *world, struct kf_actor *self) ··· 74 110 if (menu == menu_none) 75 111 _player_tick_move(self); 76 112 77 - kf_actor_move(world, self, dt); 113 + kf_actor_move(world, self, kf_dts); 78 114 } 79 115 80 116 static void _player_draw(struct kf_world *world, struct kf_actor *self) 81 117 { 82 118 (void)world; 83 - DrawCircle(self->pos.x, self->pos.y, 4, RED); 119 + // DrawRectangleLines(self->pos.x + self->size.x/2 + self->sizeoffset.x, self->pos.y + self->size.y/2 + self->sizeoffset.y, self->size.x, self->size.y, RED); 120 + kf_actor_draw(self); 84 121 85 - cam.target.x = self->pos.x; 86 - cam.target.y = self->pos.y; 122 + cam.target.x = self->pos.x + (self->size.x / 2); 123 + cam.target.y = self->pos.y + (self->size.y / 2); 87 124 } 88 125 89 126 int main(int argc, const char *argv[]) ··· 91 128 (void)argc; 92 129 (void)argv; 93 130 94 - loadbinds(); 95 - 96 131 SetTraceLogLevel(LOG_WARNING); 97 132 InitWindow(800, 600, "Keraforge"); 98 133 SetTargetFPS(60); 99 134 SetExitKey(KEY_NULL); 135 + 136 + loadbinds(); 100 137 101 138 kf_tiles.key[0] = "grass"; 102 139 kf_tiles.color[0] = GREEN; ··· 110 147 struct kf_uiconfig *uiconfig = kf_ui_getconfig(); 111 148 uiconfig->select = inputbind_select; 112 149 uiconfig->cancel = inputbind_cancel; 113 - uiconfig->up = inputbind_move_up; 114 - uiconfig->down = inputbind_move_down; 150 + uiconfig->up = inputbind_ui_up; 151 + uiconfig->down = inputbind_ui_down; 115 152 116 153 if (!DirectoryExists("data")) 117 154 MakeDirectory("data"); ··· 146 183 exit(1); 147 184 } 148 185 149 - struct kf_actor *player = kf_actor_new(kf_loadspritesheet("data/res/player.png", 16, 16), 6, 6, true); 186 + struct kf_actor *player = kf_actor_new(kf_actor_loadspritesheet("data/res/img/char/whom.png"), 10, 10, true); 187 + player->sizeoffset.y = 6; 150 188 player->pos.x = world->width / 4.0f * KF_TILE_SIZE_PX; 151 189 player->pos.y = world->height / 4.0f * KF_TILE_SIZE_PX; 152 190 player->tick = _player_tick; 153 191 player->draw = _player_draw; 192 + player->controlled = true; 154 193 155 - cam = (Camera2D){{GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f}, {0, 0}, 0, 2}; 194 + cam = (Camera2D){0}; 195 + cam.offset.x = GetScreenWidth() / 2.0f; 196 + cam.offset.y = GetScreenHeight() / 2.0f; 197 + cam.zoom = 2; 198 + 156 199 int running = 1; 157 200 while (!WindowShouldClose() && running) 158 201 { ··· 170 213 171 214 if (kf_checkinputpress(inputbind_palette)) 172 215 menu = menu_palette; 173 - else if (IsKeyPressed(KEY_ESCAPE) && menu == menu_none) 216 + else if (kf_checkinputpress(inputbind_pause) && menu == menu_none) 174 217 menu = menu_escape; 218 + else if (kf_checkinputpress(inputbind_zoom_reset)) 219 + cam.zoom = 2; 220 + else if (kf_checkinputpress(inputbind_zoom_in) && cam.zoom < 3.50f) 221 + cam.zoom += 0.25f; 222 + else if (kf_checkinputpress(inputbind_zoom_out) && cam.zoom > 1.00f) 223 + cam.zoom -= 0.25f; 175 224 176 225 BeginDrawing(); 177 226 ClearBackground(WHITE); 178 227 179 228 BeginMode2D(cam); 180 229 kf_world_draw(world, cam); 230 + kf_world_drawcolliders(world, player, cam); 181 231 if (select.x < world->width && select.y < world->height) 182 232 { 183 233 if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) ··· 213 263 } 214 264 215 265 DrawFPS(0, 0); 216 - DrawText(TextFormat("%f", dt), 0, 20, 20, RED); 266 + DrawText(TextFormat("%f", kf_dts), 0, 20, 20, RED); 217 267 218 268 EndDrawing(); 219 269 220 - dt = GetFrameTime(); 270 + kf_frame++; 271 + kf_dts = GetFrameTime(); 272 + kf_dtms = kf_dts * 1000; 221 273 } 222 274 223 275 if (world)
+22
src/sprites.c
··· 1 1 #include <keraforge.h> 2 + #include <raylib.h> 2 3 3 4 struct kf_spritesheet kf_loadspritesheet(char *filename, int spritewidth, int spriteheight) 4 5 { ··· 10 11 .nsprites = (tex.width / spritewidth) * (tex.height / spriteheight), 11 12 }; 12 13 } 14 + 15 + inline 16 + void kf_drawsprite(struct kf_spritesheet *sheet, f32 x, f32 y, int spritex, int spritey) 17 + { 18 + DrawTexturePro( 19 + sheet->texture, 20 + (Rectangle){ 21 + spritex * sheet->spritewidth, 22 + spritey * sheet->spriteheight, 23 + sheet->spritewidth, 24 + sheet->spriteheight }, 25 + (Rectangle){ 26 + x, 27 + y, 28 + sheet->spritewidth, 29 + sheet->spriteheight }, 30 + (Vector2){0, 0}, 31 + 0, 32 + WHITE 33 + ); 34 + }
+3 -3
src/ui.c
··· 62 62 63 63 skip_text: 64 64 65 - if (kf_checkkeypress(_kf_uiconfig.select) || kf_checkkeypress(_kf_uiconfig.cancel)) 65 + if (kf_checkinputpress(_kf_uiconfig.select) || kf_checkinputpress(_kf_uiconfig.cancel)) 66 66 return 1; 67 - else if (kf_checkkeypress(_kf_uiconfig.down) && *choice < nchoices) 67 + else if (kf_checkinputpress(_kf_uiconfig.down) && *choice < nchoices) 68 68 (*choice)++; 69 - else if (kf_checkkeypress(_kf_uiconfig.up) && *choice > 0) 69 + else if (kf_checkinputpress(_kf_uiconfig.up) && *choice > 0) 70 70 (*choice)--; 71 71 72 72 return 0;
+46
src/world.c
··· 27 27 return &world->map[y*world->width + x]; 28 28 } 29 29 30 + void kf_world_drawcolliders(struct kf_world *world, struct kf_actor *player, Camera2D camera) 31 + { 32 + Rectangle r = { 33 + player->pos.x + (player->size.x / 2) + player->sizeoffset.x, 34 + player->pos.y + (player->size.y / 2) + player->sizeoffset.y, 35 + player->size.x, 36 + player->size.y 37 + }; 38 + 39 + DrawRectangleLinesEx(r, 1, BLUE); 40 + 41 + const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera); 42 + const Vector2 end = GetScreenToWorld2D((Vector2){GetScreenWidth(), GetScreenHeight()}, camera); 43 + const u32 sx = fmax(0, floorf(start.x / KF_TILE_SIZE_PX)); 44 + const u32 sy = fmax(0, floorf(start.y / KF_TILE_SIZE_PX)); 45 + const u32 ex = fmin(world->width, ceilf(end.x / KF_TILE_SIZE_PX)); 46 + const u32 ey = fmin(world->height, ceilf(end.y / KF_TILE_SIZE_PX)); 47 + const size_t down = world->width - ex + sx - 1; /* number of indexes to add to reach the next tile down */ 48 + kf_tileid_t *tile = kf_world_gettile(world, sx, sy); 49 + 50 + /* check if any tiles will collide with the actor's rect */ 51 + const f32 trx = sx * KF_TILE_SIZE_PX; 52 + Rectangle tr = { 53 + trx, 54 + sy * KF_TILE_SIZE_PX, 55 + KF_TILE_SIZE_PX, 56 + KF_TILE_SIZE_PX, 57 + }; /* tile rect */ 58 + u32 x; 59 + 60 + for (u32 y = sy ; y <= ey ; y++) 61 + { 62 + for (x = sx ; x <= ex ; x++) 63 + { 64 + if (kf_tiles.collide[*tile]) 65 + DrawRectangleLinesEx(tr, 1, CheckCollisionRecs(r, tr) ? RED : BLACK); 66 + 67 + tile++; /* shift tile pointer to the right */ 68 + tr.x += KF_TILE_SIZE_PX; 69 + } 70 + tile += down; /* shift tile pointer down */ 71 + tr.x = trx; 72 + tr.y += KF_TILE_SIZE_PX; 73 + } 74 + } 75 + 30 76 void kf_world_draw(struct kf_world *world, Camera2D camera) 31 77 { 32 78 const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera);