game.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #include "game.h"
  2. /****** Entities: Player ******/
  3. typedef struct
  4. {
  5. Sprite *sprite;
  6. } PlayerContext;
  7. // Forward declaration of player_desc, because it's used in player_spawn function.
  8. static const EntityDescription player_desc;
  9. static void player_spawn(Level *level, GameManager *manager)
  10. {
  11. Entity *player = level_add_entity(level, &player_desc);
  12. // Set player position.
  13. // Depends on your game logic, it can be done in start entity function, but also can be done here.
  14. entity_pos_set(player, (Vector){64, 32});
  15. // Add collision box to player entity
  16. // Box is centered in player x and y, and it's size is 10x10
  17. entity_collider_add_rect(player, 10, 10);
  18. // Get player context
  19. PlayerContext *player_context = entity_context_get(player);
  20. // Load player sprite
  21. player_context->sprite = game_manager_sprite_load(manager, "player.fxbm");
  22. }
  23. static void player_update(Entity *self, GameManager *manager, void *context)
  24. {
  25. UNUSED(context);
  26. // Get game input
  27. InputState input = game_manager_input_get(manager);
  28. // Get player position
  29. Vector pos = entity_pos_get(self);
  30. // Control player movement
  31. if (input.held & GameKeyUp)
  32. pos.y -= 2;
  33. if (input.held & GameKeyDown)
  34. pos.y += 2;
  35. if (input.held & GameKeyLeft)
  36. pos.x -= 2;
  37. if (input.held & GameKeyRight)
  38. pos.x += 2;
  39. // Clamp player position to screen bounds, considering player sprite size (10x10)
  40. pos.x = CLAMP(pos.x, 123, 5);
  41. pos.y = CLAMP(pos.y, 59, 5);
  42. // Set new player position
  43. entity_pos_set(self, pos);
  44. // Control game exit
  45. if (input.pressed & GameKeyBack)
  46. {
  47. game_manager_game_stop(manager);
  48. }
  49. }
  50. static void player_render(Entity *self, GameManager *manager, Canvas *canvas, void *context)
  51. {
  52. // Get player context
  53. PlayerContext *player = context;
  54. // Get player position
  55. Vector pos = entity_pos_get(self);
  56. // Draw player sprite
  57. // We subtract 5 from x and y, because collision box is 10x10, and we want to center sprite in it.
  58. canvas_draw_sprite(canvas, player->sprite, pos.x - 5, pos.y - 5);
  59. // Get game context
  60. GameContext *game_context = game_manager_game_context_get(manager);
  61. // Draw score
  62. canvas_printf(canvas, 0, 7, "Score: %lu", game_context->score);
  63. }
  64. static const EntityDescription player_desc = {
  65. .start = NULL, // called when entity is added to the level
  66. .stop = NULL, // called when entity is removed from the level
  67. .update = player_update, // called every frame
  68. .render = player_render, // called every frame, after update
  69. .collision = NULL, // called when entity collides with another entity
  70. .event = NULL, // called when entity receives an event
  71. .context_size =
  72. sizeof(PlayerContext), // size of entity context, will be automatically allocated and freed
  73. };
  74. /****** Entities: Target ******/
  75. static Vector random_pos(void)
  76. {
  77. return (Vector){rand() % 120 + 4, rand() % 58 + 4};
  78. }
  79. static void target_start(Entity *self, GameManager *manager, void *context)
  80. {
  81. UNUSED(context);
  82. UNUSED(manager);
  83. // Set target position
  84. entity_pos_set(self, random_pos());
  85. // Add collision circle to target entity
  86. // Circle is centered in target x and y, and it's radius is 3
  87. entity_collider_add_circle(self, 3);
  88. }
  89. static void target_render(Entity *self, GameManager *manager, Canvas *canvas, void *context)
  90. {
  91. UNUSED(context);
  92. UNUSED(manager);
  93. // Get target position
  94. Vector pos = entity_pos_get(self);
  95. // Draw target
  96. canvas_draw_disc(canvas, pos.x, pos.y, 3);
  97. // Draw background
  98. canvas_draw_box(canvas, 0, 20, 40, 20);
  99. }
  100. static void target_collision(Entity *self, Entity *other, GameManager *manager, void *context)
  101. {
  102. UNUSED(context);
  103. // Check if target collided with player
  104. if (entity_description_get(other) == &player_desc)
  105. {
  106. // Increase score
  107. GameContext *game_context = game_manager_game_context_get(manager);
  108. game_context->score++;
  109. // Move target to new random position
  110. entity_pos_set(self, random_pos());
  111. }
  112. }
  113. static const EntityDescription target_desc = {
  114. .start = target_start, // called when entity is added to the level
  115. .stop = NULL, // called when entity is removed from the level
  116. .update = NULL, // called every frame
  117. .render = target_render, // called every frame, after update
  118. .collision = target_collision, // called when entity collides with another entity
  119. .event = NULL, // called when entity receives an event
  120. .context_size = 0, // size of entity context, will be automatically allocated and freed
  121. };
  122. /****** Level ******/
  123. static void level_alloc(Level *level, GameManager *manager, void *context)
  124. {
  125. UNUSED(manager);
  126. UNUSED(context);
  127. // Add player entity to the level
  128. player_spawn(level, manager);
  129. // Add first target entity to the level
  130. level_add_entity(level, &target_desc);
  131. // Add second target entity to the level
  132. level_add_entity(level, &target_desc);
  133. }
  134. static const LevelBehaviour level = {
  135. .alloc = level_alloc, // called once, when level allocated
  136. .free = NULL, // called once, when level freed
  137. .start = NULL, // called when level is changed to this level
  138. .stop = NULL, // called when level is changed from this level
  139. .context_size = 0, // size of level context, will be automatically allocated and freed
  140. };
  141. /****** Game ******/
  142. /*
  143. Write here the start code for your game, for example: creating a level and so on.
  144. Game context is allocated (game.context_size) and passed to this function, you can use it to store your game data.
  145. */
  146. static void game_start(GameManager *game_manager, void *ctx)
  147. {
  148. UNUSED(game_manager);
  149. // Do some initialization here, for example you can load score from storage.
  150. // For simplicity, we will just set it to 0.
  151. GameContext *game_context = ctx;
  152. game_context->score = 0;
  153. // Add level to the game
  154. game_manager_add_level(game_manager, &level);
  155. }
  156. /*
  157. Write here the stop code for your game, for example, freeing memory, if it was allocated.
  158. You don't need to free level, sprites or entities, it will be done automatically.
  159. Also, you don't need to free game_context, it will be done automatically, after this function.
  160. */
  161. static void game_stop(void *ctx)
  162. {
  163. GameContext *game_context = ctx;
  164. // Do some deinitialization here, for example you can save score to storage.
  165. // For simplicity, we will just print it.
  166. FURI_LOG_I("Game", "Your score: %lu", game_context->score);
  167. }
  168. /*
  169. Your game configuration, do not rename this variable, but you can change its content here.
  170. */
  171. const Game game = {
  172. .target_fps = 30, // target fps, game will try to keep this value
  173. .show_fps = false, // show fps counter on the screen
  174. .always_backlight = true, // keep display backlight always on
  175. .start = game_start, // will be called once, when game starts
  176. .stop = game_stop, // will be called once, when game stops
  177. .context_size = sizeof(GameContext), // size of game context
  178. };