1use jengine::engine::{Color, Game, jEngine, KeyCode};
25use jengine::renderer::text::Font;
26use jengine::ui::modern::Panel;
27use jengine::{DEFAULT_FONT_METADATA, DEFAULT_TILE_H, DEFAULT_TILE_W, DEFAULT_TILESET};
28
29const WORLD_W: u32 = 80;
33const WORLD_H: u32 = 50;
34
35struct CameraDemo {
38 font_loaded: bool,
39 zoom_target: f32,
41 cam_x: f32,
43 cam_y: f32,
44}
45
46impl CameraDemo {
47 fn new() -> Self {
48 let cx = WORLD_W as f32 * DEFAULT_TILE_W as f32 * 0.5;
50 let cy = WORLD_H as f32 * DEFAULT_TILE_H as f32 * 0.5;
51 Self {
52 font_loaded: false,
53 zoom_target: 1.0,
54 cam_x: cx,
55 cam_y: cy,
56 }
57 }
58
59 fn default_camera_pos() -> (f32, f32) {
60 (
61 WORLD_W as f32 * DEFAULT_TILE_W as f32 * 0.5,
62 WORLD_H as f32 * DEFAULT_TILE_H as f32 * 0.5,
63 )
64 }
65}
66
67impl Game for CameraDemo {
68 fn update(&mut self, engine: &mut jEngine) {
69 if engine.is_key_pressed(KeyCode::Escape) {
70 engine.request_quit();
71 return;
72 }
73
74 let step = engine.tile_width() as f32 * 3.0; if engine.is_key_held(KeyCode::ArrowLeft) { self.cam_x -= step; }
78 if engine.is_key_held(KeyCode::ArrowRight) { self.cam_x += step; }
79 if engine.is_key_held(KeyCode::ArrowUp) { self.cam_y -= step; }
80 if engine.is_key_held(KeyCode::ArrowDown) { self.cam_y += step; }
81
82 let tw = engine.tile_width() as f32;
84 let th = engine.tile_height() as f32;
85 self.cam_x = self.cam_x.clamp(0.0, WORLD_W as f32 * tw);
86 self.cam_y = self.cam_y.clamp(0.0, WORLD_H as f32 * th);
87
88 engine.move_camera_pos(self.cam_x, self.cam_y);
90
91 if engine.is_key_pressed(KeyCode::Equal) || engine.is_key_pressed(KeyCode::NumpadAdd) {
93 self.zoom_target = (self.zoom_target * 1.25).min(4.0);
94 engine.set_camera_zoom(self.zoom_target);
95 }
96 if engine.is_key_pressed(KeyCode::Minus) || engine.is_key_pressed(KeyCode::NumpadSubtract) {
97 self.zoom_target = (self.zoom_target / 1.25).max(0.25);
98 engine.set_camera_zoom(self.zoom_target);
99 }
100
101 if engine.is_key_pressed(KeyCode::Space) {
103 engine.camera_shake(12.0);
105 }
106
107 if engine.is_key_pressed(KeyCode::KeyR) {
109 let (cx, cy) = Self::default_camera_pos();
110 self.cam_x = cx;
111 self.cam_y = cy;
112 self.zoom_target = 1.0;
113 engine.set_camera_pos(cx, cy);
115 engine.set_camera_zoom(1.0);
116 }
117 }
118
119 fn render(&mut self, engine: &mut jEngine) {
120 if !self.font_loaded {
122 if let Ok(font) = Font::from_mtsdf_json(DEFAULT_FONT_METADATA) {
123 engine.ui.text.set_font(font);
124 }
125 self.font_loaded = true;
126 }
127
128 engine.clear();
129
130 let gw = engine.grid_width().min(WORLD_W);
135 let gh = engine.grid_height().min(WORLD_H);
136
137 for y in 0..gh {
138 for x in 0..gw {
139 let color = world_color(x, y);
141 engine.set_background(x, y, color);
142
143 if x % 5 == 0 && y % 5 == 0 {
145 engine.set_foreground(x, y, '+', Color([0.3, 0.3, 0.35, 1.0]));
146 }
147 }
148 }
149
150 let mx = WORLD_W / 2;
152 let my = WORLD_H / 2;
153 if mx < gw && my < gh {
154 engine.set_background(mx, my, Color::WHITE);
155 engine.set_foreground(mx, my, 'X', Color::BLACK);
156 }
157
158 let tw = engine.tile_width() as f32;
160 let th = engine.tile_height() as f32;
161 let sw = engine.grid_width() as f32 * tw;
162 let zoom_now = engine.camera_zoom();
163
164 Panel::new(0.0, 0.0, sw, 30.0).with_color(Color([0.0, 0.0, 0.0, 0.85])).draw(engine);
166 engine.ui.ui_text(
167 20.0,
168 8.0,
169 &format!(
170 "Camera: ({:.0}, {:.0}) Zoom: {:.2} Target zoom: {:.2}",
171 self.cam_x, self.cam_y, zoom_now, self.zoom_target
172 ),
173 Color::WHITE,
174 Color::TRANSPARENT, Some(14.0));
175
176 let sh = engine.grid_height() as f32 * th;
178 Panel::new(0.0, sh - 30.0, sw, 30.0).with_color(Color([0.0, 0.0, 0.0, 0.85])).draw(engine);
179 engine.ui.ui_text(
180 20.0,
181 sh - 22.0,
182 "[Arrows] pan [=/-] zoom [Space] shake [R] reset [Esc] quit",
183 Color([0.6, 0.7, 0.65, 1.0]),
184 Color::TRANSPARENT, Some(14.0));
185 }
186}
187
188fn world_color(x: u32, y: u32) -> Color {
193 let band = (x + y) / 4 % 6;
194 match band {
195 0 => Color([0.10, 0.12, 0.18, 1.0]),
196 1 => Color([0.12, 0.18, 0.12, 1.0]),
197 2 => Color([0.18, 0.12, 0.10, 1.0]),
198 3 => Color([0.14, 0.14, 0.20, 1.0]),
199 4 => Color([0.10, 0.18, 0.18, 1.0]),
200 _ => Color([0.16, 0.16, 0.12, 1.0]),
201 }
202}
203
204fn main() {
205 jEngine::builder()
206 .with_title("jengine — Camera")
207 .with_size(800, 576)
208 .with_tileset(DEFAULT_TILESET, DEFAULT_TILE_W, DEFAULT_TILE_H)
209 .run(CameraDemo::new());
210}