pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3Expand description
Examples found in repository?
examples/ribbons.rs (line 10)
7fn fly(ctx: &mut Ctx) {
8 let t = ctx.time() as f32;
9 ctx.world.query::<(&mut Transform3D, &Orbiting)>().for_each(|(at, _)| {
10 at.position = vec3(t.cos() * 3.0, 1.0 + (t * 2.0).sin() * 0.5, t.sin() * 3.0);
11 });
12}
13
14fn setup(ctx: &mut Ctx) {
15 ctx.camera3d.position = vec3(0.0, 4.0, 9.0);
16 ctx.camera3d.look_at(vec3(0.0, 0.5, 0.0));
17 let floor_material = ctx.create_material(MaterialDesc::color(Color::hex(0x6B7B58)));
18 let floor_data = MeshData::plane(12.0);
19 let floor = ctx.create_mesh(&floor_data, floor_material);
20 ctx.world.spawn((Transform3D::default(), Model::new(floor)));
21
22 // ANCHOR: trail
23 // A glowing tracer behind a flying ball: a quarter second long, thin at
24 // the end and fading out.
25 let mut tracer = Trail3D::new(0.25, 0.12);
26 tracer.color = ParticleGradient::new(vec![
27 (0.0, Color::rgb(1.0, 0.8, 0.3)),
28 (1.0, Color::rgba(1.0, 0.3, 0.0, 0.0)),
29 ]);
30 tracer.blend = ParticleBlend3D::Additive;
31 let white = ctx.default_material();
32 let ball = ctx.create_mesh(&MeshData::sphere(0.15, 16), white);
33 ctx.world
34 .spawn((Transform3D::default(), Model::new(ball), tracer, Orbiting));
35 // After a teleport: ctx.world.get_mut::<Trail3D>(entity).unwrap().clear();
36 // ANCHOR_END: trail
37
38 // ANCHOR: line
39 // A laser beam 5 m long, from the entity forward.
40 let beam = Line3D {
41 blend: ParticleBlend3D::Additive,
42 ..Line3D::new(vec![Vec3::ZERO, vec3(0.0, 0.0, -5.0)], 0.03, Color::RED)
43 };
44 ctx.world.spawn((Transform3D::at(vec3(-4.0, 1.5, 3.0)), beam));
45 // ANCHOR_END: line
46
47 // ANCHOR: decal
48 // A 1 m scorch mark on the floor at (1, 0, -1): the box is 1 x 1 m wide
49 // and 0.4 m deep, its +Z turned up, so the image is projected down.
50 let up = Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2);
51 let projector = Mat4::from_scale_rotation_translation(vec3(1.0, 1.0, 0.4), up, vec3(1.0, 0.0, -1.0));
52 let mark = floor_data.decal(projector, 0.003);
53 if !mark.indices.is_empty() {
54 let scorch = ctx.create_material(MaterialDesc {
55 blend: Blend::Transparent,
56 ..MaterialDesc::color(Color::rgba(0.05, 0.04, 0.03, 0.8))
57 });
58 let mark = ctx.create_mesh(&mark, scorch);
59 // Drawn where the floor is.
60 ctx.world.spawn((Transform3D::default(), Model::new(mark)));
61 }
62 // A mesh from a glTF file works the same: ctx.load_mesh_data("models/wall.glb")?.decal(..)
63 // ANCHOR_END: decal
64}More examples
examples/navmesh3d.rs (line 7)
5fn navigation() -> NavMesh3D {
6 let floor = NavSource::from_mesh(&MeshData::plane(30.0), Transform3D::default());
7 let wall = NavSource::from_mesh(&MeshData::cube(4.0), Transform3D::at(vec3(0.0, 2.0, 0.0)));
8 NavMesh3D::bake(&[floor, wall], NavBakeSettings::default()).unwrap()
9}
10// ANCHOR_END: bake
11
12fn main() {
13 // ANCHOR: query
14 let nav = navigation();
15 let start = nav.sample_position(vec3(-8.0, 1.0, 0.0), 2.0).unwrap();
16 let destination = vec3(8.0, 0.0, 0.0);
17 let corners = nav.find_path(start, destination).unwrap();
18 assert!(corners.len() >= 3);
19 // ANCHOR_END: query
20
21 // ANCHOR: agents
22 let mut world = Game::new("Navigation server", 1, 1)
23 .fixed_hz(60)
24 .sim_system(step_navmesh3d)
25 .run_headless(600, |ctx| {
26 ctx.world.insert_resource(nav);
27 let mut agent = NavAgent3D::default();
28 agent.destination = Some(destination);
29 ctx.world.spawn((Transform3D::at(start), agent));
30 // World-space AABB; move its center or despawn it to reopen polygons.
31 ctx.world
32 .spawn((NavObstacle3D::new(vec3(0.0, 1.0, 8.0), vec3(1.0, 1.0, 1.0)),));
33 });
34 let position = world.query::<&Transform3D>().iter().next().unwrap().position;
35 assert!(position.distance(destination) < 0.3);
36 // ANCHOR_END: agents
37}examples/scene_viewer.rs (line 36)
29fn camera_from_env() -> Option<(Vec3, f32, f32)> {
30 let v: Vec<f32> = std::env::var("CAMERA")
31 .ok()?
32 .split(',')
33 .map(|n| n.trim().parse().ok())
34 .collect::<Option<_>>()?;
35 let [x, y, z, yaw, pitch] = v[..] else { return None };
36 Some((vec3(x, y, z), yaw.to_radians(), pitch.to_radians()))
37}
38
39fn main() {
40 let scene = std::env::var("SCENE").unwrap_or_else(|_| {
41 eprintln!("say which scene: SCENE=town/Town.json (a file under assets/)");
42 std::process::exit(2);
43 });
44 if std::env::var("HEADLESS").is_ok() {
45 Game::new("scene viewer", 1, 1).run_headless(0, |ctx| {
46 let start = Instant::now();
47 ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
48 let models = ctx.world.query::<&Model>().iter().count();
49 let (middle, size) = extent(&mut ctx.world);
50 println!(
51 "{scene}: {models} models in {:.2} s, around {middle} across {size:.0} m",
52 start.elapsed().as_secs_f32()
53 );
54 });
55 return;
56 }
57 let shot = std::env::var("SHOT").ok();
58 let (mut yaw, mut pitch) = (0.0f32, -0.3f32);
59 Game::new("Crabby Engine: scene viewer", 1280, 720)
60 .system(move |ctx: &mut Ctx| {
61 if ctx.key_pressed(Key::Escape) {
62 ctx.quit();
63 }
64 let dt = ctx.dt();
65 if ctx.frame() == 1 {
66 let (y, x, _) = ctx.camera3d.rotation.to_euler(glam::EulerRot::YXZ);
67 (yaw, pitch) = (y, x);
68 }
69 let turn = 1.2 * dt;
70 if ctx.key_down(Key::Left) {
71 yaw += turn;
72 }
73 if ctx.key_down(Key::Right) {
74 yaw -= turn;
75 }
76 if ctx.key_down(Key::Up) {
77 pitch = (pitch + turn).min(1.5);
78 }
79 if ctx.key_down(Key::Down) {
80 pitch = (pitch - turn).max(-1.5);
81 }
82 let rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
83 let speed = if ctx.key_down(Key::LeftShift) { 120.0 } else { 20.0 } * dt;
84 let mut go = Vec3::ZERO;
85 for (key, way) in [
86 (Key::W, Vec3::NEG_Z),
87 (Key::S, Vec3::Z),
88 (Key::A, Vec3::NEG_X),
89 (Key::D, Vec3::X),
90 ] {
91 if ctx.key_down(key) {
92 go += rotation * way;
93 }
94 }
95 if ctx.key_down(Key::E) {
96 go += Vec3::Y;
97 }
98 if ctx.key_down(Key::Q) {
99 go -= Vec3::Y;
100 }
101 ctx.camera3d.position += go * speed;
102 ctx.camera3d.rotation = rotation;
103 if ctx.frame() == 300
104 && let Some(path) = &shot
105 {
106 ctx.save_screenshot(path);
107 }
108 if ctx.frame() == 302 && shot.is_some() {
109 ctx.quit();
110 }
111 })
112 .run(move |ctx| {
113 ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
114 ctx.lighting.sun_direction = Sky::sun_direction_at(15.0);
115 ctx.lighting.shadow_distance = 400.0;
116 ctx.camera3d.far = 3000.0;
117 ctx.post.bloom = 0.05;
118 let (middle, size) = extent(&mut ctx.world);
119 let (at, yaw, pitch) =
120 camera_from_env().unwrap_or((middle + vec3(0.0, size * 0.25, size * 0.4), 0.0, -0.5));
121 ctx.camera3d.position = at;
122 ctx.camera3d.rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
123 });
124}examples/shaders.rs (line 17)
14fn blade() -> MeshData {
15 MeshData {
16 positions: vec![
17 vec3(-0.03, 0.0, 0.0),
18 vec3(0.03, 0.0, 0.0),
19 vec3(0.01, 0.6, 0.0),
20 vec3(-0.01, 0.6, 0.0),
21 ],
22 normals: vec![Vec3::Z; 4],
23 uvs: vec![vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)],
24 indices: vec![0, 1, 2, 0, 2, 3],
25 ..Default::default()
26 }
27}
28
29/// A pattern made in the shader: no texture needed.
30const TILES: &str = "
31fn surface(s: Surface) -> Surface {
32 var out = s;
33 let cell = floor(s.uv * vec2<f32>(16.0, 8.0));
34 let checker = (cell.x + cell.y) % 2.0;
35 out.albedo = mix(vec3<f32>(0.8, 0.3, 0.1), vec3<f32>(0.95, 0.85, 0.6), checker);
36 out.roughness = mix(0.25, 0.7, checker);
37 return out;
38}";
39
40fn main() {
41 let env = |name| std::env::var(name).ok().and_then(|v| v.parse::<f32>().ok());
42 let mut hour = env("HOUR").unwrap_or(15.5);
43 let mut fog = env("FOG").unwrap_or(0.0) > 0.0;
44 Game::new("Crabby Engine: shaders", 1280, 720)
45 .system(move |ctx: &mut Ctx| {
46 if ctx.key_pressed(Key::Escape) {
47 ctx.quit();
48 }
49 // Two hours a second.
50 if ctx.key_down(Key::Right) {
51 hour = (hour + ctx.dt() * 2.0) % 24.0;
52 }
53 if ctx.key_down(Key::Left) {
54 hour = (hour - ctx.dt() * 2.0).rem_euclid(24.0);
55 }
56 if ctx.key_pressed(Key::F) {
57 fog = !fog;
58 }
59 ctx.lighting.sun_direction = Sky::sun_direction_at(hour);
60 ctx.sky.fog_density = if fog { 0.02 } else { 0.0 };
61 ctx.sky.fog_falloff = 8.0;
62 if ctx.frame() == 2
63 && let Ok(path) = std::env::var("SHOT")
64 {
65 ctx.save_screenshot(path);
66 }
67 if ctx.frame() == 4 && std::env::var("SHOT").is_ok() {
68 ctx.quit();
69 }
70 })
71 .run(|ctx| {
72 ctx.clear(Color::hex(0x9CC4E4));
73 ctx.camera3d.position = vec3(0.0, 4.0, 11.0);
74 ctx.camera3d.look_at(vec3(0.0, 0.5, 0.0));
75 ctx.post.bloom = 0.1;
76
77 let ground_look = MaterialDesc {
78 roughness: 0.95,
79 ..MaterialDesc::color(Color::hex(0x5E7A3A))
80 };
81 let ground = ctx.create_material(ground_look);
82 let ground = ctx.create_mesh(&MeshData::plane(600.0), ground);
83 ctx.world.spawn((Transform3D::default(), Model::new(ground)));
84
85 let water = ctx.load_shader("shaders/water.wgsl");
86 let lake = ctx.create_material(MaterialDesc {
87 shader: Some(water),
88 blend: Blend::Transparent,
89 custom: [vec4(0.06, 1.5, 1.2, 1.2), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
90 ..MaterialDesc::color(Color::hex(0x1E4D6B))
91 });
92 let mut surface = MeshData::plane(12.0);
93 // More vertices, so the waves have something to move.
94 surface = subdivide(surface, 5);
95 let lake = ctx.create_mesh(&surface, lake);
96 ctx.world
97 .spawn((Transform3D::at(vec3(-3.0, 0.15, -4.0)), Model::new(lake)));
98
99 // The lake bed, under the water: sand sloping down, and stones.
100 let sand = ctx.create_material(MaterialDesc {
101 roughness: 0.9,
102 ..MaterialDesc::color(Color::hex(0xC2A878))
103 });
104 let bed = ctx.create_mesh(&MeshData::plane(12.0), sand);
105 let tilt = Quat::from_rotation_x(0.12);
106 ctx.world.spawn((
107 Transform3D {
108 rotation: tilt,
109 ..Transform3D::at(vec3(-3.0, -0.6, -4.0))
110 },
111 Model::new(bed),
112 ));
113 let stone = ctx.create_material(MaterialDesc {
114 roughness: 0.7,
115 ..MaterialDesc::color(Color::hex(0x8A8A80))
116 });
117 let pebble = ctx.create_mesh(&MeshData::sphere(0.35, 16), stone);
118 for i in 0..12 {
119 let at = vec3(
120 -7.0 + i as f32 * 0.8,
121 -0.3 - (i % 3) as f32 * 0.2,
122 -3.0 + (i % 4) as f32 * -1.5,
123 );
124 ctx.world.spawn((Transform3D::at(at), Model::new(pebble)));
125 }
126
127 let wind = ctx.load_shader("shaders/wind.wgsl");
128 let grass = ctx.create_material(MaterialDesc {
129 shader: Some(wind),
130 double_sided: true,
131 roughness: 0.8,
132 custom: [vec4(0.25, 2.0, 0.0, 0.0), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
133 ..MaterialDesc::color(Color::hex(0x7FA83C))
134 });
135 let blade = ctx.create_mesh(&blade(), grass);
136 let mut seed = 7u32;
137 let mut random = move || {
138 seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
139 (seed >> 8) as f32 / (1 << 24) as f32
140 };
141 for _ in 0..6000 {
142 let at = vec3(random() * 14.0 - 2.0, 0.0, random() * 10.0 - 3.0);
143 if at.x < 4.0 && at.z < 2.5 {
144 continue; // the lake
145 }
146 let t = Transform3D {
147 rotation: Quat::from_rotation_y(random() * 6.3),
148 scale: Vec3::splat(0.6 + random() * 0.8),
149 ..Transform3D::at(at)
150 };
151 ctx.world.spawn((t, Model::new(blade)));
152 }
153
154 let tiles = ctx.create_shader("tiles", TILES);
155 let ball = ctx.create_material(MaterialDesc {
156 shader: Some(tiles),
157 ..Default::default()
158 });
159 let ball = ctx.create_mesh(&MeshData::sphere(1.0, 48), ball);
160 ctx.world
161 .spawn((Transform3D::at(vec3(3.5, 1.0, 1.0)), Model::new(ball)));
162 });
163}examples/vehicle.rs (line 44)
41fn spawn_car(ctx: &mut Ctx, at: Vec3) -> Entity {
42 let mut car = Vehicle3D::new();
43 for (x, z) in [(-0.8, -1.3), (0.8, -1.3), (-0.8, 1.3), (0.8, 1.3)] {
44 let mut wheel = Wheel3D::new(vec3(x, -0.3, z), 0.35);
45 wheel.sideways_friction.stiffness = 1.3;
46 car = car.with_wheel(wheel);
47 }
48 ctx.world.spawn((
49 Transform3D::at(at),
50 RigidBody3D::dynamic()
51 .mass(1200.0)
52 .center_of_mass(vec3(0.0, -0.35, 0.0)),
53 Collider3D::cuboid(vec3(1.8, 0.6, 4.2)),
54 Velocity3D::default(),
55 car,
56 ))
57}
58// ANCHOR_END: car
59
60// ANCHOR: drive
61/// The game's half of driving, before each physics step: rear-wheel
62/// drive, brakes on all four, steering that eases in and tightens less at
63/// speed.
64fn drive(ctx: &mut Ctx) {
65 let controls = *ctx.world.resource::<Controls>();
66 let dt = ctx.dt();
67 let car = ctx.world.resource::<Player>().car;
68 let forward = forward_speed(&ctx.world, car);
69 let wanted = controls.steer * 0.6 / (1.0 + forward.abs() * 0.08);
70 let player = ctx.world.resource_mut::<Player>();
71 player.steer += (wanted - player.steer).clamp(-2.0 * dt, 2.0 * dt);
72 let steer = player.steer;
73 let Some(vehicle) = ctx.world.get_mut::<Vehicle3D>(car) else {
74 return;
75 };
76 // Pressing against the way it rolls brakes; from (almost) still it drives.
77 let braking = controls.throttle * forward < 0.0 && forward.abs() > 0.5;
78 for (i, wheel) in vehicle.wheels_mut().iter_mut().enumerate() {
79 let front = i < 2;
80 wheel.steer_angle = if front { steer } else { 0.0 };
81 wheel.motor_torque = if front || braking {
82 0.0
83 } else {
84 controls.throttle * 900.0
85 };
86 wheel.brake_torque = if braking {
87 3000.0
88 } else if controls.handbrake && !front {
89 5000.0
90 } else {
91 0.0
92 };
93 }
94}
95// ANCHOR_END: drive
96
97fn speed_of(world: &World, car: Entity) -> f32 {
98 world.get::<Velocity3D>(car).map_or(0.0, |v| v.linear.length())
99}
100
101/// Speed along where the car faces (negative backing up).
102fn forward_speed(world: &World, car: Entity) -> f32 {
103 match (world.get::<Velocity3D>(car), world.get::<Transform3D>(car)) {
104 (Some(v), Some(t)) => v.linear.dot(t.forward()),
105 _ => 0.0,
106 }
107}
108
109/// Keys to controls (windowed), in the same tick as `drive`.
110fn keys(ctx: &mut Ctx) {
111 let axis = |plus: bool, minus: bool| plus as i32 as f32 - minus as i32 as f32;
112 let controls = Controls {
113 throttle: axis(ctx.key_down(Key::W), ctx.key_down(Key::S)),
114 steer: axis(ctx.key_down(Key::D), ctx.key_down(Key::A)),
115 handbrake: ctx.key_down(Key::Space),
116 };
117 *ctx.world.resource_mut::<Controls>() = controls;
118 if ctx.key_pressed(Key::R) {
119 let car = ctx.world.resource::<Player>().car;
120 if let Some(t) = ctx.world.get_mut::<Transform3D>(car) {
121 let p = t.position;
122 *t = Transform3D::at(vec3(p.x, height(p.x, p.z) + 1.5, p.z));
123 }
124 }
125}
126
127// ANCHOR: wheels
128/// Every frame: each wheel drawn where its car's last step put it (steer,
129/// spin and suspension travel), following the chassis's smoothed pose.
130fn place_wheels(ctx: &mut Ctx) {
131 let player = ctx.world.resource::<Player>();
132 let car = player.car;
133 let (Some(&vehicle), Some(&chassis)) = (ctx.world.get::<Vehicle3D>(car), ctx.world.get::<Transform3D>(car)) else {
134 return;
135 };
136 for i in 0..player.wheels.len() {
137 let e = ctx.world.resource::<Player>().wheels[i];
138 let pose = vehicle.wheels()[i].pose(&chassis);
139 if let Some(t) = ctx.world.get_mut::<Transform3D>(e) {
140 *t = Transform3D { scale: t.scale, ..pose };
141 }
142 }
143}
144// ANCHOR_END: wheels
145
146/// Every frame: the camera trails the car.
147fn chase(ctx: &mut Ctx) {
148 let car = ctx.world.resource::<Player>().car;
149 let Some(t) = ctx.world.get::<Transform3D>(car).copied() else {
150 return;
151 };
152 let back = -t.forward().with_y(0.0).normalize_or(Vec3::Z);
153 let eye = t.position + back * 9.0 + Vec3::Y * 3.5;
154 let k = (ctx.dt() * 4.0).min(1.0);
155 ctx.camera3d.position = ctx.camera3d.position.lerp(eye, k);
156 let look = Transform3D::at(ctx.camera3d.position).looking_at(t.position + Vec3::Y);
157 ctx.camera3d.rotation = look.rotation;
158}
159
160fn build_world(ctx: &mut Ctx, draw: bool) {
161 let corner = vec3(-SIZE / 2.0, 0.0, -SIZE / 2.0);
162 let step = SIZE / (N - 1) as f32;
163 let heights: Vec<f32> = (0..N * N)
164 .map(|i| height(corner.x + (i % N) as f32 * step, corner.z + (i / N) as f32 * step))
165 .collect();
166 let ground = ctx.physics3d.heightfield(&heights, SIZE);
167 let terrain = ctx
168 .world
169 .spawn((Transform3D::at(corner), RigidBody3D::fixed(), Collider3D::mesh(ground)));
170 let car = spawn_car(ctx, vec3(0.0, height(0.0, 0.0) + 1.5, 0.0));
171 ctx.world.insert_resource(Player {
172 car,
173 steer: 0.0,
174 wheels: Vec::new(),
175 });
176 ctx.world.insert_resource(Controls::default());
177 if !draw {
178 return;
179 }
180 let look = grass(ctx);
181 let tile = ctx.create_terrain_tile(&heights, SIZE, look);
182 ctx.world.insert(terrain, tile);
183 let paint = ctx.create_material(MaterialDesc::color(Color::hex(0xC8402E)));
184 let body = ctx.create_mesh(&MeshData::cube(1.0), paint);
185 if let Some(t) = ctx.world.get_mut::<Transform3D>(car) {
186 t.scale = vec3(1.8, 0.6, 4.2);
187 }
188 ctx.world.insert(car, Model::new(body));
189 let rubber = ctx.create_material(MaterialDesc::color(Color::hex(0x202020)));
190 let tyre = ctx.create_mesh(&MeshData::cube(1.0), rubber);
191 for _ in 0..4 {
192 let wheel = ctx.world.spawn((
193 Transform3D {
194 scale: vec3(0.3, 0.7, 0.7),
195 ..Transform3D::default()
196 },
197 Model::new(tyre),
198 ));
199 ctx.world.resource_mut::<Player>().wheels.push(wheel);
200 }
201}
202
203/// A one-layer terrain material: grass everywhere.
204fn grass(ctx: &mut Ctx) -> Material {
205 let green: Vec<u8> = (0..16 * 16)
206 .flat_map(|i| {
207 let shade = 200 + (i * 37 % 56) as u8;
208 [
209 (70 * shade as u32 / 255) as u8,
210 (120 * shade as u32 / 255) as u8,
211 45,
212 255,
213 ]
214 })
215 .collect();
216 let layers = ctx.create_texture_array(16, 16, &[&green], TextureKind::Color);
217 let weights = ctx.create_texture_array(1, 1, &[&[255, 0, 0, 0]], TextureKind::Data);
218 let corner = vec2(-SIZE / 2.0, -SIZE / 2.0);
219 ctx.create_terrain_material(&TerrainMaterialDesc::new(layers, weights, corner, vec2(SIZE, SIZE)))
220}
221
222/// The script for `HEADLESS=1`: full throttle for 4 s, then steer right
223/// for 2 s, then brake to a stop and hold it with the handbrake.
224fn autopilot(ctx: &mut Ctx) {
225 let t = ctx.sim_tick() as f32 * ctx.dt();
226 let car = ctx.world.resource::<Player>().car;
227 let rolling = forward_speed(&ctx.world, car) > 0.3;
228 *ctx.world.resource_mut::<Controls>() = Controls {
229 throttle: if t < 6.0 {
230 1.0
231 } else if rolling {
232 -1.0
233 } else {
234 0.0
235 },
236 steer: if (4.0..6.0).contains(&t) { 1.0 } else { 0.0 },
237 handbrake: t >= 6.0 && !rolling,
238 };
239}
240
241/// What the car did, second by second.
242#[derive(Default)]
243struct Log(Vec<(f32, Vec3)>);
244
245fn record(ctx: &mut Ctx) {
246 if ctx.sim_tick().is_multiple_of(50) {
247 let car = ctx.world.resource::<Player>().car;
248 let speed = speed_of(&ctx.world, car);
249 let facing = ctx.world.get::<Transform3D>(car).map_or(Vec3::NEG_Z, |t| t.forward());
250 ctx.world.resource_mut::<Log>().0.push((speed, facing));
251 }
252}
253
254/// Runs the script and checks what the car did.
255fn headless() {
256 let world = Game::new("vehicle", 1, 1)
257 .sim_system(autopilot)
258 .sim_system(drive)
259 .physics3d(vec3(0.0, -9.81, 0.0))
260 .sim_system(record)
261 .run_headless(500, |ctx| {
262 build_world(ctx, false);
263 ctx.world.insert_resource(Log::default());
264 });
265 let log = &world.resource::<Log>().0;
266 for (second, (speed, facing)) in log.iter().enumerate() {
267 println!("{:2} s: {speed:5.2} m/s, facing {facing:.2}", second + 1);
268 }
269 let (accelerated, _) = log[3];
270 let (_, turned) = log[5];
271 let (stopped, _) = log[log.len() - 1];
272 assert!(accelerated > 8.0, "accelerates: {accelerated} m/s after 4 s");
273 assert!(turned.x > 0.3, "steers right: facing {turned}");
274 assert!(stopped < 0.5, "stops: {stopped} m/s");
275 println!("accelerates, steers and stops");
276}
277
278fn main() {
279 if std::env::var("HEADLESS").is_ok() {
280 headless();
281 return;
282 }
283 // ANCHOR: setup
284 Game::new("Crabby Engine: vehicle", 1280, 720)
285 .sim_system(keys)
286 .sim_system(drive) // sets the wheels' inputs...
287 .physics3d(vec3(0.0, -9.81, 0.0)) // ...for the step that drives them
288 .system(place_wheels)
289 .system(chase)
290 // ANCHOR_END: setup
291 .run(|ctx| {
292 ctx.clear(Color::hex(0x9CC4E4));
293 build_world(ctx, true);
294 });
295}examples/audio3d.rs (line 22)
5fn setup(ctx: &mut Ctx) {
6 let samples: Vec<f32> = (0..44100)
7 .map(|i| (i as f32 * 220.0 * std::f32::consts::TAU / 44100.0).sin() * 0.1)
8 .collect();
9 let hum = ctx.create_sound(44100, &samples);
10 let settings = AudioSettings3D {
11 sound: SoundParams {
12 volume: 0.4,
13 looping: true,
14 ..Default::default()
15 },
16 min_distance: 2.0,
17 max_distance: 60.0,
18 rolloff: AudioRolloff::Linear,
19 ..Default::default()
20 };
21 ctx.world
22 .spawn((Transform3D::at(vec3(4.0, 0.0, 0.0)), AudioSource3D::new(hum, settings)));
23 ctx.play_sound_at(&hum, vec3(-4.0, 0.0, 0.0), AudioSettings3D::default());
24}
25
26fn main() {
27 // The sounds follow their entities and the listener (ctx.camera3d) by themselves.
28 Game::new("Spatial audio", 800, 600).run(setup);
29}
30// ANCHOR_END: sound3d
31
32// ANCHOR: audio_mixer
33#[allow(dead_code)]
34fn buses_and_effects(ctx: &mut Ctx, sound: Sound) {
35 let music = ctx.audio_bus("music"); // Save this handle; names are looked up once.
36 ctx.set_audio_bus_volume(music, 0.6);
37 let voice = ctx.play_sound_on(
38 &sound,
39 SoundParams {
40 looping: true,
41 ..Default::default()
42 },
43 music,
44 AudioEffects {
45 low_pass_hz: 4_000.0,
46 reverb: 0.2,
47 },
48 );
49 ctx.set_sound_effects(
50 voice,
51 AudioEffects {
52 low_pass_hz: 800.0,
53 reverb: 0.5,
54 },
55 );
56 ctx.mute_audio_bus(music, true);
57 ctx.mute_audio_bus(music, false); // Restores the configured gain.
58 let settings = AudioSettings3D {
59 bus: music,
60 effects: AudioEffects {
61 low_pass_hz: 2_000.0,
62 reverb: 0.3,
63 },
64 ..Default::default()
65 };
66 ctx.play_sound_at(&sound, vec3(3.0, 0.0, 0.0), settings);
67}