1
1
#[ cfg( not( feature = "std" ) ) ]
2
2
use embedded_graphics:: pixelcolor:: Rgb565 ;
3
-
4
- // Import embedded_graphics traits.
5
3
use embedded_graphics:: {
6
4
image:: Image ,
7
5
prelude:: * ,
8
6
primitives:: { Rectangle , PrimitiveStyle } ,
9
7
} ;
10
-
11
8
use bevy_ecs:: prelude:: * ;
12
9
use spooky_core:: resources:: { MazeResource , PlayerPosition } ;
13
- // In no_std mode our TextureAssets were defined in spooky_core's setup module.
10
+
14
11
#[ cfg( not( feature = "std" ) ) ]
15
12
use spooky_core:: systems:: setup:: TextureAssets ;
16
13
17
- /// Render the maze tile map, coins, and the player ghost into the off‑screen framebuffer,
18
- /// then flush it to the physical display.
19
- ///
20
- /// In no_std mode this uses our embedded TextureAssets (loaded via tinybmp) to draw each tile.
21
- /// The drawing order is:
22
- /// 1. The full maze tile map (background)
23
- /// 2. The coins (from maze.coins)
24
- /// 3. The player ghost (from PlayerPosition)
14
+ /// Render the maze, coins, and player ghost onto the off‑screen framebuffer and then flush
15
+ /// the result to the display. This version calculates the visible world area based on the
16
+ /// camera offset (computed from the player’s position) so that only the visible tiles are drawn.
25
17
pub fn render_system (
26
18
mut display_res : NonSendMut < crate :: DisplayResource > ,
27
19
mut fb_res : ResMut < crate :: FrameBufferResource > ,
@@ -33,15 +25,50 @@ pub fn render_system(
33
25
fb_res. frame_buf . clear ( Rgb565 :: BLACK ) . unwrap ( ) ;
34
26
35
27
let maze = & maze_res. maze ;
28
+ // Get the maze playable bounds (world coordinates of the maze’s bottom‐left corner).
36
29
let ( left, bottom, _right, _top) = maze. playable_bounds ( ) ;
37
30
38
- // --- Render the maze tile map (background) ---
39
- for ty in 0 ..maze. height as i32 {
40
- for tx in 0 ..maze. width as i32 {
41
- // Maze data: row 0 is at the top so flip the row index.
31
+ // Our “camera” centers on the player.
32
+ let display_width = crate :: LCD_H_RES as i32 ;
33
+ let display_height = crate :: LCD_V_RES as i32 ;
34
+ let display_center_x = display_width / 2 ;
35
+ let display_center_y = display_height / 2 ;
36
+
37
+ // Calculate the offset: the translation we apply so that the player (in world coordinates)
38
+ // appears at the center of the display.
39
+ let offset_x = display_center_x - player_pos. x as i32 ;
40
+ let offset_y = display_center_y - player_pos. y as i32 ;
41
+
42
+ // Determine tile size (in pixels).
43
+ // Assume these values are already computed as i32:
44
+ let tile_w = maze. tile_width as i32 ;
45
+ let tile_h = maze. tile_height as i32 ;
46
+
47
+ // The visible world coordinate range based on the camera offset:
48
+ let min_world_x = 0 - offset_x;
49
+ let max_world_x = display_width - offset_x;
50
+ let min_world_y = 0 - offset_y;
51
+ let max_world_y = display_height - offset_y;
52
+
53
+ // Use integer arithmetic to calculate the tile indices (avoiding floats)
54
+ let min_tx = ( min_world_x - left) / tile_w;
55
+ let max_tx = ( max_world_x - left + tile_w - 1 ) / tile_w;
56
+ let min_ty = ( min_world_y - bottom) / tile_h;
57
+ let max_ty = ( max_world_y - bottom + tile_h - 1 ) / tile_h;
58
+
59
+ // Clamp indices to maze dimensions.
60
+ let start_tx = min_tx. max ( 0 ) ;
61
+ let end_tx = max_tx. min ( maze. width as i32 ) ;
62
+ let start_ty = min_ty. max ( 0 ) ;
63
+ let end_ty = max_ty. min ( maze. height as i32 ) ;
64
+
65
+ // Now iterate only over the visible tiles.
66
+ for ty in start_ty..end_ty {
67
+ for tx in start_tx..end_tx {
68
+ // In maze data, row 0 is at the top, so flip the ty index.
42
69
let maze_row = ( maze. height as i32 - 1 ) - ty;
43
70
let index = ( maze_row * maze. width as i32 + tx) as usize ;
44
- // Choose the texture based on maze data.
71
+ // Select the texture based on maze. data[index] .
45
72
let bmp_opt = match maze. data [ index] {
46
73
1 => texture_assets. wall . as_ref ( ) ,
47
74
0 => texture_assets. ground . as_ref ( ) ,
@@ -50,42 +77,41 @@ pub fn render_system(
50
77
} ;
51
78
52
79
if let Some ( bmp) = bmp_opt {
53
- // Compute the pixel position for this tile.
54
- let x = left + tx * maze. tile_width as i32 ;
55
- let y = bottom + ty * maze. tile_height as i32 ;
56
- let pos = Point :: new ( x, y) ;
80
+ // Compute the world position of the tile.
81
+ let world_x = left + tx * tile_w;
82
+ let world_y = bottom + ty * tile_h;
83
+ // Convert to screen coordinates:
84
+ let screen_x = world_x + offset_x;
85
+ let screen_y = world_y + offset_y;
86
+ let pos = Point :: new ( screen_x, screen_y) ;
57
87
Image :: new ( bmp, pos)
58
88
. draw ( & mut fb_res. frame_buf )
59
89
. unwrap ( ) ;
60
90
}
61
91
}
62
92
}
63
93
64
- // --- Render coins on top of the maze ---
94
+ // --- Render coins ---
65
95
for coin in & maze. coins {
66
96
if coin. x != -1 && coin. y != -1 {
67
97
if let Some ( bmp) = texture_assets. coin . as_ref ( ) {
68
- // Assume coin.x, coin.y are already in pixel coordinates.
69
- let pos = Point :: new ( coin. x , coin . y ) ;
70
- Image :: new ( bmp , pos )
71
- . draw ( & mut fb_res . frame_buf )
72
- . unwrap ( ) ;
98
+ // Assume coin.x and coin.y are in world coordinates.
99
+ let screen_x = coin. x + offset_x ;
100
+ let screen_y = coin . y + offset_y ;
101
+ let pos = Point :: new ( screen_x , screen_y ) ;
102
+ Image :: new ( bmp , pos ) . draw ( & mut fb_res . frame_buf ) . unwrap ( ) ;
73
103
}
74
104
}
75
105
}
76
106
77
107
// --- Render the player ghost ---
78
108
if let Some ( bmp) = texture_assets. ghost . as_ref ( ) {
79
- let pos = Point :: new ( player_pos. x as i32 , player_pos. y as i32 ) ;
80
- Image :: new ( bmp, pos)
81
- . draw ( & mut fb_res. frame_buf )
82
- . unwrap ( ) ;
109
+ // Draw the player at the center of the display.
110
+ let pos = Point :: new ( display_center_x, display_center_y) ;
111
+ Image :: new ( bmp, pos) . draw ( & mut fb_res. frame_buf ) . unwrap ( ) ;
83
112
}
84
113
85
114
// Flush the completed framebuffer to the physical display.
86
115
let area = Rectangle :: new ( Point :: zero ( ) , fb_res. frame_buf . size ( ) ) ;
87
- display_res
88
- . display
89
- . fill_contiguous ( & area, fb_res. frame_buf . data . iter ( ) . copied ( ) )
90
- . unwrap ( ) ;
116
+ display_res. display . fill_contiguous ( & area, fb_res. frame_buf . data . iter ( ) . copied ( ) ) . unwrap ( ) ;
91
117
}
0 commit comments