Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ bevy_text = ["bevy/bevy_text", "bevy/bevy_render", "bevy/bevy_sprite"]

[dependencies]
# Note: abuse 'bevy_color' to force 'bevy_math/curve' feature, which defines EaseFunction
bevy = { version = "0.18", default-features = false, features = [
bevy = { version = "0.19", default-features = false, features = [
"bevy_color",
"bevy_asset",
"bevy_log",
] }
thiserror = "2"

[dev-dependencies]
bevy-inspector-egui = { version = "0.36", default-features = false, features = [
bevy-inspector-egui = { version = "0.37", default-features = false, features = [
"bevy_render",
"bevy_pbr",
] }
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ criterion = { version = "0.5", features = ["html_reports"] }
bevy_tweening = { path = "../" }

[dependencies.bevy]
version = "0.18"
version = "0.19"
default-features = false
features = ["bevy_render", "bevy_sprite", "bevy_text", "bevy_ui"]

Expand Down
23 changes: 12 additions & 11 deletions examples/ambient_light.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
//! Example demonstrating resource animation and various transform shortcuts.
//!
//! The example animates the `AmbientLight` resource of Bevy's PBR renderer.
//! This is mostly for example purpose; you probably want to animate some other
//! (custom) resource. It also moves a capsule object back and forth with the
//! `move_to()` command extension, and make it "resonate" by quickly scaling it
//! between 100% and 110% size with the `scale_to()` command extension.
//! The example animates the `GlobalAmbientLight` resource of Bevy's PBR
//! renderer. This is mostly for example purpose; you probably want to animate
//! some other (custom) resource. It also moves a capsule object back and forth
//! with the `move_to()` command extension, and make it "resonate" by quickly
//! scaling it between 100% and 110% size with the `scale_to()` command
//! extension.

use std::{
f32::consts::{FRAC_PI_2, FRAC_PI_4},
time::Duration,
};

use bevy::{color::palettes::css::*, post_process::bloom::Bloom, prelude::*, render::view::Hdr};
use bevy::{camera::Hdr, color::palettes::css::*, post_process::bloom::Bloom, prelude::*};
use bevy_tweening::{lens::*, *};

mod utils;

// Define our own `Lens` to animate the `AmbientLight` resource.
// Define our own `Lens` to animate the `GlobalAmbientLight` resource.
struct AmbientLightBrightnessLens {
pub start: f32,
pub end: f32,
}

// Implement the `Lens` trait.
impl Lens<AmbientLight> for AmbientLightBrightnessLens {
fn lerp(&mut self, mut target: Mut<AmbientLight>, ratio: f32) {
impl Lens<GlobalAmbientLight> for AmbientLightBrightnessLens {
fn lerp(&mut self, mut target: Mut<GlobalAmbientLight>, ratio: f32) {
target.brightness = self.start.lerp(self.end, ratio);
}
}
Expand All @@ -50,7 +51,7 @@ fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut ambient_light: ResMut<AmbientLight>,
mut ambient_light: ResMut<GlobalAmbientLight>,
) -> Result<(), BevyError> {
// Some fancy 3D camera with HDR and bloom, to emphasize the change of ambient
// brightness.
Expand Down Expand Up @@ -97,7 +98,7 @@ fn setup(
.with_repeat(RepeatCount::Infinite, RepeatStrategy::MirroredRepeat);
commands.spawn((
TweenAnim::new(tween),
AnimTarget::resource::<AmbientLight>(),
AnimTarget::resource::<GlobalAmbientLight>(),
));

// Spawn some animated character-like capsule...
Expand Down
9 changes: 6 additions & 3 deletions examples/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,15 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
children![(
Text::new(text.to_string()),
TextFont {
font: font.clone(),
font_size: 48.0,
font: font.clone().into(),
font_size: 48.0.into(),
..default()
},
TextColor(TEXT_COLOR),
TextLayout::new_with_justify(Justify::Center),
TextLayout {
justify: Justify::Center,
..default()
},
)],
))
.id();
Expand Down
14 changes: 10 additions & 4 deletions examples/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) -> Result<()> {

let font = asset_server.load("fonts/FiraMono-Regular.ttf");
let text_font = TextFont {
font,
font_size: 50.0,
font: font.into(),
font_size: 50.0.into(),
..default()
};

Expand All @@ -57,7 +57,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) -> Result<()> {
commands
.spawn((
Text2d::default(),
TextLayout::new_with_justify(justify),
TextLayout {
justify,
..default()
},
Transform::from_translation(Vec3::new(0., 40., 0.)),
RedProgress,
))
Expand All @@ -79,7 +82,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) -> Result<()> {
commands
.spawn((
Text2d::default(),
TextLayout::new_with_justify(justify),
TextLayout {
justify,
..default()
},
Transform::from_translation(Vec3::new(0., -40., 0.)),
BlueProgress,
))
Expand Down
4 changes: 2 additions & 2 deletions examples/text_color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
Text::new(*ease_name),
TextFont {
font: font.clone(),
font_size: 24.0,
font: font.clone().into(),
font_size: 24.0.into(),
..default()
},
TextColor(Color::WHITE),
Expand Down
6 changes: 3 additions & 3 deletions src/lens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ mod tests {
let mut added = Tick::new(0);
let mut last_changed = Tick::new(0);
let mut caller = MaybeLocation::caller();
let asset = assets.get_mut(handle.id()).unwrap();
let asset = assets.get_mut_untracked(handle.id()).unwrap();
let target = Mut::new(
asset,
&mut added,
Expand All @@ -1167,7 +1167,7 @@ mod tests {
let mut added = Tick::new(0);
let mut last_changed = Tick::new(0);
let mut caller = MaybeLocation::caller();
let asset = assets.get_mut(handle.id()).unwrap();
let asset = assets.get_mut_untracked(handle.id()).unwrap();
let target = Mut::new(
asset,
&mut added,
Expand All @@ -1184,7 +1184,7 @@ mod tests {
let mut added = Tick::new(0);
let mut last_changed = Tick::new(0);
let mut caller = MaybeLocation::caller();
let asset = assets.get_mut(handle.id()).unwrap();
let asset = assets.get_mut_untracked(handle.id()).unwrap();
let target = Mut::new(
asset,
&mut added,
Expand Down
56 changes: 45 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ pub(crate) struct MoveToCommand {
}

impl EntityCommand for MoveToCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(start) = entity.get::<Transform>().map(|tr| tr.translation) {
let lens = TransformPositionLens {
Expand Down Expand Up @@ -950,6 +951,7 @@ pub(crate) struct MoveFromCommand {
}

impl EntityCommand for MoveFromCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(end) = entity.get::<Transform>().map(|tr| tr.translation) {
let lens = TransformPositionLens {
Expand Down Expand Up @@ -985,6 +987,7 @@ pub(crate) struct ScaleToCommand {
}

impl EntityCommand for ScaleToCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(start) = entity.get::<Transform>().map(|tr| tr.scale) {
let lens = TransformScaleLens {
Expand Down Expand Up @@ -1020,6 +1023,7 @@ pub(crate) struct ScaleFromCommand {
}

impl EntityCommand for ScaleFromCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(end) = entity.get::<Transform>().map(|tr| tr.scale) {
let lens = TransformScaleLens {
Expand Down Expand Up @@ -1054,6 +1058,7 @@ pub(crate) struct RotateXCommand {
}

impl EntityCommand for RotateXCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveXLens {
Expand Down Expand Up @@ -1090,6 +1095,7 @@ pub(crate) struct RotateYCommand {
}

impl EntityCommand for RotateYCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveYLens {
Expand Down Expand Up @@ -1126,6 +1132,7 @@ pub(crate) struct RotateZCommand {
}

impl EntityCommand for RotateZCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveZLens {
Expand Down Expand Up @@ -1163,6 +1170,7 @@ pub(crate) struct RotateXByCommand {
}

impl EntityCommand for RotateXByCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveXLens {
Expand Down Expand Up @@ -1199,6 +1207,7 @@ pub(crate) struct RotateYByCommand {
}

impl EntityCommand for RotateYByCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveYLens {
Expand Down Expand Up @@ -1235,6 +1244,7 @@ pub(crate) struct RotateZByCommand {
}

impl EntityCommand for RotateZByCommand {
type Out = ();
fn apply(self, mut entity: EntityWorldMut) {
if let Some(base_rotation) = entity.get::<Transform>().map(|tr| tr.rotation) {
let lens = TransformRotateAdditiveZLens {
Expand Down Expand Up @@ -2200,10 +2210,10 @@ impl TweenAnim {
.get_id(type_id)
.ok_or(TweeningError::ComponentNotRegistered(type_id))?,
AnimTargetKind::Resource => components
.get_resource_id(type_id)
.get_id(type_id)
.ok_or(TweeningError::ResourceNotRegistered(type_id))?,
AnimTargetKind::Asset { assets_type_id, .. } => components
.get_resource_id(*assets_type_id)
.get_id(*assets_type_id)
.ok_or(TweeningError::AssetNotRegistered(type_id))?,
};
let is_retargetable = false; // explicit target
Expand Down Expand Up @@ -2622,7 +2632,7 @@ pub struct TweenResolver {
impl TweenResolver {
/// Register a resolver for the given resource type.
pub(crate) fn register_resource_resolver_for<R: Resource>(&mut self, components: &Components) {
let resource_id = components.resource_id::<R>().unwrap();
let resource_id = components.component_id::<R>().unwrap();
let resolver = |world: &mut World,
entity: Entity,
target_type_id: &TypeId,
Expand Down Expand Up @@ -2669,7 +2679,7 @@ impl TweenResolver {

/// Register a resolver for the given asset type.
pub(crate) fn register_asset_resolver_for<A: Asset>(&mut self, components: &Components) {
let resource_id = components.resource_id::<Assets<A>>().unwrap();
let resource_id = components.component_id::<Assets<A>>().unwrap();
let resolver = |world: &mut World,
asset_id: UntypedAssetId,
entity: Entity,
Expand All @@ -2681,9 +2691,19 @@ impl TweenResolver {
let asset_id = asset_id.typed::<A>();
// First, remove the Assets<A> from the world so we can access it mutably in
// parallel of the TweenAnim
world.resource_scope(|world, assets: Mut<Assets<A>>| {
// Next, fetch the asset A itself from its Assets<A> based on its asset ID
let Some(asset) = assets.filter_map_unchanged(|assets| assets.get_mut(asset_id))
world.resource_scope(|world, mut assets: Mut<Assets<A>>| {
// Make a copy of the Mut<Assets<A>> so we can use it later to mark the
// asset as modified if needed. This doesn't copy the asset, only the
// Mut<> itself.
let assets_mut = assets.reborrow();

// Next, fetch the asset A itself from its Assets<A> based on its asset ID.
// This is a bit convoluted because we want to keep a Mut<A>, and not the
// new AssetMut<A>, which unfortunately has nothing to do with Mut<A>.
// We also don't want to trigger any change detection (ECS) or asset mutation
// (Assets<A>) yet, as we only do that lazily when we detected the animation
// actually modified the asset.
let Some(asset_mut) = assets_mut.filter_map_unchanged(|assets| assets.get_mut_untracked(asset_id))
else {
return Err(TweeningError::InvalidAssetId(asset_id.into()));
};
Expand All @@ -2704,16 +2724,30 @@ impl TweenResolver {
};

// Finally, step the TweenAnim and mutate the target
let mut mut_untyped: MutUntyped<'_> = asset_mut.into();
let ret = anim.step_self(
commands,
entity,
delta_time,
&target,
asset.into(),
mut_untyped.reborrow(),
target_type_id,
cycle_events.reborrow(),
anim_events.reborrow(),
);

// If the asset actually changed (as reported by Mut<>), mark it as such
// through Assets<A> (which will send the AssetModified message). This works
// because all Mut<> from reborrow() share the same internal tracking fields,
// so even if we passed a Mut<>::reborrow() copy, the source Mut<> "sees" the
// same change. However that change detection only marks Assets<A> as changed,
// and doesn't trigger the regular flow for a modified asset.
if mut_untyped.is_changed() {
// into_inner() marks the asset as changed, which triggers the regular
// asset modified flow (as if we had used AssetMut<A>).
let _ = assets.get_mut(asset_id).unwrap().into_inner();
}

ret.map(|result| {
assert!(!result.needs_retarget, "Cannot use a multi-target sequence of tweenable animations with an asset target.");
result.retain
Expand Down Expand Up @@ -2916,7 +2950,7 @@ mod tests {
let mut added = Tick::new(0);
let mut last_changed = Tick::new(0);
let mut caller = MaybeLocation::caller();
let asset = assets.get_mut(handle.id()).unwrap();
let asset = assets.get_mut_untracked(handle.id()).unwrap();
let target = Mut::new(
asset,
&mut added,
Expand Down Expand Up @@ -3800,7 +3834,7 @@ mod tests {
env.world.flush();

let delta_time = Duration::from_millis(200);
let resource_id = env.world.resource_id::<DummyResource>().unwrap();
let resource_id = env.world.component_id::<DummyResource>().unwrap();

// Resource resolver not registered; fails
env.world
Expand Down Expand Up @@ -3882,7 +3916,7 @@ mod tests {
env.world.flush();

let delta_time = Duration::from_millis(200);
let resource_id = env.world.resource_id::<Assets<DummyAsset>>().unwrap();
let resource_id = env.world.component_id::<Assets<DummyAsset>>().unwrap();

// Asset resolver not registered; fails
env.world
Expand Down
4 changes: 2 additions & 2 deletions src/tweenable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1933,8 +1933,8 @@ mod tests {

// Messages are only sent when playing forward
if playback_direction.is_forward() {
//let component_id = world.component_id::<Transform>().unwrap();
let mut event_reader = event_reader_system_state.get_mut(&mut world);
let mut event_reader =
event_reader_system_state.get_mut(&mut world).unwrap();
let event = event_reader.read().next();
if just_completed {
assert!(event.is_some());
Expand Down
Loading