Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/animation/color_animation.rs
6592 views
1
//! Demonstrates how to animate colors in different color spaces using mixing and splines.
2
3
use bevy::{math::VectorSpace, prelude::*};
4
5
// We define this trait so we can reuse the same code for multiple color types that may be implemented using curves.
6
trait CurveColor: VectorSpace<Scalar = f32> + Into<Color> + Send + Sync + 'static {}
7
8
impl<T: VectorSpace<Scalar = f32> + Into<Color> + Send + Sync + 'static> CurveColor for T {}
9
10
// We define this trait so we can reuse the same code for multiple color types that may be implemented using mixing.
11
trait MixedColor: Mix + Into<Color> + Send + Sync + 'static {}
12
13
impl<T: Mix + Into<Color> + Send + Sync + 'static> MixedColor for T {}
14
15
#[derive(Debug, Component)]
16
struct Curve<T: CurveColor>(CubicCurve<T>);
17
18
#[derive(Debug, Component)]
19
struct Mixed<T: MixedColor>([T; 4]);
20
21
fn main() {
22
App::new()
23
.add_plugins(DefaultPlugins)
24
.add_systems(Startup, setup)
25
.add_systems(
26
Update,
27
(
28
animate_curve::<LinearRgba>,
29
animate_curve::<Oklaba>,
30
animate_curve::<Xyza>,
31
animate_mixed::<Hsla>,
32
animate_mixed::<Srgba>,
33
animate_mixed::<Oklcha>,
34
),
35
)
36
.run();
37
}
38
39
fn setup(mut commands: Commands) {
40
commands.spawn(Camera2d);
41
42
// The color spaces `Oklaba`, `Laba`, `LinearRgba`, `Srgba` and `Xyza` all are either perceptually or physically linear.
43
// This property allows us to define curves, e.g. bezier curves through these spaces.
44
45
// Define the control points for the curve.
46
// For more information, please see the cubic curve example.
47
let colors = [
48
LinearRgba::WHITE,
49
LinearRgba::rgb(1., 1., 0.), // Yellow
50
LinearRgba::RED,
51
LinearRgba::BLACK,
52
];
53
// Spawn a sprite using the provided colors as control points.
54
spawn_curve_sprite(&mut commands, 275., colors);
55
56
// Spawn another sprite using the provided colors as control points after converting them to the `Xyza` color space.
57
spawn_curve_sprite(&mut commands, 175., colors.map(Xyza::from));
58
59
spawn_curve_sprite(&mut commands, 75., colors.map(Oklaba::from));
60
61
// Other color spaces like `Srgba` or `Hsva` are neither perceptually nor physically linear.
62
// As such, we cannot use curves in these spaces.
63
// However, we can still mix these colors and animate that way. In fact, mixing colors works in any color space.
64
65
// Spawn a sprite using the provided colors for mixing.
66
spawn_mixed_sprite(&mut commands, -75., colors.map(Hsla::from));
67
68
spawn_mixed_sprite(&mut commands, -175., colors.map(Srgba::from));
69
70
spawn_mixed_sprite(&mut commands, -275., colors.map(Oklcha::from));
71
}
72
73
fn 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
81
fn 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
}
88
89
fn animate_curve<T: CurveColor>(
90
time: Res<Time>,
91
mut query: Query<(&mut Transform, &mut Sprite, &Curve<T>)>,
92
) {
93
let t = (ops::sin(time.elapsed_secs()) + 1.) / 2.;
94
95
for (mut transform, mut sprite, cubic_curve) in &mut query {
96
// position takes a point from the curve where 0 is the initial point
97
// and 1 is the last point
98
sprite.color = cubic_curve.0.position(t).into();
99
transform.translation.x = 600. * (t - 0.5);
100
}
101
}
102
103
fn animate_mixed<T: MixedColor>(
104
time: Res<Time>,
105
mut query: Query<(&mut Transform, &mut Sprite, &Mixed<T>)>,
106
) {
107
let t = (ops::sin(time.elapsed_secs()) + 1.) / 2.;
108
109
for (mut transform, mut sprite, mixed) in &mut query {
110
sprite.color = {
111
// First, we determine the amount of intervals between colors.
112
// For four colors, there are three intervals between those colors;
113
let intervals = (mixed.0.len() - 1) as f32;
114
115
// Next we determine the index of the first of the two colors to mix.
116
let start_i = (t * intervals).floor().min(intervals - 1.);
117
118
// Lastly we determine the 'local' value of t in this interval.
119
let local_t = (t * intervals) - start_i;
120
121
let color = mixed.0[start_i as usize].mix(&mixed.0[start_i as usize + 1], local_t);
122
color.into()
123
};
124
transform.translation.x = 600. * (t - 0.5);
125
}
126
}
127
128