Skip to content

Commit 0dd30b9

Browse files
committed
esp32-s3: render only necessary tiles
1 parent 50b46a3 commit 0dd30b9

File tree

2 files changed

+63
-37
lines changed

2 files changed

+63
-37
lines changed
+61-35
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
11
#[cfg(not(feature = "std"))]
22
use embedded_graphics::pixelcolor::Rgb565;
3-
4-
// Import embedded_graphics traits.
53
use embedded_graphics::{
64
image::Image,
75
prelude::*,
86
primitives::{Rectangle, PrimitiveStyle},
97
};
10-
118
use bevy_ecs::prelude::*;
129
use spooky_core::resources::{MazeResource, PlayerPosition};
13-
// In no_std mode our TextureAssets were defined in spooky_core's setup module.
10+
1411
#[cfg(not(feature = "std"))]
1512
use spooky_core::systems::setup::TextureAssets;
1613

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.
2517
pub fn render_system(
2618
mut display_res: NonSendMut<crate::DisplayResource>,
2719
mut fb_res: ResMut<crate::FrameBufferResource>,
@@ -33,15 +25,50 @@ pub fn render_system(
3325
fb_res.frame_buf.clear(Rgb565::BLACK).unwrap();
3426

3527
let maze = &maze_res.maze;
28+
// Get the maze playable bounds (world coordinates of the maze’s bottom‐left corner).
3629
let (left, bottom, _right, _top) = maze.playable_bounds();
3730

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.
4269
let maze_row = (maze.height as i32 - 1) - ty;
4370
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].
4572
let bmp_opt = match maze.data[index] {
4673
1 => texture_assets.wall.as_ref(),
4774
0 => texture_assets.ground.as_ref(),
@@ -50,42 +77,41 @@ pub fn render_system(
5077
};
5178

5279
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);
5787
Image::new(bmp, pos)
5888
.draw(&mut fb_res.frame_buf)
5989
.unwrap();
6090
}
6191
}
6292
}
6393

64-
// --- Render coins on top of the maze ---
94+
// --- Render coins ---
6595
for coin in &maze.coins {
6696
if coin.x != -1 && coin.y != -1 {
6797
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();
73103
}
74104
}
75105
}
76106

77107
// --- Render the player ghost ---
78108
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();
83112
}
84113

85114
// Flush the completed framebuffer to the physical display.
86115
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();
91117
}

spooky-core/src/systems/setup.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ pub fn setup(mut commands: Commands, #[cfg(feature = "std")] asset_server: Res<A
120120

121121
// Compute playable bounds.
122122
let (left, bottom, _right, _top) = maze.playable_bounds();
123-
let initial_x = left as f32 + 32.0;
124-
let initial_y = bottom as f32 + 32.0;
123+
let initial_x = left as f32 + 10.0*16.0;
124+
let initial_y = bottom as f32 + 8.0*16.0;
125125
let player_start = Vec3::new(initial_x, initial_y, 2.0);
126126

127127
// Insert the initial player position resource.

0 commit comments

Comments
 (0)