mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
de888a373d
# Objective Another step in the migration to required components: lights! Note that this does not include `EnvironmentMapLight` or reflection probes yet, because their API hasn't been fully chosen yet. ## Solution As per the [selected proposals](https://hackmd.io/@bevy/required_components/%2FLLnzwz9XTxiD7i2jiUXkJg): - Deprecate `PointLightBundle` in favor of the `PointLight` component - Deprecate `SpotLightBundle` in favor of the `PointLight` component - Deprecate `DirectionalLightBundle` in favor of the `DirectionalLight` component ## Testing I ran some examples with lights. --- ## Migration Guide `PointLightBundle`, `SpotLightBundle`, and `DirectionalLightBundle` have been deprecated. Use the `PointLight`, `SpotLight`, and `DirectionalLight` components instead. Adding them will now insert the other components required by them automatically.
181 lines
6.5 KiB
Rust
181 lines
6.5 KiB
Rust
//! Create and play an animation defined by code that operates on the [`Transform`] component.
|
|
|
|
use std::f32::consts::PI;
|
|
|
|
use bevy::{
|
|
animation::{AnimationTarget, AnimationTargetId},
|
|
prelude::*,
|
|
};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.insert_resource(AmbientLight {
|
|
color: Color::WHITE,
|
|
brightness: 150.0,
|
|
})
|
|
.add_systems(Startup, setup)
|
|
.run();
|
|
}
|
|
|
|
fn setup(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
mut animations: ResMut<Assets<AnimationClip>>,
|
|
mut graphs: ResMut<Assets<AnimationGraph>>,
|
|
) {
|
|
// Camera
|
|
commands.spawn(Camera3dBundle {
|
|
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
..default()
|
|
});
|
|
|
|
// Light
|
|
commands.spawn((
|
|
PointLight {
|
|
intensity: 500_000.0,
|
|
..default()
|
|
},
|
|
Transform::from_xyz(0.0, 2.5, 0.0),
|
|
));
|
|
|
|
// Let's use the `Name` component to target entities. We can use anything we
|
|
// like, but names are convenient.
|
|
let planet = Name::new("planet");
|
|
let orbit_controller = Name::new("orbit_controller");
|
|
let satellite = Name::new("satellite");
|
|
|
|
// Creating the animation
|
|
let mut animation = AnimationClip::default();
|
|
// A curve can modify a single part of a transform: here, the translation.
|
|
let planet_animation_target_id = AnimationTargetId::from_name(&planet);
|
|
animation.add_curve_to_target(
|
|
planet_animation_target_id,
|
|
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
|
|
Vec3::new(1.0, 0.0, 1.0),
|
|
Vec3::new(-1.0, 0.0, 1.0),
|
|
Vec3::new(-1.0, 0.0, -1.0),
|
|
Vec3::new(1.0, 0.0, -1.0),
|
|
// in case seamless looping is wanted, the last keyframe should
|
|
// be the same as the first one
|
|
Vec3::new(1.0, 0.0, 1.0),
|
|
]))
|
|
.map(TranslationCurve)
|
|
.expect("should be able to build translation curve because we pass in valid samples"),
|
|
);
|
|
// Or it can modify the rotation of the transform.
|
|
// To find the entity to modify, the hierarchy will be traversed looking for
|
|
// an entity with the right name at each level.
|
|
let orbit_controller_animation_target_id =
|
|
AnimationTargetId::from_names([planet.clone(), orbit_controller.clone()].iter());
|
|
animation.add_curve_to_target(
|
|
orbit_controller_animation_target_id,
|
|
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
|
|
Quat::IDENTITY,
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2.),
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
|
|
Quat::IDENTITY,
|
|
]))
|
|
.map(RotationCurve)
|
|
.expect("Failed to build rotation curve"),
|
|
);
|
|
// If a curve in an animation is shorter than the other, it will not repeat
|
|
// until all other curves are finished. In that case, another animation should
|
|
// be created for each part that would have a different duration / period.
|
|
let satellite_animation_target_id = AnimationTargetId::from_names(
|
|
[planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
|
|
);
|
|
animation.add_curve_to_target(
|
|
satellite_animation_target_id,
|
|
UnevenSampleAutoCurve::new(
|
|
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
|
|
.into_iter()
|
|
.zip([
|
|
Vec3::splat(0.8),
|
|
Vec3::splat(1.2),
|
|
Vec3::splat(0.8),
|
|
Vec3::splat(1.2),
|
|
Vec3::splat(0.8),
|
|
Vec3::splat(1.2),
|
|
Vec3::splat(0.8),
|
|
Vec3::splat(1.2),
|
|
Vec3::splat(0.8),
|
|
]),
|
|
)
|
|
.map(ScaleCurve)
|
|
.expect("Failed to build scale curve"),
|
|
);
|
|
// There can be more than one curve targeting the same entity path.
|
|
animation.add_curve_to_target(
|
|
AnimationTargetId::from_names(
|
|
[planet.clone(), orbit_controller.clone(), satellite.clone()].iter(),
|
|
),
|
|
UnevenSampleAutoCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
|
|
Quat::IDENTITY,
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2.),
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
|
|
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
|
|
Quat::IDENTITY,
|
|
]))
|
|
.map(RotationCurve)
|
|
.expect("should be able to build translation curve because we pass in valid samples"),
|
|
);
|
|
|
|
// Create the animation graph
|
|
let (graph, animation_index) = AnimationGraph::from_clip(animations.add(animation));
|
|
|
|
// Create the animation player, and set it to repeat
|
|
let mut player = AnimationPlayer::default();
|
|
player.play(animation_index).repeat();
|
|
|
|
// Create the scene that will be animated
|
|
// First entity is the planet
|
|
let planet_entity = commands
|
|
.spawn((
|
|
PbrBundle {
|
|
mesh: meshes.add(Sphere::default()),
|
|
material: materials.add(Color::srgb(0.8, 0.7, 0.6)),
|
|
..default()
|
|
},
|
|
// Add the animation graph and player
|
|
planet,
|
|
graphs.add(graph),
|
|
player,
|
|
))
|
|
.id();
|
|
commands
|
|
.entity(planet_entity)
|
|
.insert(AnimationTarget {
|
|
id: planet_animation_target_id,
|
|
player: planet_entity,
|
|
})
|
|
.with_children(|p| {
|
|
// This entity is just used for animation, but doesn't display anything
|
|
p.spawn((
|
|
SpatialBundle::INHERITED_IDENTITY,
|
|
orbit_controller,
|
|
AnimationTarget {
|
|
id: orbit_controller_animation_target_id,
|
|
player: planet_entity,
|
|
},
|
|
))
|
|
.with_children(|p| {
|
|
// The satellite, placed at a distance of the planet
|
|
p.spawn((
|
|
PbrBundle {
|
|
transform: Transform::from_xyz(1.5, 0.0, 0.0),
|
|
mesh: meshes.add(Cuboid::new(0.5, 0.5, 0.5)),
|
|
material: materials.add(Color::srgb(0.3, 0.9, 0.3)),
|
|
..default()
|
|
},
|
|
AnimationTarget {
|
|
id: satellite_animation_target_id,
|
|
player: planet_entity,
|
|
},
|
|
satellite,
|
|
));
|
|
});
|
|
});
|
|
}
|