mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Add examples for Transforms (#2441)
# Add Transform Examples - Adding examples for moving/rotating entities (with its own section) to resolve #2400 I've stumbled upon this project and been fiddling around a little. Saw the issue and thought I might just add some examples for the proposed transformations. Mind to check if I got the gist correctly and suggest anything I can improve?
This commit is contained in:
parent
6e61fef67d
commit
9dfd4e4b08
7 changed files with 595 additions and 0 deletions
21
Cargo.toml
21
Cargo.toml
|
@ -502,6 +502,27 @@ path = "examples/shader/compute_shader_game_of_life.rs"
|
||||||
name = "bevymark"
|
name = "bevymark"
|
||||||
path = "examples/tools/bevymark.rs"
|
path = "examples/tools/bevymark.rs"
|
||||||
|
|
||||||
|
# Transforms
|
||||||
|
[[example]]
|
||||||
|
name = "global_vs_local_translation"
|
||||||
|
path = "examples/transforms/global_vs_local_translation.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "3d_rotation"
|
||||||
|
path = "examples/transforms/3d_rotation.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "scale"
|
||||||
|
path = "examples/transforms/scale.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "transform"
|
||||||
|
path = "examples/transforms/transform.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "translation"
|
||||||
|
path = "examples/transforms/translation.rs"
|
||||||
|
|
||||||
# UI (User Interface)
|
# UI (User Interface)
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "button"
|
name = "button"
|
||||||
|
|
|
@ -52,6 +52,7 @@ git checkout v0.4.0
|
||||||
- [Shaders](#shaders)
|
- [Shaders](#shaders)
|
||||||
- [Tests](#tests)
|
- [Tests](#tests)
|
||||||
- [Tools](#tools)
|
- [Tools](#tools)
|
||||||
|
- [Transforms](#transforms)
|
||||||
- [UI (User Interface)](#ui-user-interface)
|
- [UI (User Interface)](#ui-user-interface)
|
||||||
- [Window](#window)
|
- [Window](#window)
|
||||||
- [Platform-Specific Examples](#platform-specific-examples)
|
- [Platform-Specific Examples](#platform-specific-examples)
|
||||||
|
@ -247,6 +248,16 @@ Example | File | Description
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
`bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy sprite rendering workload to benchmark your system with Bevy
|
`bevymark` | [`tools/bevymark.rs`](./tools/bevymark.rs) | A heavy sprite rendering workload to benchmark your system with Bevy
|
||||||
|
|
||||||
|
## Transforms
|
||||||
|
|
||||||
|
Example | File | Description
|
||||||
|
--- | --- | ---
|
||||||
|
`global_vs_local_translation` | [`transforms/global_vs_local_translation.rs`](./transforms/global_vs_local_translation.rs) | Illustrates the difference between direction of a translation in respect to local object or global object Transform
|
||||||
|
`3d_rotation` | [`transforms/3d_rotation.rs`](./transforms/3d_rotation.rs) | Illustrates how to (constantly) rotate an object around an axis
|
||||||
|
`scale` | [`transforms/scale.rs`](./transforms/scale.rs) | Illustrates how to scale an object in each direction
|
||||||
|
`transform` | [`transforms/transfrom.rs`](./transforms/transform.rs) | Shows multiple transformations of objects
|
||||||
|
`translation` | [`transforms/translation.rs`](./transforms/translation.rs) | Illustrates how to move an object along an axis
|
||||||
|
|
||||||
## UI (User Interface)
|
## UI (User Interface)
|
||||||
|
|
||||||
Example | File | Description
|
Example | File | Description
|
||||||
|
|
57
examples/transforms/3d_rotation.rs
Normal file
57
examples/transforms/3d_rotation.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
const FULL_TURN: f32 = 2.0 * PI;
|
||||||
|
|
||||||
|
// Define a component to designate a rotation speed to an entity.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Rotatable {
|
||||||
|
speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(rotate_cube)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Spawn a cube to rotate.
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(Color::WHITE.into()),
|
||||||
|
transform: Transform::from_translation(Vec3::ZERO),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(Rotatable { speed: 0.3 });
|
||||||
|
|
||||||
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a light source for better 3d visibility.
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::ONE * 3.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will rotate any entity in the scene with an assigned Rotatable around its z-axis.
|
||||||
|
fn rotate_cube(mut cubes: Query<(&mut Transform, &Rotatable)>, timer: Res<Time>) {
|
||||||
|
for (mut transform, cube) in cubes.iter_mut() {
|
||||||
|
// The speed is taken as a percentage of a full 360 degree turn.
|
||||||
|
// The timers delta_seconds is used to smooth out the movement.
|
||||||
|
let rotation_change = Quat::from_rotation_y(FULL_TURN * cube.speed * timer.delta_seconds());
|
||||||
|
transform.rotate(rotation_change);
|
||||||
|
}
|
||||||
|
}
|
188
examples/transforms/global_vs_local_translation.rs
Normal file
188
examples/transforms/global_vs_local_translation.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// Define a marker for entities that should be changed via their global transform.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ChangeGlobal;
|
||||||
|
|
||||||
|
// Define a marker for entities that should be changed via their local transform.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ChangeLocal;
|
||||||
|
|
||||||
|
// Define a marker for entities that should move.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Move;
|
||||||
|
|
||||||
|
// Define a resource for the current movement direction;
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Direction(Vec3);
|
||||||
|
|
||||||
|
// Define component to decide when an entity should be ignored by the movement systems.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ToggledBy(KeyCode);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.init_resource::<Direction>()
|
||||||
|
.add_system(move_cubes_according_to_global_transform)
|
||||||
|
.add_system(move_cubes_according_to_local_transform)
|
||||||
|
.add_system(update_directional_input)
|
||||||
|
.add_system(toggle_movement)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup system to setup the scene and spawn all relevant entities.
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
// To show the difference between a local transform (rotation, scale and position in respect to a given entity)
|
||||||
|
// and global transform (rotation, scale and position in respect to the base coordinate system of the visible scene)
|
||||||
|
// it's helpful to add multiple entities that are attached to each other.
|
||||||
|
// This way we'll see that the transform in respect to an entity's parent is different to the
|
||||||
|
// global transform within the visible scene.
|
||||||
|
// This example focuses on translation only to clearly demonstrate the differences.
|
||||||
|
|
||||||
|
// Spawn a basic cube to have an entity as reference.
|
||||||
|
let mut main_entity = commands.spawn();
|
||||||
|
main_entity
|
||||||
|
.insert_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(Color::YELLOW.into()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(ChangeGlobal)
|
||||||
|
.insert(Move)
|
||||||
|
.insert(ToggledBy(KeyCode::Key1));
|
||||||
|
|
||||||
|
// Spawn two entities as children above the original main entity.
|
||||||
|
// The red entity spawned here will be changed via its global transform
|
||||||
|
// where the green one will be changed via its local transform.
|
||||||
|
main_entity.with_children(|child_builder| {
|
||||||
|
// also see parenting example
|
||||||
|
child_builder
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
|
||||||
|
material: materials.add(Color::RED.into()),
|
||||||
|
transform: Transform::from_translation(Vec3::Y - Vec3::Z),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(ChangeGlobal)
|
||||||
|
.insert(Move)
|
||||||
|
.insert(ToggledBy(KeyCode::Key2));
|
||||||
|
child_builder
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
|
||||||
|
material: materials.add(Color::GREEN.into()),
|
||||||
|
transform: Transform::from_translation(Vec3::Y + Vec3::Z),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(ChangeLocal)
|
||||||
|
.insert(Move)
|
||||||
|
.insert(ToggledBy(KeyCode::Key3));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a light source for better 3d visibility.
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::splat(3.0)),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a UI cam and text to explain inputs and what is happening.
|
||||||
|
commands.spawn_bundle(UiCameraBundle::default());
|
||||||
|
commands.spawn_bundle(TextBundle {
|
||||||
|
text: Text::with_section(
|
||||||
|
"Press the arrow keys to move the cubes. Toggle movement for yellow (1), red (2) and green (3) cubes via number keys.
|
||||||
|
|
||||||
|
Notice how the green cube will translate further in respect to the yellow in contrast to the red cube.
|
||||||
|
This is due to the use of its LocalTransform that is relative to the yellow cubes transform instead of the GlobalTransform as in the case of the red cube.
|
||||||
|
The red cube is moved through its GlobalTransform and thus is unaffected by the yellows transform.",
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||||
|
font_size: 22.0,
|
||||||
|
color: Color::WHITE,
|
||||||
|
},
|
||||||
|
TextAlignment {
|
||||||
|
horizontal: HorizontalAlign::Left,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
..Default::default()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will move all cubes that are marked as ChangeGlobal according to their global transform.
|
||||||
|
fn move_cubes_according_to_global_transform(
|
||||||
|
mut cubes: Query<&mut GlobalTransform, (With<ChangeGlobal>, With<Move>)>,
|
||||||
|
direction: Res<Direction>,
|
||||||
|
timer: Res<Time>,
|
||||||
|
) {
|
||||||
|
for mut global_transform in cubes.iter_mut() {
|
||||||
|
global_transform.translation += direction.0 * timer.delta_seconds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will move all cubes that are marked as ChangeLocal according to their local transform.
|
||||||
|
fn move_cubes_according_to_local_transform(
|
||||||
|
mut cubes: Query<&mut Transform, (With<ChangeLocal>, With<Move>)>,
|
||||||
|
direction: Res<Direction>,
|
||||||
|
timer: Res<Time>,
|
||||||
|
) {
|
||||||
|
for mut transform in cubes.iter_mut() {
|
||||||
|
transform.translation += direction.0 * timer.delta_seconds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system updates a resource that defines in which direction the cubes should move.
|
||||||
|
// The direction is defined by the input of arrow keys and is only in left/right and up/down direction.
|
||||||
|
fn update_directional_input(mut direction: ResMut<Direction>, keyboard_input: Res<Input<KeyCode>>) {
|
||||||
|
let horizontal_movement = Vec3::X
|
||||||
|
* (keyboard_input.pressed(KeyCode::Right) as i32
|
||||||
|
- keyboard_input.pressed(KeyCode::Left) as i32) as f32;
|
||||||
|
let vertical_movement = Vec3::Y
|
||||||
|
* (keyboard_input.pressed(KeyCode::Up) as i32
|
||||||
|
- keyboard_input.pressed(KeyCode::Down) as i32) as f32;
|
||||||
|
direction.0 = horizontal_movement + vertical_movement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system enables and disables the movement for each entity if their assigned key is pressed.
|
||||||
|
fn toggle_movement(
|
||||||
|
mut commands: Commands,
|
||||||
|
movable_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), With<Move>>,
|
||||||
|
static_entities: Query<(Entity, &Handle<StandardMaterial>, &ToggledBy), Without<Move>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
keyboard_input: Res<Input<KeyCode>>,
|
||||||
|
) {
|
||||||
|
// Update the currently movable entities and remove their Move component if the assigned key was pressed to disable their movement.
|
||||||
|
// This will also make them transparent so they can be identified as 'disabled' in the scene.
|
||||||
|
for (entity, material_handle, toggled_by) in movable_entities.iter() {
|
||||||
|
if keyboard_input.just_pressed(toggled_by.0) {
|
||||||
|
materials
|
||||||
|
.get_mut(material_handle)
|
||||||
|
.unwrap()
|
||||||
|
.base_color
|
||||||
|
.set_a(0.5);
|
||||||
|
commands.entity(entity).remove::<Move>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the currently non-movable entities and add a Move component if the assigned key was pressed to enable their movement.
|
||||||
|
// This will also make them opaque so they can be identified as 'enabled' in the scene.
|
||||||
|
for (entity, material_handle, toggled_by) in static_entities.iter() {
|
||||||
|
if keyboard_input.just_pressed(toggled_by.0) {
|
||||||
|
materials
|
||||||
|
.get_mut(material_handle)
|
||||||
|
.unwrap()
|
||||||
|
.base_color
|
||||||
|
.set_a(1.0);
|
||||||
|
commands.entity(entity).insert(Move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
examples/transforms/scale.rs
Normal file
96
examples/transforms/scale.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use bevy::math::Vec3Swizzles;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
// Define a component to keep information for the scaled object.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Scaling {
|
||||||
|
scale_direction: Vec3,
|
||||||
|
scale_speed: f32,
|
||||||
|
max_element_size: f32,
|
||||||
|
min_element_size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement a simple initialisation.
|
||||||
|
impl Scaling {
|
||||||
|
fn new() -> Self {
|
||||||
|
Scaling {
|
||||||
|
scale_direction: Vec3::X,
|
||||||
|
scale_speed: 2.0,
|
||||||
|
max_element_size: 5.0,
|
||||||
|
min_element_size: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(change_scale_direction)
|
||||||
|
.add_system(scale_cube)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup system to setup the scene and spawn all relevant entities.
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Spawn a cube to scale.
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(Color::WHITE.into()),
|
||||||
|
transform: Transform::from_rotation(Quat::from_rotation_y(PI / 4.0)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(Scaling::new());
|
||||||
|
|
||||||
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a light source for better 3d visibility.
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::ONE * 3.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will check if a scaled entity went above or below the entities scaling bounds
|
||||||
|
// and change the direction of the scaling vector.
|
||||||
|
fn change_scale_direction(mut cubes: Query<(&mut Transform, &mut Scaling)>) {
|
||||||
|
for (mut transform, mut cube) in cubes.iter_mut() {
|
||||||
|
// If an entity scaled beyond the maximum of its size in any dimension
|
||||||
|
// the scaling vector is flipped so the scaling is gradually reverted.
|
||||||
|
// Additionally, to ensure the condition does not trigger again we floor the elements to
|
||||||
|
// their next full value, which should be max_element_size at max.
|
||||||
|
if transform.scale.max_element() > cube.max_element_size {
|
||||||
|
cube.scale_direction *= -1.0;
|
||||||
|
transform.scale = transform.scale.floor();
|
||||||
|
}
|
||||||
|
// If an entity scaled beyond the minimum of its size in any dimension
|
||||||
|
// the scaling vector is also flipped.
|
||||||
|
// Additionally the Values are ceiled to be min_element_size at least
|
||||||
|
// and the scale direction is flipped.
|
||||||
|
// This way the entity will change the dimension in which it is scaled any time it
|
||||||
|
// reaches its min_element_size.
|
||||||
|
if transform.scale.min_element() < cube.min_element_size {
|
||||||
|
cube.scale_direction *= -1.0;
|
||||||
|
transform.scale = transform.scale.ceil();
|
||||||
|
cube.scale_direction = cube.scale_direction.zxy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will scale any entity with assigned Scaling in each direction
|
||||||
|
// by cycling through the directions to scale.
|
||||||
|
fn scale_cube(mut cubes: Query<(&mut Transform, &Scaling)>, timer: Res<Time>) {
|
||||||
|
for (mut transform, cube) in cubes.iter_mut() {
|
||||||
|
transform.scale += cube.scale_direction * cube.scale_speed * timer.delta_seconds();
|
||||||
|
}
|
||||||
|
}
|
151
examples/transforms/transform.rs
Normal file
151
examples/transforms/transform.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
// A struct for additional data of for a moving cube.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct CubeState {
|
||||||
|
start_pos: Vec3,
|
||||||
|
move_speed: f32,
|
||||||
|
turn_speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A struct adding information to a scalable entity,
|
||||||
|
// that will be stationary at the center of the scene.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Center {
|
||||||
|
max_size: f32,
|
||||||
|
min_size: f32,
|
||||||
|
scale_factor: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(move_cube)
|
||||||
|
.add_system(rotate_cube)
|
||||||
|
.add_system(scale_down_sphere_proportional_to_cube_travel_distance)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup system to setup the scene and spawn all relevant entities.
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Add an object (sphere) for visualizing scaling.
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Icosphere {
|
||||||
|
radius: 3.0,
|
||||||
|
subdivisions: 32,
|
||||||
|
})),
|
||||||
|
material: materials.add(Color::YELLOW.into()),
|
||||||
|
transform: Transform::from_translation(Vec3::ZERO),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(Center {
|
||||||
|
max_size: 1.0,
|
||||||
|
min_size: 0.1,
|
||||||
|
scale_factor: 0.05,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the cube to visualize rotation and translation.
|
||||||
|
// This cube will circle around the center_sphere
|
||||||
|
// by changing its rotation each frame and moving forward.
|
||||||
|
// Define a start transform for an orbiting cube, that's away from our central object (sphere)
|
||||||
|
// and rotate it so it will be able to move around the sphere and not towards it.
|
||||||
|
let angle_90 = PI / 2.0;
|
||||||
|
let mut cube_spawn = Transform::from_translation(Vec3::Z * -10.0);
|
||||||
|
cube_spawn.rotation = Quat::from_rotation_y(angle_90);
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(Color::WHITE.into()),
|
||||||
|
transform: cube_spawn,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(CubeState {
|
||||||
|
start_pos: cube_spawn.translation,
|
||||||
|
move_speed: 2.0,
|
||||||
|
turn_speed: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a light source for better 3d visibility.
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::ONE * 3.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will move the cube forward.
|
||||||
|
fn move_cube(mut cubes: Query<(&mut Transform, &mut CubeState)>, timer: Res<Time>) {
|
||||||
|
for (mut transform, cube) in cubes.iter_mut() {
|
||||||
|
// Move the cube forward smoothly at a given move_speed.
|
||||||
|
let forward = transform.forward();
|
||||||
|
transform.translation += forward * cube.move_speed * timer.delta_seconds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will rotate the cube slightly towards the center_sphere.
|
||||||
|
// Due to the forward movement the resulting movement
|
||||||
|
// will be a circular motion around the center_sphere.
|
||||||
|
fn rotate_cube(
|
||||||
|
mut cubes: Query<(&mut Transform, &mut CubeState), Without<Center>>,
|
||||||
|
center_spheres: Query<&Transform, With<Center>>,
|
||||||
|
timer: Res<Time>,
|
||||||
|
) {
|
||||||
|
// Calculate the point to circle around. (The position of the center_sphere)
|
||||||
|
let mut center: Vec3 = Vec3::ZERO;
|
||||||
|
for sphere in center_spheres.iter() {
|
||||||
|
center += sphere.translation;
|
||||||
|
}
|
||||||
|
// Update the rotation of the cube(s).
|
||||||
|
for (mut transform, cube) in cubes.iter_mut() {
|
||||||
|
// Calculate the rotation of the cube if it would be looking at the sphere in the center.
|
||||||
|
let look_at_sphere = transform.looking_at(center, transform.local_y());
|
||||||
|
// Interpolate between the current rotation and the fully turned rotation
|
||||||
|
// when looking a the sphere, with a given turn speed to get a smooth motion.
|
||||||
|
// With higher speed the curvature of the orbit would be smaller.
|
||||||
|
let incremental_turn_weight = cube.turn_speed * timer.delta_seconds();
|
||||||
|
let old_rotation = transform.rotation;
|
||||||
|
transform.rotation = old_rotation.lerp(look_at_sphere.rotation, incremental_turn_weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will scale down the sphere in the center of the scene
|
||||||
|
// according to the traveling distance of the orbiting cube(s) from their start position(s).
|
||||||
|
fn scale_down_sphere_proportional_to_cube_travel_distance(
|
||||||
|
cubes: Query<(&Transform, &CubeState), Without<Center>>,
|
||||||
|
mut centers: Query<(&mut Transform, &Center)>,
|
||||||
|
) {
|
||||||
|
// First we need to calculate the length of between
|
||||||
|
// the current position of the orbiting cube and the spawn position.
|
||||||
|
let mut distances = 0.0;
|
||||||
|
for (cube_transform, cube_state) in cubes.iter() {
|
||||||
|
distances += (cube_state.start_pos - cube_transform.translation).length();
|
||||||
|
}
|
||||||
|
// Now we use the calculated value to scale the sphere in the center accordingly.
|
||||||
|
for (mut transform, center) in centers.iter_mut() {
|
||||||
|
// Calculate the new size from the calculated distances and the centers scale_factor.
|
||||||
|
// Since we want to have the sphere at its max_size at the cubes spawn location we start by
|
||||||
|
// using the max_size as start value and subtract the distances scaled by a scaling factor.
|
||||||
|
let mut new_size: f32 = center.max_size - center.scale_factor * distances;
|
||||||
|
|
||||||
|
// The new size should also not be smaller than the centers min_size.
|
||||||
|
// Therefore the max value out of (new_size, center.min_size) is used.
|
||||||
|
new_size = new_size.max(center.min_size);
|
||||||
|
|
||||||
|
// Now scale the sphere uniformly in all directions using new_size.
|
||||||
|
// Here Vec3:splat is used to create a vector with new_size in x, y and z direction.
|
||||||
|
transform.scale = Vec3::splat(new_size);
|
||||||
|
}
|
||||||
|
}
|
71
examples/transforms/translation.rs
Normal file
71
examples/transforms/translation.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// Define a struct to keep some information about our entity.
|
||||||
|
// Here it's an arbitrary movement speed, the spawn location, and a maximum distance from it.
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Movable {
|
||||||
|
spawn: Vec3,
|
||||||
|
max_distance: f32,
|
||||||
|
speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement a utility function for easier Movable struct creation.
|
||||||
|
impl Movable {
|
||||||
|
fn new(spawn: Vec3) -> Self {
|
||||||
|
Movable {
|
||||||
|
spawn,
|
||||||
|
max_distance: 5.0,
|
||||||
|
speed: 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(move_cube)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup system to setup the scene and spawn all relevant entities.
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Add a cube to visualize translation.
|
||||||
|
let entity_spawn = Vec3::ZERO;
|
||||||
|
commands
|
||||||
|
.spawn_bundle(PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
material: materials.add(Color::WHITE.into()),
|
||||||
|
transform: Transform::from_translation(entity_spawn),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(Movable::new(entity_spawn));
|
||||||
|
|
||||||
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
||||||
|
commands.spawn_bundle(PerspectiveCameraBundle {
|
||||||
|
transform: Transform::from_xyz(0.0, 10.0, 20.0).looking_at(entity_spawn, Vec3::Y),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a light source for better 3d visibility.
|
||||||
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::ONE * 3.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This system will move all Movable entities with a Transform
|
||||||
|
fn move_cube(mut cubes: Query<(&mut Transform, &mut Movable)>, timer: Res<Time>) {
|
||||||
|
for (mut transform, mut cube) in cubes.iter_mut() {
|
||||||
|
// Check if the entity moved too far from its spawn, if so invert the moving direction.
|
||||||
|
if (cube.spawn - transform.translation).length() > cube.max_distance {
|
||||||
|
cube.speed *= -1.0;
|
||||||
|
}
|
||||||
|
let direction = transform.local_x();
|
||||||
|
transform.translation += direction * cube.speed * timer.delta_seconds();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue