input_and_camera/
input_and_camera.rs1use primback::prelude::*;
2
3struct InputCameraApp {
4 yaw: f32,
5 pitch: f32,
6 mouse_locked: bool,
7}
8
9impl PrimbackApp for InputCameraApp {
10 fn init(&mut self, update_ctx: &mut UpdateContext) {
11 let camera = Camera::look_at(vec3(0.0, 2.0, 5.0), vec3(0.0, 1.0, 0.0), Vec3::Y);
13 update_ctx.set_camera(camera);
14
15 update_ctx.set_cursor_grab(true);
17 update_ctx.set_cursor_visible(false);
18 }
19
20 fn update(&mut self, update_ctx: &mut UpdateContext) {
21 if update_ctx.is_key_pressed(KeyCode::Escape) {
23 self.mouse_locked = !self.mouse_locked;
24 update_ctx.set_cursor_grab(self.mouse_locked);
25 update_ctx.set_cursor_visible(!self.mouse_locked);
26 }
27
28 if self.mouse_locked {
30 let delta = update_ctx.mouse_delta();
31 let sensitivity = 0.15;
32 self.yaw -= delta.x * sensitivity;
33 self.pitch -= delta.y * sensitivity;
34 self.pitch = self.pitch.clamp(-89.0, 89.0);
36
37 let mut camera = *update_ctx.camera();
39 camera.transform.rotation = Quat::from_euler(
40 EulerRot::YXZ,
41 self.yaw.to_radians(),
42 self.pitch.to_radians(),
43 0.0,
44 );
45 update_ctx.set_camera(camera);
46 }
47
48 let mut camera = *update_ctx.camera();
50 let move_speed = 5.0 * update_ctx.delta_time();
51 let mut move_dir = Vec3::ZERO;
52
53 let forward = camera.transform.forward();
54 let forward_horizontal = vec3(forward.x, 0.0, forward.z).normalize_or_zero();
56 let right = camera.transform.right();
57
58 if update_ctx.is_key_down(KeyCode::KeyW) {
59 move_dir += forward_horizontal;
60 }
61 if update_ctx.is_key_down(KeyCode::KeyS) {
62 move_dir -= forward_horizontal;
63 }
64 if update_ctx.is_key_down(KeyCode::KeyA) {
65 move_dir -= right;
66 }
67 if update_ctx.is_key_down(KeyCode::KeyD) {
68 move_dir += right;
69 }
70 if update_ctx.is_key_down(KeyCode::Space) {
71 move_dir += Vec3::Y;
72 }
73 if update_ctx.is_key_down(KeyCode::ShiftLeft) {
74 move_dir -= Vec3::Y;
75 }
76
77 camera.transform.position += move_dir.normalize_or_zero() * move_speed;
78 update_ctx.set_camera(camera);
79
80 if update_ctx.is_mouse_button_pressed(MouseButton::Left) {
82 update_ctx.play_synth(Synth::coin());
83 }
84 }
85
86 fn draw(&mut self, draw_ctx: &mut DrawContext) {
87 draw_ctx.clear(Color::new(0.1, 0.1, 0.12));
88
89 let grid_size = 10;
91 let spacing = 2.0;
92 for x in -grid_size..=grid_size {
93 for z in -grid_size..=grid_size {
94 let color = if (x + z) % 2 == 0 {
95 Color::new(0.2, 0.2, 0.22)
96 } else {
97 Color::new(0.15, 0.15, 0.17)
98 };
99 let pos = vec3(x as f32 * spacing, 0.0, z as f32 * spacing);
100 draw_ctx.draw_shape(
101 Shape::plane(),
102 Transform::new_position(pos).scale(vec3(spacing, 1.0, spacing)),
103 color,
104 );
105 }
106 }
107
108 for i in 0..8 {
110 let angle = (i as f32) * (std::f32::consts::PI / 4.0);
111 let radius = 6.0;
112 let px = angle.cos() * radius;
113 let pz = angle.sin() * radius;
114 let color = Color::from_hsv((i as f32) * 45.0, 0.8, 0.8);
115 draw_ctx.draw_shape(
116 Shape::cylinder().radius(0.3).height(2.0),
117 Transform::new_position(vec3(px, 1.0, pz)),
118 color,
119 );
120 }
121
122 let screen_w = draw_ctx.render_size().x;
124
125 let ui_rect = Rect::new(vec2(280.0, 110.0))
127 .color(Color::new(0.0, 0.0, 0.0))
128 .radius(8.0)
129 .shadow_offset(vec2(2.0, 2.0));
130 let ui_trans = RectTransform::new_position(vec2(10.0, 10.0));
131 draw_ctx.draw_rect(ui_rect, ui_trans);
132
133 let instructions = [
135 "FPS Camera Controls:",
136 "- WASD: Move horizontally",
137 "- Space / Shift: Fly Up / Down",
138 "- Mouse: Look around",
139 "- Left Click: Play coin sound",
140 "- ESC: Toggle Mouse Lock",
141 ];
142
143 for (idx, line) in instructions.iter().enumerate() {
144 let text_trans = RectTransform::new_position(vec2(20.0, 18.0 + (idx as f32) * 14.0));
145 let color = if idx == 0 {
146 Color::new(1.0, 0.8, 0.2) } else {
148 Color::WHITE
149 };
150 draw_ctx.draw_text(Text::new(*line).scale(1.0), text_trans, color);
151 }
152
153 let status_str = if self.mouse_locked {
155 "Mouse Locked (ESC to Unlock)"
156 } else {
157 "Mouse Unlocked (ESC to Lock)"
158 };
159 let status_color = if self.mouse_locked {
160 Color::new(0.2, 0.8, 0.2)
161 } else {
162 Color::new(0.8, 0.2, 0.2)
163 };
164 let text_size = draw_ctx.measure_text(&Text::new(status_str));
165 let status_trans = RectTransform::new_position(vec2(
166 (screen_w - text_size.x) * 0.5,
167 draw_ctx.render_size().y - 25.0,
168 ));
169 draw_ctx.draw_text(
170 Text::new(status_str)
171 .scale(1.0)
172 .shadow_offset(vec2(1.0, 1.0)),
173 status_trans,
174 status_color,
175 );
176 }
177}
178
179fn main() {
180 let config = PrimbackConfig {
181 window_title: "Primback - Input and Camera Demo".to_string(),
182 render_width: 640,
183 render_height: 480,
184 ..Default::default()
185 };
186
187 Primback::run(
188 config,
189 InputCameraApp {
190 yaw: 0.0,
191 pitch: 0.0,
192 mouse_locked: true,
193 },
194 );
195}