demo/snake/snake.c

changeset 288
8796f03aac26
parent 287
359eaf2a8bd2
--- a/demo/snake/snake.c	Sun Nov 16 22:00:13 2025 +0100
+++ b/demo/snake/snake.c	Sun Nov 16 23:02:11 2025 +0100
@@ -58,10 +58,15 @@
 static asc_vec2i directions[4];
 
 typedef struct {
+    AscSceneNode *node;
     asc_color color;
     enum MoveDirection direction;
     enum MoveDirection target_direction;
     /**
+     * The number of tiles this player can look.
+     */
+    float view_distance;
+    /**
      * The speed in tiles per second.
      */
     float speed;
@@ -473,6 +478,8 @@
     );
     asc_scene_add_node(MAIN_SCENE, node);
     Player *player = asc_scene_node_allocate_data(node, sizeof(Player));
+    player->node = node;
+    player->view_distance = 7.f; // start with 7 tiles
     player->speed = 3.f; // start with 3 tiles/sec
     player->number = 1;
     player->health = 100;
@@ -491,8 +498,7 @@
     return player;
 }
 
-static asc_rect main_scene_viewport_update(asc_vec2u window_size) {
-
+static void main_scene_viewport_update(AscCamera *camera, asc_vec2u window_size) {
     // margins
     const unsigned margin = 16;
 
@@ -505,7 +511,8 @@
 
     // check if there is still a viewport left and chicken out when not
     if (window_size.width < rw || window_size.height < rh) {
-        return ASC_RECT(0, 0, 0, 0);
+        camera->viewport = ASC_RECT(0, 0, 0, 0);
+        return;
     }
     window_size.width -= rw;
     window_size.height -= rh;
@@ -525,7 +532,30 @@
     offset_y += margin;
 
     // Set the viewport to the scaled and centered region
-    return ASC_RECT(offset_x, offset_y, viewport_size, viewport_size);
+    camera->viewport = ASC_RECT(offset_x, offset_y, viewport_size, viewport_size);
+}
+
+static void main_scene_camera_update(AscCamera *camera) {
+    // follow the player
+    // TODO: for hot seat / split screen updates we need to attach custom data to the camera
+    //       s.t. the camera knows which player to follow
+    const Player *player = game.players[0];
+
+    const AscSceneNode *player_node = player->node;
+    const float view_distance = player->view_distance;
+    const float cam_view_distance = (1+view_distance) * GAME_FIELD_TILE_SIZE;
+    const float proj_size = 2 * cam_view_distance;
+
+    // TODO: create camera API to avoid direct access to the matrices
+
+    // update the projection matrix
+    asc_mat4f_ortho_update_size(camera->projection, proj_size, proj_size);
+
+    // update the view matrix
+    asc_vec2f pos = asc_scene_node_get_position2f(player_node);
+    pos.x = -pos.x + cam_view_distance;
+    pos.y = -pos.y + cam_view_distance;
+    asc_transform_translate2f(camera->view, pos);
 }
 
 int main(void) {
@@ -553,23 +583,19 @@
     AscCamera backdrop_camera;
     asc_camera_init(&backdrop_camera,
         .type = ASC_CAMERA_ORTHO,
-        .projection_update_func = asc_camera_ortho_update_size
+        .viewport_update_func = asc_camera_ortho_update_size
     );
     asc_scene_init(BACKDROP_SCENE, "backdrop", &backdrop_camera);
     backdrop_create();
 
     // Initialize the main scene
-    const unsigned initial_view_distance = 10;
     AscCamera main_camera;
     asc_camera_init(&main_camera,
         .type = ASC_CAMERA_ORTHO,
-        .ortho.rect = ASC_RECT(0, 0,
-            initial_view_distance*2*GAME_FIELD_TILE_SIZE,
-            initial_view_distance*2*GAME_FIELD_TILE_SIZE
-        ),
         .viewport_clear = true,
         .clear_color = ASC_RGBi(0, 32, 16),
-        .viewport_update_func = main_scene_viewport_update
+        .viewport_update_func = main_scene_viewport_update,
+        .update_func = main_scene_camera_update
     );
     asc_scene_init(MAIN_SCENE, "main", &main_camera);
 

mercurial