#[repr(C)]pub struct Vec2 {
pub x: f32,
pub y: f32,
}Expand description
A 2-dimensional vector.
Fields§
§x: f32§y: f32Implementations§
Source§impl Vec2
impl Vec2
Sourcepub const NEG_INFINITY: Vec2
pub const NEG_INFINITY: Vec2
All f32::NEG_INFINITY.
Sourcepub const USES_CORE_SIMD: bool = false
pub const USES_CORE_SIMD: bool = false
Vec2 uses Rust Portable SIMD
Sourcepub const USES_SCALAR_MATH: bool = true
pub const USES_SCALAR_MATH: bool = true
Vec2 uses scalar math
Sourcepub const USES_WASM_SIMD: bool = false
pub const USES_WASM_SIMD: bool = false
Vec2 uses WebAssembly 128-bit SIMD
pub const USES_WASM32_SIMD: bool = false
Renamed to USES_WASM_SIMD
Sourcepub const fn new(x: f32, y: f32) -> Vec2
pub const fn new(x: f32, y: f32) -> Vec2
Creates a new vector.
Examples found in repository?
More examples
18const EXTENT: Vec2 = Vec2::new(1172.0, 520.0);
19const PLOT_SIZE: Vec2 = Vec2::splat(80.0);
20
21fn setup(mut commands: Commands) {
22 commands.spawn(Camera2d);
23
24 let text_font = TextFont {
25 font_size: FontSize::Px(10.0),
26 ..default()
27 };
28
29 let chunks = [
30 // "In" row
31 EaseFunction::SineIn,
32 EaseFunction::QuadraticIn,
33 EaseFunction::CubicIn,
34 EaseFunction::QuarticIn,
35 EaseFunction::QuinticIn,
36 EaseFunction::SmoothStepIn,
37 EaseFunction::SmootherStepIn,
38 EaseFunction::CircularIn,
39 EaseFunction::ExponentialIn,
40 EaseFunction::ElasticIn,
41 EaseFunction::BackIn,
42 EaseFunction::BounceIn,
43 // "Out" row
44 EaseFunction::SineOut,
45 EaseFunction::QuadraticOut,
46 EaseFunction::CubicOut,
47 EaseFunction::QuarticOut,
48 EaseFunction::QuinticOut,
49 EaseFunction::SmoothStepOut,
50 EaseFunction::SmootherStepOut,
51 EaseFunction::CircularOut,
52 EaseFunction::ExponentialOut,
53 EaseFunction::ElasticOut,
54 EaseFunction::BackOut,
55 EaseFunction::BounceOut,
56 // "InOut" row
57 EaseFunction::SineInOut,
58 EaseFunction::QuadraticInOut,
59 EaseFunction::CubicInOut,
60 EaseFunction::QuarticInOut,
61 EaseFunction::QuinticInOut,
62 EaseFunction::SmoothStep,
63 EaseFunction::SmootherStep,
64 EaseFunction::CircularInOut,
65 EaseFunction::ExponentialInOut,
66 EaseFunction::ElasticInOut,
67 EaseFunction::BackInOut,
68 EaseFunction::BounceInOut,
69 // "Other" row
70 EaseFunction::Linear,
71 EaseFunction::Steps(4, JumpAt::End),
72 EaseFunction::Steps(4, JumpAt::Start),
73 EaseFunction::Steps(4, JumpAt::Both),
74 EaseFunction::Steps(4, JumpAt::None),
75 EaseFunction::Elastic(50.0),
76 ]
77 .chunks(COLS);
78
79 let max_rows = chunks.clone().count();
80
81 let half_extent = EXTENT / 2.;
82 let half_size = PLOT_SIZE / 2.;
83
84 for (row, functions) in chunks.enumerate() {
85 for (col, function) in functions.iter().enumerate() {
86 let color = Hsla::hsl(col as f32 / COLS as f32 * 360.0, 0.8, 0.75).into();
87 commands.spawn((
88 EaseFunctionPlot(*function, color),
89 Transform::from_xyz(
90 -half_extent.x + EXTENT.x / (COLS - 1) as f32 * col as f32,
91 half_extent.y - EXTENT.y / (max_rows - 1) as f32 * row as f32,
92 0.0,
93 ),
94 children![
95 (
96 Sprite::from_color(color, Vec2::splat(5.0)),
97 Transform::from_xyz(half_size.x + 5.0, -half_size.y, 0.0),
98 ),
99 (
100 Sprite::from_color(color, Vec2::splat(4.0)),
101 Transform::from_xyz(-half_size.x, -half_size.y, 0.0),
102 ),
103 (
104 Text2d(format!("{function:?}")),
105 text_font.clone(),
106 TextColor(color),
107 Transform::from_xyz(0.0, -half_size.y - 15.0, 0.0),
108 )
109 ],
110 ));
111 }
112 }
113 commands.spawn((
114 Text::default(),
115 Node {
116 position_type: PositionType::Absolute,
117 top: px(12),
118 left: px(12),
119 ..default()
120 },
121 ));
122}
123
124fn display_curves(
125 mut gizmos: Gizmos,
126 ease_functions: Query<(&EaseFunctionPlot, &Transform, &Children)>,
127 mut transforms: Query<&mut Transform, Without<EaseFunctionPlot>>,
128 mut ui_text: Single<&mut Text>,
129 time: Res<Time>,
130) {
131 let samples = 100;
132 let duration = 2.5;
133 let time_margin = 0.5;
134
135 let now = ((time.elapsed_secs() % (duration + time_margin * 2.0) - time_margin) / duration)
136 .clamp(0.0, 1.0);
137
138 ui_text.0 = format!("Progress: {now:.2}");
139
140 for (EaseFunctionPlot(function, color), transform, children) in &ease_functions {
141 let center = transform.translation.xy();
142 let half_size = PLOT_SIZE / 2.0;
143
144 // Draw a box around the curve
145 gizmos.linestrip_2d(
146 [
147 center + half_size,
148 center + half_size * Vec2::new(-1., 1.),
149 center + half_size * Vec2::new(-1., -1.),
150 center + half_size * Vec2::new(1., -1.),
151 center + half_size,
152 ],
153 color.darker(0.4),
154 );
155
156 // Draw the curve
157 let f = EasingCurve::new(0.0, 1.0, *function);
158 let drawn_curve = f
159 .by_ref()
160 .graph()
161 .map(|(x, y)| center - half_size + Vec2::new(x, y) * PLOT_SIZE);
162 gizmos.curve_2d(
163 &drawn_curve,
164 drawn_curve.domain().spaced_points(samples).unwrap(),
165 *color,
166 );
167
168 // Show progress along the curve for the current time
169 let y = f.sample(now).unwrap() * PLOT_SIZE.y;
170 transforms.get_mut(children[0]).unwrap().translation.y = -half_size.y + y;
171 transforms.get_mut(children[1]).unwrap().translation =
172 -half_size.extend(0.0) + Vec3::new(now * PLOT_SIZE.x, y, 0.0);
173
174 // Show horizontal bar at y value
175 gizmos.linestrip_2d(
176 [
177 center - half_size + Vec2::Y * y,
178 center - half_size + Vec2::new(PLOT_SIZE.x, y),
179 ],
180 color.darker(0.2),
181 );
182 }
183}14const PADDLE_SIZE: Vec2 = Vec2::new(120.0, 20.0);
15const GAP_BETWEEN_PADDLE_AND_FLOOR: f32 = 60.0;
16const PADDLE_SPEED: f32 = 500.0;
17// How close can the paddle get to the wall
18const PADDLE_PADDING: f32 = 10.0;
19
20// We set the z-value of the ball to 1 so it renders on top in the case of overlapping sprites.
21const BALL_STARTING_POSITION: Vec3 = Vec3::new(0.0, -50.0, 1.0);
22const BALL_DIAMETER: f32 = 30.;
23const BALL_SPEED: f32 = 400.0;
24const INITIAL_BALL_DIRECTION: Vec2 = Vec2::new(0.5, -0.5);
25
26const WALL_THICKNESS: f32 = 10.0;
27// x coordinates
28const LEFT_WALL: f32 = -450.;
29const RIGHT_WALL: f32 = 450.;
30// y coordinates
31const BOTTOM_WALL: f32 = -300.;
32const TOP_WALL: f32 = 300.;
33
34const BRICK_SIZE: Vec2 = Vec2::new(100., 30.);
35// These values are exact
36const GAP_BETWEEN_PADDLE_AND_BRICKS: f32 = 270.0;
37const GAP_BETWEEN_BRICKS: f32 = 5.0;
38// These values are lower bounds, as the number of bricks is computed
39const GAP_BETWEEN_BRICKS_AND_CEILING: f32 = 20.0;
40const GAP_BETWEEN_BRICKS_AND_SIDES: f32 = 20.0;
41
42const SCOREBOARD_FONT_SIZE: FontSize = FontSize::Px(33.0);
43const SCOREBOARD_TEXT_PADDING: Val = Val::Px(5.0);
44
45const BACKGROUND_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
46const PADDLE_COLOR: Color = Color::srgb(0.3, 0.3, 0.7);
47const BALL_COLOR: Color = Color::srgb(1.0, 0.5, 0.5);
48const BRICK_COLOR: Color = Color::srgb(0.5, 0.5, 1.0);
49const WALL_COLOR: Color = Color::srgb(0.8, 0.8, 0.8);
50const TEXT_COLOR: Color = Color::srgb(0.5, 0.5, 1.0);
51const SCORE_COLOR: Color = Color::srgb(1.0, 0.5, 0.5);
52
53fn main() {
54 App::new()
55 .add_plugins(DefaultPlugins)
56 .add_plugins(
57 stepping::SteppingPlugin::default()
58 .add_schedule(Update)
59 .at(percent(35), percent(50)),
60 )
61 .insert_resource(Score(0))
62 .insert_resource(ClearColor(BACKGROUND_COLOR))
63 .add_systems(Startup, setup)
64 // Add our simulation systems to the update schedule
65 // which is called once per frame.
66 .add_systems(
67 Update,
68 (apply_velocity, move_paddle, check_for_collisions)
69 // `chain`ing systems together runs them in order
70 .chain(),
71 )
72 .add_systems(Update, update_scoreboard)
73 .add_observer(play_collision_sound)
74 .run();
75}
76
77#[derive(Component)]
78struct Paddle;
79
80#[derive(Component)]
81struct Ball;
82
83#[derive(Component, Deref, DerefMut)]
84struct Velocity(Vec2);
85
86#[derive(Event)]
87struct BallCollided;
88
89#[derive(Component)]
90struct Brick;
91
92#[derive(Resource, Deref)]
93struct CollisionSound(Handle<AudioSource>);
94
95// Default must be implemented to define this as a required component for the Wall component below
96#[derive(Component, Default)]
97struct Collider;
98
99// This is a collection of the components that define a "Wall" in our game
100#[derive(Component)]
101#[require(Sprite, Transform, Collider)]
102struct Wall;
103
104/// Which side of the arena is this wall located on?
105enum WallLocation {
106 Left,
107 Right,
108 Bottom,
109 Top,
110}
111
112impl WallLocation {
113 /// Location of the *center* of the wall, used in `transform.translation()`
114 fn position(&self) -> Vec2 {
115 match self {
116 WallLocation::Left => Vec2::new(LEFT_WALL, 0.),
117 WallLocation::Right => Vec2::new(RIGHT_WALL, 0.),
118 WallLocation::Bottom => Vec2::new(0., BOTTOM_WALL),
119 WallLocation::Top => Vec2::new(0., TOP_WALL),
120 }
121 }
122
123 /// (x, y) dimensions of the wall, used in `transform.scale()`
124 fn size(&self) -> Vec2 {
125 let arena_height = TOP_WALL - BOTTOM_WALL;
126 let arena_width = RIGHT_WALL - LEFT_WALL;
127 // Make sure we haven't messed up our constants
128 assert!(arena_height > 0.0);
129 assert!(arena_width > 0.0);
130
131 match self {
132 WallLocation::Left | WallLocation::Right => {
133 Vec2::new(WALL_THICKNESS, arena_height + WALL_THICKNESS)
134 }
135 WallLocation::Bottom | WallLocation::Top => {
136 Vec2::new(arena_width + WALL_THICKNESS, WALL_THICKNESS)
137 }
138 }
139 }
140}
141
142impl Wall {
143 // This "builder method" allows us to reuse logic across our wall entities,
144 // making our code easier to read and less prone to bugs when we change the logic
145 // Notice the use of Sprite and Transform alongside Wall, overwriting the default values defined for the required components
146 fn new(location: WallLocation) -> (Wall, Sprite, Transform) {
147 (
148 Wall,
149 Sprite::from_color(WALL_COLOR, Vec2::ONE),
150 Transform {
151 // We need to convert our Vec2 into a Vec3, by giving it a z-coordinate
152 // This is used to determine the order of our sprites
153 translation: location.position().extend(0.0),
154 // The z-scale of 2D objects must always be 1.0,
155 // or their ordering will be affected in surprising ways.
156 // See https://github.com/bevyengine/bevy/issues/4149
157 scale: location.size().extend(1.0),
158 ..default()
159 },
160 )
161 }
162}
163
164// This resource tracks the game's score
165#[derive(Resource, Deref, DerefMut)]
166struct Score(usize);
167
168#[derive(Component)]
169struct ScoreboardUi;
170
171// Add the game's entities to our world
172fn setup(
173 mut commands: Commands,
174 mut meshes: ResMut<Assets<Mesh>>,
175 mut materials: ResMut<Assets<ColorMaterial>>,
176 asset_server: Res<AssetServer>,
177) {
178 // Camera
179 commands.spawn(Camera2d);
180
181 // Sound
182 let ball_collision_sound = asset_server.load("sounds/breakout_collision.ogg");
183 commands.insert_resource(CollisionSound(ball_collision_sound));
184
185 // Paddle
186 let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR;
187
188 commands.spawn((
189 Sprite::from_color(PADDLE_COLOR, Vec2::ONE),
190 Transform {
191 translation: Vec3::new(0.0, paddle_y, 0.0),
192 scale: PADDLE_SIZE.extend(1.0),
193 ..default()
194 },
195 Paddle,
196 Collider,
197 ));
198
199 // Ball
200 commands.spawn((
201 Mesh2d(meshes.add(Circle::default())),
202 MeshMaterial2d(materials.add(BALL_COLOR)),
203 Transform::from_translation(BALL_STARTING_POSITION)
204 .with_scale(Vec2::splat(BALL_DIAMETER).extend(1.)),
205 Ball,
206 Velocity(INITIAL_BALL_DIRECTION.normalize() * BALL_SPEED),
207 ));
208
209 // Scoreboard
210 commands.spawn((
211 Text::new("Score: "),
212 TextFont {
213 font_size: SCOREBOARD_FONT_SIZE,
214 ..default()
215 },
216 TextColor(TEXT_COLOR),
217 ScoreboardUi,
218 Node {
219 position_type: PositionType::Absolute,
220 top: SCOREBOARD_TEXT_PADDING,
221 left: SCOREBOARD_TEXT_PADDING,
222 ..default()
223 },
224 children![(
225 TextSpan::default(),
226 TextFont {
227 font_size: SCOREBOARD_FONT_SIZE,
228 ..default()
229 },
230 TextColor(SCORE_COLOR),
231 )],
232 ));
233
234 // Walls
235 commands.spawn(Wall::new(WallLocation::Left));
236 commands.spawn(Wall::new(WallLocation::Right));
237 commands.spawn(Wall::new(WallLocation::Bottom));
238 commands.spawn(Wall::new(WallLocation::Top));
239
240 // Bricks
241 let total_width_of_bricks = (RIGHT_WALL - LEFT_WALL) - 2. * GAP_BETWEEN_BRICKS_AND_SIDES;
242 let bottom_edge_of_bricks = paddle_y + GAP_BETWEEN_PADDLE_AND_BRICKS;
243 let total_height_of_bricks = TOP_WALL - bottom_edge_of_bricks - GAP_BETWEEN_BRICKS_AND_CEILING;
244
245 assert!(total_width_of_bricks > 0.0);
246 assert!(total_height_of_bricks > 0.0);
247
248 // Given the space available, compute how many rows and columns of bricks we can fit
249 let n_columns = (total_width_of_bricks / (BRICK_SIZE.x + GAP_BETWEEN_BRICKS)).floor() as usize;
250 let n_rows = (total_height_of_bricks / (BRICK_SIZE.y + GAP_BETWEEN_BRICKS)).floor() as usize;
251 let n_vertical_gaps = n_columns - 1;
252
253 // Because we need to round the number of columns,
254 // the space on the top and sides of the bricks only captures a lower bound, not an exact value
255 let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0;
256 let left_edge_of_bricks = center_of_bricks
257 // Space taken up by the bricks
258 - (n_columns as f32 / 2.0 * BRICK_SIZE.x)
259 // Space taken up by the gaps
260 - n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS;
261
262 // In Bevy, the `translation` of an entity describes the center point,
263 // not its bottom-left corner
264 let offset_x = left_edge_of_bricks + BRICK_SIZE.x / 2.;
265 let offset_y = bottom_edge_of_bricks + BRICK_SIZE.y / 2.;
266
267 for row in 0..n_rows {
268 for column in 0..n_columns {
269 let brick_position = Vec2::new(
270 offset_x + column as f32 * (BRICK_SIZE.x + GAP_BETWEEN_BRICKS),
271 offset_y + row as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS),
272 );
273
274 // brick
275 commands.spawn((
276 Sprite {
277 color: BRICK_COLOR,
278 ..default()
279 },
280 Transform {
281 translation: brick_position.extend(0.0),
282 scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0),
283 ..default()
284 },
285 Brick,
286 Collider,
287 ));
288 }
289 }
290}153const RECTANGLE: Rectangle = Rectangle {
154 half_size: Vec2::new(SMALL_2D, BIG_2D),
155};
156const CUBOID: Cuboid = Cuboid {
157 half_size: Vec3::new(BIG_3D, SMALL_3D, BIG_3D),
158};
159
160const CIRCLE: Circle = Circle { radius: BIG_2D };
161const SPHERE: Sphere = Sphere { radius: BIG_3D };
162
163const ELLIPSE: Ellipse = Ellipse {
164 half_size: Vec2::new(BIG_2D, SMALL_2D),
165};
166
167const TRIANGLE_2D: Triangle2d = Triangle2d {
168 vertices: [
169 Vec2::new(BIG_2D, 0.0),
170 Vec2::new(0.0, BIG_2D),
171 Vec2::new(-BIG_2D, 0.0),
172 ],
173};
174
175const TRIANGLE_3D: Triangle3d = Triangle3d {
176 vertices: [
177 Vec3::new(BIG_3D, 0.0, 0.0),
178 Vec3::new(0.0, BIG_3D, 0.0),
179 Vec3::new(-BIG_3D, 0.0, 0.0),
180 ],
181};
182
183const PLANE_2D: Plane2d = Plane2d { normal: Dir2::Y };
184const PLANE_3D: Plane3d = Plane3d {
185 normal: Dir3::Y,
186 half_size: Vec2::new(BIG_3D, BIG_3D),
187};
188
189const LINE_2D: Line2d = Line2d { direction: Dir2::X };
190const LINE_3D: Line3d = Line3d { direction: Dir3::X };
191
192const SEGMENT_2D: Segment2d = Segment2d {
193 vertices: [Vec2::new(-BIG_2D / 2., 0.), Vec2::new(BIG_2D / 2., 0.)],
194};
195
196const SEGMENT_3D: Segment3d = Segment3d {
197 vertices: [
198 Vec3::new(-BIG_3D / 2., 0., 0.),
199 Vec3::new(BIG_3D / 2., 0., 0.),
200 ],
201};
202
203const POLYLINE_2D_VERTICES: [Vec2; 4] = [
204 Vec2::new(-BIG_2D, -SMALL_2D),
205 Vec2::new(-SMALL_2D, SMALL_2D),
206 Vec2::new(SMALL_2D, -SMALL_2D),
207 Vec2::new(BIG_2D, SMALL_2D),
208];
209
210const POLYLINE_3D_VERTICES: [Vec3; 4] = [
211 Vec3::new(-BIG_3D, -SMALL_3D, -SMALL_3D),
212 Vec3::new(SMALL_3D, SMALL_3D, 0.0),
213 Vec3::new(-SMALL_3D, -SMALL_3D, 0.0),
214 Vec3::new(BIG_3D, SMALL_3D, SMALL_3D),
215];
216
217const CONVEX_POLYGON_VERTICES: [Vec2; 5] = [
218 Vec2::new(-BIG_2D, -SMALL_2D),
219 Vec2::new(BIG_2D, -SMALL_2D),
220 Vec2::new(BIG_2D, SMALL_2D),
221 Vec2::new(BIG_2D / 2.0, SMALL_2D * 2.0),
222 Vec2::new(-BIG_2D, SMALL_2D),
223];
224
225const REGULAR_POLYGON: RegularPolygon = RegularPolygon {
226 circumcircle: Circle { radius: BIG_2D },
227 sides: 5,
228};
229
230const CAPSULE_2D: Capsule2d = Capsule2d {
231 radius: SMALL_2D,
232 half_length: SMALL_2D,
233};
234
235const CAPSULE_3D: Capsule3d = Capsule3d {
236 radius: SMALL_3D,
237 half_length: SMALL_3D,
238};
239
240const CYLINDER: Cylinder = Cylinder {
241 radius: SMALL_3D,
242 half_height: SMALL_3D,
243};
244
245const CONE: Cone = Cone {
246 radius: BIG_3D,
247 height: BIG_3D,
248};
249
250const CONICAL_FRUSTUM: ConicalFrustum = ConicalFrustum {
251 radius_top: BIG_3D,
252 radius_bottom: SMALL_3D,
253 height: BIG_3D,
254};
255
256const ANNULUS: Annulus = Annulus {
257 inner_circle: Circle { radius: SMALL_2D },
258 outer_circle: Circle { radius: BIG_2D },
259};
260
261const TORUS: Torus = Torus {
262 minor_radius: SMALL_3D / 2.0,
263 major_radius: SMALL_3D * 1.5,
264};
265
266const TETRAHEDRON: Tetrahedron = Tetrahedron {
267 vertices: [
268 Vec3::new(-BIG_3D, 0.0, 0.0),
269 Vec3::new(BIG_3D, 0.0, 0.0),
270 Vec3::new(0.0, 0.0, -BIG_3D * 1.67),
271 Vec3::new(0.0, BIG_3D * 1.67, -BIG_3D * 0.5),
272 ],
273};
274
275const ARC: Arc2d = Arc2d {
276 radius: BIG_2D,
277 half_angle: std::f32::consts::FRAC_PI_4,
278};
279
280const CIRCULAR_SECTOR: CircularSector = CircularSector {
281 arc: Arc2d {
282 radius: BIG_2D,
283 half_angle: std::f32::consts::FRAC_PI_4,
284 },
285};
286
287const CIRCULAR_SEGMENT: CircularSegment = CircularSegment {
288 arc: Arc2d {
289 radius: BIG_2D,
290 half_angle: std::f32::consts::FRAC_PI_4,
291 },
292};
293
294fn setup_cameras(mut commands: Commands) {
295 let start_in_2d = true;
296 let make_camera = |is_active| Camera {
297 is_active,
298 ..Default::default()
299 };
300
301 commands.spawn((Camera2d, make_camera(start_in_2d)));
302
303 commands.spawn((
304 Camera3d::default(),
305 make_camera(!start_in_2d),
306 Transform::from_xyz(0.0, 10.0, 0.0).looking_at(Vec3::ZERO, Vec3::Z),
307 ));
308}
309
310fn setup_ambient_light(mut ambient_light: ResMut<GlobalAmbientLight>) {
311 ambient_light.brightness = 50.0;
312}
313
314fn setup_lights(mut commands: Commands) {
315 commands.spawn((
316 PointLight {
317 intensity: 5000.0,
318 ..default()
319 },
320 Transform::from_translation(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 2.0, 0.0))
321 .looking_at(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 0.0, 0.0), Vec3::Y),
322 ));
323}
324
325/// Marker component for header text
326#[derive(Debug, Clone, Component, Default, Reflect)]
327pub struct HeaderText;
328
329/// Marker component for header node
330#[derive(Debug, Clone, Component, Default, Reflect)]
331pub struct HeaderNode;
332
333fn update_active_cameras(
334 state: Res<State<CameraActive>>,
335 camera_2d: Single<(Entity, &mut Camera), With<Camera2d>>,
336 camera_3d: Single<(Entity, &mut Camera), (With<Camera3d>, Without<Camera2d>)>,
337 mut text: Query<&mut UiTargetCamera, With<HeaderNode>>,
338) {
339 let (entity_2d, mut cam_2d) = camera_2d.into_inner();
340 let (entity_3d, mut cam_3d) = camera_3d.into_inner();
341 let is_camera_2d_active = matches!(*state.get(), CameraActive::Dim2);
342
343 cam_2d.is_active = is_camera_2d_active;
344 cam_3d.is_active = !is_camera_2d_active;
345
346 let active_camera = if is_camera_2d_active {
347 entity_2d
348 } else {
349 entity_3d
350 };
351
352 text.iter_mut().for_each(|mut target_camera| {
353 *target_camera = UiTargetCamera(active_camera);
354 });
355}
356
357fn switch_cameras(current: Res<State<CameraActive>>, mut next: ResMut<NextState<CameraActive>>) {
358 let next_state = match current.get() {
359 CameraActive::Dim2 => CameraActive::Dim3,
360 CameraActive::Dim3 => CameraActive::Dim2,
361 };
362 next.set(next_state);
363}
364
365fn setup_text(mut commands: Commands, cameras: Query<(Entity, &Camera)>) {
366 let active_camera = cameras
367 .iter()
368 .find_map(|(entity, camera)| camera.is_active.then_some(entity))
369 .expect("run condition ensures existence");
370 commands.spawn((
371 HeaderNode,
372 Node {
373 justify_self: JustifySelf::Center,
374 top: px(5),
375 ..Default::default()
376 },
377 UiTargetCamera(active_camera),
378 children![(
379 Text::default(),
380 HeaderText,
381 TextLayout::justify(Justify::Center),
382 children![
383 TextSpan::new("Primitive: "),
384 TextSpan(format!("{text}", text = PrimitiveSelected::default())),
385 TextSpan::new("\n\n"),
386 TextSpan::new(
387 "Press 'C' to switch between 2D and 3D mode\n\
388 Press 'Up' or 'Down' to switch to the next/previous primitive",
389 ),
390 TextSpan::new("\n\n"),
391 TextSpan::new("(If nothing is displayed, there's no rendering support yet)",),
392 ]
393 )],
394 ));
395}
396
397fn update_text(
398 primitive_state: Res<State<PrimitiveSelected>>,
399 header: Query<Entity, With<HeaderText>>,
400 mut writer: TextUiWriter,
401) {
402 let new_text = format!("{text}", text = primitive_state.get());
403 header.iter().for_each(|header_text| {
404 if let Some(mut text) = writer.get_text(header_text, 2) {
405 (*text).clone_from(&new_text);
406 };
407 });
408}
409
410fn switch_to_next_primitive(
411 current: Res<State<PrimitiveSelected>>,
412 mut next: ResMut<NextState<PrimitiveSelected>>,
413) {
414 let next_state = current.get().next();
415 next.set(next_state);
416}
417
418fn switch_to_previous_primitive(
419 current: Res<State<PrimitiveSelected>>,
420 mut next: ResMut<NextState<PrimitiveSelected>>,
421) {
422 let next_state = current.get().previous();
423 next.set(next_state);
424}
425
426fn in_mode(active: CameraActive) -> impl Fn(Res<State<CameraActive>>) -> bool {
427 move |state| *state.get() == active
428}
429
430fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
431 const POSITION: Vec2 = Vec2::new(-LEFT_RIGHT_OFFSET_2D, 0.0);
432 let angle = time.elapsed_secs();
433 let isometry = Isometry2d::new(POSITION, Rot2::radians(angle));
434 let color = Color::WHITE;
435
436 #[expect(
437 clippy::match_same_arms,
438 reason = "Certain primitives don't have any 2D rendering support yet."
439 )]
440 match state.get() {
441 PrimitiveSelected::RectangleAndCuboid => {
442 gizmos.primitive_2d(&RECTANGLE, isometry, color);
443 }
444 PrimitiveSelected::CircleAndSphere => {
445 gizmos.primitive_2d(&CIRCLE, isometry, color);
446 }
447 PrimitiveSelected::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, isometry, color)),
448 PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, isometry, color),
449 PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, isometry, color),
450 PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE_2D, isometry, color)),
451 PrimitiveSelected::Segment => {
452 drop(gizmos.primitive_2d(&SEGMENT_2D, isometry, color));
453 }
454 PrimitiveSelected::Polyline => gizmos.primitive_2d(
455 &Polyline2d {
456 vertices: POLYLINE_2D_VERTICES.to_vec(),
457 },
458 isometry,
459 color,
460 ),
461 PrimitiveSelected::ConvexPolygon => gizmos.primitive_2d(
462 &Polygon::from(ConvexPolygon::new(CONVEX_POLYGON_VERTICES).unwrap()),
463 isometry,
464 color,
465 ),
466 PrimitiveSelected::Polygon => gizmos.primitive_2d(
467 &Polygon {
468 vertices: vec![
469 Vec2::new(-BIG_2D, -SMALL_2D),
470 Vec2::new(BIG_2D, -SMALL_2D),
471 Vec2::new(BIG_2D, SMALL_2D),
472 Vec2::new(0.0, 0.0),
473 Vec2::new(-BIG_2D, SMALL_2D),
474 ],
475 },
476 isometry,
477 color,
478 ),
479 PrimitiveSelected::RegularPolygon => {
480 gizmos.primitive_2d(®ULAR_POLYGON, isometry, color);
481 }
482 PrimitiveSelected::Capsule => gizmos.primitive_2d(&CAPSULE_2D, isometry, color),
483 PrimitiveSelected::Cylinder => {}
484 PrimitiveSelected::Cone => {}
485 PrimitiveSelected::ConicalFrustum => {}
486 PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, isometry, color)),
487 PrimitiveSelected::Tetrahedron => {}
488 PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, isometry, color),
489 PrimitiveSelected::CircularSector => {
490 gizmos.primitive_2d(&CIRCULAR_SECTOR, isometry, color);
491 }
492 PrimitiveSelected::CircularSegment => {
493 gizmos.primitive_2d(&CIRCULAR_SEGMENT, isometry, color);
494 }
495 }
496}73fn spawn_curve_sprite<T: CurveColor>(commands: &mut Commands, y: f32, points: [T; 4]) {
74 commands.spawn((
75 Sprite::sized(Vec2::new(75., 75.)),
76 Transform::from_xyz(0., y, 0.),
77 Curve(CubicBezier::new([points]).to_curve().unwrap()),
78 ));
79}
80
81fn spawn_mixed_sprite<T: MixedColor>(commands: &mut Commands, y: f32, colors: [T; 4]) {
82 commands.spawn((
83 Transform::from_xyz(0., y, 0.),
84 Sprite::sized(Vec2::new(75., 75.)),
85 Mixed(colors),
86 ));
87}- examples/math/custom_primitives.rs
- examples/window/window_resizing.rs
- examples/3d/motion_blur.rs
- tests/window/minimizing.rs
- tests/window/resizing.rs
- examples/camera/first_person_view_model.rs
- examples/movement/physics_in_fixed_timestep.rs
- examples/ecs/parallel_query.rs
- examples/ui/scroll_and_overflow/scroll.rs
- examples/asset/asset_saving_with_subassets.rs
- examples/remote/server.rs
- examples/ecs/fallible_params.rs
- examples/gizmos/2d_text_gizmos.rs
- examples/2d/wireframe_2d.rs
- examples/stress_tests/many_sprites.rs
- examples/stress_tests/many_sprite_meshes.rs
- examples/testbed/2d.rs
- examples/math/bounding_2d.rs
- examples/stress_tests/many_animated_sprites.rs
- examples/stress_tests/many_animated_sprite_meshes.rs
- examples/stress_tests/many_text2d.rs
- examples/camera/2d_on_ui.rs
- examples/3d/light_textures.rs
- examples/2d/cpu_draw.rs
- examples/gizmos/2d_gizmos.rs
- examples/testbed/ui.rs
- examples/2d/sprite_slice.rs
- examples/asset/repeated_texture.rs
- examples/2d/mesh2d_repeated_texture.rs
- examples/gizmos/3d_gizmos.rs
- examples/3d/auto_exposure.rs
- examples/stress_tests/many_cubes.rs
- examples/ui/scroll_and_overflow/scrollbars.rs
- examples/2d/2d_shapes.rs
- examples/2d/sprite_scale.rs
- examples/2d/text2d.rs
- examples/3d/3d_shapes.rs
- examples/3d/camera_sub_view.rs
Sourcepub const fn splat(v: f32) -> Vec2
pub const fn splat(v: f32) -> Vec2
Creates a vector with all elements set to v.
Examples found in repository?
19const PLOT_SIZE: Vec2 = Vec2::splat(80.0);
20
21fn setup(mut commands: Commands) {
22 commands.spawn(Camera2d);
23
24 let text_font = TextFont {
25 font_size: FontSize::Px(10.0),
26 ..default()
27 };
28
29 let chunks = [
30 // "In" row
31 EaseFunction::SineIn,
32 EaseFunction::QuadraticIn,
33 EaseFunction::CubicIn,
34 EaseFunction::QuarticIn,
35 EaseFunction::QuinticIn,
36 EaseFunction::SmoothStepIn,
37 EaseFunction::SmootherStepIn,
38 EaseFunction::CircularIn,
39 EaseFunction::ExponentialIn,
40 EaseFunction::ElasticIn,
41 EaseFunction::BackIn,
42 EaseFunction::BounceIn,
43 // "Out" row
44 EaseFunction::SineOut,
45 EaseFunction::QuadraticOut,
46 EaseFunction::CubicOut,
47 EaseFunction::QuarticOut,
48 EaseFunction::QuinticOut,
49 EaseFunction::SmoothStepOut,
50 EaseFunction::SmootherStepOut,
51 EaseFunction::CircularOut,
52 EaseFunction::ExponentialOut,
53 EaseFunction::ElasticOut,
54 EaseFunction::BackOut,
55 EaseFunction::BounceOut,
56 // "InOut" row
57 EaseFunction::SineInOut,
58 EaseFunction::QuadraticInOut,
59 EaseFunction::CubicInOut,
60 EaseFunction::QuarticInOut,
61 EaseFunction::QuinticInOut,
62 EaseFunction::SmoothStep,
63 EaseFunction::SmootherStep,
64 EaseFunction::CircularInOut,
65 EaseFunction::ExponentialInOut,
66 EaseFunction::ElasticInOut,
67 EaseFunction::BackInOut,
68 EaseFunction::BounceInOut,
69 // "Other" row
70 EaseFunction::Linear,
71 EaseFunction::Steps(4, JumpAt::End),
72 EaseFunction::Steps(4, JumpAt::Start),
73 EaseFunction::Steps(4, JumpAt::Both),
74 EaseFunction::Steps(4, JumpAt::None),
75 EaseFunction::Elastic(50.0),
76 ]
77 .chunks(COLS);
78
79 let max_rows = chunks.clone().count();
80
81 let half_extent = EXTENT / 2.;
82 let half_size = PLOT_SIZE / 2.;
83
84 for (row, functions) in chunks.enumerate() {
85 for (col, function) in functions.iter().enumerate() {
86 let color = Hsla::hsl(col as f32 / COLS as f32 * 360.0, 0.8, 0.75).into();
87 commands.spawn((
88 EaseFunctionPlot(*function, color),
89 Transform::from_xyz(
90 -half_extent.x + EXTENT.x / (COLS - 1) as f32 * col as f32,
91 half_extent.y - EXTENT.y / (max_rows - 1) as f32 * row as f32,
92 0.0,
93 ),
94 children![
95 (
96 Sprite::from_color(color, Vec2::splat(5.0)),
97 Transform::from_xyz(half_size.x + 5.0, -half_size.y, 0.0),
98 ),
99 (
100 Sprite::from_color(color, Vec2::splat(4.0)),
101 Transform::from_xyz(-half_size.x, -half_size.y, 0.0),
102 ),
103 (
104 Text2d(format!("{function:?}")),
105 text_font.clone(),
106 TextColor(color),
107 Transform::from_xyz(0.0, -half_size.y - 15.0, 0.0),
108 )
109 ],
110 ));
111 }
112 }
113 commands.spawn((
114 Text::default(),
115 Node {
116 position_type: PositionType::Absolute,
117 top: px(12),
118 left: px(12),
119 ..default()
120 },
121 ));
122}More examples
86static LIGHTMAPS: [(&str, Rect); 5] = [
87 (
88 "Plane",
89 uv_rect_opengl(Vec2::splat(0.026), Vec2::splat(0.710)),
90 ),
91 (
92 "SheenChair_fabric",
93 uv_rect_opengl(vec2(0.7864, 0.02377), vec2(0.1910, 0.1912)),
94 ),
95 (
96 "SheenChair_label",
97 uv_rect_opengl(vec2(0.275, -0.016), vec2(0.858, 0.486)),
98 ),
99 (
100 "SheenChair_metal",
101 uv_rect_opengl(vec2(0.998, 0.506), vec2(-0.029, -0.067)),
102 ),
103 (
104 "SheenChair_wood",
105 uv_rect_opengl(vec2(0.787, 0.257), vec2(0.179, 0.177)),
106 ),
107];
108
109static SPHERE_UV_RECT: Rect = uv_rect_opengl(vec2(0.788, 0.484), Vec2::splat(0.062));324fn spawn_metallic_base(
325 commands: &mut Commands,
326 meshes: &mut Assets<Mesh>,
327 standard_materials: &mut Assets<StandardMaterial>,
328) {
329 commands.spawn((
330 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
331 MeshMaterial3d(standard_materials.add(StandardMaterial {
332 base_color: Color::from(bevy::color::palettes::css::DARK_GRAY),
333 metallic: 1.0,
334 perceptual_roughness: 0.3,
335 ..default()
336 })),
337 Transform::from_scale(Vec3::splat(100.0)),
338 MetallicBaseModel,
339 Visibility::Hidden,
340 ));
341}
342
343// Spawns the non-metallic base.
344fn spawn_non_metallic_base(
345 commands: &mut Commands,
346 meshes: &mut Assets<Mesh>,
347 standard_materials: &mut Assets<StandardMaterial>,
348) {
349 commands.spawn((
350 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
351 MeshMaterial3d(standard_materials.add(StandardMaterial {
352 base_color: Color::from(bevy::color::palettes::css::RED),
353 metallic: 0.0,
354 perceptual_roughness: 0.2,
355 ..default()
356 })),
357 Transform::from_scale(Vec3::splat(100.0)),
358 RedPlaneBaseModel,
359 Visibility::Hidden,
360 ));
361}
362
363// Spawns the water plane.
364fn spawn_water(
365 commands: &mut Commands,
366 asset_server: &AssetServer,
367 meshes: &mut Assets<Mesh>,
368 water_materials: &mut Assets<ExtendedMaterial<StandardMaterial, Water>>,
369) {
370 commands.spawn((
371 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
372 MeshMaterial3d(
373 water_materials.add(ExtendedMaterial {
374 base: StandardMaterial {
375 base_color: BLACK.into(),
376 perceptual_roughness: 0.09,
377 ..default()
378 },
379 extension: Water {
380 normals: asset_server
381 .load_builder()
382 .with_settings::<ImageLoaderSettings>(|settings| {
383 settings.is_srgb = false;
384 settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
385 address_mode_u: ImageAddressMode::Repeat,
386 address_mode_v: ImageAddressMode::Repeat,
387 mag_filter: ImageFilterMode::Linear,
388 min_filter: ImageFilterMode::Linear,
389 ..default()
390 });
391 })
392 .load("textures/water_normals.png"),
393 // These water settings are just random values to create some
394 // variety.
395 settings: WaterSettings {
396 octave_vectors: [
397 vec4(0.080, 0.059, 0.073, -0.062),
398 vec4(0.153, 0.138, -0.149, -0.195),
399 ],
400 octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 5.0,
401 octave_strengths: vec4(0.16, 0.18, 0.093, 0.044),
402 },
403 },
404 }),
405 ),
406 Transform::from_scale(Vec3::splat(100.0)),
407 WaterModel,
408 ));
409}217fn spawn_plane(
218 commands: &mut Commands,
219 meshes: &mut Assets<Mesh>,
220 show_depth_texture_materials: &mut Assets<ShowDepthTextureMaterial>,
221 demo_depth_texture: &DemoDepthTexture,
222) {
223 let plane_handle = meshes.add(Plane3d::new(Vec3::Z, Vec2::splat(2.0)));
224 let show_depth_texture_material = show_depth_texture_materials.add(ShowDepthTextureMaterial {
225 depth_texture: Some(demo_depth_texture.0.clone()),
226 });
227 commands.spawn((
228 Mesh3d(plane_handle),
229 MeshMaterial3d(show_depth_texture_material),
230 Transform::from_xyz(10.0, 4.0, 0.0).with_scale(Vec3::splat(2.5)),
231 ));
232}- examples/gizmos/anchored_text_gizmos.rs
- examples/ecs/parallel_query.rs
- examples/math/bounding_2d.rs
- examples/camera/first_person_view_model.rs
- examples/3d/clustered_decals.rs
- examples/math/custom_primitives.rs
- examples/3d/clustered_decal_maps.rs
- examples/ui/ui_transform.rs
- examples/stress_tests/many_sprites.rs
- examples/stress_tests/many_sprite_meshes.rs
- examples/audio/spatial_audio_2d.rs
- examples/testbed/2d.rs
- examples/showcase/contributors.rs
- examples/stress_tests/many_animated_sprites.rs
- examples/2d/bloom_2d.rs
- examples/stress_tests/many_animated_sprite_meshes.rs
- examples/stress_tests/many_text2d.rs
- examples/camera/free_camera_controller.rs
- examples/3d/atmosphere.rs
- examples/time/virtual_time.rs
- examples/picking/sprite_picking.rs
- examples/gizmos/2d_gizmos.rs
- examples/2d/mesh2d_alpha_mode.rs
- examples/testbed/ui.rs
- examples/2d/sprite_slice.rs
- examples/gizmos/3d_gizmos.rs
- examples/stress_tests/many_cubes.rs
- examples/stress_tests/bevymark.rs
- examples/2d/2d_shapes.rs
- examples/showcase/breakout.rs
- examples/showcase/desk_toy.rs
- examples/ui/navigation/directional_navigation.rs
- examples/3d/3d_shapes.rs
Sourcepub fn map<F>(self, f: F) -> Vec2
pub fn map<F>(self, f: F) -> Vec2
Returns a vector containing each element of self modified by a mapping function f.
Sourcepub fn select(mask: BVec2, if_true: Vec2, if_false: Vec2) -> Vec2
pub fn select(mask: BVec2, if_true: Vec2, if_false: Vec2) -> Vec2
Creates a vector from the elements in if_true and if_false, selecting which to use
for each element of self.
A true element in the mask uses the corresponding element from if_true, and false
uses the element from if_false.
Sourcepub const fn from_array(a: [f32; 2]) -> Vec2
pub const fn from_array(a: [f32; 2]) -> Vec2
Creates a new vector from an array.
Sourcepub const fn from_slice(slice: &[f32]) -> Vec2
pub const fn from_slice(slice: &[f32]) -> Vec2
Creates a vector from the first 2 values in slice.
§Panics
Panics if slice is less than 2 elements long.
Sourcepub fn write_to_slice(self, slice: &mut [f32])
pub fn write_to_slice(self, slice: &mut [f32])
Writes the elements of self to the first 2 elements in slice.
§Panics
Panics if slice is less than 2 elements long.
Sourcepub const fn extend(self, z: f32) -> Vec3
pub const fn extend(self, z: f32) -> Vec3
Creates a 3D vector from self and the given z value.
Examples found in repository?
More examples
343fn rotate_camera(
344 time: Res<Time>,
345 mut camera_query: Query<&mut Transform, With<Camera3d>>,
346 app_status: Res<AppStatus>,
347) {
348 if !app_status.rotating {
349 return;
350 }
351
352 for mut transform in camera_query.iter_mut() {
353 transform.translation = Vec2::from_angle(time.delta_secs() * PI / 5.0)
354 .rotate(transform.translation.xz())
355 .extend(transform.translation.y)
356 .xzy();
357 transform.look_at(Vec3::ZERO, Vec3::Y);
358 }
359}349fn rotate_camera(
350 mut camera_query: Query<&mut Transform, With<Camera3d>>,
351 time: Res<Time>,
352 app_status: Res<AppStatus>,
353) {
354 if !app_status.rotating {
355 return;
356 }
357
358 for mut transform in camera_query.iter_mut() {
359 transform.translation = Vec2::from_angle(ROTATION_SPEED * time.delta_secs())
360 .rotate(transform.translation.xz())
361 .extend(transform.translation.y)
362 .xzy();
363 transform.look_at(Vec3::ZERO, Vec3::Y);
364 }
365}427fn animate_image_scale(
428 mut animated_images_query: Query<&mut Transform, With<AnimatedImage>>,
429 windows_query: Query<&Window, With<PrimaryWindow>>,
430 app_status: Res<AppStatus>,
431 time: Res<Time>,
432) {
433 let window_size = windows_query.iter().next().unwrap().size();
434 let animated_mesh_size = app_status.animated_mesh_size(window_size);
435
436 for mut animated_image_transform in &mut animated_images_query {
437 animated_image_transform.scale =
438 animated_mesh_size.extend(1.0) * triangle_wave(time.elapsed_secs(), ANIMATION_PERIOD);
439 }
440}
441
442/// Evaluates a [triangle wave] with the given wavelength.
443///
444/// This is used as part of [`animate_image_scale`], to derive the scale from
445/// the current elapsed time.
446///
447/// [triangle wave]: https://en.wikipedia.org/wiki/Triangle_wave#Definition
448fn triangle_wave(time: f32, wavelength: f32) -> f32 {
449 2.0 * ops::abs(time / wavelength - ops::floor(time / wavelength + 0.5))
450}
451
452/// Adds the top mipmap level of the image to [`MipGenerationJobs`].
453///
454/// Note that this must run in the render world, not the main world, as
455/// [`MipGenerationJobs`] is a resource that exists in the former. Consequently,
456/// it must use [`Extract`] to access main world resources.
457fn extract_mipmap_source_image(
458 mipmap_source_image: Extract<Res<MipmapSourceImage>>,
459 app_status: Extract<Res<AppStatus>>,
460 mut mip_generation_jobs: ResMut<MipGenerationJobs>,
461) {
462 if app_status.enable_mip_generation == EnableMipGeneration::On {
463 mip_generation_jobs.add(MIP_GENERATION_PHASE_ID, mipmap_source_image.id());
464 }
465}
466
467/// Updates the widgets at the bottom of the screen to reflect the settings that
468/// the user has chosen.
469fn update_radio_buttons(
470 mut widgets: Query<
471 (
472 Entity,
473 Option<&mut BackgroundColor>,
474 Has<Text>,
475 &WidgetClickSender<AppSetting>,
476 ),
477 Or<(With<RadioButton>, With<RadioButtonText>)>,
478 >,
479 app_status: Res<AppStatus>,
480 mut writer: TextUiWriter,
481) {
482 for (entity, image, has_text, sender) in widgets.iter_mut() {
483 let selected = match **sender {
484 AppSetting::RegenerateTopMipLevel => continue,
485 AppSetting::EnableMipGeneration(enable_mip_generation) => {
486 enable_mip_generation == app_status.enable_mip_generation
487 }
488 AppSetting::ImageWidth(image_width) => image_width == app_status.image_width,
489 AppSetting::ImageHeight(image_height) => image_height == app_status.image_height,
490 };
491
492 if let Some(mut bg_color) = image {
493 widgets::update_ui_radio_button(&mut bg_color, selected);
494 }
495 if has_text {
496 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
497 }
498 }
499}
500
501/// Handles a request from the user to change application settings via the UI.
502///
503/// This also handles clicks on the "Regenerate Top Mip Level" button.
504fn handle_app_setting_change(
505 mut events: MessageReader<WidgetClickEvent<AppSetting>>,
506 mut app_status: ResMut<AppStatus>,
507 mut regenerate_image_message_writer: MessageWriter<RegenerateImage>,
508) {
509 for event in events.read() {
510 // If this is a setting, update the setting. Fall through if, in
511 // addition to updating the setting, we need to regenerate the image.
512 match **event {
513 AppSetting::EnableMipGeneration(enable_mip_generation) => {
514 app_status.enable_mip_generation = enable_mip_generation;
515 continue;
516 }
517
518 AppSetting::RegenerateTopMipLevel => {}
519 AppSetting::ImageWidth(image_size) => app_status.image_width = image_size,
520 AppSetting::ImageHeight(image_size) => app_status.image_height = image_size,
521 }
522
523 // Schedule the image to be regenerated.
524 regenerate_image_message_writer.write(RegenerateImage);
525 }
526}
527
528/// Handles resize events for the window.
529///
530/// Resizing the window invalidates the image and repositions all image views.
531/// (Regenerating the image isn't strictly necessary, but it's simplest to have
532/// a single function that both regenerates the image and recreates the image
533/// views.)
534fn handle_window_resize_events(
535 mut events: MessageReader<WindowResized>,
536 mut regenerate_image_message_writer: MessageWriter<RegenerateImage>,
537) {
538 for _ in events.read() {
539 regenerate_image_message_writer.write(RegenerateImage);
540 }
541}
542
543/// Recreates the image, as well as all views that show the image, when a
544/// [`RegenerateImage`] message is received.
545///
546/// The views that show the image consist of the animated mesh on the left side
547/// of the window and the column of mipmap level views on the right side of the
548/// window.
549fn regenerate_image_when_requested(
550 mut commands: Commands,
551 image_views_query: Query<Entity, With<ImageView>>,
552 windows_query: Query<&Window, With<PrimaryWindow>>,
553 app_assets: Res<AppAssets>,
554 mut app_status: ResMut<AppStatus>,
555 mut images: ResMut<Assets<Image>>,
556 mut single_mip_level_materials: ResMut<Assets<SingleMipLevelMaterial>>,
557 mut color_materials: ResMut<Assets<ColorMaterial>>,
558 mut message_reader: MessageReader<RegenerateImage>,
559) {
560 // Only do this at most once per frame, or else the despawn logic below will
561 // get confused.
562 if message_reader.read().count() == 0 {
563 return;
564 }
565
566 // Despawn all entities that show the image.
567 for entity in image_views_query.iter() {
568 commands.entity(entity).despawn();
569 }
570
571 // Regenerate the image.
572 let image_handle = app_status.regenerate_mipmap_source_image(&mut commands, &mut images);
573
574 // Respawn the animated image view on the left side of the window.
575 spawn_animated_mesh(
576 &mut commands,
577 &app_status,
578 &app_assets,
579 &windows_query,
580 &mut color_materials,
581 &image_handle,
582 );
583
584 // Respawn the column of mip level views on the right side of the window.
585 spawn_mip_level_views(
586 &mut commands,
587 &app_status,
588 &app_assets,
589 &windows_query,
590 &mut single_mip_level_materials,
591 &image_handle,
592 );
593}
594
595/// Spawns the image on the left that continually changes scale.
596///
597/// Continually changing scale effectively cycles though each mip level,
598/// demonstrating the difference between mip level images being present and mip
599/// level image being absent.
600fn spawn_animated_mesh(
601 commands: &mut Commands,
602 app_status: &AppStatus,
603 app_assets: &AppAssets,
604 windows_query: &Query<&Window, With<PrimaryWindow>>,
605 color_materials: &mut Assets<ColorMaterial>,
606 image_handle: &Handle<Image>,
607) {
608 let window_size = windows_query.iter().next().unwrap().size();
609 let animated_mesh_area_size = app_status.animated_mesh_area_size(window_size);
610 let animated_mesh_size = app_status.animated_mesh_size(window_size);
611
612 commands.spawn((
613 Mesh2d(app_assets.rectangle.clone()),
614 MeshMaterial2d(color_materials.add(ColorMaterial {
615 texture: Some(image_handle.clone()),
616 ..default()
617 })),
618 Transform::from_translation(
619 (animated_mesh_area_size * 0.5 - window_size * 0.5).extend(0.0),
620 )
621 .with_scale(animated_mesh_size.extend(1.0)),
622 AnimatedImage,
623 ImageView,
624 ));
625}
626
627/// Creates the column on the right side of the window that displays each mip
628/// level by itself.
629fn spawn_mip_level_views(
630 commands: &mut Commands,
631 app_status: &AppStatus,
632 app_assets: &AppAssets,
633 windows_query: &Query<&Window, With<PrimaryWindow>>,
634 single_mip_level_materials: &mut Assets<SingleMipLevelMaterial>,
635 image_handle: &Handle<Image>,
636) {
637 let window_size = windows_query.iter().next().unwrap().size();
638
639 // Calculate the placement of the column of mipmap levels.
640 let max_slice_size = app_status.max_mip_slice_size(window_size);
641 let y_origin = app_status.vertical_mip_slice_origin(window_size);
642 let y_spacing = app_status.vertical_mip_slice_spacing(window_size);
643 let x_origin = app_status.horizontal_mip_slice_origin(window_size);
644
645 for (mip_level, mip_size) in MipmapSizeIterator::new(app_status).enumerate() {
646 let y_center = y_origin - y_spacing * mip_level as f32;
647
648 // Size each image to fit its container, preserving aspect ratio.
649 let mut slice_size = mip_size.as_vec2();
650 let ratios = max_slice_size / slice_size;
651 let slice_scale = ratios.x.min(ratios.y).min(1.0);
652 slice_size *= slice_scale;
653
654 // Spawn the image. Use the `SingleMipLevelMaterial` with its custom
655 // shader so that only the mip level in question is displayed.
656 commands.spawn((
657 Mesh2d(app_assets.rectangle.clone()),
658 MeshMaterial2d(single_mip_level_materials.add(SingleMipLevelMaterial {
659 mip_level: mip_level as u32,
660 texture: image_handle.clone(),
661 })),
662 Transform::from_xyz(x_origin, y_center, 0.0).with_scale(slice_size.extend(1.0)),
663 ImageView,
664 ));
665
666 // Display a label to the side.
667 commands.spawn((
668 Text2d::new(format!(
669 "Level {}\n{}×{}",
670 mip_level, mip_size.x, mip_size.y
671 )),
672 app_assets.text_font.clone(),
673 TextLayout::justify(Justify::Center),
674 Text2dShadow::default(),
675 Transform::from_xyz(x_origin - max_slice_size.x * 0.5 - 64.0, y_center, 0.0),
676 ImageView,
677 ));
678 }
679}- examples/stress_tests/many_gizmos.rs
- examples/showcase/breakout.rs
- examples/ecs/parallel_query.rs
- examples/camera/2d_top_down_camera.rs
- examples/2d/rotation.rs
- examples/2d/2d_viewport_to_world.rs
- examples/asset/asset_saving_with_subassets.rs
- examples/asset/asset_saving.rs
- examples/stress_tests/many_sprites.rs
- examples/stress_tests/many_sprite_meshes.rs
- examples/testbed/2d.rs
- examples/stress_tests/many_animated_sprites.rs
- examples/stress_tests/many_animated_sprite_meshes.rs
- examples/stress_tests/many_text2d.rs
- examples/animation/easing_functions.rs
- examples/3d/light_textures.rs
- examples/time/virtual_time.rs
- examples/gizmos/3d_gizmos.rs
- examples/showcase/desk_toy.rs
- examples/2d/text2d.rs
Sourcepub fn dot(self, rhs: Vec2) -> f32
pub fn dot(self, rhs: Vec2) -> f32
Computes the dot product of self and rhs.
Examples found in repository?
195fn rotate_to_player_system(
196 time: Res<Time>,
197 mut query: Query<(&RotateToPlayer, &mut Transform), Without<Player>>,
198 player_transform: Single<&Transform, With<Player>>,
199) {
200 // Get the player translation in 2D
201 let player_translation = player_transform.translation.xy();
202
203 for (config, mut enemy_transform) in &mut query {
204 // Get the enemy ship forward vector in 2D (already unit length)
205 let enemy_forward = (enemy_transform.rotation * Vec3::Y).xy();
206
207 // Get the vector from the enemy ship to the player ship in 2D and normalize it.
208 let to_player = (player_translation - enemy_transform.translation.xy()).normalize();
209
210 // Get the dot product between the enemy forward vector and the direction to the player.
211 let forward_dot_player = enemy_forward.dot(to_player);
212
213 // If the dot product is approximately 1.0 then the enemy is already facing the player and
214 // we can early out.
215 if (forward_dot_player - 1.0).abs() < f32::EPSILON {
216 continue;
217 }
218
219 // Get the right vector of the enemy ship in 2D (already unit length)
220 let enemy_right = (enemy_transform.rotation * Vec3::X).xy();
221
222 // Get the dot product of the enemy right vector and the direction to the player ship.
223 // If the dot product is negative them we need to rotate counter clockwise, if it is
224 // positive we need to rotate clockwise. Note that `copysign` will still return 1.0 if the
225 // dot product is 0.0 (because the player is directly behind the enemy, so perpendicular
226 // with the right vector).
227 let right_dot_player = enemy_right.dot(to_player);
228
229 // Determine the sign of rotation from the right dot player. We need to negate the sign
230 // here as the 2D bevy co-ordinate system rotates around +Z, which is pointing out of the
231 // screen. Due to the right hand rule, positive rotation around +Z is counter clockwise and
232 // negative is clockwise.
233 let rotation_sign = -f32::copysign(1.0, right_dot_player);
234
235 // Limit rotation so we don't overshoot the target. We need to convert our dot product to
236 // an angle here so we can get an angle of rotation to clamp against.
237 let max_angle = ops::acos(forward_dot_player.clamp(-1.0, 1.0)); // Clamp acos for safety
238
239 // Calculate angle of rotation with limit
240 let rotation_angle =
241 rotation_sign * (config.rotation_speed * time.delta_secs()).min(max_angle);
242
243 // Rotate the enemy to face the player
244 enemy_transform.rotate_z(rotation_angle);
245 }
246}Sourcepub fn dot_into_vec(self, rhs: Vec2) -> Vec2
pub fn dot_into_vec(self, rhs: Vec2) -> Vec2
Returns a vector where every component is the dot product of self and rhs.
Sourcepub fn min(self, rhs: Vec2) -> Vec2
pub fn min(self, rhs: Vec2) -> Vec2
Returns a vector containing the minimum values for each element of self and rhs.
In other words this computes [min(x, rhs.x), min(self.y, rhs.y), ..].
NaN propogation does not follow IEEE 754-2008 semantics for minNum and may differ on different SIMD architectures.
Examples found in repository?
435 fn aabb_2d(&self, isometry: impl Into<Isometry2d>) -> Aabb2d {
436 let isometry = isometry.into();
437
438 // The center of the circle at the center of the right wing of the heart
439 let circle_center = isometry.rotation * Vec2::new(self.radius, 0.0);
440 // The maximum X and Y positions of the two circles of the wings of the heart.
441 let max_circle = circle_center.abs() + Vec2::splat(self.radius);
442 // Since the two circles of the heart are mirrored around the origin, the minimum position is the negative of the maximum.
443 let min_circle = -max_circle;
444
445 // The position of the tip at the bottom of the heart
446 let tip_position = isometry.rotation * Vec2::new(0.0, -self.radius * (1. + SQRT_2));
447
448 Aabb2d {
449 min: isometry.translation + min_circle.min(tip_position),
450 max: isometry.translation + max_circle.max(tip_position),
451 }
452 }Sourcepub fn max(self, rhs: Vec2) -> Vec2
pub fn max(self, rhs: Vec2) -> Vec2
Returns a vector containing the maximum values for each element of self and rhs.
In other words this computes [max(self.x, rhs.x), max(self.y, rhs.y), ..].
NaN propogation does not follow IEEE 754-2008 semantics for maxNum and may differ on different SIMD architectures.
Examples found in repository?
435 fn aabb_2d(&self, isometry: impl Into<Isometry2d>) -> Aabb2d {
436 let isometry = isometry.into();
437
438 // The center of the circle at the center of the right wing of the heart
439 let circle_center = isometry.rotation * Vec2::new(self.radius, 0.0);
440 // The maximum X and Y positions of the two circles of the wings of the heart.
441 let max_circle = circle_center.abs() + Vec2::splat(self.radius);
442 // Since the two circles of the heart are mirrored around the origin, the minimum position is the negative of the maximum.
443 let min_circle = -max_circle;
444
445 // The position of the tip at the bottom of the heart
446 let tip_position = isometry.rotation * Vec2::new(0.0, -self.radius * (1. + SQRT_2));
447
448 Aabb2d {
449 min: isometry.translation + min_circle.min(tip_position),
450 max: isometry.translation + max_circle.max(tip_position),
451 }
452 }More examples
22fn setup(mut commands: Commands) {
23 let w = 60;
24 let h = 40;
25
26 commands.spawn(Camera2d);
27 commands.insert_resource(UiScale(0.5));
28
29 commands
30 .spawn((
31 Node {
32 width: percent(100),
33 height: percent(100),
34 overflow: Overflow::scroll(),
35 ..Default::default()
36 },
37 ScrollPosition(Vec2::ZERO),
38 ScrollableNode,
39 ScrollStart(Vec2::ZERO),
40 ))
41 .observe(
42 |drag: On<Pointer<Drag>>,
43 ui_scale: Res<UiScale>,
44 mut scroll_position_query: Query<
45 (&mut ScrollPosition, &ScrollStart),
46 With<ScrollableNode>,
47 >| {
48 if let Ok((mut scroll_position, start)) = scroll_position_query.single_mut() {
49 scroll_position.0 = (start.0 - drag.distance / ui_scale.0).max(Vec2::ZERO);
50 }
51 },
52 )
53 .observe(
54 |_: On<Pointer<DragStart>>,
55 mut scroll_position_query: Query<
56 (&ComputedNode, &mut ScrollStart),
57 With<ScrollableNode>,
58 >| {
59 if let Ok((computed_node, mut start)) = scroll_position_query.single_mut() {
60 start.0 = computed_node.scroll_position * computed_node.inverse_scale_factor;
61 }
62 },
63 )
64 .with_children(|commands| {
65 commands
66 .spawn((
67 Node {
68 display: Display::Grid,
69 grid_template_rows: RepeatedGridTrack::px(w as i32, 100.),
70 grid_template_columns: RepeatedGridTrack::px(h as i32, 100.),
71 ..default()
72 },
73 Pickable {
74 is_hoverable: false,
75 should_block_lower: true,
76 }
77 ))
78 .with_children(|commands| {
79 for y in 0..h {
80 for x in 0..w {
81 let tile_color = if (x + y) % 2 == 1 {
82 let hue = ((x as f32 / w as f32) * 270.0)
83 + ((y as f32 / h as f32) * 90.0);
84 Color::hsl(hue, 1., 0.5)
85 } else {
86 Color::BLACK
87 };
88 commands.spawn((
89 Node {
90 grid_row: GridPlacement::start(y + 1),
91 grid_column: GridPlacement::start(x + 1),
92 ..default()
93 },
94 Pickable {
95 should_block_lower: false,
96 is_hoverable: true,
97 },
98 TileColor(tile_color),
99 BackgroundColor(tile_color),
100 ))
101 .observe(|over: On<Pointer<Over>>, mut query: Query<&mut BackgroundColor>,| {
102 if let Ok(mut background_color) = query.get_mut(over.entity) {
103 background_color.0 = RED.into();
104 }
105 })
106 .observe(|out: On<Pointer<Out>>, mut query: Query<(&mut BackgroundColor, &TileColor)>| {
107 if let Ok((mut background_color, tile_color)) = query.get_mut(out.entity) {
108 background_color.0 = tile_color.0;
109 }
110 });
111 }
112 }
113 });
114 });
115}Sourcepub fn clamp(self, min: Vec2, max: Vec2) -> Vec2
pub fn clamp(self, min: Vec2, max: Vec2) -> Vec2
Component-wise clamping of values, similar to f32::clamp.
Each element in min must be less-or-equal to the corresponding element in max.
NaN propogation does not follow IEEE 754-2008 semantics and may differ on different SIMD architectures.
§Panics
Will panic if min is greater than max when glam_assert is enabled.
Examples found in repository?
34fn button_system(
35 mut interaction_query: Query<
36 (
37 &Interaction,
38 &mut BackgroundColor,
39 Option<&RotateButton>,
40 Option<&ScaleButton>,
41 ),
42 (Changed<Interaction>, With<Button>),
43 >,
44 mut rotator_query: Query<&mut UiTransform, With<TargetNode>>,
45) {
46 for (interaction, mut color, maybe_rotate, maybe_scale) in &mut interaction_query {
47 match *interaction {
48 Interaction::Pressed => {
49 *color = PRESSED_BUTTON.into();
50 if let Some(step) = maybe_rotate {
51 for mut transform in rotator_query.iter_mut() {
52 transform.rotation *= step.0;
53 }
54 }
55 if let Some(step) = maybe_scale {
56 for mut transform in rotator_query.iter_mut() {
57 transform.scale += step.0;
58 transform.scale =
59 transform.scale.clamp(Vec2::splat(0.25), Vec2::splat(3.0));
60 }
61 }
62 }
63 Interaction::Hovered => {
64 *color = HOVERED_BUTTON.into();
65 }
66 Interaction::None => {
67 *color = NORMAL_BUTTON.into();
68 }
69 }
70 }
71}Sourcepub fn min_element(self) -> f32
pub fn min_element(self) -> f32
Returns the horizontal minimum of self.
In other words this computes min(x, y, ..).
NaN propogation does not follow IEEE 754-2008 semantics and may differ on different SIMD architectures.
Sourcepub fn max_element(self) -> f32
pub fn max_element(self) -> f32
Returns the horizontal maximum of self.
In other words this computes max(x, y, ..).
NaN propogation does not follow IEEE 754-2008 semantics and may differ on different SIMD architectures.
Sourcepub fn min_position(self) -> usize
pub fn min_position(self) -> usize
Returns the index of the first minimum element of self.
Sourcepub fn max_position(self) -> usize
pub fn max_position(self) -> usize
Returns the index of the first maximum element of self.
Sourcepub fn element_sum(self) -> f32
pub fn element_sum(self) -> f32
Returns the sum of all elements of self.
In other words, this computes self.x + self.y + ...
Sourcepub fn element_product(self) -> f32
pub fn element_product(self) -> f32
Returns the product of all elements of self.
In other words, this computes self.x * self.y * ...
Sourcepub fn cmpeq(self, rhs: Vec2) -> BVec2
pub fn cmpeq(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a == comparison for each element of
self and rhs.
In other words, this computes [self.x == rhs.x, self.y == rhs.y, ..] for all
elements.
Sourcepub fn cmpne(self, rhs: Vec2) -> BVec2
pub fn cmpne(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a != comparison for each element of
self and rhs.
In other words this computes [self.x != rhs.x, self.y != rhs.y, ..] for all
elements.
Sourcepub fn cmpge(self, rhs: Vec2) -> BVec2
pub fn cmpge(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a >= comparison for each element of
self and rhs.
In other words this computes [self.x >= rhs.x, self.y >= rhs.y, ..] for all
elements.
Sourcepub fn cmpgt(self, rhs: Vec2) -> BVec2
pub fn cmpgt(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a > comparison for each element of
self and rhs.
In other words this computes [self.x > rhs.x, self.y > rhs.y, ..] for all
elements.
Sourcepub fn cmple(self, rhs: Vec2) -> BVec2
pub fn cmple(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a <= comparison for each element of
self and rhs.
In other words this computes [self.x <= rhs.x, self.y <= rhs.y, ..] for all
elements.
Sourcepub fn cmplt(self, rhs: Vec2) -> BVec2
pub fn cmplt(self, rhs: Vec2) -> BVec2
Returns a vector mask containing the result of a < comparison for each element of
self and rhs.
In other words this computes [self.x < rhs.x, self.y < rhs.y, ..] for all
elements.
Sourcepub fn abs(self) -> Vec2
pub fn abs(self) -> Vec2
Returns a vector containing the absolute value of each element of self.
Examples found in repository?
435 fn aabb_2d(&self, isometry: impl Into<Isometry2d>) -> Aabb2d {
436 let isometry = isometry.into();
437
438 // The center of the circle at the center of the right wing of the heart
439 let circle_center = isometry.rotation * Vec2::new(self.radius, 0.0);
440 // The maximum X and Y positions of the two circles of the wings of the heart.
441 let max_circle = circle_center.abs() + Vec2::splat(self.radius);
442 // Since the two circles of the heart are mirrored around the origin, the minimum position is the negative of the maximum.
443 let min_circle = -max_circle;
444
445 // The position of the tip at the bottom of the heart
446 let tip_position = isometry.rotation * Vec2::new(0.0, -self.radius * (1. + SQRT_2));
447
448 Aabb2d {
449 min: isometry.translation + min_circle.min(tip_position),
450 max: isometry.translation + max_circle.max(tip_position),
451 }
452 }Sourcepub fn signum(self) -> Vec2
pub fn signum(self) -> Vec2
Returns a vector with elements representing the sign of self.
1.0if the number is positive,+0.0orINFINITY-1.0if the number is negative,-0.0orNEG_INFINITYNANif the number isNAN
Sourcepub fn copysign(self, rhs: Vec2) -> Vec2
pub fn copysign(self, rhs: Vec2) -> Vec2
Returns a vector with signs of rhs and the magnitudes of self.
Sourcepub fn is_negative_bitmask(self) -> u32
pub fn is_negative_bitmask(self) -> u32
Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of self.
A negative element results in a 1 bit and a positive element in a 0 bit. Element x goes
into the first lowest bit, element y into the second, etc.
An element is negative if it has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity.
Sourcepub fn is_finite(self) -> bool
pub fn is_finite(self) -> bool
Returns true if, and only if, all elements are finite. If any element is either
NaN, positive or negative infinity, this will return false.
Sourcepub fn is_finite_mask(self) -> BVec2
pub fn is_finite_mask(self) -> BVec2
Performs is_finite on each element of self, returning a vector mask of the results.
In other words, this computes [x.is_finite(), y.is_finite(), ...].
Sourcepub fn is_nan_mask(self) -> BVec2
pub fn is_nan_mask(self) -> BVec2
Performs is_nan on each element of self, returning a vector mask of the results.
In other words, this computes [x.is_nan(), y.is_nan(), ...].
Sourcepub fn length(self) -> f32
pub fn length(self) -> f32
Computes the length of self.
Examples found in repository?
246fn start_drag(
247 mut commands: Commands,
248 cursor_world_pos: Res<CursorWorldPos>,
249 bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
250) {
251 // If the cursor is not within the primary window skip this system
252 let Some(cursor_world_pos) = cursor_world_pos.0 else {
253 return;
254 };
255
256 // Get the offset from the cursor to the Bevy logo sprite
257 let drag_offset = bevy_logo_transform.translation.truncate() - cursor_world_pos;
258
259 // If the cursor is within the Bevy logo radius start the drag operation and remember the offset of the cursor from the origin
260 if drag_offset.length() < BEVY_LOGO_RADIUS {
261 commands.insert_resource(DragOperation(drag_offset));
262 }
263}
264
265/// Stop the current drag operation
266fn end_drag(mut commands: Commands) {
267 commands.remove_resource::<DragOperation>();
268}
269
270/// Drag the Bevy logo
271fn drag(
272 drag_offset: Res<DragOperation>,
273 cursor_world_pos: Res<CursorWorldPos>,
274 time: Res<Time>,
275 mut bevy_transform: Single<&mut Transform, With<BevyLogo>>,
276 mut q_pupils: Query<&mut Pupil>,
277) {
278 // If the cursor is not within the primary window skip this system
279 let Some(cursor_world_pos) = cursor_world_pos.0 else {
280 return;
281 };
282
283 // Calculate the new translation of the Bevy logo based on cursor and drag offset
284 let new_translation = cursor_world_pos + drag_offset.0;
285
286 // Calculate how fast we are dragging the Bevy logo (unit/second)
287 let drag_velocity =
288 (new_translation - bevy_transform.translation.truncate()) / time.delta_secs();
289
290 // Update the translation of Bevy logo transform to new translation
291 bevy_transform.translation = new_translation.extend(bevy_transform.translation.z);
292
293 // Add the cursor drag velocity in the opposite direction to each pupil.
294 // Remember pupils are using local coordinates to move. So when the Bevy logo moves right they need to move left to
295 // simulate inertia, otherwise they will move fixed to the parent.
296 for mut pupil in &mut q_pupils {
297 pupil.velocity -= drag_velocity;
298 }
299}
300
301/// Quit when the user right clicks the Bevy logo
302fn quit(
303 cursor_world_pos: Res<CursorWorldPos>,
304 mut app_exit: MessageWriter<AppExit>,
305 bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
306) {
307 // If the cursor is not within the primary window skip this system
308 let Some(cursor_world_pos) = cursor_world_pos.0 else {
309 return;
310 };
311
312 // If the cursor is within the Bevy logo radius send the [`AppExit`] event to quit the app
313 if bevy_logo_transform
314 .translation
315 .truncate()
316 .distance(cursor_world_pos)
317 < BEVY_LOGO_RADIUS
318 {
319 app_exit.write(AppExit::Success);
320 }
321}
322
323/// Enable transparency for the window and make it on top
324fn toggle_transparency(
325 mut commands: Commands,
326 mut window_transparency: ResMut<WindowTransparency>,
327 mut q_instructions_text: Query<&mut Visibility, With<InstructionsText>>,
328 mut primary_window: Single<&mut Window, With<PrimaryWindow>>,
329) {
330 // Toggle the window transparency resource
331 window_transparency.0 = !window_transparency.0;
332
333 // Show or hide the instructions text
334 for mut visibility in &mut q_instructions_text {
335 *visibility = if window_transparency.0 {
336 Visibility::Hidden
337 } else {
338 Visibility::Visible
339 };
340 }
341
342 // Remove the primary window's decorations (e.g. borders), make it always on top of other desktop windows, and set the clear color to transparent
343 // only if window transparency is enabled
344 let clear_color;
345 (
346 primary_window.decorations,
347 primary_window.window_level,
348 clear_color,
349 ) = if window_transparency.0 {
350 (false, WindowLevel::AlwaysOnTop, Color::NONE)
351 } else {
352 (true, WindowLevel::Normal, WINDOW_CLEAR_COLOR)
353 };
354
355 // Set the clear color
356 commands.insert_resource(ClearColor(clear_color));
357}
358
359/// Move the pupils and bounce them around
360fn move_pupils(time: Res<Time>, mut q_pupils: Query<(&mut Pupil, &mut Transform)>) {
361 for (mut pupil, mut transform) in &mut q_pupils {
362 // The wiggle radius is how much the pupil can move within the eye
363 let wiggle_radius = pupil.eye_radius - pupil.pupil_radius;
364 // Store the Z component
365 let z = transform.translation.z;
366 // Truncate the Z component to make the calculations be on [`Vec2`]
367 let mut translation = transform.translation.truncate();
368 // Decay the pupil velocity
369 pupil.velocity *= ops::powf(0.04f32, time.delta_secs());
370 // Move the pupil
371 translation += pupil.velocity * time.delta_secs();
372 // If the pupil hit the outside border of the eye, limit the translation to be within the wiggle radius and invert the velocity.
373 // This is not physically accurate but it's good enough for the googly eyes effect.
374 if translation.length() > wiggle_radius {
375 translation = translation.normalize() * wiggle_radius;
376 // Invert and decrease the velocity of the pupil when it bounces
377 pupil.velocity *= -0.75;
378 }
379 // Update the entity transform with the new translation after reading the Z component
380 transform.translation = translation.extend(z);
381 }
382}More examples
497fn handle_camera_mode_change(
498 mut commands: Commands,
499 cameras_query: Query<(Entity, &Transform), With<Camera3d>>,
500 sphere_query: Query<&Transform, (With<ReflectiveSphere>, Without<Camera3d>)>,
501 mut help_text_query: Query<&mut Text, With<HelpText>>,
502 mut windows_query: Query<&mut CursorOptions>,
503 mut app_status: ResMut<AppStatus>,
504 mut messages: MessageReader<WidgetClickEvent<CameraMode>>,
505) {
506 let Some(sphere_transform) = sphere_query.iter().next() else {
507 return;
508 };
509
510 let mut any_changes = false;
511 for message in messages.read() {
512 app_status.camera_mode = **message;
513
514 match **message {
515 CameraMode::Orbit => {
516 for (camera_entity, camera_transform) in &cameras_query {
517 // Convert from Cartesian coordinates back to spherical
518 // coordinates.
519 let relative_camera_position =
520 camera_transform.translation - sphere_transform.translation;
521 let radius = relative_camera_position.length();
522 let inclination = atan2(
523 relative_camera_position.xz().length() / radius,
524 relative_camera_position.y / radius,
525 );
526 let azimuth = atan2(
527 relative_camera_position.z * relative_camera_position.xz().length_recip(),
528 relative_camera_position.x * relative_camera_position.xz().length_recip(),
529 );
530
531 commands
532 .entity(camera_entity)
533 .remove::<FreeCamera>()
534 .insert(OrbitCamera {
535 radius,
536 inclination,
537 azimuth,
538 });
539 }
540 }
541
542 CameraMode::Free => {
543 for (camera_entity, _) in &cameras_query {
544 commands
545 .entity(camera_entity)
546 .remove::<OrbitCamera>()
547 .insert(FreeCamera::default());
548 }
549 }
550 }
551
552 any_changes = true;
553 }
554
555 if any_changes {
556 set_help_text(&app_status, &mut help_text_query);
557
558 // Reset the cursor grab mode, because the free camera controller may
559 // have enabled it, and we don't want the cursor to disappear.
560 for mut cursor_options in &mut windows_query {
561 cursor_options.grab_mode = CursorGrabMode::None;
562 cursor_options.visible = true;
563 }
564 }
565}Sourcepub fn length_squared(self) -> f32
pub fn length_squared(self) -> f32
Computes the squared length of self.
This is faster than length() as it avoids a square root operation.
Sourcepub fn length_recip(self) -> f32
pub fn length_recip(self) -> f32
Computes 1.0 / length().
For valid results, self must not be of length zero.
Examples found in repository?
386fn process_move_input(
387 mut selections: Query<(&mut Transform, &Selection)>,
388 mouse_buttons: Res<ButtonInput<MouseButton>>,
389 mouse_motion: Res<AccumulatedMouseMotion>,
390 app_status: Res<AppStatus>,
391) {
392 // Only process drags when movement is selected.
393 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Move {
394 return;
395 }
396
397 for (mut transform, selection) in &mut selections {
398 if app_status.selection != *selection {
399 continue;
400 }
401
402 let position = transform.translation;
403
404 // Convert to spherical coordinates.
405 let radius = position.length();
406 let mut theta = acos(position.y / radius);
407 let mut phi = position.z.signum() * acos(position.x * position.xz().length_recip());
408
409 // Camera movement is the inverse of object movement.
410 let (phi_factor, theta_factor) = match *selection {
411 Selection::Camera => (1.0, -1.0),
412 Selection::DecalA | Selection::DecalB => (-1.0, 1.0),
413 };
414
415 // Adjust the spherical coordinates. Clamp the inclination to (0, π).
416 phi += phi_factor * mouse_motion.delta.x * MOVE_SPEED;
417 theta = f32::clamp(
418 theta + theta_factor * mouse_motion.delta.y * MOVE_SPEED,
419 0.001,
420 PI - 0.001,
421 );
422
423 // Convert spherical coordinates back to Cartesian coordinates.
424 transform.translation =
425 radius * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
426
427 // Look at the center, but preserve the previous roll angle.
428 let roll = transform.rotation.to_euler(EulerRot::YXZ).2;
429 transform.look_at(Vec3::ZERO, Vec3::Y);
430 let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
431 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
432 }
433}More examples
461fn process_move_input(
462 mut selections: Query<(&mut Transform, &Selection)>,
463 mouse_buttons: Res<ButtonInput<MouseButton>>,
464 mouse_motion: Res<AccumulatedMouseMotion>,
465 app_status: Res<AppStatus>,
466) {
467 // Only process drags when movement is selected.
468 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Move {
469 return;
470 }
471
472 for (mut transform, selection) in &mut selections {
473 if app_status.selection != *selection {
474 continue;
475 }
476
477 // use simple movement for the point light
478 if *selection == Selection::PointLight {
479 transform.translation +=
480 (mouse_motion.delta * Vec2::new(1.0, -1.0) * MOVE_SPEED).extend(0.0);
481 return;
482 }
483
484 let position = transform.translation;
485
486 // Convert to spherical coordinates.
487 let radius = position.length();
488 let mut theta = acos(position.y / radius);
489 let mut phi = position.z.signum() * acos(position.x * position.xz().length_recip());
490
491 // Camera movement is the inverse of object movement.
492 let (phi_factor, theta_factor) = match *selection {
493 Selection::Camera => (1.0, -1.0),
494 _ => (-1.0, 1.0),
495 };
496
497 // Adjust the spherical coordinates. Clamp the inclination to (0, π).
498 phi += phi_factor * mouse_motion.delta.x * MOVE_SPEED;
499 theta = f32::clamp(
500 theta + theta_factor * mouse_motion.delta.y * MOVE_SPEED,
501 0.001,
502 PI - 0.001,
503 );
504
505 // Convert spherical coordinates back to Cartesian coordinates.
506 transform.translation =
507 radius * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
508
509 // Look at the center, but preserve the previous roll angle.
510 let roll = transform.rotation.to_euler(EulerRot::YXZ).2;
511 transform.look_at(Vec3::ZERO, Vec3::Y);
512 let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
513 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
514 }
515}497fn handle_camera_mode_change(
498 mut commands: Commands,
499 cameras_query: Query<(Entity, &Transform), With<Camera3d>>,
500 sphere_query: Query<&Transform, (With<ReflectiveSphere>, Without<Camera3d>)>,
501 mut help_text_query: Query<&mut Text, With<HelpText>>,
502 mut windows_query: Query<&mut CursorOptions>,
503 mut app_status: ResMut<AppStatus>,
504 mut messages: MessageReader<WidgetClickEvent<CameraMode>>,
505) {
506 let Some(sphere_transform) = sphere_query.iter().next() else {
507 return;
508 };
509
510 let mut any_changes = false;
511 for message in messages.read() {
512 app_status.camera_mode = **message;
513
514 match **message {
515 CameraMode::Orbit => {
516 for (camera_entity, camera_transform) in &cameras_query {
517 // Convert from Cartesian coordinates back to spherical
518 // coordinates.
519 let relative_camera_position =
520 camera_transform.translation - sphere_transform.translation;
521 let radius = relative_camera_position.length();
522 let inclination = atan2(
523 relative_camera_position.xz().length() / radius,
524 relative_camera_position.y / radius,
525 );
526 let azimuth = atan2(
527 relative_camera_position.z * relative_camera_position.xz().length_recip(),
528 relative_camera_position.x * relative_camera_position.xz().length_recip(),
529 );
530
531 commands
532 .entity(camera_entity)
533 .remove::<FreeCamera>()
534 .insert(OrbitCamera {
535 radius,
536 inclination,
537 azimuth,
538 });
539 }
540 }
541
542 CameraMode::Free => {
543 for (camera_entity, _) in &cameras_query {
544 commands
545 .entity(camera_entity)
546 .remove::<OrbitCamera>()
547 .insert(FreeCamera::default());
548 }
549 }
550 }
551
552 any_changes = true;
553 }
554
555 if any_changes {
556 set_help_text(&app_status, &mut help_text_query);
557
558 // Reset the cursor grab mode, because the free camera controller may
559 // have enabled it, and we don't want the cursor to disappear.
560 for mut cursor_options in &mut windows_query {
561 cursor_options.grab_mode = CursorGrabMode::None;
562 cursor_options.visible = true;
563 }
564 }
565}Sourcepub fn distance(self, rhs: Vec2) -> f32
pub fn distance(self, rhs: Vec2) -> f32
Computes the Euclidean distance between two points in space.
Examples found in repository?
220fn update_cursor_hit_test(
221 cursor_world_pos: Res<CursorWorldPos>,
222 primary_window: Single<(&Window, &mut CursorOptions), With<PrimaryWindow>>,
223 bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
224) {
225 let (window, mut cursor_options) = primary_window.into_inner();
226 // If the window has decorations (e.g. a border) then it should be clickable
227 if window.decorations {
228 cursor_options.hit_test = true;
229 return;
230 }
231
232 // If the cursor is not within the window we don't need to update whether the window is clickable or not
233 let Some(cursor_world_pos) = cursor_world_pos.0 else {
234 return;
235 };
236
237 // If the cursor is within the radius of the Bevy logo make the window clickable otherwise the window is not clickable
238 cursor_options.hit_test = bevy_logo_transform
239 .translation
240 .truncate()
241 .distance(cursor_world_pos)
242 < BEVY_LOGO_RADIUS;
243}
244
245/// Start the drag operation and record the offset we started dragging from
246fn start_drag(
247 mut commands: Commands,
248 cursor_world_pos: Res<CursorWorldPos>,
249 bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
250) {
251 // If the cursor is not within the primary window skip this system
252 let Some(cursor_world_pos) = cursor_world_pos.0 else {
253 return;
254 };
255
256 // Get the offset from the cursor to the Bevy logo sprite
257 let drag_offset = bevy_logo_transform.translation.truncate() - cursor_world_pos;
258
259 // If the cursor is within the Bevy logo radius start the drag operation and remember the offset of the cursor from the origin
260 if drag_offset.length() < BEVY_LOGO_RADIUS {
261 commands.insert_resource(DragOperation(drag_offset));
262 }
263}
264
265/// Stop the current drag operation
266fn end_drag(mut commands: Commands) {
267 commands.remove_resource::<DragOperation>();
268}
269
270/// Drag the Bevy logo
271fn drag(
272 drag_offset: Res<DragOperation>,
273 cursor_world_pos: Res<CursorWorldPos>,
274 time: Res<Time>,
275 mut bevy_transform: Single<&mut Transform, With<BevyLogo>>,
276 mut q_pupils: Query<&mut Pupil>,
277) {
278 // If the cursor is not within the primary window skip this system
279 let Some(cursor_world_pos) = cursor_world_pos.0 else {
280 return;
281 };
282
283 // Calculate the new translation of the Bevy logo based on cursor and drag offset
284 let new_translation = cursor_world_pos + drag_offset.0;
285
286 // Calculate how fast we are dragging the Bevy logo (unit/second)
287 let drag_velocity =
288 (new_translation - bevy_transform.translation.truncate()) / time.delta_secs();
289
290 // Update the translation of Bevy logo transform to new translation
291 bevy_transform.translation = new_translation.extend(bevy_transform.translation.z);
292
293 // Add the cursor drag velocity in the opposite direction to each pupil.
294 // Remember pupils are using local coordinates to move. So when the Bevy logo moves right they need to move left to
295 // simulate inertia, otherwise they will move fixed to the parent.
296 for mut pupil in &mut q_pupils {
297 pupil.velocity -= drag_velocity;
298 }
299}
300
301/// Quit when the user right clicks the Bevy logo
302fn quit(
303 cursor_world_pos: Res<CursorWorldPos>,
304 mut app_exit: MessageWriter<AppExit>,
305 bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
306) {
307 // If the cursor is not within the primary window skip this system
308 let Some(cursor_world_pos) = cursor_world_pos.0 else {
309 return;
310 };
311
312 // If the cursor is within the Bevy logo radius send the [`AppExit`] event to quit the app
313 if bevy_logo_transform
314 .translation
315 .truncate()
316 .distance(cursor_world_pos)
317 < BEVY_LOGO_RADIUS
318 {
319 app_exit.write(AppExit::Success);
320 }
321}More examples
34fn click(
35 click: On<Pointer<Click>>,
36 mut commands: Commands,
37 squares: Query<(Entity, &Transform), With<BlinkySquare>>,
38 cameras: Query<(&Camera, &GlobalTransform)>,
39) {
40 let (camera, camera_transform) = cameras.single().unwrap();
41 let mut delayed = commands.delayed();
42 for (entity, transform) in squares.iter() {
43 // convert the pointer position to world position
44 let mouse_world_pos = camera
45 .viewport_to_world_2d(camera_transform, click.pointer_location.position)
46 .unwrap();
47
48 // delay the blinkiness by distance to cursor
49 let dist = mouse_world_pos.distance(transform.translation.truncate());
50 let delay = dist / 1000.0;
51 delayed
52 .secs(delay)
53 .entity(entity)
54 .insert(Sprite::from_color(Color::WHITE, SQUARE_SIZE));
55 delayed
56 .secs(delay + 0.1)
57 .entity(entity)
58 .insert(Sprite::from_color(Color::BLACK, SQUARE_SIZE));
59 }
60}8fn main() {
9 App::new()
10 .add_plugins(DefaultPlugins)
11 .init_resource::<SpatialIndex>()
12 .init_resource::<ExplosionsEnabled>()
13 .add_systems(Startup, setup)
14 .add_systems(Update, (draw_shapes, handle_click, toggle_explosions))
15 // Observers are systems that run when an event is "triggered". This observer runs whenever
16 // `ExplodeMines` is triggered.
17 //
18 // Observers can have run conditions, just like systems! This observer only runs when
19 // explosions are enabled. Press Space to toggle.
20 .add_observer(
21 (|explode_mines: On<ExplodeMines>,
22 mines: Query<&Mine>,
23 index: Res<SpatialIndex>,
24 mut commands: Commands| {
25 // Access resources
26 for entity in index.get_nearby(explode_mines.pos) {
27 // Run queries
28 let mine = mines.get(entity).unwrap();
29 if mine.pos.distance(explode_mines.pos) < mine.size + explode_mines.radius {
30 // And queue commands, including triggering additional events
31 // Here we trigger the `Explode` event for entity `e`
32 commands.trigger(Explode { entity });
33 }
34 }
35 })
36 .run_if(|enabled: Res<ExplosionsEnabled>| enabled.0),
37 )
38 // This observer runs whenever the `Mine` component is added to an entity, and places it in a simple spatial index.
39 .add_observer(on_add_mine)
40 // This observer runs whenever the `Mine` component is removed from an entity (including despawning it)
41 // and removes it from the spatial index.
42 .add_observer(on_remove_mine)
43 .run();
44}38fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
39 commands.spawn(Camera2d);
40
41 // Create an image that we are going to draw into
42 let mut image = Image::new_fill(
43 // 2D image of size 256x256
44 Extent3d {
45 width: IMAGE_WIDTH,
46 height: IMAGE_HEIGHT,
47 depth_or_array_layers: 1,
48 },
49 TextureDimension::D2,
50 // Initialize it with a beige color
51 &(css::BEIGE.to_u8_array()),
52 // Use the same encoding as the color we set
53 TextureFormat::Rgba8UnormSrgb,
54 RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
55 );
56
57 // To make it extra fancy, we can set the Alpha of each pixel,
58 // so that it fades out in a circular fashion.
59 for y in 0..IMAGE_HEIGHT {
60 for x in 0..IMAGE_WIDTH {
61 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
62 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
63 let r = Vec2::new(x as f32, y as f32).distance(center);
64 let a = 1.0 - (r / max_radius).clamp(0.0, 1.0);
65
66 // Here we will set the A value by accessing the raw data bytes.
67 // (it is the 4th byte of each pixel, as per our `TextureFormat`)
68
69 // Find our pixel by its coordinates
70 let pixel_bytes = image.pixel_bytes_mut(UVec3::new(x, y, 0)).unwrap();
71 // Convert our f32 to u8
72 pixel_bytes[3] = (a * u8::MAX as f32) as u8;
73 }
74 }
75
76 // Add it to Bevy's assets, so it can be used for rendering
77 // this will give us a handle we can use
78 // (to display it in a sprite, or as part of UI, etc.)
79 let handle = images.add(image);
80
81 // Create a sprite entity using our image
82 commands.spawn(Sprite::from_image(handle.clone()));
83 commands.insert_resource(MyProcGenImage(handle));
84
85 // We're seeding the PRNG here to make this example deterministic for testing purposes.
86 // This isn't strictly required in practical use unless you need your app to be deterministic.
87 let seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
88 commands.insert_resource(SeededRng(seeded_rng));
89}Sourcepub fn distance_squared(self, rhs: Vec2) -> f32
pub fn distance_squared(self, rhs: Vec2) -> f32
Compute the squared euclidean distance between two points in space.
Sourcepub fn div_euclid(self, rhs: Vec2) -> Vec2
pub fn div_euclid(self, rhs: Vec2) -> Vec2
Returns the element-wise quotient of [Euclidean division] of self by rhs.
Sourcepub fn rem_euclid(self, rhs: Vec2) -> Vec2
pub fn rem_euclid(self, rhs: Vec2) -> Vec2
Returns the element-wise remainder of Euclidean division of self by rhs.
Sourcepub fn normalize(self) -> Vec2
pub fn normalize(self) -> Vec2
Returns self normalized to length 1.0.
For valid results, self must be finite and not of length zero, nor very close to zero.
See also Self::try_normalize() and Self::normalize_or_zero().
§Panics
Will panic if the resulting normalized vector is not finite when glam_assert is enabled.
Examples found in repository?
154fn snap_to_player_system(
155 mut query: Query<&mut Transform, (With<SnapToPlayer>, Without<Player>)>,
156 player_transform: Single<&Transform, With<Player>>,
157) {
158 // Get the player translation in 2D
159 let player_translation = player_transform.translation.xy();
160
161 for mut enemy_transform in &mut query {
162 // Get the vector from the enemy ship to the player ship in 2D and normalize it.
163 let to_player = (player_translation - enemy_transform.translation.xy()).normalize();
164
165 // Get the quaternion to rotate from the initial enemy facing direction to the direction
166 // facing the player
167 let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, to_player.extend(0.));
168
169 // Rotate the enemy to face the player
170 enemy_transform.rotation = rotate_to_player;
171 }
172}
173
174/// Demonstrates rotating an enemy ship to face the player ship at a given rotation speed.
175///
176/// This method uses the vector dot product to determine if the enemy is facing the player and
177/// if not, which way to rotate to face the player. The dot product on two unit length vectors
178/// will return a value between -1.0 and +1.0 which tells us the following about the two vectors:
179///
180/// * If the result is 1.0 the vectors are pointing in the same direction, the angle between them is
181/// 0 degrees.
182/// * If the result is 0.0 the vectors are perpendicular, the angle between them is 90 degrees.
183/// * If the result is -1.0 the vectors are parallel but pointing in opposite directions, the angle
184/// between them is 180 degrees.
185/// * If the result is positive the vectors are pointing in roughly the same direction, the angle
186/// between them is greater than 0 and less than 90 degrees.
187/// * If the result is negative the vectors are pointing in roughly opposite directions, the angle
188/// between them is greater than 90 and less than 180 degrees.
189///
190/// It is possible to get the angle by taking the arc cosine (`acos`) of the dot product. It is
191/// often unnecessary to do this though. Beware than `acos` will return `NaN` if the input is less
192/// than -1.0 or greater than 1.0. This can happen even when working with unit vectors due to
193/// floating point precision loss, so it pays to clamp your dot product value before calling
194/// `acos`.
195fn rotate_to_player_system(
196 time: Res<Time>,
197 mut query: Query<(&RotateToPlayer, &mut Transform), Without<Player>>,
198 player_transform: Single<&Transform, With<Player>>,
199) {
200 // Get the player translation in 2D
201 let player_translation = player_transform.translation.xy();
202
203 for (config, mut enemy_transform) in &mut query {
204 // Get the enemy ship forward vector in 2D (already unit length)
205 let enemy_forward = (enemy_transform.rotation * Vec3::Y).xy();
206
207 // Get the vector from the enemy ship to the player ship in 2D and normalize it.
208 let to_player = (player_translation - enemy_transform.translation.xy()).normalize();
209
210 // Get the dot product between the enemy forward vector and the direction to the player.
211 let forward_dot_player = enemy_forward.dot(to_player);
212
213 // If the dot product is approximately 1.0 then the enemy is already facing the player and
214 // we can early out.
215 if (forward_dot_player - 1.0).abs() < f32::EPSILON {
216 continue;
217 }
218
219 // Get the right vector of the enemy ship in 2D (already unit length)
220 let enemy_right = (enemy_transform.rotation * Vec3::X).xy();
221
222 // Get the dot product of the enemy right vector and the direction to the player ship.
223 // If the dot product is negative them we need to rotate counter clockwise, if it is
224 // positive we need to rotate clockwise. Note that `copysign` will still return 1.0 if the
225 // dot product is 0.0 (because the player is directly behind the enemy, so perpendicular
226 // with the right vector).
227 let right_dot_player = enemy_right.dot(to_player);
228
229 // Determine the sign of rotation from the right dot player. We need to negate the sign
230 // here as the 2D bevy co-ordinate system rotates around +Z, which is pointing out of the
231 // screen. Due to the right hand rule, positive rotation around +Z is counter clockwise and
232 // negative is clockwise.
233 let rotation_sign = -f32::copysign(1.0, right_dot_player);
234
235 // Limit rotation so we don't overshoot the target. We need to convert our dot product to
236 // an angle here so we can get an angle of rotation to clamp against.
237 let max_angle = ops::acos(forward_dot_player.clamp(-1.0, 1.0)); // Clamp acos for safety
238
239 // Calculate angle of rotation with limit
240 let rotation_angle =
241 rotation_sign * (config.rotation_speed * time.delta_secs()).min(max_angle);
242
243 // Rotate the enemy to face the player
244 enemy_transform.rotate_z(rotation_angle);
245 }
246}More examples
570 fn perimeter(&self) -> Vec<PerimeterSegment> {
571 let resolution = self.resolution as u32;
572 vec![
573 // The left wing of the heart
574 PerimeterSegment::Smooth {
575 // The normals of the first and last vertices of smooth segments have to be specified manually.
576 first_normal: Vec2::X,
577 last_normal: Vec2::new(-1.0, -1.0).normalize(),
578 // These indices are used to index into the `ATTRIBUTE_POSITION` vec of your 2D mesh.
579 indices: (0..resolution).collect(),
580 },
581 // The bottom tip of the heart
582 PerimeterSegment::Flat {
583 indices: vec![resolution - 1, resolution, resolution + 1],
584 },
585 // The right wing of the heart
586 PerimeterSegment::Smooth {
587 first_normal: Vec2::new(1.0, -1.0).normalize(),
588 last_normal: Vec2::NEG_X,
589 indices: (resolution + 1..2 * resolution).chain([0]).collect(),
590 },
591 ]
592 }360fn move_pupils(time: Res<Time>, mut q_pupils: Query<(&mut Pupil, &mut Transform)>) {
361 for (mut pupil, mut transform) in &mut q_pupils {
362 // The wiggle radius is how much the pupil can move within the eye
363 let wiggle_radius = pupil.eye_radius - pupil.pupil_radius;
364 // Store the Z component
365 let z = transform.translation.z;
366 // Truncate the Z component to make the calculations be on [`Vec2`]
367 let mut translation = transform.translation.truncate();
368 // Decay the pupil velocity
369 pupil.velocity *= ops::powf(0.04f32, time.delta_secs());
370 // Move the pupil
371 translation += pupil.velocity * time.delta_secs();
372 // If the pupil hit the outside border of the eye, limit the translation to be within the wiggle radius and invert the velocity.
373 // This is not physically accurate but it's good enough for the googly eyes effect.
374 if translation.length() > wiggle_radius {
375 translation = translation.normalize() * wiggle_radius;
376 // Invert and decrease the velocity of the pupil when it bounces
377 pupil.velocity *= -0.75;
378 }
379 // Update the entity transform with the new translation after reading the Z component
380 transform.translation = translation.extend(z);
381 }
382}172fn setup(
173 mut commands: Commands,
174 mut meshes: ResMut<Assets<Mesh>>,
175 mut materials: ResMut<Assets<ColorMaterial>>,
176 asset_server: Res<AssetServer>,
177) {
178 // Camera
179 commands.spawn(Camera2d);
180
181 // Sound
182 let ball_collision_sound = asset_server.load("sounds/breakout_collision.ogg");
183 commands.insert_resource(CollisionSound(ball_collision_sound));
184
185 // Paddle
186 let paddle_y = BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR;
187
188 commands.spawn((
189 Sprite::from_color(PADDLE_COLOR, Vec2::ONE),
190 Transform {
191 translation: Vec3::new(0.0, paddle_y, 0.0),
192 scale: PADDLE_SIZE.extend(1.0),
193 ..default()
194 },
195 Paddle,
196 Collider,
197 ));
198
199 // Ball
200 commands.spawn((
201 Mesh2d(meshes.add(Circle::default())),
202 MeshMaterial2d(materials.add(BALL_COLOR)),
203 Transform::from_translation(BALL_STARTING_POSITION)
204 .with_scale(Vec2::splat(BALL_DIAMETER).extend(1.)),
205 Ball,
206 Velocity(INITIAL_BALL_DIRECTION.normalize() * BALL_SPEED),
207 ));
208
209 // Scoreboard
210 commands.spawn((
211 Text::new("Score: "),
212 TextFont {
213 font_size: SCOREBOARD_FONT_SIZE,
214 ..default()
215 },
216 TextColor(TEXT_COLOR),
217 ScoreboardUi,
218 Node {
219 position_type: PositionType::Absolute,
220 top: SCOREBOARD_TEXT_PADDING,
221 left: SCOREBOARD_TEXT_PADDING,
222 ..default()
223 },
224 children![(
225 TextSpan::default(),
226 TextFont {
227 font_size: SCOREBOARD_FONT_SIZE,
228 ..default()
229 },
230 TextColor(SCORE_COLOR),
231 )],
232 ));
233
234 // Walls
235 commands.spawn(Wall::new(WallLocation::Left));
236 commands.spawn(Wall::new(WallLocation::Right));
237 commands.spawn(Wall::new(WallLocation::Bottom));
238 commands.spawn(Wall::new(WallLocation::Top));
239
240 // Bricks
241 let total_width_of_bricks = (RIGHT_WALL - LEFT_WALL) - 2. * GAP_BETWEEN_BRICKS_AND_SIDES;
242 let bottom_edge_of_bricks = paddle_y + GAP_BETWEEN_PADDLE_AND_BRICKS;
243 let total_height_of_bricks = TOP_WALL - bottom_edge_of_bricks - GAP_BETWEEN_BRICKS_AND_CEILING;
244
245 assert!(total_width_of_bricks > 0.0);
246 assert!(total_height_of_bricks > 0.0);
247
248 // Given the space available, compute how many rows and columns of bricks we can fit
249 let n_columns = (total_width_of_bricks / (BRICK_SIZE.x + GAP_BETWEEN_BRICKS)).floor() as usize;
250 let n_rows = (total_height_of_bricks / (BRICK_SIZE.y + GAP_BETWEEN_BRICKS)).floor() as usize;
251 let n_vertical_gaps = n_columns - 1;
252
253 // Because we need to round the number of columns,
254 // the space on the top and sides of the bricks only captures a lower bound, not an exact value
255 let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0;
256 let left_edge_of_bricks = center_of_bricks
257 // Space taken up by the bricks
258 - (n_columns as f32 / 2.0 * BRICK_SIZE.x)
259 // Space taken up by the gaps
260 - n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS;
261
262 // In Bevy, the `translation` of an entity describes the center point,
263 // not its bottom-left corner
264 let offset_x = left_edge_of_bricks + BRICK_SIZE.x / 2.;
265 let offset_y = bottom_edge_of_bricks + BRICK_SIZE.y / 2.;
266
267 for row in 0..n_rows {
268 for column in 0..n_columns {
269 let brick_position = Vec2::new(
270 offset_x + column as f32 * (BRICK_SIZE.x + GAP_BETWEEN_BRICKS),
271 offset_y + row as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS),
272 );
273
274 // brick
275 commands.spawn((
276 Sprite {
277 color: BRICK_COLOR,
278 ..default()
279 },
280 Transform {
281 translation: brick_position.extend(0.0),
282 scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0),
283 ..default()
284 },
285 Brick,
286 Collider,
287 ));
288 }
289 }
290}Sourcepub fn try_normalize(self) -> Option<Vec2>
pub fn try_normalize(self) -> Option<Vec2>
Returns self normalized to length 1.0 if possible, else returns None.
In particular, if the input is zero (or very close to zero), or non-finite,
the result of this operation will be None.
See also Self::normalize_or_zero().
Sourcepub fn normalize_or(self, fallback: Vec2) -> Vec2
pub fn normalize_or(self, fallback: Vec2) -> Vec2
Returns self normalized to length 1.0 if possible, else returns a
fallback value.
In particular, if the input is zero (or very close to zero), or non-finite, the result of this operation will be the fallback value.
See also Self::try_normalize().
Sourcepub fn normalize_or_zero(self) -> Vec2
pub fn normalize_or_zero(self) -> Vec2
Returns self normalized to length 1.0 if possible, else returns zero.
In particular, if the input is zero (or very close to zero), or non-finite, the result of this operation will be zero.
See also Self::try_normalize().
Examples found in repository?
88fn move_player(
89 mut player: Single<&mut Transform, With<Player>>,
90 time: Res<Time>,
91 kb_input: Res<ButtonInput<KeyCode>>,
92) {
93 let mut direction = Vec2::ZERO;
94
95 if kb_input.pressed(KeyCode::KeyW) {
96 direction.y += 1.;
97 }
98
99 if kb_input.pressed(KeyCode::KeyS) {
100 direction.y -= 1.;
101 }
102
103 if kb_input.pressed(KeyCode::KeyA) {
104 direction.x -= 1.;
105 }
106
107 if kb_input.pressed(KeyCode::KeyD) {
108 direction.x += 1.;
109 }
110
111 // Progressively update the player's position over time. Normalize the
112 // direction vector to prevent it from exceeding a magnitude of 1 when
113 // moving diagonally.
114 let move_delta = direction.normalize_or_zero() * PLAYER_SPEED * time.delta_secs();
115 player.translation += move_delta.extend(0.);
116}More examples
227fn resize_image(
228 image_mesh: Query<(&MeshMaterial3d<StandardMaterial>, &Mesh3d), With<HDRViewer>>,
229 materials: Res<Assets<StandardMaterial>>,
230 mut meshes: ResMut<Assets<Mesh>>,
231 images: Res<Assets<Image>>,
232 mut image_event_reader: MessageReader<AssetEvent<Image>>,
233) {
234 for event in image_event_reader.read() {
235 let (AssetEvent::Added { id } | AssetEvent::Modified { id }) = event else {
236 continue;
237 };
238
239 for (mat_h, mesh_h) in &image_mesh {
240 let Some(mat) = materials.get(mat_h) else {
241 continue;
242 };
243
244 let Some(ref base_color_texture) = mat.base_color_texture else {
245 continue;
246 };
247
248 if *id != base_color_texture.id() {
249 continue;
250 };
251
252 let Some(image_changed) = images.get(*id) else {
253 continue;
254 };
255
256 let size = image_changed.size_f32().normalize_or_zero() * 1.4;
257 // Resize Mesh
258 let quad = Mesh::from(Rectangle::from_size(size));
259 meshes.insert(mesh_h, quad).unwrap();
260 }
261 }
262}Sourcepub fn normalize_and_length(self) -> (Vec2, f32)
pub fn normalize_and_length(self) -> (Vec2, f32)
Returns self normalized to length 1.0 and the length of self.
If self is zero length then (Self::X, 0.0) is returned.
Sourcepub fn is_normalized(self) -> bool
pub fn is_normalized(self) -> bool
Returns whether self is length 1.0 or not.
Uses a precision threshold of approximately 1e-4.
Sourcepub fn project_onto(self, rhs: Vec2) -> Vec2
pub fn project_onto(self, rhs: Vec2) -> Vec2
Returns the vector projection of self onto rhs.
rhs must be of non-zero length.
§Panics
Will panic if rhs is zero length when glam_assert is enabled.
Sourcepub fn reject_from(self, rhs: Vec2) -> Vec2
pub fn reject_from(self, rhs: Vec2) -> Vec2
Returns the vector rejection of self from rhs.
The vector rejection is the vector perpendicular to the projection of self onto
rhs, in rhs words the result of self - self.project_onto(rhs).
rhs must be of non-zero length.
§Panics
Will panic if rhs has a length of zero when glam_assert is enabled.
Sourcepub fn project_onto_normalized(self, rhs: Vec2) -> Vec2
pub fn project_onto_normalized(self, rhs: Vec2) -> Vec2
Returns the vector projection of self onto rhs.
rhs must be normalized.
§Panics
Will panic if rhs is not normalized when glam_assert is enabled.
Sourcepub fn reject_from_normalized(self, rhs: Vec2) -> Vec2
pub fn reject_from_normalized(self, rhs: Vec2) -> Vec2
Returns the vector rejection of self from rhs.
The vector rejection is the vector perpendicular to the projection of self onto
rhs, in rhs words the result of self - self.project_onto(rhs).
rhs must be normalized.
§Panics
Will panic if rhs is not normalized when glam_assert is enabled.
Sourcepub fn round(self) -> Vec2
pub fn round(self) -> Vec2
Returns a vector containing the nearest integer to a number for each element of self.
Round half-way cases away from 0.0.
Sourcepub fn floor(self) -> Vec2
pub fn floor(self) -> Vec2
Returns a vector containing the largest integer less than or equal to a number for each
element of self.
Examples found in repository?
208fn try_plot(
209 event: On<TryPlot>,
210 sprite: Query<(&Sprite, &Anchor, &GlobalTransform), With<SpriteToSave>>,
211 camera: Single<(&Camera, &GlobalTransform)>,
212 texture_atlases: Res<Assets<TextureAtlasLayout>>,
213 draw_color: Res<DrawColor>,
214 mut images: ResMut<Assets<Image>>,
215) {
216 let Ok((sprite, anchor, sprite_transform)) = sprite.get(event.entity) else {
217 return;
218 };
219 let (camera, camera_transform) = camera.into_inner();
220 let Ok(world_position) = camera.viewport_to_world_2d(camera_transform, event.location.position)
221 else {
222 return;
223 };
224 let relative_to_sprite = sprite_transform
225 .affine()
226 .inverse()
227 .transform_point3(world_position.extend(0.0));
228 let Ok(pixel_space) = sprite.compute_pixel_space_point(
229 relative_to_sprite.xy(),
230 *anchor,
231 &images,
232 &texture_atlases,
233 ) else {
234 return;
235 };
236 let pixel_coordinates = pixel_space.floor().as_uvec2();
237 let mut image = images.get_mut(&sprite.image).unwrap();
238 // For an actual drawing app, you'd at least draw a line from the last point, but this is
239 // simpler.
240 image
241 .set_color_at(pixel_coordinates.x, pixel_coordinates.y, draw_color.0)
242 .unwrap();
243}Sourcepub fn ceil(self) -> Vec2
pub fn ceil(self) -> Vec2
Returns a vector containing the smallest integer greater than or equal to a number for
each element of self.
Examples found in repository?
161fn setup(
162 mut commands: Commands,
163 args: Res<Args>,
164 mesh_assets: ResMut<Assets<Mesh>>,
165 material_assets: ResMut<Assets<StandardMaterial>>,
166 images: ResMut<Assets<Image>>,
167) {
168 warn!(include_str!("warning_string.txt"));
169
170 let args = args.into_inner();
171 let images = images.into_inner();
172 let material_assets = material_assets.into_inner();
173 let mesh_assets = mesh_assets.into_inner();
174
175 let meshes = init_meshes(args, mesh_assets);
176
177 let material_textures = init_textures(args, images);
178 let materials = init_materials(args, &material_textures, material_assets);
179
180 // We're seeding the PRNG here to make this example deterministic for testing purposes.
181 // This isn't strictly required in practical use unless you need your app to be deterministic.
182 let mut material_rng = ChaCha8Rng::seed_from_u64(42);
183 match args.layout {
184 Layout::Sphere => {
185 // NOTE: This pattern is good for testing performance of culling as it provides roughly
186 // the same number of visible meshes regardless of the viewing angle.
187 let n_points: usize = args.instance_count;
188 // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
189 let radius = WIDTH as f64 * 2.5;
190 let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt());
191 for i in 0..n_points {
192 let spherical_polar_theta_phi =
193 fibonacci_spiral_on_sphere(golden_ratio, i, n_points);
194 let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
195 let (mesh, transform) = meshes.choose(&mut material_rng).unwrap();
196 commands
197 .spawn((
198 Mesh3d(mesh.clone()),
199 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
200 Transform::from_translation((radius * unit_sphere_p).as_vec3())
201 .looking_at(Vec3::ZERO, Vec3::Y)
202 .mul_transform(*transform),
203 ))
204 .insert_if(NoFrustumCulling, || args.no_frustum_culling)
205 .insert_if(NoAutomaticBatching, || args.no_automatic_batching)
206 .insert_if(NoCpuCulling, || args.no_cpu_culling);
207 }
208
209 // camera
210 let mut camera = commands.spawn(Camera3d::default());
211 if args.no_indirect_drawing {
212 camera.insert(NoIndirectDrawing);
213 }
214 if args.no_cpu_culling {
215 camera.insert(NoCpuCulling);
216 }
217 if args.motion_blur {
218 camera.insert((
219 MotionBlur {
220 // Use an unrealistically large shutter angle so that motion blur is clearly visible.
221 shutter_angle: 3.0,
222 ..Default::default()
223 },
224 // MSAA and MotionBlur are not compatible on WebGL.
225 #[cfg(all(
226 feature = "webgl2",
227 target_arch = "wasm32",
228 not(feature = "webgpu")
229 ))]
230 Msaa::Off,
231 ));
232 }
233
234 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
235 commands.spawn((
236 Mesh3d(mesh_assets.add(Cuboid::from_size(Vec3::splat(radius as f32 * 2.2)))),
237 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
238 Transform::from_scale(-Vec3::ONE),
239 NotShadowCaster,
240 ));
241 }
242 Layout::Cube => {
243 // NOTE: This pattern is good for demonstrating that frustum culling is working correctly
244 // as the number of visible meshes rises and falls depending on the viewing angle.
245 let scale = 2.5;
246
247 // Scale the width and height by the same factor so that we have the
248 // right number of instances.
249 // Because of the moiré pattern check and the fact that we're
250 // spawning 4 instances per trip around the inner loop below, we're
251 // solving the following equation for the factor variable:
252 //
253 // 4 * (9/10 * factor * width * 9/10 * factor * height) = count
254 //
255 // The solution is the value below.
256 let factor = (5.0 / 9.0) * sqrt(args.instance_count as f32)
257 / (sqrt(HEIGHT as f32) * sqrt(WIDTH as f32));
258 let dimensions = (vec2(WIDTH as f32, HEIGHT as f32) * factor)
259 .ceil()
260 .as_uvec2();
261
262 for x in 0..dimensions.x {
263 for y in 0..dimensions.y {
264 // introduce spaces to break any kind of moiré pattern
265 if x % 10 == 0 || y % 10 == 0 {
266 continue;
267 }
268 // cube
269 commands
270 .spawn((
271 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
272 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
273 Transform::from_xyz((x as f32) * scale, (y as f32) * scale, 0.0),
274 ))
275 .insert_if(NoCpuCulling, || args.no_cpu_culling);
276 commands
277 .spawn((
278 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
279 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
280 Transform::from_xyz(
281 (x as f32) * scale,
282 dimensions.y as f32 * scale,
283 (y as f32) * scale,
284 ),
285 ))
286 .insert_if(NoCpuCulling, || args.no_cpu_culling);
287 commands
288 .spawn((
289 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
290 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
291 Transform::from_xyz((x as f32) * scale, 0.0, (y as f32) * scale),
292 ))
293 .insert_if(NoCpuCulling, || args.no_cpu_culling);
294 commands
295 .spawn((
296 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
297 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
298 Transform::from_xyz(0.0, (x as f32) * scale, (y as f32) * scale),
299 ))
300 .insert_if(NoCpuCulling, || args.no_cpu_culling);
301 }
302 }
303 // camera
304 let center = 0.5
305 * scale
306 * Vec3::new(
307 dimensions.x as f32,
308 dimensions.y as f32,
309 dimensions.x as f32,
310 );
311 commands.spawn((Camera3d::default(), Transform::from_translation(center)));
312 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
313 commands.spawn((
314 Mesh3d(mesh_assets.add(Cuboid::from_size(2.0 * 1.1 * center))),
315 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
316 Transform::from_scale(-Vec3::ONE).with_translation(center),
317 NotShadowCaster,
318 ));
319 }
320 Layout::Dense => {
321 // NOTE: This pattern is good for demonstrating a dense configuration of cubes
322 // overlapping each other, all within the camera frustum.
323 let count = args.instance_count;
324 let size = cbrt(count as f32).round();
325 let gap = 1.25;
326
327 for i in 0..count {
328 let x = i as f32 % size;
329 let y = (i as f32 / size) % size;
330 let z = i as f32 / (size * size);
331 let pos = Vec3::new(x * gap, y * gap, z * gap);
332 commands
333 .spawn((
334 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
335 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
336 Transform::from_translation(pos),
337 ))
338 .insert_if(NoCpuCulling, || args.no_cpu_culling);
339 }
340
341 // camera
342 commands.spawn((
343 Camera3d::default(),
344 Transform::from_xyz(100.0, 90.0, 100.0)
345 .looking_at(Vec3::new(0.0, -10.0, 0.0), Vec3::Y),
346 ));
347 }
348 }
349
350 commands.spawn((
351 DirectionalLight {
352 shadow_maps_enabled: args.shadows,
353 ..default()
354 },
355 Transform::IDENTITY.looking_at(Vec3::new(0.0, -1.0, -1.0), Vec3::Y),
356 ));
357}Sourcepub fn trunc(self) -> Vec2
pub fn trunc(self) -> Vec2
Returns a vector containing the integer part each element of self. This means numbers are
always truncated towards zero.
Sourcepub fn step(self, rhs: Vec2) -> Vec2
pub fn step(self, rhs: Vec2) -> Vec2
Returns a vector containing 0.0 if rhs < self and 1.0 otherwise.
Similar to glsl’s step(edge, x), which translates into edge.step(x)
Sourcepub fn saturate(self) -> Vec2
pub fn saturate(self) -> Vec2
Returns a vector containing all elements of self clamped to the range of [0, 1].
Sourcepub fn fract(self) -> Vec2
pub fn fract(self) -> Vec2
Returns a vector containing the fractional part of the vector as self - self.trunc().
Note that this differs from the GLSL implementation of fract which returns
self - self.floor().
Note that this is fast but not precise for large numbers.
Sourcepub fn fract_gl(self) -> Vec2
pub fn fract_gl(self) -> Vec2
Returns a vector containing the fractional part of the vector as self - self.floor().
Note that this differs from the Rust implementation of fract which returns
self - self.trunc().
Note that this is fast but not precise for large numbers.
Sourcepub fn exp(self) -> Vec2
pub fn exp(self) -> Vec2
Returns a vector containing e^self (the exponential function) for each element of
self.
Sourcepub fn ln(self) -> Vec2
pub fn ln(self) -> Vec2
Returns a vector containing the natural logarithm for each element of self.
This returns NaN when the element is negative and negative infinity when the element is zero.
Sourcepub fn log2(self) -> Vec2
pub fn log2(self) -> Vec2
Returns a vector containing the base 2 logarithm for each element of self.
This returns NaN when the element is negative and negative infinity when the element is zero.
Sourcepub fn powf(self, n: f32) -> Vec2
pub fn powf(self, n: f32) -> Vec2
Returns a vector containing each element of self raised to the power of n.
Sourcepub fn sqrt(self) -> Vec2
pub fn sqrt(self) -> Vec2
Returns a vector containing the square root for each element of self.
This returns NaN when the element is negative.
Sourcepub fn sin_cos(self) -> (Vec2, Vec2)
pub fn sin_cos(self) -> (Vec2, Vec2)
Returns a tuple of two vectors containing the sine and cosine for each element of self.
Sourcepub fn recip(self) -> Vec2
pub fn recip(self) -> Vec2
Returns a vector containing the reciprocal 1.0/n of each element of self.
Sourcepub fn lerp(self, rhs: Vec2, s: f32) -> Vec2
pub fn lerp(self, rhs: Vec2, s: f32) -> Vec2
Performs a linear interpolation between self and rhs based on the value s.
When s is 0.0, the result will be equal to self. When s is 1.0, the result
will be equal to rhs. When s is outside of range [0, 1], the result is linearly
extrapolated.
Sourcepub fn move_towards(self, rhs: Vec2, d: f32) -> Vec2
pub fn move_towards(self, rhs: Vec2, d: f32) -> Vec2
Moves towards rhs based on the value d.
When d is 0.0, the result will be equal to self. When d is equal to
self.distance(rhs), the result will be equal to rhs. Will not go past rhs.
Sourcepub fn midpoint(self, rhs: Vec2) -> Vec2
pub fn midpoint(self, rhs: Vec2) -> Vec2
Calculates the midpoint between self and rhs.
The midpoint is the average of, or halfway point between, two vectors.
a.midpoint(b) should yield the same result as a.lerp(b, 0.5)
while being slightly cheaper to compute.
Sourcepub fn abs_diff_eq(self, rhs: Vec2, max_abs_diff: f32) -> bool
pub fn abs_diff_eq(self, rhs: Vec2, max_abs_diff: f32) -> bool
Returns true if the absolute difference of all elements between self and rhs is
less than or equal to max_abs_diff.
This can be used to compare if two vectors contain similar elements. It works best when
comparing with a known value. The max_abs_diff that should be used used depends on
the values being compared against.
For more see comparing floating point numbers.
Sourcepub fn clamp_length(self, min: f32, max: f32) -> Vec2
pub fn clamp_length(self, min: f32, max: f32) -> Vec2
Returns a vector with a length no less than min and no more than max.
§Panics
Will panic if min is greater than max, or if either min or max is negative, when glam_assert is enabled.
Sourcepub fn clamp_length_max(self, max: f32) -> Vec2
pub fn clamp_length_max(self, max: f32) -> Vec2
Returns a vector with a length no more than max.
§Panics
Will panic if max is negative when glam_assert is enabled.
Sourcepub fn clamp_length_min(self, min: f32) -> Vec2
pub fn clamp_length_min(self, min: f32) -> Vec2
Returns a vector with a length no less than min.
§Panics
Will panic if min is negative when glam_assert is enabled.
Sourcepub fn mul_add(self, a: Vec2, b: Vec2) -> Vec2
pub fn mul_add(self, a: Vec2, b: Vec2) -> Vec2
Fused multiply-add. Computes (self * a) + b element-wise with only one rounding
error, yielding a more accurate result than an unfused multiply-add.
Using mul_add may be more performant than an unfused multiply-add if the target
architecture has a dedicated fma CPU instruction. However, this is not always true,
and will be heavily dependant on designing algorithms with specific target hardware in
mind.
Sourcepub fn reflect(self, normal: Vec2) -> Vec2
pub fn reflect(self, normal: Vec2) -> Vec2
Returns the reflection vector for a given incident vector self and surface normal
normal.
normal must be normalized.
§Panics
Will panic if normal is not normalized when glam_assert is enabled.
Sourcepub fn refract(self, normal: Vec2, eta: f32) -> Vec2
pub fn refract(self, normal: Vec2, eta: f32) -> Vec2
Returns the refraction direction for a given incident vector self, surface normal
normal and ratio of indices of refraction, eta. When total internal reflection occurs,
a zero vector will be returned.
self and normal must be normalized.
§Panics
Will panic if self or normal is not normalized when glam_assert is enabled.
Sourcepub fn from_angle(angle: f32) -> Vec2
pub fn from_angle(angle: f32) -> Vec2
Creates a 2D vector containing [angle.cos(), angle.sin()]. This can be used in
conjunction with the rotate() method, e.g.
Vec2::from_angle(PI).rotate(Vec2::Y) will create the vector [-1, 0]
and rotate Vec2::Y around it returning -Vec2::Y.
Examples found in repository?
343fn rotate_camera(
344 time: Res<Time>,
345 mut camera_query: Query<&mut Transform, With<Camera3d>>,
346 app_status: Res<AppStatus>,
347) {
348 if !app_status.rotating {
349 return;
350 }
351
352 for mut transform in camera_query.iter_mut() {
353 transform.translation = Vec2::from_angle(time.delta_secs() * PI / 5.0)
354 .rotate(transform.translation.xz())
355 .extend(transform.translation.y)
356 .xzy();
357 transform.look_at(Vec3::ZERO, Vec3::Y);
358 }
359}More examples
349fn rotate_camera(
350 mut camera_query: Query<&mut Transform, With<Camera3d>>,
351 time: Res<Time>,
352 app_status: Res<AppStatus>,
353) {
354 if !app_status.rotating {
355 return;
356 }
357
358 for mut transform in camera_query.iter_mut() {
359 transform.translation = Vec2::from_angle(ROTATION_SPEED * time.delta_secs())
360 .rotate(transform.translation.xz())
361 .extend(transform.translation.y)
362 .xzy();
363 transform.look_at(Vec3::ZERO, Vec3::Y);
364 }
365}92fn draw(
93 my_handle: Res<MyProcGenImage>,
94 mut images: ResMut<Assets<Image>>,
95 // Used to keep track of where we are
96 mut i: Local<u32>,
97 mut draw_color: Local<Color>,
98 mut seeded_rng: ResMut<SeededRng>,
99) {
100 if *i == 0 {
101 // Generate a random color on first run.
102 *draw_color = Color::linear_rgb(
103 seeded_rng.0.random(),
104 seeded_rng.0.random(),
105 seeded_rng.0.random(),
106 );
107 }
108
109 // Get the image from Bevy's asset storage.
110 let mut image = images.get_mut(&my_handle.0).expect("Image not found");
111
112 // Compute the position of the pixel to draw.
113
114 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
115 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
116 let rot_speed = 0.0123;
117 let period = 0.12345;
118
119 let r = ops::sin(*i as f32 * period) * max_radius;
120 let xy = Vec2::from_angle(*i as f32 * rot_speed) * r + center;
121 let (x, y) = (xy.x as u32, xy.y as u32);
122
123 // Get the old color of that pixel.
124 let old_color = image.get_color_at(x, y).unwrap();
125
126 // If the old color is our current color, change our drawing color.
127 let tolerance = 1.0 / 255.0;
128 if old_color.distance(&draw_color) <= tolerance {
129 *draw_color = Color::linear_rgb(
130 seeded_rng.0.random(),
131 seeded_rng.0.random(),
132 seeded_rng.0.random(),
133 );
134 }
135
136 // Set the new color, but keep old alpha value from image.
137 image
138 .set_color_at(x, y, draw_color.with_alpha(old_color.alpha()))
139 .unwrap();
140
141 *i += 1;
142}281fn spawn_decal(
282 mut commands: Commands,
283 app_status: Res<AppStatus>,
284 app_textures: Res<AppTextures>,
285 time: Res<Time>,
286 mut decal_spawn_timer: Local<Option<Timer>>,
287 mut seeded_rng: ResMut<SeededRng>,
288) {
289 // Tick the decal spawn timer. Check to see if we should spawn a new decal,
290 // and bail out if it's not yet time to.
291 let decal_spawn_timer = decal_spawn_timer
292 .get_or_insert_with(|| Timer::new(Duration::from_millis(1000), TimerMode::Repeating));
293 decal_spawn_timer.tick(time.delta());
294 if !decal_spawn_timer.just_finished() {
295 return;
296 }
297
298 // Generate a random position along the plane.
299 let decal_position = vec3(
300 seeded_rng.0.random_range(-PLANE_HALF_SIZE..PLANE_HALF_SIZE),
301 seeded_rng.0.random_range(-PLANE_HALF_SIZE..PLANE_HALF_SIZE),
302 0.0,
303 );
304
305 // Generate a random size for the decal.
306 let decal_size = seeded_rng.0.random_range(DECAL_MIN_SIZE..DECAL_MAX_SIZE);
307
308 // Generate a random rotation for the decal.
309 let theta = seeded_rng.0.random_range(0.0f32..PI);
310
311 // Now spawn the decal.
312 commands.spawn((
313 // Apply the textures.
314 ClusteredDecal {
315 base_color_texture: Some(app_textures.decal_base_color_texture.clone()),
316 normal_map_texture: Some(app_textures.decal_normal_map_texture.clone()),
317 metallic_roughness_texture: Some(
318 app_textures.decal_metallic_roughness_map_texture.clone(),
319 ),
320 emissive_texture: if app_status.emissive_decals {
321 Some(app_textures.decal_emissive_texture.clone())
322 } else {
323 None
324 },
325 ..ClusteredDecal::default()
326 },
327 // Spawn the decal at the right place. Note that the scale is initially
328 // zero; we'll animate it later.
329 Transform::from_translation(decal_position)
330 .with_scale(Vec3::ZERO)
331 .looking_to(Vec3::Z, Vec3::ZERO.with_xy(Vec2::from_angle(theta))),
332 // Create the component that tracks the animation state.
333 ExampleDecal {
334 size: decal_size,
335 state: ExampleDecalState::AnimatingIn(Timer::new(
336 DECAL_ANIMATE_IN_DURATION,
337 TimerMode::Once,
338 )),
339 },
340 ));
341}41fn draw_example_collection(
42 mut gizmos: Gizmos,
43 mut my_gizmos: Gizmos<MyRoundGizmos>,
44 time: Res<Time>,
45) {
46 let sin_t_scaled = ops::sin(time.elapsed_secs()) * 50.;
47 gizmos.line_2d(Vec2::Y * -sin_t_scaled, Vec2::splat(-80.), RED);
48 gizmos.ray_2d(Vec2::Y * sin_t_scaled, Vec2::splat(80.), LIME);
49
50 gizmos
51 .grid_2d(
52 Isometry2d::IDENTITY,
53 UVec2::new(16, 9),
54 Vec2::new(80., 80.),
55 // Dark gray
56 LinearRgba::gray(0.05),
57 )
58 .outer_edges();
59
60 // Triangle
61 gizmos.linestrip_gradient_2d([
62 (Vec2::Y * 300., BLUE),
63 (Vec2::new(-255., -155.), RED),
64 (Vec2::new(255., -155.), LIME),
65 (Vec2::Y * 300., BLUE),
66 ]);
67
68 gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::splat(650.), BLACK);
69
70 gizmos.cross_2d(Vec2::new(-160., 120.), 12., FUCHSIA);
71
72 let domain = Interval::EVERYWHERE;
73 let curve = FunctionCurve::new(domain, |t| Vec2::new(t, ops::sin(t / 25.0) * 100.0));
74 let resolution = ((ops::sin(time.elapsed_secs()) + 1.0) * 50.0) as usize;
75 let times_and_colors = (0..=resolution)
76 .map(|n| n as f32 / resolution as f32)
77 .map(|t| (t - 0.5) * 600.0)
78 .map(|t| (t, TEAL.mix(&HOT_PINK, (t + 300.0) / 600.0)));
79 gizmos.curve_gradient_2d(curve, times_and_colors);
80
81 my_gizmos
82 .rounded_rect_2d(Isometry2d::IDENTITY, Vec2::splat(630.), BLACK)
83 .corner_radius(ops::cos(time.elapsed_secs() / 3.) * 100.);
84
85 // Circles have 32 line-segments by default.
86 // You may want to increase this for larger circles.
87 my_gizmos
88 .circle_2d(Isometry2d::IDENTITY, 300., NAVY)
89 .resolution(64);
90
91 my_gizmos.ellipse_2d(
92 Rot2::radians(time.elapsed_secs() % TAU),
93 Vec2::new(100., 200.),
94 YELLOW_GREEN,
95 );
96
97 // Arcs default resolution is linearly interpolated between
98 // 1 and 32, using the arc length as scalar.
99 my_gizmos.arc_2d(
100 Rot2::radians(sin_t_scaled / 10.),
101 FRAC_PI_2,
102 310.,
103 ORANGE_RED,
104 );
105 my_gizmos.arc_2d(Isometry2d::IDENTITY, FRAC_PI_2, 80.0, ORANGE_RED);
106 my_gizmos.long_arc_2d_between(Vec2::ZERO, Vec2::X * 20.0, Vec2::Y * 20.0, ORANGE_RED);
107 my_gizmos.short_arc_2d_between(Vec2::ZERO, Vec2::X * 40.0, Vec2::Y * 40.0, ORANGE_RED);
108
109 gizmos.arrow_2d(
110 Vec2::ZERO,
111 Vec2::from_angle(sin_t_scaled / -10. + PI / 2.) * 50.,
112 YELLOW,
113 );
114
115 // You can create more complex arrows using the arrow builder.
116 gizmos
117 .arrow_2d(
118 Vec2::ZERO,
119 Vec2::from_angle(sin_t_scaled / -10.) * 50.,
120 GREEN,
121 )
122 .with_double_end()
123 .with_tip_length(10.);
124}Sourcepub fn to_angle(self) -> f32
pub fn to_angle(self) -> f32
Returns the angle (in radians) of this vector in the range [-π, +π].
The input does not need to be a unit vector however it must be non-zero.
Examples found in repository?
38fn player_movement_system(
39 mut player: Single<&mut Transform, With<Player>>,
40 camera_query: Single<(&Camera, &GlobalTransform)>,
41 window: Single<&Window>,
42) {
43 let (camera, camera_transform) = *camera_query;
44
45 if let Some(cursor_position) = window.cursor_position()
46 // Calculate a world position based on the cursor's position.
47 && let Ok(cursor_world_pos) = camera.viewport_to_world_2d(camera_transform, cursor_position)
48 {
49 // The angle an entity needs to rotate to face a point is defined
50 // by the vector between the two points (Vec2 - Vec2), which we can then
51 // turn into radians using to_angle.
52 //
53 // FRAC_PI_2 is because our sprite's ship is facing "up" so we rotate it an additional 90 degrees
54 // so that it faces the cursor.
55 player.rotation = Quat::from_rotation_z(
56 (cursor_world_pos - player.translation.xy()).to_angle() - FRAC_PI_2,
57 );
58 }
59}Sourcepub fn angle_to(self, rhs: Vec2) -> f32
pub fn angle_to(self, rhs: Vec2) -> f32
Returns the angle of rotation (in radians) from self to rhs in the range [-π, +π].
The inputs do not need to be unit vectors however they must be non-zero.
Sourcepub fn perp_dot(self, rhs: Vec2) -> f32
pub fn perp_dot(self, rhs: Vec2) -> f32
The perpendicular dot product of self and rhs.
Also known as the wedge product, 2D cross product, and determinant.
Sourcepub fn rotate(self, rhs: Vec2) -> Vec2
pub fn rotate(self, rhs: Vec2) -> Vec2
Returns rhs rotated by the angle of self. If self is normalized,
then this just rotation. This is what you usually want. Otherwise,
it will be like a rotation with a multiplication by self’s length.
This can be used in conjunction with the from_angle() method, e.g.
Vec2::from_angle(PI).rotate(Vec2::Y) will create the vector [-1, 0]
and rotate Vec2::Y around it returning -Vec2::Y.
Examples found in repository?
343fn rotate_camera(
344 time: Res<Time>,
345 mut camera_query: Query<&mut Transform, With<Camera3d>>,
346 app_status: Res<AppStatus>,
347) {
348 if !app_status.rotating {
349 return;
350 }
351
352 for mut transform in camera_query.iter_mut() {
353 transform.translation = Vec2::from_angle(time.delta_secs() * PI / 5.0)
354 .rotate(transform.translation.xz())
355 .extend(transform.translation.y)
356 .xzy();
357 transform.look_at(Vec3::ZERO, Vec3::Y);
358 }
359}More examples
349fn rotate_camera(
350 mut camera_query: Query<&mut Transform, With<Camera3d>>,
351 time: Res<Time>,
352 app_status: Res<AppStatus>,
353) {
354 if !app_status.rotating {
355 return;
356 }
357
358 for mut transform in camera_query.iter_mut() {
359 transform.translation = Vec2::from_angle(ROTATION_SPEED * time.delta_secs())
360 .rotate(transform.translation.xz())
361 .extend(transform.translation.y)
362 .xzy();
363 transform.look_at(Vec3::ZERO, Vec3::Y);
364 }
365}Sourcepub fn rotate_towards(self, rhs: Vec2, max_angle: f32) -> Vec2
pub fn rotate_towards(self, rhs: Vec2, max_angle: f32) -> Vec2
Rotates towards rhs up to max_angle (in radians).
When max_angle is 0.0, the result will be equal to self. When max_angle is equal to
self.angle_between(rhs), the result will be parallel to rhs. If max_angle is negative,
rotates towards the exact opposite of rhs. Will not go past the target.
Sourcepub fn as_i16vec2(self) -> I16Vec2
pub fn as_i16vec2(self) -> I16Vec2
Casts all elements of self to i16.
Sourcepub fn as_u16vec2(self) -> U16Vec2
pub fn as_u16vec2(self) -> U16Vec2
Casts all elements of self to u16.
Sourcepub fn as_uvec2(self) -> UVec2
pub fn as_uvec2(self) -> UVec2
Casts all elements of self to u32.
Examples found in repository?
208fn try_plot(
209 event: On<TryPlot>,
210 sprite: Query<(&Sprite, &Anchor, &GlobalTransform), With<SpriteToSave>>,
211 camera: Single<(&Camera, &GlobalTransform)>,
212 texture_atlases: Res<Assets<TextureAtlasLayout>>,
213 draw_color: Res<DrawColor>,
214 mut images: ResMut<Assets<Image>>,
215) {
216 let Ok((sprite, anchor, sprite_transform)) = sprite.get(event.entity) else {
217 return;
218 };
219 let (camera, camera_transform) = camera.into_inner();
220 let Ok(world_position) = camera.viewport_to_world_2d(camera_transform, event.location.position)
221 else {
222 return;
223 };
224 let relative_to_sprite = sprite_transform
225 .affine()
226 .inverse()
227 .transform_point3(world_position.extend(0.0));
228 let Ok(pixel_space) = sprite.compute_pixel_space_point(
229 relative_to_sprite.xy(),
230 *anchor,
231 &images,
232 &texture_atlases,
233 ) else {
234 return;
235 };
236 let pixel_coordinates = pixel_space.floor().as_uvec2();
237 let mut image = images.get_mut(&sprite.image).unwrap();
238 // For an actual drawing app, you'd at least draw a line from the last point, but this is
239 // simpler.
240 image
241 .set_color_at(pixel_coordinates.x, pixel_coordinates.y, draw_color.0)
242 .unwrap();
243}More examples
42fn controls(
43 camera_query: Single<(&mut Camera, &mut Transform, &mut Projection)>,
44 window: Single<&Window>,
45 input: Res<ButtonInput<KeyCode>>,
46 time: Res<Time<Fixed>>,
47) {
48 let (mut camera, mut transform, mut projection) = camera_query.into_inner();
49
50 let fspeed = 600.0 * time.delta_secs();
51 let uspeed = fspeed as u32;
52 let window_size = window.resolution.physical_size();
53
54 // Camera movement controls
55 if input.pressed(KeyCode::ArrowUp) {
56 transform.translation.y += fspeed;
57 }
58 if input.pressed(KeyCode::ArrowDown) {
59 transform.translation.y -= fspeed;
60 }
61 if input.pressed(KeyCode::ArrowLeft) {
62 transform.translation.x -= fspeed;
63 }
64 if input.pressed(KeyCode::ArrowRight) {
65 transform.translation.x += fspeed;
66 }
67
68 // Camera zoom controls
69 if let Projection::Orthographic(projection2d) = &mut *projection {
70 if input.pressed(KeyCode::Comma) {
71 projection2d.scale *= powf(4.0f32, time.delta_secs());
72 }
73
74 if input.pressed(KeyCode::Period) {
75 projection2d.scale *= powf(0.25f32, time.delta_secs());
76 }
77 }
78
79 if let Some(viewport) = camera.viewport.as_mut() {
80 // Reset viewport size on window resize
81 if viewport.physical_size.x > window_size.x || viewport.physical_size.y > window_size.y {
82 viewport.physical_size = (window_size.as_vec2() * 0.75).as_uvec2();
83 }
84
85 // Viewport movement controls
86 if input.pressed(KeyCode::KeyW) {
87 viewport.physical_position.y = viewport.physical_position.y.saturating_sub(uspeed);
88 }
89 if input.pressed(KeyCode::KeyS) {
90 viewport.physical_position.y += uspeed;
91 }
92 if input.pressed(KeyCode::KeyA) {
93 viewport.physical_position.x = viewport.physical_position.x.saturating_sub(uspeed);
94 }
95 if input.pressed(KeyCode::KeyD) {
96 viewport.physical_position.x += uspeed;
97 }
98
99 // Bound viewport position so it doesn't go off-screen
100 viewport.physical_position = viewport
101 .physical_position
102 .min(window_size - viewport.physical_size);
103
104 // Viewport size controls
105 if input.pressed(KeyCode::KeyI) {
106 viewport.physical_size.y = viewport.physical_size.y.saturating_sub(uspeed);
107 }
108 if input.pressed(KeyCode::KeyK) {
109 viewport.physical_size.y += uspeed;
110 }
111 if input.pressed(KeyCode::KeyJ) {
112 viewport.physical_size.x = viewport.physical_size.x.saturating_sub(uspeed);
113 }
114 if input.pressed(KeyCode::KeyL) {
115 viewport.physical_size.x += uspeed;
116 }
117
118 // Bound viewport size so it doesn't go off-screen
119 viewport.physical_size = viewport
120 .physical_size
121 .min(window_size - viewport.physical_position)
122 .max(UVec2::new(20, 20));
123 }
124}
125
126fn setup(
127 mut commands: Commands,
128 mut meshes: ResMut<Assets<Mesh>>,
129 mut materials: ResMut<Assets<ColorMaterial>>,
130 window: Single<&Window>,
131) {
132 let window_size = window.resolution.physical_size().as_vec2();
133
134 // Initialize centered, non-window-filling viewport
135 commands.spawn((
136 Camera2d,
137 Camera {
138 viewport: Some(Viewport {
139 physical_position: (window_size * 0.125).as_uvec2(),
140 physical_size: (window_size * 0.75).as_uvec2(),
141 ..default()
142 }),
143 ..default()
144 },
145 ));
146
147 // Create a minimal UI explaining how to interact with the example
148 commands.spawn((
149 Text::new(
150 "Move the mouse to see the circle follow your cursor.\n\
151 Use the arrow keys to move the camera.\n\
152 Use the comma and period keys to zoom in and out.\n\
153 Use the WASD keys to move the viewport.\n\
154 Use the IJKL keys to resize the viewport.",
155 ),
156 Node {
157 position_type: PositionType::Absolute,
158 top: px(12),
159 left: px(12),
160 ..default()
161 },
162 ));
163
164 // Add mesh to make camera movement visible
165 commands.spawn((
166 Mesh2d(meshes.add(Rectangle::new(40.0, 20.0))),
167 MeshMaterial2d(materials.add(Color::from(GREEN))),
168 ));
169
170 // Add background to visualize viewport bounds
171 commands.spawn((
172 Mesh2d(meshes.add(Rectangle::new(50000.0, 50000.0))),
173 MeshMaterial2d(materials.add(Color::linear_rgb(0.01, 0.01, 0.01))),
174 Transform::from_translation(Vec3::new(0.0, 0.0, -200.0)),
175 ));
176}161fn setup(
162 mut commands: Commands,
163 args: Res<Args>,
164 mesh_assets: ResMut<Assets<Mesh>>,
165 material_assets: ResMut<Assets<StandardMaterial>>,
166 images: ResMut<Assets<Image>>,
167) {
168 warn!(include_str!("warning_string.txt"));
169
170 let args = args.into_inner();
171 let images = images.into_inner();
172 let material_assets = material_assets.into_inner();
173 let mesh_assets = mesh_assets.into_inner();
174
175 let meshes = init_meshes(args, mesh_assets);
176
177 let material_textures = init_textures(args, images);
178 let materials = init_materials(args, &material_textures, material_assets);
179
180 // We're seeding the PRNG here to make this example deterministic for testing purposes.
181 // This isn't strictly required in practical use unless you need your app to be deterministic.
182 let mut material_rng = ChaCha8Rng::seed_from_u64(42);
183 match args.layout {
184 Layout::Sphere => {
185 // NOTE: This pattern is good for testing performance of culling as it provides roughly
186 // the same number of visible meshes regardless of the viewing angle.
187 let n_points: usize = args.instance_count;
188 // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
189 let radius = WIDTH as f64 * 2.5;
190 let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt());
191 for i in 0..n_points {
192 let spherical_polar_theta_phi =
193 fibonacci_spiral_on_sphere(golden_ratio, i, n_points);
194 let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
195 let (mesh, transform) = meshes.choose(&mut material_rng).unwrap();
196 commands
197 .spawn((
198 Mesh3d(mesh.clone()),
199 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
200 Transform::from_translation((radius * unit_sphere_p).as_vec3())
201 .looking_at(Vec3::ZERO, Vec3::Y)
202 .mul_transform(*transform),
203 ))
204 .insert_if(NoFrustumCulling, || args.no_frustum_culling)
205 .insert_if(NoAutomaticBatching, || args.no_automatic_batching)
206 .insert_if(NoCpuCulling, || args.no_cpu_culling);
207 }
208
209 // camera
210 let mut camera = commands.spawn(Camera3d::default());
211 if args.no_indirect_drawing {
212 camera.insert(NoIndirectDrawing);
213 }
214 if args.no_cpu_culling {
215 camera.insert(NoCpuCulling);
216 }
217 if args.motion_blur {
218 camera.insert((
219 MotionBlur {
220 // Use an unrealistically large shutter angle so that motion blur is clearly visible.
221 shutter_angle: 3.0,
222 ..Default::default()
223 },
224 // MSAA and MotionBlur are not compatible on WebGL.
225 #[cfg(all(
226 feature = "webgl2",
227 target_arch = "wasm32",
228 not(feature = "webgpu")
229 ))]
230 Msaa::Off,
231 ));
232 }
233
234 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
235 commands.spawn((
236 Mesh3d(mesh_assets.add(Cuboid::from_size(Vec3::splat(radius as f32 * 2.2)))),
237 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
238 Transform::from_scale(-Vec3::ONE),
239 NotShadowCaster,
240 ));
241 }
242 Layout::Cube => {
243 // NOTE: This pattern is good for demonstrating that frustum culling is working correctly
244 // as the number of visible meshes rises and falls depending on the viewing angle.
245 let scale = 2.5;
246
247 // Scale the width and height by the same factor so that we have the
248 // right number of instances.
249 // Because of the moiré pattern check and the fact that we're
250 // spawning 4 instances per trip around the inner loop below, we're
251 // solving the following equation for the factor variable:
252 //
253 // 4 * (9/10 * factor * width * 9/10 * factor * height) = count
254 //
255 // The solution is the value below.
256 let factor = (5.0 / 9.0) * sqrt(args.instance_count as f32)
257 / (sqrt(HEIGHT as f32) * sqrt(WIDTH as f32));
258 let dimensions = (vec2(WIDTH as f32, HEIGHT as f32) * factor)
259 .ceil()
260 .as_uvec2();
261
262 for x in 0..dimensions.x {
263 for y in 0..dimensions.y {
264 // introduce spaces to break any kind of moiré pattern
265 if x % 10 == 0 || y % 10 == 0 {
266 continue;
267 }
268 // cube
269 commands
270 .spawn((
271 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
272 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
273 Transform::from_xyz((x as f32) * scale, (y as f32) * scale, 0.0),
274 ))
275 .insert_if(NoCpuCulling, || args.no_cpu_culling);
276 commands
277 .spawn((
278 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
279 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
280 Transform::from_xyz(
281 (x as f32) * scale,
282 dimensions.y as f32 * scale,
283 (y as f32) * scale,
284 ),
285 ))
286 .insert_if(NoCpuCulling, || args.no_cpu_culling);
287 commands
288 .spawn((
289 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
290 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
291 Transform::from_xyz((x as f32) * scale, 0.0, (y as f32) * scale),
292 ))
293 .insert_if(NoCpuCulling, || args.no_cpu_culling);
294 commands
295 .spawn((
296 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
297 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
298 Transform::from_xyz(0.0, (x as f32) * scale, (y as f32) * scale),
299 ))
300 .insert_if(NoCpuCulling, || args.no_cpu_culling);
301 }
302 }
303 // camera
304 let center = 0.5
305 * scale
306 * Vec3::new(
307 dimensions.x as f32,
308 dimensions.y as f32,
309 dimensions.x as f32,
310 );
311 commands.spawn((Camera3d::default(), Transform::from_translation(center)));
312 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
313 commands.spawn((
314 Mesh3d(mesh_assets.add(Cuboid::from_size(2.0 * 1.1 * center))),
315 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
316 Transform::from_scale(-Vec3::ONE).with_translation(center),
317 NotShadowCaster,
318 ));
319 }
320 Layout::Dense => {
321 // NOTE: This pattern is good for demonstrating a dense configuration of cubes
322 // overlapping each other, all within the camera frustum.
323 let count = args.instance_count;
324 let size = cbrt(count as f32).round();
325 let gap = 1.25;
326
327 for i in 0..count {
328 let x = i as f32 % size;
329 let y = (i as f32 / size) % size;
330 let z = i as f32 / (size * size);
331 let pos = Vec3::new(x * gap, y * gap, z * gap);
332 commands
333 .spawn((
334 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
335 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
336 Transform::from_translation(pos),
337 ))
338 .insert_if(NoCpuCulling, || args.no_cpu_culling);
339 }
340
341 // camera
342 commands.spawn((
343 Camera3d::default(),
344 Transform::from_xyz(100.0, 90.0, 100.0)
345 .looking_at(Vec3::new(0.0, -10.0, 0.0), Vec3::Y),
346 ));
347 }
348 }
349
350 commands.spawn((
351 DirectionalLight {
352 shadow_maps_enabled: args.shadows,
353 ..default()
354 },
355 Transform::IDENTITY.looking_at(Vec3::new(0.0, -1.0, -1.0), Vec3::Y),
356 ));
357}Sourcepub fn as_i64vec2(self) -> I64Vec2
pub fn as_i64vec2(self) -> I64Vec2
Casts all elements of self to i64.
Sourcepub fn as_u64vec2(self) -> U64Vec2
pub fn as_u64vec2(self) -> U64Vec2
Casts all elements of self to u64.
Sourcepub fn as_isizevec2(self) -> ISizeVec2
pub fn as_isizevec2(self) -> ISizeVec2
Casts all elements of self to isize.
Sourcepub fn as_usizevec2(self) -> USizeVec2
pub fn as_usizevec2(self) -> USizeVec2
Casts all elements of self to usize.
Trait Implementations§
Source§impl AddAssign for Vec2
impl AddAssign for Vec2
Source§fn add_assign(&mut self, rhs: Vec2)
fn add_assign(&mut self, rhs: Vec2)
+= operation. Read moreSource§impl AddAssign<&Vec2> for Vec2
impl AddAssign<&Vec2> for Vec2
Source§fn add_assign(&mut self, rhs: &Vec2)
fn add_assign(&mut self, rhs: &Vec2)
+= operation. Read moreSource§impl AddAssign<&f32> for Vec2
impl AddAssign<&f32> for Vec2
Source§fn add_assign(&mut self, rhs: &f32)
fn add_assign(&mut self, rhs: &f32)
+= operation. Read moreSource§impl AddAssign<f32> for Vec2
impl AddAssign<f32> for Vec2
Source§fn add_assign(&mut self, rhs: f32)
fn add_assign(&mut self, rhs: f32)
+= operation. Read moreSource§impl Animatable for Vec2
impl Animatable for Vec2
Source§impl AsMutVectorParts<f32, 2> for Vec2
impl AsMutVectorParts<f32, 2> for Vec2
fn as_mut_parts(&mut self) -> &mut [f32; 2]
Source§impl AsRefVectorParts<f32, 2> for Vec2
impl AsRefVectorParts<f32, 2> for Vec2
fn as_ref_parts(&self) -> &[f32; 2]
impl Copy for Vec2
Source§impl CreateFrom for Vec2
impl CreateFrom for Vec2
Source§impl<'de> Deserialize<'de> for Vec2
Deserialize expects a sequence of 2 values.
impl<'de> Deserialize<'de> for Vec2
Deserialize expects a sequence of 2 values.
Source§fn deserialize<D>(
deserializer: D,
) -> Result<Vec2, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
fn deserialize<D>(
deserializer: D,
) -> Result<Vec2, <D as Deserializer<'de>>::Error>where
D: Deserializer<'de>,
Source§impl DivAssign for Vec2
impl DivAssign for Vec2
Source§fn div_assign(&mut self, rhs: Vec2)
fn div_assign(&mut self, rhs: Vec2)
/= operation. Read moreSource§impl DivAssign<&Vec2> for Vec2
impl DivAssign<&Vec2> for Vec2
Source§fn div_assign(&mut self, rhs: &Vec2)
fn div_assign(&mut self, rhs: &Vec2)
/= operation. Read moreSource§impl DivAssign<&f32> for Vec2
impl DivAssign<&f32> for Vec2
Source§fn div_assign(&mut self, rhs: &f32)
fn div_assign(&mut self, rhs: &f32)
/= operation. Read moreSource§impl DivAssign<f32> for Vec2
impl DivAssign<f32> for Vec2
Source§fn div_assign(&mut self, rhs: f32)
fn div_assign(&mut self, rhs: f32)
/= operation. Read moreSource§impl From<Vec2> for Isometry2d
impl From<Vec2> for Isometry2d
Source§fn from(translation: Vec2) -> Isometry2d
fn from(translation: Vec2) -> Isometry2d
Source§impl From<Vec2> for TextBounds
impl From<Vec2> for TextBounds
Source§fn from(v: Vec2) -> TextBounds
fn from(v: Vec2) -> TextBounds
Source§impl From<Vec2> for ScrollPosition
impl From<Vec2> for ScrollPosition
Source§fn from(value: Vec2) -> ScrollPosition
fn from(value: Vec2) -> ScrollPosition
Source§impl FromIterator<Vec2> for Polyline2d
Available on crate feature alloc only.
impl FromIterator<Vec2> for Polyline2d
alloc only.Source§fn from_iter<I>(iter: I) -> Polyline2dwhere
I: IntoIterator<Item = Vec2>,
fn from_iter<I>(iter: I) -> Polyline2dwhere
I: IntoIterator<Item = Vec2>,
Source§impl FromIterator<Vec2> for Polygon
Available on crate feature alloc only.
impl FromIterator<Vec2> for Polygon
alloc only.Source§impl FromReflect for Vec2
impl FromReflect for Vec2
Source§fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<Vec2>
fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<Vec2>
Self from a reflected value.Source§fn take_from_reflect(
reflect: Box<dyn PartialReflect>,
) -> Result<Self, Box<dyn PartialReflect>>
fn take_from_reflect( reflect: Box<dyn PartialReflect>, ) -> Result<Self, Box<dyn PartialReflect>>
Self using,
constructing the value using from_reflect if that fails. Read moreSource§impl FromVectorParts<f32, 2> for Vec2
impl FromVectorParts<f32, 2> for Vec2
Source§impl GetTypeRegistration for Vec2
impl GetTypeRegistration for Vec2
Source§fn get_type_registration() -> TypeRegistration
fn get_type_registration() -> TypeRegistration
TypeRegistration for this type.Source§fn register_type_dependencies(registry: &mut TypeRegistry)
fn register_type_dependencies(registry: &mut TypeRegistry)
Source§impl IntoReturn for Vec2
impl IntoReturn for Vec2
Source§impl Mul<Vec2> for Isometry2d
impl Mul<Vec2> for Isometry2d
Source§impl Mul<Vec2> for UiGlobalTransform
impl Mul<Vec2> for UiGlobalTransform
Source§impl MulAssign for Vec2
impl MulAssign for Vec2
Source§fn mul_assign(&mut self, rhs: Vec2)
fn mul_assign(&mut self, rhs: Vec2)
*= operation. Read moreSource§impl MulAssign<&Vec2> for Vec2
impl MulAssign<&Vec2> for Vec2
Source§fn mul_assign(&mut self, rhs: &Vec2)
fn mul_assign(&mut self, rhs: &Vec2)
*= operation. Read moreSource§impl MulAssign<&f32> for Vec2
impl MulAssign<&f32> for Vec2
Source§fn mul_assign(&mut self, rhs: &f32)
fn mul_assign(&mut self, rhs: &f32)
*= operation. Read moreSource§impl MulAssign<f32> for Vec2
impl MulAssign<f32> for Vec2
Source§fn mul_assign(&mut self, rhs: f32)
fn mul_assign(&mut self, rhs: f32)
*= operation. Read moreSource§impl NormedVectorSpace for Vec2
impl NormedVectorSpace for Vec2
Source§fn norm_squared(self) -> f32
fn norm_squared(self) -> f32
NormedVectorSpace::norm.Source§fn distance(self, rhs: Self) -> Self::Scalar
fn distance(self, rhs: Self) -> Self::Scalar
Source§fn distance_squared(self, rhs: Self) -> Self::Scalar
fn distance_squared(self, rhs: Self) -> Self::Scalar
NormedVectorSpace::distance.Source§impl PartialReflect for Vec2
impl PartialReflect for Vec2
Source§fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
Source§fn try_apply(
&mut self,
value: &(dyn PartialReflect + 'static),
) -> Result<(), ApplyError>
fn try_apply( &mut self, value: &(dyn PartialReflect + 'static), ) -> Result<(), ApplyError>
Source§fn reflect_kind(&self) -> ReflectKind
fn reflect_kind(&self) -> ReflectKind
Source§fn reflect_ref(&self) -> ReflectRef<'_>
fn reflect_ref(&self) -> ReflectRef<'_>
Source§fn reflect_mut(&mut self) -> ReflectMut<'_>
fn reflect_mut(&mut self) -> ReflectMut<'_>
Source§fn reflect_owned(self: Box<Vec2>) -> ReflectOwned
fn reflect_owned(self: Box<Vec2>) -> ReflectOwned
Source§fn try_into_reflect(
self: Box<Vec2>,
) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
fn try_into_reflect( self: Box<Vec2>, ) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
Source§fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
Source§fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
Source§fn into_partial_reflect(self: Box<Vec2>) -> Box<dyn PartialReflect>
fn into_partial_reflect(self: Box<Vec2>) -> Box<dyn PartialReflect>
Source§fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
Source§fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
Source§fn reflect_partial_eq(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<bool>
fn reflect_partial_eq( &self, value: &(dyn PartialReflect + 'static), ) -> Option<bool>
Source§fn reflect_partial_cmp(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<Ordering>
fn reflect_partial_cmp( &self, value: &(dyn PartialReflect + 'static), ) -> Option<Ordering>
Source§fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
Source§fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
Self using reflection. Read moreSource§fn apply(&mut self, value: &(dyn PartialReflect + 'static))
fn apply(&mut self, value: &(dyn PartialReflect + 'static))
Source§fn to_dynamic(&self) -> Box<dyn PartialReflect>
fn to_dynamic(&self) -> Box<dyn PartialReflect>
Source§fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
PartialReflect, combines reflect_clone and
take in a useful fashion, automatically constructing an appropriate
ReflectCloneError if the downcast fails.Source§fn reflect_hash(&self) -> Option<u64>
fn reflect_hash(&self) -> Option<u64>
Source§fn is_dynamic(&self) -> bool
fn is_dynamic(&self) -> bool
impl Pod for Vec2
Source§impl Reflect for Vec2
impl Reflect for Vec2
Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut dyn Any. Read moreSource§fn into_reflect(self: Box<Vec2>) -> Box<dyn Reflect>
fn into_reflect(self: Box<Vec2>) -> Box<dyn Reflect>
Source§fn as_reflect(&self) -> &(dyn Reflect + 'static)
fn as_reflect(&self) -> &(dyn Reflect + 'static)
Source§fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
Source§impl RemAssign for Vec2
impl RemAssign for Vec2
Source§fn rem_assign(&mut self, rhs: Vec2)
fn rem_assign(&mut self, rhs: Vec2)
%= operation. Read moreSource§impl RemAssign<&Vec2> for Vec2
impl RemAssign<&Vec2> for Vec2
Source§fn rem_assign(&mut self, rhs: &Vec2)
fn rem_assign(&mut self, rhs: &Vec2)
%= operation. Read moreSource§impl RemAssign<&f32> for Vec2
impl RemAssign<&f32> for Vec2
Source§fn rem_assign(&mut self, rhs: &f32)
fn rem_assign(&mut self, rhs: &f32)
%= operation. Read moreSource§impl RemAssign<f32> for Vec2
impl RemAssign<f32> for Vec2
Source§fn rem_assign(&mut self, rhs: f32)
fn rem_assign(&mut self, rhs: f32)
%= operation. Read moreSource§impl SampleUniform for Vec2
impl SampleUniform for Vec2
Source§type Sampler = UniformVec2<UniformFloat<f32>>
type Sampler = UniformVec2<UniformFloat<f32>>
UniformSampler implementation supporting type X.Source§impl Serialize for Vec2
Serialize as a sequence of 2 values.
impl Serialize for Vec2
Serialize as a sequence of 2 values.
Source§fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
Source§impl ShaderSize for Vec2where
f32: ShaderSize,
impl ShaderSize for Vec2where
f32: ShaderSize,
Source§const SHADER_SIZE: NonZero<u64> = _
const SHADER_SIZE: NonZero<u64> = _
ShaderType::min_size)Source§impl ShaderType for Vec2where
f32: ShaderSize,
impl ShaderType for Vec2where
f32: ShaderSize,
Source§fn assert_uniform_compat()
fn assert_uniform_compat()
Self meets the requirements of the
uniform address space restrictions on stored values and the
uniform address space layout constraints Read moreSource§impl Struct for Vec2
impl Struct for Vec2
Source§fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
name as a &dyn PartialReflect.Source§fn field_mut(
&mut self,
name: &str,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_mut( &mut self, name: &str, ) -> Option<&mut (dyn PartialReflect + 'static)>
name as a
&mut dyn PartialReflect.Source§fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
index as a
&dyn PartialReflect.Source§fn field_at_mut(
&mut self,
index: usize,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_at_mut( &mut self, index: usize, ) -> Option<&mut (dyn PartialReflect + 'static)>
index
as a &mut dyn PartialReflect.Source§fn index_of_name(&self, name: &str) -> Option<usize>
fn index_of_name(&self, name: &str) -> Option<usize>
Source§fn iter_fields(&self) -> FieldIter<'_> ⓘ
fn iter_fields(&self) -> FieldIter<'_> ⓘ
Source§fn to_dynamic_struct(&self) -> DynamicStruct
fn to_dynamic_struct(&self) -> DynamicStruct
DynamicStruct from this struct.Source§fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
None if TypeInfo is not available.impl StructuralPartialEq for Vec2
Source§impl SubAssign for Vec2
impl SubAssign for Vec2
Source§fn sub_assign(&mut self, rhs: Vec2)
fn sub_assign(&mut self, rhs: Vec2)
-= operation. Read moreSource§impl SubAssign<&Vec2> for Vec2
impl SubAssign<&Vec2> for Vec2
Source§fn sub_assign(&mut self, rhs: &Vec2)
fn sub_assign(&mut self, rhs: &Vec2)
-= operation. Read moreSource§impl SubAssign<&f32> for Vec2
impl SubAssign<&f32> for Vec2
Source§fn sub_assign(&mut self, rhs: &f32)
fn sub_assign(&mut self, rhs: &f32)
-= operation. Read moreSource§impl SubAssign<f32> for Vec2
impl SubAssign<f32> for Vec2
Source§fn sub_assign(&mut self, rhs: f32)
fn sub_assign(&mut self, rhs: f32)
-= operation. Read moreSource§impl TryFrom<Vec2> for AspectRatio
impl TryFrom<Vec2> for AspectRatio
Source§impl TypePath for Vec2
impl TypePath for Vec2
Source§fn type_path() -> &'static str
fn type_path() -> &'static str
Source§fn short_type_path() -> &'static str
fn short_type_path() -> &'static str
Source§fn type_ident() -> Option<&'static str>
fn type_ident() -> Option<&'static str>
Source§fn crate_name() -> Option<&'static str>
fn crate_name() -> Option<&'static str>
Source§impl Vec2Swizzles for Vec2
impl Vec2Swizzles for Vec2
type Vec3 = Vec3
type Vec4 = Vec4
fn xx(self) -> Vec2
fn yx(self) -> Vec2
fn yy(self) -> Vec2
fn xxx(self) -> Vec3
fn xxy(self) -> Vec3
fn xyx(self) -> Vec3
fn xyy(self) -> Vec3
fn yxx(self) -> Vec3
fn yxy(self) -> Vec3
fn yyx(self) -> Vec3
fn yyy(self) -> Vec3
fn xxxx(self) -> Vec4
fn xxxy(self) -> Vec4
fn xxyx(self) -> Vec4
fn xxyy(self) -> Vec4
fn xyxx(self) -> Vec4
fn xyxy(self) -> Vec4
fn xyyx(self) -> Vec4
fn xyyy(self) -> Vec4
fn yxxx(self) -> Vec4
fn yxxy(self) -> Vec4
fn yxyx(self) -> Vec4
fn yxyy(self) -> Vec4
fn yyxx(self) -> Vec4
fn yyxy(self) -> Vec4
fn yyyx(self) -> Vec4
fn yyyy(self) -> Vec4
fn xy(self) -> Self
Source§impl VectorSpace for Vec2
impl VectorSpace for Vec2
Auto Trait Implementations§
impl Freeze for Vec2
impl RefUnwindSafe for Vec2
impl Send for Vec2
impl Sync for Vec2
impl Unpin for Vec2
impl UnsafeUnpin for Vec2
impl UnwindSafe for Vec2
Blanket Implementations§
impl<T> AnyBitPattern for Twhere
T: Pod,
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> Brush for T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
Source§type Bits = T
type Bits = T
Self must have the same layout as the specified Bits except for
the possible invalid bit patterns being checked during
is_valid_bit_pattern.Source§fn is_valid_bit_pattern(_bits: &T) -> bool
fn is_valid_bit_pattern(_bits: &T) -> bool
bits
as &Self.Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ConditionalSend for Twhere
T: Send,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<T> DynamicTypePath for Twhere
T: TypePath,
impl<T> DynamicTypePath for Twhere
T: TypePath,
Source§fn reflect_type_path(&self) -> &str
fn reflect_type_path(&self) -> &str
TypePath::type_path.Source§fn reflect_short_type_path(&self) -> &str
fn reflect_short_type_path(&self) -> &str
Source§fn reflect_type_ident(&self) -> Option<&str>
fn reflect_type_ident(&self) -> Option<&str>
TypePath::type_ident.Source§fn reflect_crate_name(&self) -> Option<&str>
fn reflect_crate_name(&self) -> Option<&str>
TypePath::crate_name.Source§fn reflect_module_path(&self) -> Option<&str>
fn reflect_module_path(&self) -> Option<&str>
Source§impl<T> DynamicTyped for Twhere
T: Typed,
impl<T> DynamicTyped for Twhere
T: Typed,
Source§fn reflect_type_info(&self) -> &'static TypeInfo
fn reflect_type_info(&self) -> &'static TypeInfo
Typed::type_info.Source§impl<V> Ease for Vwhere
V: VectorSpace<Scalar = f32>,
impl<V> Ease for Vwhere
V: VectorSpace<Scalar = f32>,
Source§fn interpolating_curve_unbounded(start: V, end: V) -> impl Curve<V>
fn interpolating_curve_unbounded(start: V, end: V) -> impl Curve<V>
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> FromTemplate for T
impl<T> FromTemplate for T
Source§impl<T> FromWorld for Twhere
T: Default,
impl<T> FromWorld for Twhere
T: Default,
Source§fn from_world(_world: &mut World) -> T
fn from_world(_world: &mut World) -> T
Creates Self using default().
Source§impl<S> GetField for Swhere
S: Struct,
impl<S> GetField for Swhere
S: Struct,
Source§impl<T> GetPath for T
impl<T> GetPath for T
Source§fn reflect_path<'p>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path<'p>( &self, path: impl ReflectPath<'p>, ) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn reflect_path_mut<'p>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path_mut<'p>( &mut self, path: impl ReflectPath<'p>, ) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreSource§fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreimpl<T> GpuArrayBufferable for T
Source§impl<V> HasTangent for Vwhere
V: VectorSpace,
impl<V> HasTangent for Vwhere
V: VectorSpace,
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T> HitDataExtra for T
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
impl<T> NoUninit for Twhere
T: Pod,
impl<T, Rhs> NumAssignOps<Rhs> for T
impl<T, Rhs, Output> NumOps<Rhs, Output> for T
Source§impl<G> PatchFromTemplate for Gwhere
G: FromTemplate,
impl<G> PatchFromTemplate for Gwhere
G: FromTemplate,
Source§fn patch<F>(func: F) -> TemplatePatch<F, <G as PatchFromTemplate>::Template>
fn patch<F>(func: F) -> TemplatePatch<F, <G as PatchFromTemplate>::Template>
func, and turns it into a TemplatePatch.Source§impl<T> PatchTemplate for Twhere
T: Template,
impl<T> PatchTemplate for Twhere
T: Template,
Source§fn patch_template<F>(func: F) -> TemplatePatch<F, T>
fn patch_template<F>(func: F) -> TemplatePatch<F, T>
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().impl<T, Base> RefNum<Base> for T
impl<T> Reflectable for T
Source§impl<Borrowed> SampleBorrow<Borrowed> for Borrowedwhere
Borrowed: SampleUniform,
impl<Borrowed> SampleBorrow<Borrowed> for Borrowedwhere
Borrowed: SampleUniform,
Source§fn borrow(&self) -> &Borrowed
fn borrow(&self) -> &Borrowed
Borrow::borrowSource§impl<T> Serialize for T
impl<T> Serialize for T
fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>
fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>
impl<T> Settings for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<V> StableInterpolate for Vwhere
V: NormedVectorSpace<Scalar = f32>,
impl<V> StableInterpolate for Vwhere
V: NormedVectorSpace<Scalar = f32>,
Source§fn interpolate_stable(&self, other: &V, t: f32) -> V
fn interpolate_stable(&self, other: &V, t: f32) -> V
other given value using the parameter t. At
t = 0.0, a value equivalent to self is recovered, while t = 1.0 recovers a value
equivalent to other, with intermediate values interpolating between the two.
See the trait-level documentation for details.Source§fn interpolate_stable_assign(&mut self, other: &Self, t: f32)
fn interpolate_stable_assign(&mut self, other: &Self, t: f32)
interpolate_stable that assigns the result to self for convenience.Source§fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32)
fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32)
target at a given decay rate. The decay_rate
parameter controls how fast the distance between self and target decays relative to
the units of delta; the intended usage is for decay_rate to generally remain fixed,
while delta is something like delta_time from an updating system. This produces a
smooth following of the target that is independent of framerate. Read moreSource§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.Source§impl<T> Template for T
impl<T> Template for T
Source§fn build_template(
&self,
_context: &mut TemplateContext<'_, '_>,
) -> Result<<T as Template>::Output, BevyError>
fn build_template( &self, _context: &mut TemplateContext<'_, '_>, ) -> Result<<T as Template>::Output, BevyError>
entity context to produce a Template::Output.Source§fn clone_template(&self) -> T
fn clone_template(&self) -> T
Clone.