From ee61b507d291f43c280065306acf2990e0f325cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Thulliez?=
<43185161+grzi@users.noreply.github.com>
Date: Sun, 15 Aug 2021 02:49:44 +0200
Subject: [PATCH] Feat/blink and layer to z (#168)
* feat: Blink animation
* chore: Rename layer to z in coords and position
---
README.md | 18 +++++-----
.../bomberman/character_control_system.rs | 14 ++++----
examples/bomberman/main.rs | 4 +--
examples/mario/main.rs | 4 +--
examples/tetris/layer.rs | 8 ++---
examples/tetris/systems/piece_system.rs | 2 +-
examples/world-cup/main.rs | 13 +++++---
src/core/components/animations.rs | 33 +++++++++++++------
src/core/components/maths/coordinates.rs | 10 +++---
src/core/components/maths/transform.rs | 14 ++++----
src/core/components/tiles/tilemap.rs | 6 ++--
src/core/systems/animations_system.rs | 24 ++++++++++++++
src/core/systems/ui_text_system.rs | 2 +-
src/rendering/gl_representations.rs | 2 +-
src/rendering/scion2d.rs | 8 ++---
src/utils/maths.rs | 14 ++++----
16 files changed, 107 insertions(+), 69 deletions(-)
diff --git a/README.md b/README.md
index 6b80c98..cd55044 100644
--- a/README.md
+++ b/README.md
@@ -7,27 +7,26 @@ Scion is a 2D game library made in rust.
## Why this project ?
-Well, firstly because it' a good way to learn.
+Well, firstly because it' a good way to learn.
+Then, because I wanted to create something with modest goals, focused on ease of use and a short list of principles that also serves as a guideline.
-Then because a lot of projects these days focus on adding a lot of feature pretexting feature parity with big editors or game engines.
-Here I focus on the features that I really need for my projects. I won't add things just because they are cool, but because I need them in
-a game project
-
-Scion relies on a short list of principles that also serves as a guideline.
+Scion is not 'inspired' by any other engine, it's inspired by the needs from real projects.
### Goals
- Strong focus on **2D** only.
- **Easy** and **Fun** to use.
-- Clean and readable source code.
-- Tiled integration
-- Maybe one day (Editor)
+- Clean and readable source code
+- I hope one day (Editor)
### Non goals
- Ultra/over optimized code and performances. For this, please try other engines or build your own !
- 3D
+## Documentations, Tutorials ?
+
+Yes, they are planned and being worked on. If you want to see scion in action, check the examples for now.
## Why ECS ?
@@ -46,7 +45,6 @@ These are the dependencies this project is relying on. Thanks to these awesome c
- legion (Entity component system)
- ultraviolet (Maths)
-
## Showcase
diff --git a/examples/bomberman/character_control_system.rs b/examples/bomberman/character_control_system.rs
index 5e10824..d038b26 100644
--- a/examples/bomberman/character_control_system.rs
+++ b/examples/bomberman/character_control_system.rs
@@ -26,7 +26,7 @@ pub fn controller(
.get(posx + 1).unwrap() == &0
{
character.pos_x += 1;
- animations.run_animation("MOVE_RIGHT".to_string());
+ animations.run_animation("MOVE_RIGHT");
}
});
inputs.keyboard_mut().on_key_pressed(KeyCode::Left, || {
@@ -37,7 +37,7 @@ pub fn controller(
.get(posx - 1).unwrap() == &0
{
character.pos_x -= 1;
- animations.run_animation("MOVE_LEFT".to_string());
+ animations.run_animation("MOVE_LEFT");
}
});
inputs.keyboard_mut().on_key_pressed(KeyCode::Up, || {
@@ -48,7 +48,7 @@ pub fn controller(
.get(posx).unwrap() == &0
{
character.pos_y -= 1;
- animations.run_animation("MOVE_TOP".to_string());
+ animations.run_animation("MOVE_TOP");
}
});
inputs.keyboard_mut().on_key_pressed(KeyCode::Down, || {
@@ -59,15 +59,15 @@ pub fn controller(
.get(posx).unwrap() == &0
{
character.pos_y += 1;
- animations.run_animation("MOVE_BOTTOM".to_string());
+ animations.run_animation("MOVE_BOTTOM");
}
});
inputs.keyboard_mut().on_key_pressed(KeyCode::Space, || {
let mut animations =
- Animations::single("EXPLODE".to_string(), bomb_animations::explode());
- animations.run_animation("EXPLODE".to_string());
+ Animations::single("EXPLODE", bomb_animations::explode());
+ animations.run_animation("EXPLODE");
cmd.push((
- Transform::from_xy_with_layer(
+ Transform::from_xyz(
(character.pos_x * 64) as f32,
(character.pos_y * 64) as f32,
level_data.tilemap.len() + 1,
diff --git a/examples/bomberman/main.rs b/examples/bomberman/main.rs
index d5ba069..5bd3210 100644
--- a/examples/bomberman/main.rs
+++ b/examples/bomberman/main.rs
@@ -74,7 +74,7 @@ impl SimpleGameLayer for BombermanLayer {
Some(
*level
.tilemap
- .get(p.layer())
+ .get(p.z())
.unwrap()
.values
.get(p.y())
@@ -106,7 +106,7 @@ fn create_char(
level: &Level,
) -> (Transform, Sprite, AssetRef, Animations, BombermanInfos) {
(
- Transform::from_xy_with_layer(
+ Transform::from_xyz(
(level.character_x * 64) as f32,
(level.character_y * 64) as f32,
level.tilemap.len() + 2,
diff --git a/examples/mario/main.rs b/examples/mario/main.rs
index 9f6c215..b33afec 100644
--- a/examples/mario/main.rs
+++ b/examples/mario/main.rs
@@ -146,7 +146,7 @@ fn add_background(world: &mut World) {
let background = (
Rectangle::new(2560., 640., None),
Material::Texture(app_base_path().join("examples/mario/assets/level.png").get()),
- Transform::from_xy_with_layer(0., 0., 1),
+ Transform::from_xyz(0., 0., 1),
);
world.push(background);
}
@@ -160,7 +160,7 @@ fn add_character(world: &mut World) -> Entity {
ColliderType::Square(64),
),
Square::new(64., None),
- Transform::from_xy_with_layer(256., 320., 2),
+ Transform::from_xyz(256., 320., 2),
Material::Color(Color::new_rgb(100, 120, 23)),
))
}
diff --git a/examples/tetris/layer.rs b/examples/tetris/layer.rs
index 95a7c33..e3c4f9a 100644
--- a/examples/tetris/layer.rs
+++ b/examples/tetris/layer.rs
@@ -62,14 +62,14 @@ fn add_score_ui(world: &mut World) -> Entity {
let txt = UiText::new("SCORE".to_string(), font.clone());
let mut transform = Transform::default();
transform.append_translation(394., 250.);
- transform.set_layer(2);
+ transform.set_z(2);
world.push((txt, transform));
let txt = UiText::new("".to_string(), font);
let mut transform = Transform::default();
transform.append_translation(394., 290.);
- transform.set_layer(2);
+ transform.set_z(2);
world.push((txt, transform))
}
@@ -78,7 +78,7 @@ fn add_main_ui_mask(world: &mut World) {
let image = UiImage::new(544., 704., path);
let mut t = Transform::default();
- t.set_layer(0);
+ t.set_z(0);
world.push((image, t));
}
@@ -87,7 +87,7 @@ fn add_ui_top_overflow(world: &mut World) {
let image = UiImage::new(324., 32., path);
let mut t = Transform::default();
- t.set_layer(2);
+ t.set_z(2);
t.append_translation(32., 0.);
world.push((image, t));
}
diff --git a/examples/tetris/systems/piece_system.rs b/examples/tetris/systems/piece_system.rs
index 3355e33..d616550 100644
--- a/examples/tetris/systems/piece_system.rs
+++ b/examples/tetris/systems/piece_system.rs
@@ -105,7 +105,7 @@ pub fn initialize_bloc(
coord_x * BLOC_SIZE + offset.0 * BLOC_SIZE,
coord_y * BLOC_SIZE + offset.1 * BLOC_SIZE,
);
- bloc_transform.set_layer(1);
+ bloc_transform.set_z(1);
let tuple = (
bloc_transform,
Sprite::new(if is_next_bloc { tetris.next_piece.color } else { tetris.active_piece.color }),
diff --git a/examples/world-cup/main.rs b/examples/world-cup/main.rs
index 80f9679..105187f 100644
--- a/examples/world-cup/main.rs
+++ b/examples/world-cup/main.rs
@@ -27,19 +27,18 @@ pub struct WorldCup {
impl SimpleGameLayer for WorldCup {
fn on_start(&mut self, world: &mut World, _resources: &mut Resources) {
let animation = Animation::new(
- Duration::from_millis(2000),
- vec![AnimationModifier::color(60, Color::new(125, 176, 0, 1.0))],
+ Duration::from_millis(500),
+ vec![AnimationModifier::blink(1)],
false,
);
- let animations = Animations::single("color".to_string(), animation);
+ let animations = Animations::single("color", animation);
self.entity = Some(world.push((
Square::new(500., None),
Transform::from_xy(100., 100.),
Material::Color(Color::new(0, 0, 255, 1.0)),
animations,
- Hide,
)));
world.push((
@@ -56,7 +55,11 @@ impl SimpleGameLayer for WorldCup {
let mut entry = world.entry_mut(*self.entity.as_ref().unwrap()).unwrap();
let animations = entry.get_component_mut::().unwrap();
resources.inputs().keyboard_mut().on_key_pressed(KeyCode::P, || {
- animations.run_animation("color".to_string());
+ if animations.any_animation_running() {
+ animations.stop_animation("color",false);
+ }else{
+ animations.loop_animation("color");
+ }
});
}
}
diff --git a/src/core/components/animations.rs b/src/core/components/animations.rs
index ce69265..9d9a0fb 100644
--- a/src/core/components/animations.rs
+++ b/src/core/components/animations.rs
@@ -17,17 +17,17 @@ impl Animations {
pub fn new(animations: HashMap) -> Self { Animations { animations } }
/// Create a new Animations component with a single animation provided
- pub fn single(name: String, animation: Animation) -> Self {
+ pub fn single(name: &str, animation: Animation) -> Self {
let mut animations = HashMap::new();
- animations.insert(name, animation);
+ animations.insert(name.to_string(), animation);
Animations { animations }
}
- fn run(&mut self, animation_name: String, status: AnimationStatus) -> bool {
- if self.animations.contains_key(animation_name.as_str()) {
+ fn run(&mut self, animation_name: &str, status: AnimationStatus) -> bool {
+ if self.animations.contains_key(animation_name) {
let mut animation = self
.animations
- .get_mut(animation_name.as_str())
+ .get_mut(animation_name)
.expect("An animation has not been found after the security check");
if AnimationStatus::STOPPED == animation.status {
animation.status = status;
@@ -41,21 +41,21 @@ impl Animations {
}
/// Runs the animation `name`. Returns true is the animation has been started, false if it does not exist or was already running
- pub fn run_animation(&mut self, animation_name: String) -> bool {
+ pub fn run_animation(&mut self, animation_name: &str) -> bool {
self.run(animation_name, AnimationStatus::RUNNING)
}
/// Runs the animation `name`. Returns true is the animation has been started, false if it does not exist or was already running
- pub fn loop_animation(&mut self, animation_name: String) -> bool {
+ pub fn loop_animation(&mut self, animation_name: &str) -> bool {
self.run(animation_name, AnimationStatus::LOOPING)
}
/// Stops the animation `name`. Returns true is the animation has been stopped, false if it does not exist or was already stopped
- pub fn stop_animation(&mut self, animation_name: String, force: bool) -> bool {
- if self.animations.contains_key(animation_name.as_str()) {
+ pub fn stop_animation(&mut self, animation_name: &str, force: bool) -> bool {
+ if self.animations.contains_key(animation_name) {
let mut animation = self
.animations
- .get_mut(animation_name.as_str())
+ .get_mut(animation_name)
.expect("An animation has not been found after the security check");
if animation.status == AnimationStatus::LOOPING
|| animation.status == AnimationStatus::RUNNING
@@ -185,6 +185,7 @@ impl AnimationModifier {
)
}
+ /// Convenience function to create a color animation
pub fn color(number_of_keyframes: usize, target_color: Color) -> Self {
AnimationModifier::new(
number_of_keyframes,
@@ -192,6 +193,11 @@ impl AnimationModifier {
)
}
+ /// Convenience function to create a blink animation.
+ pub fn blink(number_of_blinks: usize) -> Self {
+ AnimationModifier::new(number_of_blinks * 2, AnimationModifierType::Blink)
+ }
+
pub(crate) fn compute_keyframe_modifier_for_animation(&mut self, initial_color: &Color) {
self.single_keyframe_modifier = match &self.modifier_type {
AnimationModifierType::Color { target } => {
@@ -220,6 +226,7 @@ pub enum AnimationModifierType {
TransformModifier { vector: Option, scale: Option, rotation: Option },
SpriteModifier { tile_numbers: Vec, end_tile_number: usize },
Color { target: Color },
+ Blink
}
pub(crate) enum ComputedKeyframeModifier {
@@ -242,6 +249,9 @@ impl Display for AnimationModifier {
AnimationModifierType::Color { .. } => {
"Color"
}
+ AnimationModifierType::Blink => {
+ "Blink"
+ }
}
)
}
@@ -264,6 +274,9 @@ fn compute_animation_keyframe_modifier(modifier: &mut AnimationModifier) {
// We can't compute here because we need the initial color
None
}
+ AnimationModifierType::Blink => {
+ None
+ }
};
}
diff --git a/src/core/components/maths/coordinates.rs b/src/core/components/maths/coordinates.rs
index 70610c5..61a2a2c 100644
--- a/src/core/components/maths/coordinates.rs
+++ b/src/core/components/maths/coordinates.rs
@@ -3,23 +3,23 @@
pub struct Coordinates {
pub(crate) x: f32,
pub(crate) y: f32,
- pub(crate) layer: usize,
+ pub(crate) z: usize,
}
impl Coordinates {
- pub fn new(x: f32, y: f32) -> Self { Self { x, y, layer: 0 } }
+ pub fn new(x: f32, y: f32) -> Self { Self { x, y, z: 0 } }
- pub fn new_with_layer(x: f32, y: f32, layer: usize) -> Self { Self { x, y, layer } }
+ pub fn new_with_z(x: f32, y: f32, layer: usize) -> Self { Self { x, y, z: layer } }
pub fn x(&self) -> f32 { self.x }
pub fn y(&self) -> f32 { self.y }
- pub fn layer(&self) -> usize { self.layer }
+ pub fn z(&self) -> usize { self.z }
pub fn set_x(&mut self, x: f32) { self.x = x }
pub fn set_y(&mut self, y: f32) { self.y = y; }
- pub fn set_layer(&mut self, layer: usize) { self.layer = layer; }
+ pub fn set_z(&mut self, z: usize) { self.z = z; }
}
diff --git a/src/core/components/maths/transform.rs b/src/core/components/maths/transform.rs
index b5b6c0e..061af44 100644
--- a/src/core/components/maths/transform.rs
+++ b/src/core/components/maths/transform.rs
@@ -52,8 +52,8 @@ impl Transform {
pub fn from_xy(x: f32, y: f32) -> Self { Self::new(Coordinates::new(x, y), 1., 0.) }
- pub fn from_xy_with_layer(x: f32, y: f32, layer: usize) -> Self {
- Self::new(Coordinates::new_with_layer(x, y, layer), 1., 0.)
+ pub fn from_xyz(x: f32, y: f32, z: usize) -> Self {
+ Self::new(Coordinates::new_with_z(x, y, z), 1., 0.)
}
/// Append a translation to this transform's position
@@ -105,8 +105,8 @@ impl Transform {
/// Change the scale value to a new one.
pub fn set_scale(&mut self, scale: f32) { self.scale = scale }
- /// Change the layer value in the coordinates.
- pub fn set_layer(&mut self, layer: usize) { self.local_translation.layer = layer }
+ /// Change the z value in the coordinates.
+ pub fn set_z(&mut self, z: usize) { self.local_translation.z = z }
/// Configure the minimum global x position for this transform to be min_x
pub fn set_min_x(&mut self, min_x: Option) {
@@ -152,7 +152,7 @@ impl Transform {
let mut new_global = parent_translation.clone();
new_global.x += self.local_translation.x;
new_global.y += self.local_translation.y;
- new_global.layer = self.local_translation.layer;
+ new_global.z = self.local_translation.z;
self.global_translation = new_global;
self.dirty = true;
self.handle_bounds();
@@ -198,8 +198,8 @@ impl TransformBuilder {
self.transform.global_translation = translation;
self
}
- pub fn with_translation(mut self, x: f32, y: f32, layer: usize) -> Self {
- let translation = Coordinates::new_with_layer(x, y, layer);
+ pub fn with_translation(mut self, x: f32, y: f32, z: usize) -> Self {
+ let translation = Coordinates::new_with_z(x, y, z);
self.transform.local_translation = translation;
self.transform.global_translation = translation;
self
diff --git a/src/core/components/tiles/tilemap.rs b/src/core/components/tiles/tilemap.rs
index 067921f..6eae67a 100644
--- a/src/core/components/tiles/tilemap.rs
+++ b/src/core/components/tiles/tilemap.rs
@@ -65,8 +65,8 @@ impl Tilemap {
for x in 0..infos.dimensions.width() {
for y in 0..infos.dimensions.height() {
- for layer in 0..infos.dimensions.number_of_layers() {
- let position = Position::new(x, y, layer);
+ for z in 0..infos.dimensions.depth() {
+ let position = Position::new(x, y, z);
let tile_infos = tile_resolver(&position);
let entity = world
@@ -78,7 +78,7 @@ impl Tilemap {
if let Some(animation) = tile_infos.animation {
world.entry(entity).unwrap().add_component(Animations::single(
- "TileAnimation".to_string(),
+ "TileAnimation",
animation,
));
}
diff --git a/src/core/systems/animations_system.rs b/src/core/systems/animations_system.rs
index 4cec8de..71fab6d 100644
--- a/src/core/systems/animations_system.rs
+++ b/src/core/systems/animations_system.rs
@@ -13,18 +13,22 @@ use crate::core::{
},
resources::time::{TimerType, Timers},
};
+use legion::systems::CommandBuffer;
+use crate::core::components::Hide;
/// System responsible of applying modifiers data to the dedicated components
/// It will use timers to keep track of the animation and will merge keyframes in case
/// of long frames.
#[system(for_each)]
pub(crate) fn animation_executer(
+ cmd: &mut CommandBuffer,
#[resource] timers: &mut Timers,
entity: &Entity,
animations: &mut Animations,
mut transform: Option<&mut Transform>,
mut sprite: Option<&mut Sprite>,
mut material: Option<&mut Material>,
+ hide: Option<&Hide>
) {
animations
.animations_mut()
@@ -73,6 +77,9 @@ pub(crate) fn animation_executer(
AnimationModifierType::Color { .. } => {
apply_color_modifier(material.as_mut(), modifier, timer_cycle)
}
+ AnimationModifierType::Blink => {
+ apply_blink_modifier(cmd, entity, modifier, timer_cycle, hide)
+ }
}
modifier.current_keyframe += timer_cycle;
if modifier.current_keyframe >= modifier.number_of_keyframes {
@@ -168,3 +175,20 @@ fn apply_color_modifier(
}
}
}
+
+fn apply_blink_modifier(cmd: &mut CommandBuffer,
+ entity: &Entity,
+ modifier: &mut AnimationModifier,
+ timer_cycle: usize,
+ hide: Option<&Hide>) {
+
+ if timer_cycle > 0 {
+ if modifier.will_be_last_keyframe(timer_cycle) {
+ cmd.remove_component::(*entity);
+ } else if hide.is_none(){
+ cmd.add_component(*entity, Hide);
+ } else {
+ cmd.remove_component::(*entity);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/systems/ui_text_system.rs b/src/core/systems/ui_text_system.rs
index 719e9e0..bd797e7 100644
--- a/src/core/systems/ui_text_system.rs
+++ b/src/core/systems/ui_text_system.rs
@@ -61,7 +61,7 @@ pub(crate) fn ui_text_bitmap_update(
];
let mut char_transform = Transform::from_xy(index as f32 * (width + 1.), 0.);
- char_transform.set_layer(transform.translation().layer());
+ char_transform.set_z(transform.translation().z());
cmd.push((
UiTextImage(UiImage::new_with_uv_map(
*width as f32,
diff --git a/src/rendering/gl_representations.rs b/src/rendering/gl_representations.rs
index 9667d91..ef6aebb 100644
--- a/src/rendering/gl_representations.rs
+++ b/src/rendering/gl_representations.rs
@@ -158,7 +158,7 @@ impl From> for GlUniform {
model_trans.append_translation(Vec3 {
x: uniform_data.transform.global_translation.x(),
y: uniform_data.transform.global_translation.y(),
- z: uniform_data.transform.global_translation.layer() as f32,
+ z: uniform_data.transform.global_translation.z() as f32,
});
if !uniform_data.is_ui_component {
model_trans.append_translation(Vec3 {
diff --git a/src/rendering/scion2d.rs b/src/rendering/scion2d.rs
index b6159bf..1c3ac9b 100644
--- a/src/rendering/scion2d.rs
+++ b/src/rendering/scion2d.rs
@@ -194,7 +194,7 @@ impl Scion2D {
gl_vertex.position.append_position(
tile_size as f32 * tile.position.x() as f32,
tile_size as f32 * tile.position.y() as f32,
- tile.position.layer() as f32 / 100.,
+ tile.position.z() as f32 / 100.,
)
});
vertexes.append(&mut vec);
@@ -354,7 +354,7 @@ impl Scion2D {
Material::Tileset(tileset) => Some(tileset.texture.clone()),
};
render_infos.push(RenderingInfos {
- layer: transform.translation().layer(),
+ layer: transform.translation().z(),
range: component.range(),
entity: *entity,
texture_path: path,
@@ -380,7 +380,7 @@ impl Scion2D {
_ => None,
};
render_infos.push(RenderingInfos {
- layer: transform.translation().layer(),
+ layer: transform.translation().z(),
range: 0..(tiles_nb * Sprite::indices().len()) as u32,
entity: *entity,
texture_path: path,
@@ -399,7 +399,7 @@ impl Scion2D {
.iter_mut(world)
{
render_infos.push(RenderingInfos {
- layer: transform.translation().layer(),
+ layer: transform.translation().z(),
range: component.range(),
entity: *entity,
texture_path: component.get_texture_path(),
diff --git a/src/utils/maths.rs b/src/utils/maths.rs
index f690f19..2adc6dc 100644
--- a/src/utils/maths.rs
+++ b/src/utils/maths.rs
@@ -3,34 +3,34 @@
pub struct Position {
x: usize,
y: usize,
- layer: usize,
+ z: usize,
}
impl Position {
- pub fn new(x: usize, y: usize, layer: usize) -> Self { Self { x, y, layer } }
+ pub fn new(x: usize, y: usize, z: usize) -> Self { Self { x, y, z } }
pub fn x(&self) -> usize { self.x }
pub fn y(&self) -> usize { self.y }
- pub fn layer(&self) -> usize { self.layer }
+ pub fn z(&self) -> usize { self.z }
}
/// The standard way to communicate 3D sizes in `Scion`
pub struct Dimensions {
width: usize,
height: usize,
- number_of_layers: usize,
+ depth: usize,
}
impl Dimensions {
- pub fn new(width: usize, height: usize, number_of_layers: usize) -> Self {
- Self { width, height, number_of_layers }
+ pub fn new(width: usize, height: usize, depth: usize) -> Self {
+ Self { width, height, depth }
}
pub fn width(&self) -> usize { self.width }
pub fn height(&self) -> usize { self.height }
- pub fn number_of_layers(&self) -> usize { self.number_of_layers }
+ pub fn depth(&self) -> usize { self.depth }
}