jojoleprowebsite

[Done] The Sauce of https://jojolepro.com
git clone https://git.jojolepro.com/jojoleprowebsite.git
Log | Files | Refs | README | LICENSE

commit 9f2301490cf3e7904cfe44a94e2197a166702a05
parent e26beccf8200f8184201a1492edc62e1cd36779e
Author: jojolepro <jojolepro@jojolepro.com>
Date:   Tue,  1 Jun 2021 17:21:23 -0400

fix typos in planck ecs tutorial.

Diffstat:
Msrc/blog/2021-06-01_getting_started_with_ecs/index.txt | 64+++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/blog/blog.xml | 4++++
Atarget/gemini/blog/2021-06-01_getting_started_with_ecs/index.gmi | 584+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atarget/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt | 595+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtarget/gemini/blog/blog.xml | 4++++
Mtarget/gemini/blog/index.gmi | 1+
Mtarget/gemini/blog/index.html | 1+
Atarget/html/blog/2021-06-01_getting_started_with_ecs/index.html | 659+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atarget/html/blog/2021-06-01_getting_started_with_ecs/index.txt | 595+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtarget/html/blog/blog.xml | 4++++
Mtarget/html/blog/index.gmi | 1+
Mtarget/html/blog/index.html | 1+
12 files changed, 2488 insertions(+), 25 deletions(-)

diff --git a/src/blog/2021-06-01_getting_started_with_ecs/index.txt b/src/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -1,7 +1,7 @@ Getting Started with ECS using Planck ECS ================================================================================ -Hello hello! This tutorial is aimed having between zero and moderate experience -with ECS. +Hello hello! This tutorial is aiming people having between zero and moderate +experience with an ECS. Let's jump right in! @@ -14,7 +14,7 @@ cargo new --bin start_with_ecs cd start_with_ecs ``` -Great! Let's add our dependencies in Cargo.toml: +Great! Let's add our dependenci in Cargo.toml: ``` [dependencies] @@ -30,12 +30,12 @@ Entity-Component-System. It is a way of organising data that is well suited for game projects and simulations. -In this tutorial, we are not going to make parallel with how Object Oriented +In this tutorial, we are not going to make parallels with how Object Oriented programming works. We will just talk about the concepts one by one. Entity -------------------------------------------------------------------------------- -An entity is a "thing" to which you can attach "properties". +An entity is a "thing" on which you can attach "properties". For example, what we call an "Apple" is an entity. You are an entity. Every object is an entity. Simply put: Entity is the fancy name we give to @@ -68,7 +68,7 @@ access to. Resource -------------------------------------------------------------------------------- -Anything contained inside of the world is called a "Resource". +Anything contained inside of the World is called a "Resource". It is just a convenient name. Dispatcher @@ -123,7 +123,7 @@ Option::None. Our First Entity -------------------------------------------------------------------------------- -Let's now use our Entities resource to create our first entity: +Let's now use our Entities Resource to create our first entity: ``` fn main() { @@ -173,7 +173,7 @@ fn main() { ``` And now, we can tell the World that we want to use this enum as a Component by -initializing a Components resource and specifying that we want the Color enum. +initializing a Components Resource and specifying that we want the Color enum. ``` fn main() { let mut world = World::default(); @@ -182,6 +182,9 @@ fn main() { } ``` +The Components<T> Resource is simply used to contain components of type T in +a way that is easy to use and also is quick to access. + Finally, let's attach our Component to the Entity! ``` @@ -196,7 +199,7 @@ fn main() { Here we use the insert(entity, component) method available on the Components resource. -Here, we have two simplifications that we can do to our code. +There are two simplifications that we can do to our code. First is to tell rust to guess that we want the Color component by using _. ``` @@ -208,7 +211,7 @@ fn main() { } ``` -Here, rust is smart enough to understand from our usage of Color::Red on the +Rust is smart enough to understand from our usage of Color::Red on the right side that what we want is Components<Color>. Now, this code looks strangely like the one we saw when creating the Entity @@ -224,7 +227,7 @@ fn main() { Yep! We can use get_mut_or_default here too! -Let summarize. Now, we have an Entity stored in the Entities resource. +Let's summarize. Now, we have an Entity stored in the Entities resource. We also have a Color::Red component that is stored in the Components<Color> resource and that is "assigned" to our Entity. @@ -264,7 +267,7 @@ fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} Now, you might wonder what that SystemResult thing is. This is a feature that is unique to Planck ECS: Error handling. -In most implementation of Entity Component Systems, it is assumed that Systems +In most implementations of Entity Component Systems, it is assumed that Systems cannot fail, which is quite naive. Here, returning SystemResult allows the user to return either Ok(()), which means no error occured, or Err(SomeErrorType), which indicates that the System @@ -284,7 +287,7 @@ fn change_color_system(colors: &mut Components<Color>) -> SystemResult { ``` The join!() macro returns an iterator over the Components we reference. -Here, we tell the join!() macro to give us all Color Components mutable, so +Here, we tell the join!() macro to give us all Color Components mutably, so that we can modify them. Then, we change the color. @@ -345,7 +348,7 @@ fn main() { } ``` -The Dispatcher is pretty much self explanatory. Simply chain +The Dispatcher creation is pretty much self explanatory. Simply chain .add(system).add(system2).add(system3) to add Systems, then complete the Dispatcher using the build(&mut world) function. @@ -382,11 +385,11 @@ We need a way to add some more meaning (data) to distinguish between the two entities. There are a couple ways to do this. -- We can add a "Apple" Component. +- We can add an "Apple" Component. - We can add a "ColorChanging" Component. -Generally speaking, the second approach is to prefer, because it encourages -re-using Systems for more than one type of Entity. +Generally speaking, the second approach is preferred, because it encourages +reusing Systems for more than one type of Entity. Let's create this Component: @@ -430,7 +433,7 @@ ColorChanging AND Color. We also specify that we want ColorChanging immutably At the left side of the for loop, we ignore the ColorChanging Component by adding a _ in front of the variable name. We do this because we used the ColorChanging Component as a filter for join, but we don't actually plan on -using it. +using it for anything else. Finally, we added .unwrap() after the color variable. We need this when joining over multiple Components at the same time. @@ -442,7 +445,8 @@ join!(&mut colors || &changings) ``` we would have pairs of Components that look like this: ``` -(&mut Option<Color>, &Option<ColorChanging>). +(&mut Option<Color>, &Option<ColorChanging>) +``` Because we use &&, we can assume that both Options have a value (Some). But if we use ||, then we cannot assume this, as one of the Entities has a @@ -456,8 +460,10 @@ Let's do this quickly in the main function: [...] dispatcher.run_seq(&mut world).unwrap(); - assert_eq!(*world.get::<Components<Color>>().unwrap().get(apple).unwrap(), Color::Blue); - assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), Color::Red); + assert_eq!(*world.get::<Components<Color>>().unwrap().get(apple).unwrap(), + Color::Blue); + assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), + Color::Red); } ``` @@ -498,11 +504,11 @@ Conclusion -------------------------------------------------------------------------------- I hope this was instructive! -It might seem like there are lots of part, but also consider that you now know -almost every single function of Planck ECS! +It might seem like there are lots of things to learn, but also consider that you +now know almost every single function of Planck ECS! If you liked this tutorial or the work I do (I wrote Planck ECS, by the way!), -consider donating on my Patreon. +consider donating on Patreon. https://patreon.com/jojolepro It is your donations that enable me to continue creating Rust libraries and @@ -578,4 +584,12 @@ fn main() { } ``` -= https://patreon.com/jojolepro = +Patreon rocket goes brrrrrrrrrrrr + +. +.. +=... +https://patreon.com/jojolepro +=... +.. +. diff --git a/src/blog/blog.xml b/src/blog/blog.xml @@ -6,6 +6,10 @@ <link>https://www.jojolepro.com</link> <atom:link href="https://www.jojolepro.com/blog/blog.xml" rel="self" type="application/rss+xml"/> <item> +<title>Getting Started with ECS using Planck ECS</title> +<link>https://www.jojolepro.com/blog/2021-06-01_getting_started_with_ecs</link> +</item> +<item> <title>Minigene And The Future</title> <link>https://www.jojolepro.com/blog/2021-05-31_minigene_and_the_future</link> </item> diff --git a/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.gmi b/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.gmi @@ -0,0 +1,584 @@ +# Jojolepro +=> blog Blog +=> projects Projects +=> quotes Quotes +=> https://github.com/jojolepro/ GitHub +=> https://git.jojolepro.com Archives + + + +## Getting Started with ECS using Planck ECS +Hello hello! This tutorial is aiming people having between zero and moderate +experience with an ECS. + +Let's jump right in! + +### Creating our test project +Let's start a project so you can follow along. + +``` +cargo new --bin start_with_ecs +cd start_with_ecs +``` + +Great! Let's add our dependenci in Cargo.toml: + +``` +[dependencies] +planck_ecs = "1.2.0" +``` + +Now, we are ready to start talking about the ECS! + +### ECS? +Entity-Component-System. + +It is a way of organising data that is well suited for game projects and +simulations. + +In this tutorial, we are not going to make parallels with how Object Oriented +programming works. We will just talk about the concepts one by one. + +### Entity +An entity is a "thing" on which you can attach "properties". + +For example, what we call an "Apple" is an entity. You are an entity. +Every object is an entity. Simply put: Entity is the fancy name we give to +"something". + +### Component +Components are the properties that make entities what they are. +For example, an "Apple" have these properties: +* Red Color +* Sweet Taste +* Round Shape + +Components are used to describe what the entity is by attaching data (meaning) +to it. + +### System +Systems are simply functions. They run code. That's it! + +### Is this all there is? +No, there are a couple extra concepts introduced by Planck ECS. +Don't be afraid, they are simple! + +### World +World is a structure which contains everything that the Systems should have +access to. + +### Resource +Anything contained inside of the World is called a "Resource". +It is just a convenient name. + +### Dispatcher +Dispatchers are used to execute systems (which are just functions!) using data +that is inside of the World. + +They are optional, but very convenient! + +### Ready? Set? +Go! + +Open up src/main.rs + +We will first import Planck ECS' features into our game. +Add the following line at the start of the file: + +``` +use planck_ecs::*; +``` + +Next, we will need something to contain the data we create. Let's create +a new World inside of the main function: + +``` +fn main() { + let mut world = World::default(); +} +``` + +### Our First Resource +Let's insert our first Resource into the world. +We will use the Entities resource, which is a structure that holds a list of +entities that exist in the World. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); +} +``` + +The initialize function will insert Entities inside of the World. + +Initialize works for all structures that have a default value +(implements the Default trait). +In case you ever need to insert a structure which does not implement Default, +you can always use Option<YourResource>, since Option's default is always +Option::None. + +### Our First Entity +Let's now use our Entities Resource to create our first entity: + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); + let entity1 = world.get_mut::<Entities>().unwrap().create(); +} +``` + +Here, get_mut gives us access to the Resource in World. +Since World doesn't contain every single possible Resource, it returns an Option +which we unwrap. + +Finally, we call the create() function on Entities, which creates a new Entity +and gives it to us. + +Since it happens often that we initialize something and then access it right +away, there is a convenient function that does both steps at the same time: +world.get_mut_or_default. + +Let's rewrite our code using it: +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +### Our first Component +Now, let's add some meaning to this Entity. We will attach a color to it. + +First, we will create the Color component. This can be anything: +a struct, an enum, you choose. + +Here, let's use an enum. Add the following before or after the main function: +``` +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +fn main() { +[...] +``` + +And now, we can tell the World that we want to use this enum as a Component by +initializing a Components Resource and specifying that we want the Color enum. +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +The Components<T> Resource is simply used to contain components of type T in +a way that is easy to use and also is quick to access. + +Finally, let's attach our Component to the Entity! + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); +} +``` + +Here we use the insert(entity, component) method available on the Components +resource. + +There are two simplifications that we can do to our code. +First is to tell rust to guess that we want the Color component by using _. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); +} +``` + +Rust is smart enough to understand from our usage of Color::Red on the +right side that what we want is Components<Color>. + +Now, this code looks strangely like the one we saw when creating the Entity +previously. Let's apply the same trick: + +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); +} +``` + +Yep! We can use get_mut_or_default here too! + +Let's summarize. Now, we have an Entity stored in the Entities resource. +We also have a Color::Red component that is stored in the Components<Color> +resource and that is "assigned" to our Entity. + +### Using Systems +Remember the core concepts? Entity, Component and System. +Well, now we are at the System part. + +As we saw previously, Systems are just functions that use data from the World. + +Let's write a System that changes the color of the apple to blue. + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + Ok(()) +} + +fn main() { + [...] +``` + +We have some things to explain here. +The parameters of Systems use references (that's the & and &mut symbols). +A special rule for Systems is that all parameters using &mut must be placed +after all parameters using &. + +For example, the following is invalid: +``` +fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} +``` + +It should instead be written as: +``` +fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} +``` + +Now, you might wonder what that SystemResult thing is. +This is a feature that is unique to Planck ECS: Error handling. + +In most implementations of Entity Component Systems, it is assumed that Systems +cannot fail, which is quite naive. +Here, returning SystemResult allows the user to return either Ok(()), which +means no error occured, or Err(SomeErrorType), which indicates that the System +failed to execute. + +For this tutorial, we will always use Ok. + +Now, let's change the color of that apple! + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + for color in join!(&mut colors) { + *color = Color::Blue; + } + Ok(()) +} +``` + +The join!() macro returns an iterator over the Components we reference. +Here, we tell the join!() macro to give us all Color Components mutably, so +that we can modify them. + +Then, we change the color. + +### Running the System +To run a System, we use: + +``` +fn main () { + [...] + change_color_system.system().run(&mut world).unwrap(); +} +``` + +This will automatically get the required Components from World and call +the function change_color_system. + +Now, what happens if the System uses a Resource which is not in World? +It will crash! + +Let's fix this up: +``` +fn main() { + [...] + let mut system = change_color_system.system(); + system.initialize(&mut world); + system.run(&mut world).unwrap(); + +} +``` + +When calling the initialize function on a System, it will use world.initialize() +for every Resource that is in the System's parameters. + +Convenient, right? + +### Dispatcher +Remember Dispatchers? We saw that they are used to execute multiple Systems. +Well they do more than that, they also call system.initialize for you! + +They are also very convenient, because without them you would need three +lines for each System! + +Here's how we use a Dispatcher: +``` +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); +} +``` + +The Dispatcher creation is pretty much self explanatory. Simply chain +.add(system).add(system2).add(system3) to add Systems, then complete the +Dispatcher using the build(&mut world) function. + +It is when calling build(&mut world) that the initialize function will be called +on each System. + +Finally, we run the Systems using dispatcher.run_seq(&mut world). + +run_seq means "Run Sequentially", which will execute the Systems one after the +other. + +Planck ECS also has a feature where you can execute multiple Systems at the +same time. We will not use it in this tutorial. + +### Multiple Components +Now, let's come back to our game! +Let's imagine that we had another Entity that also had a Color Component, but +that we don't want to change its Color. + +``` +fn main() { + let mut world = World::default(); + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); + world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +} +``` + +With our current System, both entities will have their colors changed to blue. + +We need a way to add some more meaning (data) to distinguish between the two +entities. + +There are a couple ways to do this. +* We can add an "Apple" Component. +* We can add a "ColorChanging" Component. + +Generally speaking, the second approach is preferred, because it encourages +reusing Systems for more than one type of Entity. + +Let's create this Component: + +``` +struct ColorChanging; +``` + +and add it to our apple Entity: + +``` +let apple = world.get_mut_or_default::<Entities>().create(); +let orange = world.get_mut_or_default::<Entities>().create(); +world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); +world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here +world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +``` + +Finally, let's make it so our System only runs on Entities that have a +ColorChanging Component: + +``` +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>) -> SystemResult { + + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + + Ok(()) +} +``` + +First, we add the Components<ColorChanging> parameter. + +Then, we modify the parameters of join!() macro. +Here, we tell it to give us the Component pairs only for Entities having +ColorChanging AND Color. We also specify that we want ColorChanging immutably +(we are not going to modify it) and Color mutably (we do change the color). + +At the left side of the for loop, we ignore the ColorChanging Component by +adding a _ in front of the variable name. We do this because we used the +ColorChanging Component as a filter for join, but we don't actually plan on +using it for anything else. + +Finally, we added .unwrap() after the color variable. +We need this when joining over multiple Components at the same time. + +This is because the join macro also supports || OR operations. For example, if +we used: +``` +join!(&mut colors || &changings) +``` +we would have pairs of Components that look like this: +``` +(&mut Option<Color>, &Option<ColorChanging>) +``` + +Because we use &&, we can assume that both Options have a value (Some). +But if we use ||, then we cannot assume this, as one of the Entities has a +Color but no ColorChanging Component. + +### Making sure everything works +It is always a good practice to verify that our Systems are working fine. +Let's do this quickly in the main function: +``` + [...] + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!(*world.get::<Components<Color>>().unwrap().get(apple).unwrap(), + Color::Blue); + assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), + Color::Red); +} +``` + +### Removing a Component +Simple! +Use: + +``` + world.get_mut::<Components<Color>>().unwrap().remove(apple); +``` + +Of course, this works inside of Systems too, since you have Components<Color> +as a parameter! + +### Removing an Entity +Also simple! +Use: + +``` + world.get_mut::<Entities>().unwrap().kill(apple); +``` + +However, it is important to know that removing an Entity using kill(entity) will +NOT delete its Components that are in the Components<T> Resources. + +This can be done automatically by using the world.maintain() function: + +``` + world.maintain(); +``` + +It is recommended to run world.maintain() after using +dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! + +### Conclusion +I hope this was instructive! + +It might seem like there are lots of things to learn, but also consider that you +now know almost every single function of Planck ECS! + +If you liked this tutorial or the work I do (I wrote Planck ECS, by the way!), +consider donating on Patreon. +=>https://patreon.com/jojolepro + +It is your donations that enable me to continue creating Rust libraries and +learning material like this one! + +### Full Code +Here is the full code created in this tutorial: + +``` +use planck_ecs::*; + +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +struct ColorChanging; + +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>, +) -> SystemResult { + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + Ok(()) +} + +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, Color::Red); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, ColorChanging); + world + .get_mut_or_default::<Components<_>>() + .insert(orange, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(apple) + .unwrap(), + Color::Blue + ); + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(orange) + .unwrap(), + Color::Red + ); + + world.get_mut::<Components<Color>>().unwrap().remove(apple); + world.get_mut::<Entities>().unwrap().kill(apple); + + world.maintain(); +} +``` + +Patreon rocket goes brrrrrrrrrrrr + +. +.. +=... +=>https://patreon.com/jojolepro +=... +.. +. + +(C) Joël Lupien 2020-2021 +=>/blog/2021-06-01_getting_started_with_ecs/index.txt View page source diff --git a/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt b/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -0,0 +1,595 @@ +Getting Started with ECS using Planck ECS +================================================================================ +Hello hello! This tutorial is aiming people having between zero and moderate +experience with an ECS. + +Let's jump right in! + +Creating our test project +-------------------------------------------------------------------------------- +Let's start a project so you can follow along. + +``` +cargo new --bin start_with_ecs +cd start_with_ecs +``` + +Great! Let's add our dependenci in Cargo.toml: + +``` +[dependencies] +planck_ecs = "1.2.0" +``` + +Now, we are ready to start talking about the ECS! + +ECS? +-------------------------------------------------------------------------------- +Entity-Component-System. + +It is a way of organising data that is well suited for game projects and +simulations. + +In this tutorial, we are not going to make parallels with how Object Oriented +programming works. We will just talk about the concepts one by one. + +Entity +-------------------------------------------------------------------------------- +An entity is a "thing" on which you can attach "properties". + +For example, what we call an "Apple" is an entity. You are an entity. +Every object is an entity. Simply put: Entity is the fancy name we give to +"something". + +Component +-------------------------------------------------------------------------------- +Components are the properties that make entities what they are. +For example, an "Apple" have these properties: +- Red Color +- Sweet Taste +- Round Shape + +Components are used to describe what the entity is by attaching data (meaning) +to it. + +System +-------------------------------------------------------------------------------- +Systems are simply functions. They run code. That's it! + +Is this all there is? +-------------------------------------------------------------------------------- +No, there are a couple extra concepts introduced by Planck ECS. +Don't be afraid, they are simple! + +World +-------------------------------------------------------------------------------- +World is a structure which contains everything that the Systems should have +access to. + +Resource +-------------------------------------------------------------------------------- +Anything contained inside of the World is called a "Resource". +It is just a convenient name. + +Dispatcher +-------------------------------------------------------------------------------- +Dispatchers are used to execute systems (which are just functions!) using data +that is inside of the World. + +They are optional, but very convenient! + +Ready? Set? +-------------------------------------------------------------------------------- +Go! + +Open up src/main.rs + +We will first import Planck ECS' features into our game. +Add the following line at the start of the file: + +``` +use planck_ecs::*; +``` + +Next, we will need something to contain the data we create. Let's create +a new World inside of the main function: + +``` +fn main() { + let mut world = World::default(); +} +``` + +Our First Resource +-------------------------------------------------------------------------------- +Let's insert our first Resource into the world. +We will use the Entities resource, which is a structure that holds a list of +entities that exist in the World. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); +} +``` + +The initialize function will insert Entities inside of the World. + +Initialize works for all structures that have a default value +(implements the Default trait). +In case you ever need to insert a structure which does not implement Default, +you can always use Option<YourResource>, since Option's default is always +Option::None. + +Our First Entity +-------------------------------------------------------------------------------- +Let's now use our Entities Resource to create our first entity: + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); + let entity1 = world.get_mut::<Entities>().unwrap().create(); +} +``` + +Here, get_mut gives us access to the Resource in World. +Since World doesn't contain every single possible Resource, it returns an Option +which we unwrap. + +Finally, we call the create() function on Entities, which creates a new Entity +and gives it to us. + +Since it happens often that we initialize something and then access it right +away, there is a convenient function that does both steps at the same time: +world.get_mut_or_default. + +Let's rewrite our code using it: +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +Our first Component +-------------------------------------------------------------------------------- +Now, let's add some meaning to this Entity. We will attach a color to it. + +First, we will create the Color component. This can be anything: +a struct, an enum, you choose. + +Here, let's use an enum. Add the following before or after the main function: +``` +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +fn main() { +[...] +``` + +And now, we can tell the World that we want to use this enum as a Component by +initializing a Components Resource and specifying that we want the Color enum. +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +The Components<T> Resource is simply used to contain components of type T in +a way that is easy to use and also is quick to access. + +Finally, let's attach our Component to the Entity! + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); +} +``` + +Here we use the insert(entity, component) method available on the Components +resource. + +There are two simplifications that we can do to our code. +First is to tell rust to guess that we want the Color component by using _. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); +} +``` + +Rust is smart enough to understand from our usage of Color::Red on the +right side that what we want is Components<Color>. + +Now, this code looks strangely like the one we saw when creating the Entity +previously. Let's apply the same trick: + +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); +} +``` + +Yep! We can use get_mut_or_default here too! + +Let's summarize. Now, we have an Entity stored in the Entities resource. +We also have a Color::Red component that is stored in the Components<Color> +resource and that is "assigned" to our Entity. + +Using Systems +-------------------------------------------------------------------------------- +Remember the core concepts? Entity, Component and System. +Well, now we are at the System part. + +As we saw previously, Systems are just functions that use data from the World. + +Let's write a System that changes the color of the apple to blue. + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + Ok(()) +} + +fn main() { + [...] +``` + +We have some things to explain here. +The parameters of Systems use references (that's the & and &mut symbols). +A special rule for Systems is that all parameters using &mut must be placed +after all parameters using &. + +For example, the following is invalid: +``` +fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} +``` + +It should instead be written as: +``` +fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} +``` + +Now, you might wonder what that SystemResult thing is. +This is a feature that is unique to Planck ECS: Error handling. + +In most implementations of Entity Component Systems, it is assumed that Systems +cannot fail, which is quite naive. +Here, returning SystemResult allows the user to return either Ok(()), which +means no error occured, or Err(SomeErrorType), which indicates that the System +failed to execute. + +For this tutorial, we will always use Ok. + +Now, let's change the color of that apple! + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + for color in join!(&mut colors) { + *color = Color::Blue; + } + Ok(()) +} +``` + +The join!() macro returns an iterator over the Components we reference. +Here, we tell the join!() macro to give us all Color Components mutably, so +that we can modify them. + +Then, we change the color. + +Running the System +-------------------------------------------------------------------------------- +To run a System, we use: + +``` +fn main () { + [...] + change_color_system.system().run(&mut world).unwrap(); +} +``` + +This will automatically get the required Components from World and call +the function change_color_system. + +Now, what happens if the System uses a Resource which is not in World? +It will crash! + +Let's fix this up: +``` +fn main() { + [...] + let mut system = change_color_system.system(); + system.initialize(&mut world); + system.run(&mut world).unwrap(); + +} +``` + +When calling the initialize function on a System, it will use world.initialize() +for every Resource that is in the System's parameters. + +Convenient, right? + +Dispatcher +-------------------------------------------------------------------------------- +Remember Dispatchers? We saw that they are used to execute multiple Systems. +Well they do more than that, they also call system.initialize for you! + +They are also very convenient, because without them you would need three +lines for each System! + +Here's how we use a Dispatcher: +``` +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); +} +``` + +The Dispatcher creation is pretty much self explanatory. Simply chain +.add(system).add(system2).add(system3) to add Systems, then complete the +Dispatcher using the build(&mut world) function. + +It is when calling build(&mut world) that the initialize function will be called +on each System. + +Finally, we run the Systems using dispatcher.run_seq(&mut world). + +run_seq means "Run Sequentially", which will execute the Systems one after the +other. + +Planck ECS also has a feature where you can execute multiple Systems at the +same time. We will not use it in this tutorial. + +Multiple Components +-------------------------------------------------------------------------------- +Now, let's come back to our game! +Let's imagine that we had another Entity that also had a Color Component, but +that we don't want to change its Color. + +``` +fn main() { + let mut world = World::default(); + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); + world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +} +``` + +With our current System, both entities will have their colors changed to blue. + +We need a way to add some more meaning (data) to distinguish between the two +entities. + +There are a couple ways to do this. +- We can add an "Apple" Component. +- We can add a "ColorChanging" Component. + +Generally speaking, the second approach is preferred, because it encourages +reusing Systems for more than one type of Entity. + +Let's create this Component: + +``` +struct ColorChanging; +``` + +and add it to our apple Entity: + +``` +let apple = world.get_mut_or_default::<Entities>().create(); +let orange = world.get_mut_or_default::<Entities>().create(); +world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); +world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here +world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +``` + +Finally, let's make it so our System only runs on Entities that have a +ColorChanging Component: + +``` +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>) -> SystemResult { + + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + + Ok(()) +} +``` + +First, we add the Components<ColorChanging> parameter. + +Then, we modify the parameters of join!() macro. +Here, we tell it to give us the Component pairs only for Entities having +ColorChanging AND Color. We also specify that we want ColorChanging immutably +(we are not going to modify it) and Color mutably (we do change the color). + +At the left side of the for loop, we ignore the ColorChanging Component by +adding a _ in front of the variable name. We do this because we used the +ColorChanging Component as a filter for join, but we don't actually plan on +using it for anything else. + +Finally, we added .unwrap() after the color variable. +We need this when joining over multiple Components at the same time. + +This is because the join macro also supports || OR operations. For example, if +we used: +``` +join!(&mut colors || &changings) +``` +we would have pairs of Components that look like this: +``` +(&mut Option<Color>, &Option<ColorChanging>) +``` + +Because we use &&, we can assume that both Options have a value (Some). +But if we use ||, then we cannot assume this, as one of the Entities has a +Color but no ColorChanging Component. + +Making sure everything works +-------------------------------------------------------------------------------- +It is always a good practice to verify that our Systems are working fine. +Let's do this quickly in the main function: +``` + [...] + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!(*world.get::<Components<Color>>().unwrap().get(apple).unwrap(), + Color::Blue); + assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), + Color::Red); +} +``` + +Removing a Component +-------------------------------------------------------------------------------- +Simple! +Use: + +``` + world.get_mut::<Components<Color>>().unwrap().remove(apple); +``` + +Of course, this works inside of Systems too, since you have Components<Color> +as a parameter! + +Removing an Entity +-------------------------------------------------------------------------------- +Also simple! +Use: + +``` + world.get_mut::<Entities>().unwrap().kill(apple); +``` + +However, it is important to know that removing an Entity using kill(entity) will +NOT delete its Components that are in the Components<T> Resources. + +This can be done automatically by using the world.maintain() function: + +``` + world.maintain(); +``` + +It is recommended to run world.maintain() after using +dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! + +Conclusion +-------------------------------------------------------------------------------- +I hope this was instructive! + +It might seem like there are lots of things to learn, but also consider that you +now know almost every single function of Planck ECS! + +If you liked this tutorial or the work I do (I wrote Planck ECS, by the way!), +consider donating on Patreon. +https://patreon.com/jojolepro + +It is your donations that enable me to continue creating Rust libraries and +learning material like this one! + +Full Code +-------------------------------------------------------------------------------- +Here is the full code created in this tutorial: + +``` +use planck_ecs::*; + +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +struct ColorChanging; + +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>, +) -> SystemResult { + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + Ok(()) +} + +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, Color::Red); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, ColorChanging); + world + .get_mut_or_default::<Components<_>>() + .insert(orange, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(apple) + .unwrap(), + Color::Blue + ); + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(orange) + .unwrap(), + Color::Red + ); + + world.get_mut::<Components<Color>>().unwrap().remove(apple); + world.get_mut::<Entities>().unwrap().kill(apple); + + world.maintain(); +} +``` + +Patreon rocket goes brrrrrrrrrrrr + +. +.. +=... +https://patreon.com/jojolepro +=... +.. +. diff --git a/target/gemini/blog/blog.xml b/target/gemini/blog/blog.xml @@ -6,6 +6,10 @@ <link>https://www.jojolepro.com</link> <atom:link href="https://www.jojolepro.com/blog/blog.xml" rel="self" type="application/rss+xml"/> <item> +<title>Getting Started with ECS using Planck ECS</title> +<link>https://www.jojolepro.com/blog/2021-06-01_getting_started_with_ecs</link> +</item> +<item> <title>Minigene And The Future</title> <link>https://www.jojolepro.com/blog/2021-05-31_minigene_and_the_future</link> </item> diff --git a/target/gemini/blog/index.gmi b/target/gemini/blog/index.gmi @@ -1,3 +1,4 @@ +=> 2021-06-01_getting_started_with_ecs Getting Started with ECS using Planck ECS => 2021-05-31_minigene_and_the_future Minigene And The Future => 2021-01-13_removing_the_us Removing the US => 2021-01-13_planck_ecs Planck: A Minimalistic Yet Performant ECS Library diff --git a/target/gemini/blog/index.html b/target/gemini/blog/index.html @@ -1,3 +1,4 @@ +<a href="2021-06-01_getting_started_with_ecs">Getting Started with ECS using Planck ECS</a> <a href="2021-05-31_minigene_and_the_future">Minigene And The Future</a> <a href="2021-01-13_removing_the_us">Removing the US</a> <a href="2021-01-13_planck_ecs">Planck: A Minimalistic Yet Performant ECS Library</a> diff --git a/target/html/blog/2021-06-01_getting_started_with_ecs/index.html b/target/html/blog/2021-06-01_getting_started_with_ecs/index.html @@ -0,0 +1,659 @@ +<!doctype html> +<html lang=en> + <head> + <link href='data:image/png;base64,R0lGODlhGAAYAOeIAAAAALVSwLlUxLxXyLlZxMlb1cpc1hScoRSepNBf3c9h29Rg4NJh39Nh4BWhps9j3NJi3s1k2dVh4tBk3NVi4tNj39Zi49di49di5NBl3NVj4tNk39hi5dZj49Zj5Nli5dRk4ddj5NVk4thj5dti5tNl4Nhj5tZk49Fm3tlj5dRl4Nlj5tdk49dk5Npj5ddk5dVl4dhk5dZl49lk5tdl5dpk5tZm49Nn5NVn4tVn5dZn49Ro4Nlm5tNo5ddn5NBq29Vo4dVo4haortln5dRp4Ndo49Zp49Nq5Raqr9pp59hr5NFu5Nls5s1y5BevtBawtcp25BizuRi1uhm4vi20wbqG4hm7wRq8whm9w7yI4raM4Bq/xRq/xrSO4RrByBrDyRvDyhvFyxvGzBvHzRvHzqeb4BvIzhvIzxzIzxvJz6ad4CTH0aKf3xzK0BzK0RzL0p6i3xzN1BzP1R3P1pGr3h3Q15Or3RzR2CPP2R3R2B3R2R/R2RzS2R3S2SfP2Yex3Yey3TPN2TbN2Xm43DDP2Xa63EXK2kbK2v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH+B2hhaSBoYWkAIfkEAQoA/wAsAAAAABgAGAAACP4A/wkcSLCgwYMFASgEgLDhQiFWrkxZ2JAgACl58vTZuDGPwor/ALzhyFEMmI1uGCIEUKfPHABkODoAMGZjHZUJ02wEEAUARwBofNrEOVBoH5EAzuyMA0Djxi9EAXD5mWdOS5JYo2Ld+pNjGpUAEGwVmrLPlqMkwSLhCgAqAgAHjG40wxCAE656ADzBQwWAHz5ZQ2LhashOlR4uSOToAkjQz5BesB5ic4QHiyQyiFiokQJKoZ8ATnIctCQGBCYqRHjYoWPIihUvyhA6CiDMxj10aFhI8MBGhghBZnTg8fp1FkIKa/aBk2LFhQUlbOAoooHDiRbFX2tR2EbPnw+vR0NMMAJEAQwQFBpoCDFCgoCFAOQEapJ9Q5EKDFCUKPKDAPyid6hhQnZK+GDAAPARVdQaN7yGQQEB/AdSSAlOaOGFAgUEADs=' + rel=icon> + <title>Jojolepro.com</title> + <meta charset=utf-8> + <meta name=description content="A bunch of stuff and things!"> + <style> + body{ + text-align:center; + overflow-y:scroll; + /*font:calc(0.75em + 1vmin) monospace;*/ + font: 1.3em monospace; + background-color: #181a1b; + border-color: #575757; + color: #e8e6e3; + } + pre{ + text-align:left; + display:inline-block; + } + img{ + max-width:80ch; + display:block; + height:auto; + width:100%; + } + nav a { + margin-right: 1ch; + } + a { + color: #3391ff; + } + * { + scrollbar-color: #2a2c2e #1c1e1f; + } + </style> + </head> + <body> + <nav> + <a href=/><b>Jojolepro</b></a> + <br/> + <a href=/blog>Blog</a> + <a href=/projects>Projects</a> + <a href=/quotes>Quotes</a> + <!--<a href=/focks>Focks</a>--> + <a href=https://github.com/jojolepro/>GitHub</a> + <a href=https://git.jojolepro.com>Archive</a> + </nav> + <br/> + <article> + <pre> +Getting Started with ECS using Planck ECS +================================================================================ +Hello hello! This tutorial is aiming people having between zero and moderate +experience with an ECS. + +Let's jump right in! + +Creating our test project +-------------------------------------------------------------------------------- +Let's start a project so you can follow along. + +``` +cargo new --bin start_with_ecs +cd start_with_ecs +``` + +Great! Let's add our dependenci in Cargo.toml: + +``` +[dependencies] +planck_ecs = "1.2.0" +``` + +Now, we are ready to start talking about the ECS! + +ECS? +-------------------------------------------------------------------------------- +Entity-Component-System. + +It is a way of organising data that is well suited for game projects and +simulations. + +In this tutorial, we are not going to make parallels with how Object Oriented +programming works. We will just talk about the concepts one by one. + +Entity +-------------------------------------------------------------------------------- +An entity is a "thing" on which you can attach "properties". + +For example, what we call an "Apple" is an entity. You are an entity. +Every object is an entity. Simply put: Entity is the fancy name we give to +"something". + +Component +-------------------------------------------------------------------------------- +Components are the properties that make entities what they are. +For example, an "Apple" have these properties: +- Red Color +- Sweet Taste +- Round Shape + +Components are used to describe what the entity is by attaching data (meaning) +to it. + +System +-------------------------------------------------------------------------------- +Systems are simply functions. They run code. That's it! + +Is this all there is? +-------------------------------------------------------------------------------- +No, there are a couple extra concepts introduced by Planck ECS. +Don't be afraid, they are simple! + +World +-------------------------------------------------------------------------------- +World is a structure which contains everything that the Systems should have +access to. + +Resource +-------------------------------------------------------------------------------- +Anything contained inside of the World is called a "Resource". +It is just a convenient name. + +Dispatcher +-------------------------------------------------------------------------------- +Dispatchers are used to execute systems (which are just functions!) using data +that is inside of the World. + +They are optional, but very convenient! + +Ready? Set? +-------------------------------------------------------------------------------- +Go! + +Open up src/main.rs + +We will first import Planck ECS' features into our game. +Add the following line at the start of the file: + +``` +use planck_ecs::*; +``` + +Next, we will need something to contain the data we create. Let's create +a new World inside of the main function: + +``` +fn main() { + let mut world = World::default(); +} +``` + +Our First Resource +-------------------------------------------------------------------------------- +Let's insert our first Resource into the world. +We will use the Entities resource, which is a structure that holds a list of +entities that exist in the World. + +``` +fn main() { + let mut world = World::default(); + world.initialize::&lt;Entities&gt;(); +} +``` + +The initialize function will insert Entities inside of the World. + +Initialize works for all structures that have a default value +(implements the Default trait). +In case you ever need to insert a structure which does not implement Default, +you can always use Option&lt;YourResource&gt;, since Option's default is always +Option::None. + +Our First Entity +-------------------------------------------------------------------------------- +Let's now use our Entities Resource to create our first entity: + +``` +fn main() { + let mut world = World::default(); + world.initialize::&lt;Entities&gt;(); + let entity1 = world.get_mut::&lt;Entities&gt;().unwrap().create(); +} +``` + +Here, get_mut gives us access to the Resource in World. +Since World doesn't contain every single possible Resource, it returns an Option +which we unwrap. + +Finally, we call the create() function on Entities, which creates a new Entity +and gives it to us. + +Since it happens often that we initialize something and then access it right +away, there is a convenient function that does both steps at the same time: +world.get_mut_or_default. + +Let's rewrite our code using it: +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); +} +``` + +Our first Component +-------------------------------------------------------------------------------- +Now, let's add some meaning to this Entity. We will attach a color to it. + +First, we will create the Color component. This can be anything: +a struct, an enum, you choose. + +Here, let's use an enum. Add the following before or after the main function: +``` +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +fn main() { +[...] +``` + +And now, we can tell the World that we want to use this enum as a Component by +initializing a Components Resource and specifying that we want the Color enum. +``` +fn main() { + let mut world = World::default(); + world.initialize::&lt;Components&lt;Color&gt;&gt;(); + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); +} +``` + +The Components&lt;T&gt; Resource is simply used to contain components of type T in +a way that is easy to use and also is quick to access. + +Finally, let's attach our Component to the Entity! + +``` +fn main() { + let mut world = World::default(); + world.initialize::&lt;Components&lt;Color&gt;&gt;(); + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); + world.get_mut::&lt;Components&lt;Color&gt;&gt;().unwrap().insert(entity1, Color::Red); +} +``` + +Here we use the insert(entity, component) method available on the Components +resource. + +There are two simplifications that we can do to our code. +First is to tell rust to guess that we want the Color component by using _. + +``` +fn main() { + let mut world = World::default(); + world.initialize::&lt;Components&lt;Color&gt;&gt;(); + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); + world.get_mut::&lt;Components&lt;_&gt;&gt;().unwrap().insert(entity1, Color::Red); +} +``` + +Rust is smart enough to understand from our usage of Color::Red on the +right side that what we want is Components&lt;Color&gt;. + +Now, this code looks strangely like the one we saw when creating the Entity +previously. Let's apply the same trick: + +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); + world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(entity1, Color::Red); +} +``` + +Yep! We can use get_mut_or_default here too! + +Let's summarize. Now, we have an Entity stored in the Entities resource. +We also have a Color::Red component that is stored in the Components&lt;Color&gt; +resource and that is "assigned" to our Entity. + +Using Systems +-------------------------------------------------------------------------------- +Remember the core concepts? Entity, Component and System. +Well, now we are at the System part. + +As we saw previously, Systems are just functions that use data from the World. + +Let's write a System that changes the color of the apple to blue. + +``` +fn change_color_system(colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { + Ok(()) +} + +fn main() { + [...] +``` + +We have some things to explain here. +The parameters of Systems use references (that's the &amp; and &amp;mut symbols). +A special rule for Systems is that all parameters using &amp;mut must be placed +after all parameters using &amp;. + +For example, the following is invalid: +``` +fn system(a: &amp;mut A, b: &amp;B, c: &amp;mut C) -&gt; SystemResult {...} +``` + +It should instead be written as: +``` +fn system(b: &amp;B, a: &amp;mut A, c: &amp;mut C) -&gt; SystemResult {...} +``` + +Now, you might wonder what that SystemResult thing is. +This is a feature that is unique to Planck ECS: Error handling. + +In most implementations of Entity Component Systems, it is assumed that Systems +cannot fail, which is quite naive. +Here, returning SystemResult allows the user to return either Ok(()), which +means no error occured, or Err(SomeErrorType), which indicates that the System +failed to execute. + +For this tutorial, we will always use Ok. + +Now, let's change the color of that apple! + +``` +fn change_color_system(colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { + for color in join!(&amp;mut colors) { + *color = Color::Blue; + } + Ok(()) +} +``` + +The join!() macro returns an iterator over the Components we reference. +Here, we tell the join!() macro to give us all Color Components mutably, so +that we can modify them. + +Then, we change the color. + +Running the System +-------------------------------------------------------------------------------- +To run a System, we use: + +``` +fn main () { + [...] + change_color_system.system().run(&amp;mut world).unwrap(); +} +``` + +This will automatically get the required Components from World and call +the function change_color_system. + +Now, what happens if the System uses a Resource which is not in World? +It will crash! + +Let's fix this up: +``` +fn main() { + [...] + let mut system = change_color_system.system(); + system.initialize(&amp;mut world); + system.run(&amp;mut world).unwrap(); + +} +``` + +When calling the initialize function on a System, it will use world.initialize() +for every Resource that is in the System's parameters. + +Convenient, right? + +Dispatcher +-------------------------------------------------------------------------------- +Remember Dispatchers? We saw that they are used to execute multiple Systems. +Well they do more than that, they also call system.initialize for you! + +They are also very convenient, because without them you would need three +lines for each System! + +Here's how we use a Dispatcher: +``` +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&amp;mut world); + + let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); + world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(entity1, Color::Red); + + dispatcher.run_seq(&amp;mut world).unwrap(); +} +``` + +The Dispatcher creation is pretty much self explanatory. Simply chain +.add(system).add(system2).add(system3) to add Systems, then complete the +Dispatcher using the build(&amp;mut world) function. + +It is when calling build(&amp;mut world) that the initialize function will be called +on each System. + +Finally, we run the Systems using dispatcher.run_seq(&amp;mut world). + +run_seq means "Run Sequentially", which will execute the Systems one after the +other. + +Planck ECS also has a feature where you can execute multiple Systems at the +same time. We will not use it in this tutorial. + +Multiple Components +-------------------------------------------------------------------------------- +Now, let's come back to our game! +Let's imagine that we had another Entity that also had a Color Component, but +that we don't want to change its Color. + +``` +fn main() { + let mut world = World::default(); + let apple = world.get_mut_or_default::&lt;Entities&gt;().create(); + let orange = world.get_mut_or_default::&lt;Entities&gt;().create(); + world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, Color::Red); + world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(orange, Color::Red); +} +``` + +With our current System, both entities will have their colors changed to blue. + +We need a way to add some more meaning (data) to distinguish between the two +entities. + +There are a couple ways to do this. +- We can add an "Apple" Component. +- We can add a "ColorChanging" Component. + +Generally speaking, the second approach is preferred, because it encourages +reusing Systems for more than one type of Entity. + +Let's create this Component: + +``` +struct ColorChanging; +``` + +and add it to our apple Entity: + +``` +let apple = world.get_mut_or_default::&lt;Entities&gt;().create(); +let orange = world.get_mut_or_default::&lt;Entities&gt;().create(); +world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, Color::Red); +world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, ColorChanging); //here +world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(orange, Color::Red); +``` + +Finally, let's make it so our System only runs on Entities that have a +ColorChanging Component: + +``` +fn change_color_system( + changings: &amp;Components&lt;ColorChanging&gt;, + colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { + + for (_changing, color) in join!(&amp;changings &amp;&amp; &amp;mut colors) { + *color.unwrap() = Color::Blue; + } + + Ok(()) +} +``` + +First, we add the Components&lt;ColorChanging&gt; parameter. + +Then, we modify the parameters of join!() macro. +Here, we tell it to give us the Component pairs only for Entities having +ColorChanging AND Color. We also specify that we want ColorChanging immutably +(we are not going to modify it) and Color mutably (we do change the color). + +At the left side of the for loop, we ignore the ColorChanging Component by +adding a _ in front of the variable name. We do this because we used the +ColorChanging Component as a filter for join, but we don't actually plan on +using it for anything else. + +Finally, we added .unwrap() after the color variable. +We need this when joining over multiple Components at the same time. + +This is because the join macro also supports || OR operations. For example, if +we used: +``` +join!(&amp;mut colors || &amp;changings) +``` +we would have pairs of Components that look like this: +``` +(&amp;mut Option&lt;Color&gt;, &amp;Option&lt;ColorChanging&gt;) +``` + +Because we use &amp;&amp;, we can assume that both Options have a value (Some). +But if we use ||, then we cannot assume this, as one of the Entities has a +Color but no ColorChanging Component. + +Making sure everything works +-------------------------------------------------------------------------------- +It is always a good practice to verify that our Systems are working fine. +Let's do this quickly in the main function: +``` + [...] + dispatcher.run_seq(&amp;mut world).unwrap(); + + assert_eq!(*world.get::&lt;Components&lt;Color&gt;&gt;().unwrap().get(apple).unwrap(), + Color::Blue); + assert_eq!(*world.get::&lt;Components&lt;Color&gt;&gt;().unwrap().get(orange).unwrap(), + Color::Red); +} +``` + +Removing a Component +-------------------------------------------------------------------------------- +Simple! +Use: + +``` + world.get_mut::&lt;Components&lt;Color&gt;&gt;().unwrap().remove(apple); +``` + +Of course, this works inside of Systems too, since you have Components&lt;Color&gt; +as a parameter! + +Removing an Entity +-------------------------------------------------------------------------------- +Also simple! +Use: + +``` + world.get_mut::&lt;Entities&gt;().unwrap().kill(apple); +``` + +However, it is important to know that removing an Entity using kill(entity) will +NOT delete its Components that are in the Components&lt;T&gt; Resources. + +This can be done automatically by using the world.maintain() function: + +``` + world.maintain(); +``` + +It is recommended to run world.maintain() after using +dispatcher.run_seq(&amp;mut world), as this ensures everything is cleaned up! + +Conclusion +-------------------------------------------------------------------------------- +I hope this was instructive! + +It might seem like there are lots of things to learn, but also consider that you +now know almost every single function of Planck ECS! + +If you liked this tutorial or the work I do (I wrote Planck ECS, by the way!), +consider donating on Patreon. +<a href='https://patreon.com/jojolepro'>https://patreon.com/jojolepro</a> + +It is your donations that enable me to continue creating Rust libraries and +learning material like this one! + +Full Code +-------------------------------------------------------------------------------- +Here is the full code created in this tutorial: + +``` +use planck_ecs::*; + +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +struct ColorChanging; + +fn change_color_system( + changings: &amp;Components&lt;ColorChanging&gt;, + colors: &amp;mut Components&lt;Color&gt;, +) -&gt; SystemResult { + for (_changing, color) in join!(&amp;changings &amp;&amp; &amp;mut colors) { + *color.unwrap() = Color::Blue; + } + Ok(()) +} + +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&amp;mut world); + + let apple = world.get_mut_or_default::&lt;Entities&gt;().create(); + let orange = world.get_mut_or_default::&lt;Entities&gt;().create(); + world + .get_mut_or_default::&lt;Components&lt;_&gt;&gt;() + .insert(apple, Color::Red); + world + .get_mut_or_default::&lt;Components&lt;_&gt;&gt;() + .insert(apple, ColorChanging); + world + .get_mut_or_default::&lt;Components&lt;_&gt;&gt;() + .insert(orange, Color::Red); + + dispatcher.run_seq(&amp;mut world).unwrap(); + + assert_eq!( + *world + .get::&lt;Components&lt;Color&gt;&gt;() + .unwrap() + .get(apple) + .unwrap(), + Color::Blue + ); + assert_eq!( + *world + .get::&lt;Components&lt;Color&gt;&gt;() + .unwrap() + .get(orange) + .unwrap(), + Color::Red + ); + + world.get_mut::&lt;Components&lt;Color&gt;&gt;().unwrap().remove(apple); + world.get_mut::&lt;Entities&gt;().unwrap().kill(apple); + + world.maintain(); +} +``` + +Patreon rocket goes brrrrrrrrrrrr + +. +.. +=... +<a href='https://patreon.com/jojolepro'>https://patreon.com/jojolepro</a> +=... +.. +. + </pre> + </article> + <br/> + <footer> + <div> + (C) Joël Lupien 2020-2021 + </div> + <a href="/blog/2021-06-01_getting_started_with_ecs/index.txt">View page source</a> + </footer> + </body> +</html> diff --git a/target/html/blog/2021-06-01_getting_started_with_ecs/index.txt b/target/html/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -0,0 +1,595 @@ +Getting Started with ECS using Planck ECS +================================================================================ +Hello hello! This tutorial is aiming people having between zero and moderate +experience with an ECS. + +Let's jump right in! + +Creating our test project +-------------------------------------------------------------------------------- +Let's start a project so you can follow along. + +``` +cargo new --bin start_with_ecs +cd start_with_ecs +``` + +Great! Let's add our dependenci in Cargo.toml: + +``` +[dependencies] +planck_ecs = "1.2.0" +``` + +Now, we are ready to start talking about the ECS! + +ECS? +-------------------------------------------------------------------------------- +Entity-Component-System. + +It is a way of organising data that is well suited for game projects and +simulations. + +In this tutorial, we are not going to make parallels with how Object Oriented +programming works. We will just talk about the concepts one by one. + +Entity +-------------------------------------------------------------------------------- +An entity is a "thing" on which you can attach "properties". + +For example, what we call an "Apple" is an entity. You are an entity. +Every object is an entity. Simply put: Entity is the fancy name we give to +"something". + +Component +-------------------------------------------------------------------------------- +Components are the properties that make entities what they are. +For example, an "Apple" have these properties: +- Red Color +- Sweet Taste +- Round Shape + +Components are used to describe what the entity is by attaching data (meaning) +to it. + +System +-------------------------------------------------------------------------------- +Systems are simply functions. They run code. That's it! + +Is this all there is? +-------------------------------------------------------------------------------- +No, there are a couple extra concepts introduced by Planck ECS. +Don't be afraid, they are simple! + +World +-------------------------------------------------------------------------------- +World is a structure which contains everything that the Systems should have +access to. + +Resource +-------------------------------------------------------------------------------- +Anything contained inside of the World is called a "Resource". +It is just a convenient name. + +Dispatcher +-------------------------------------------------------------------------------- +Dispatchers are used to execute systems (which are just functions!) using data +that is inside of the World. + +They are optional, but very convenient! + +Ready? Set? +-------------------------------------------------------------------------------- +Go! + +Open up src/main.rs + +We will first import Planck ECS' features into our game. +Add the following line at the start of the file: + +``` +use planck_ecs::*; +``` + +Next, we will need something to contain the data we create. Let's create +a new World inside of the main function: + +``` +fn main() { + let mut world = World::default(); +} +``` + +Our First Resource +-------------------------------------------------------------------------------- +Let's insert our first Resource into the world. +We will use the Entities resource, which is a structure that holds a list of +entities that exist in the World. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); +} +``` + +The initialize function will insert Entities inside of the World. + +Initialize works for all structures that have a default value +(implements the Default trait). +In case you ever need to insert a structure which does not implement Default, +you can always use Option<YourResource>, since Option's default is always +Option::None. + +Our First Entity +-------------------------------------------------------------------------------- +Let's now use our Entities Resource to create our first entity: + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Entities>(); + let entity1 = world.get_mut::<Entities>().unwrap().create(); +} +``` + +Here, get_mut gives us access to the Resource in World. +Since World doesn't contain every single possible Resource, it returns an Option +which we unwrap. + +Finally, we call the create() function on Entities, which creates a new Entity +and gives it to us. + +Since it happens often that we initialize something and then access it right +away, there is a convenient function that does both steps at the same time: +world.get_mut_or_default. + +Let's rewrite our code using it: +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +Our first Component +-------------------------------------------------------------------------------- +Now, let's add some meaning to this Entity. We will attach a color to it. + +First, we will create the Color component. This can be anything: +a struct, an enum, you choose. + +Here, let's use an enum. Add the following before or after the main function: +``` +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +fn main() { +[...] +``` + +And now, we can tell the World that we want to use this enum as a Component by +initializing a Components Resource and specifying that we want the Color enum. +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); +} +``` + +The Components<T> Resource is simply used to contain components of type T in +a way that is easy to use and also is quick to access. + +Finally, let's attach our Component to the Entity! + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); +} +``` + +Here we use the insert(entity, component) method available on the Components +resource. + +There are two simplifications that we can do to our code. +First is to tell rust to guess that we want the Color component by using _. + +``` +fn main() { + let mut world = World::default(); + world.initialize::<Components<Color>>(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); +} +``` + +Rust is smart enough to understand from our usage of Color::Red on the +right side that what we want is Components<Color>. + +Now, this code looks strangely like the one we saw when creating the Entity +previously. Let's apply the same trick: + +``` +fn main() { + let mut world = World::default(); + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); +} +``` + +Yep! We can use get_mut_or_default here too! + +Let's summarize. Now, we have an Entity stored in the Entities resource. +We also have a Color::Red component that is stored in the Components<Color> +resource and that is "assigned" to our Entity. + +Using Systems +-------------------------------------------------------------------------------- +Remember the core concepts? Entity, Component and System. +Well, now we are at the System part. + +As we saw previously, Systems are just functions that use data from the World. + +Let's write a System that changes the color of the apple to blue. + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + Ok(()) +} + +fn main() { + [...] +``` + +We have some things to explain here. +The parameters of Systems use references (that's the & and &mut symbols). +A special rule for Systems is that all parameters using &mut must be placed +after all parameters using &. + +For example, the following is invalid: +``` +fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} +``` + +It should instead be written as: +``` +fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} +``` + +Now, you might wonder what that SystemResult thing is. +This is a feature that is unique to Planck ECS: Error handling. + +In most implementations of Entity Component Systems, it is assumed that Systems +cannot fail, which is quite naive. +Here, returning SystemResult allows the user to return either Ok(()), which +means no error occured, or Err(SomeErrorType), which indicates that the System +failed to execute. + +For this tutorial, we will always use Ok. + +Now, let's change the color of that apple! + +``` +fn change_color_system(colors: &mut Components<Color>) -> SystemResult { + for color in join!(&mut colors) { + *color = Color::Blue; + } + Ok(()) +} +``` + +The join!() macro returns an iterator over the Components we reference. +Here, we tell the join!() macro to give us all Color Components mutably, so +that we can modify them. + +Then, we change the color. + +Running the System +-------------------------------------------------------------------------------- +To run a System, we use: + +``` +fn main () { + [...] + change_color_system.system().run(&mut world).unwrap(); +} +``` + +This will automatically get the required Components from World and call +the function change_color_system. + +Now, what happens if the System uses a Resource which is not in World? +It will crash! + +Let's fix this up: +``` +fn main() { + [...] + let mut system = change_color_system.system(); + system.initialize(&mut world); + system.run(&mut world).unwrap(); + +} +``` + +When calling the initialize function on a System, it will use world.initialize() +for every Resource that is in the System's parameters. + +Convenient, right? + +Dispatcher +-------------------------------------------------------------------------------- +Remember Dispatchers? We saw that they are used to execute multiple Systems. +Well they do more than that, they also call system.initialize for you! + +They are also very convenient, because without them you would need three +lines for each System! + +Here's how we use a Dispatcher: +``` +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let entity1 = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); +} +``` + +The Dispatcher creation is pretty much self explanatory. Simply chain +.add(system).add(system2).add(system3) to add Systems, then complete the +Dispatcher using the build(&mut world) function. + +It is when calling build(&mut world) that the initialize function will be called +on each System. + +Finally, we run the Systems using dispatcher.run_seq(&mut world). + +run_seq means "Run Sequentially", which will execute the Systems one after the +other. + +Planck ECS also has a feature where you can execute multiple Systems at the +same time. We will not use it in this tutorial. + +Multiple Components +-------------------------------------------------------------------------------- +Now, let's come back to our game! +Let's imagine that we had another Entity that also had a Color Component, but +that we don't want to change its Color. + +``` +fn main() { + let mut world = World::default(); + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); + world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +} +``` + +With our current System, both entities will have their colors changed to blue. + +We need a way to add some more meaning (data) to distinguish between the two +entities. + +There are a couple ways to do this. +- We can add an "Apple" Component. +- We can add a "ColorChanging" Component. + +Generally speaking, the second approach is preferred, because it encourages +reusing Systems for more than one type of Entity. + +Let's create this Component: + +``` +struct ColorChanging; +``` + +and add it to our apple Entity: + +``` +let apple = world.get_mut_or_default::<Entities>().create(); +let orange = world.get_mut_or_default::<Entities>().create(); +world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); +world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here +world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); +``` + +Finally, let's make it so our System only runs on Entities that have a +ColorChanging Component: + +``` +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>) -> SystemResult { + + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + + Ok(()) +} +``` + +First, we add the Components<ColorChanging> parameter. + +Then, we modify the parameters of join!() macro. +Here, we tell it to give us the Component pairs only for Entities having +ColorChanging AND Color. We also specify that we want ColorChanging immutably +(we are not going to modify it) and Color mutably (we do change the color). + +At the left side of the for loop, we ignore the ColorChanging Component by +adding a _ in front of the variable name. We do this because we used the +ColorChanging Component as a filter for join, but we don't actually plan on +using it for anything else. + +Finally, we added .unwrap() after the color variable. +We need this when joining over multiple Components at the same time. + +This is because the join macro also supports || OR operations. For example, if +we used: +``` +join!(&mut colors || &changings) +``` +we would have pairs of Components that look like this: +``` +(&mut Option<Color>, &Option<ColorChanging>) +``` + +Because we use &&, we can assume that both Options have a value (Some). +But if we use ||, then we cannot assume this, as one of the Entities has a +Color but no ColorChanging Component. + +Making sure everything works +-------------------------------------------------------------------------------- +It is always a good practice to verify that our Systems are working fine. +Let's do this quickly in the main function: +``` + [...] + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!(*world.get::<Components<Color>>().unwrap().get(apple).unwrap(), + Color::Blue); + assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), + Color::Red); +} +``` + +Removing a Component +-------------------------------------------------------------------------------- +Simple! +Use: + +``` + world.get_mut::<Components<Color>>().unwrap().remove(apple); +``` + +Of course, this works inside of Systems too, since you have Components<Color> +as a parameter! + +Removing an Entity +-------------------------------------------------------------------------------- +Also simple! +Use: + +``` + world.get_mut::<Entities>().unwrap().kill(apple); +``` + +However, it is important to know that removing an Entity using kill(entity) will +NOT delete its Components that are in the Components<T> Resources. + +This can be done automatically by using the world.maintain() function: + +``` + world.maintain(); +``` + +It is recommended to run world.maintain() after using +dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! + +Conclusion +-------------------------------------------------------------------------------- +I hope this was instructive! + +It might seem like there are lots of things to learn, but also consider that you +now know almost every single function of Planck ECS! + +If you liked this tutorial or the work I do (I wrote Planck ECS, by the way!), +consider donating on Patreon. +https://patreon.com/jojolepro + +It is your donations that enable me to continue creating Rust libraries and +learning material like this one! + +Full Code +-------------------------------------------------------------------------------- +Here is the full code created in this tutorial: + +``` +use planck_ecs::*; + +#[derive(Debug, PartialEq)] +enum Color { + Red, + Blue, + Green, +} + +struct ColorChanging; + +fn change_color_system( + changings: &Components<ColorChanging>, + colors: &mut Components<Color>, +) -> SystemResult { + for (_changing, color) in join!(&changings && &mut colors) { + *color.unwrap() = Color::Blue; + } + Ok(()) +} + +fn main() { + let mut world = World::default(); + let mut dispatcher = DispatcherBuilder::default() + .add(change_color_system) + .build(&mut world); + + let apple = world.get_mut_or_default::<Entities>().create(); + let orange = world.get_mut_or_default::<Entities>().create(); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, Color::Red); + world + .get_mut_or_default::<Components<_>>() + .insert(apple, ColorChanging); + world + .get_mut_or_default::<Components<_>>() + .insert(orange, Color::Red); + + dispatcher.run_seq(&mut world).unwrap(); + + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(apple) + .unwrap(), + Color::Blue + ); + assert_eq!( + *world + .get::<Components<Color>>() + .unwrap() + .get(orange) + .unwrap(), + Color::Red + ); + + world.get_mut::<Components<Color>>().unwrap().remove(apple); + world.get_mut::<Entities>().unwrap().kill(apple); + + world.maintain(); +} +``` + +Patreon rocket goes brrrrrrrrrrrr + +. +.. +=... +https://patreon.com/jojolepro +=... +.. +. diff --git a/target/html/blog/blog.xml b/target/html/blog/blog.xml @@ -6,6 +6,10 @@ <link>https://www.jojolepro.com</link> <atom:link href="https://www.jojolepro.com/blog/blog.xml" rel="self" type="application/rss+xml"/> <item> +<title>Getting Started with ECS using Planck ECS</title> +<link>https://www.jojolepro.com/blog/2021-06-01_getting_started_with_ecs</link> +</item> +<item> <title>Minigene And The Future</title> <link>https://www.jojolepro.com/blog/2021-05-31_minigene_and_the_future</link> </item> diff --git a/target/html/blog/index.gmi b/target/html/blog/index.gmi @@ -1,3 +1,4 @@ +=> 2021-06-01_getting_started_with_ecs Getting Started with ECS using Planck ECS => 2021-05-31_minigene_and_the_future Minigene And The Future => 2021-01-13_removing_the_us Removing the US => 2021-01-13_planck_ecs Planck: A Minimalistic Yet Performant ECS Library diff --git a/target/html/blog/index.html b/target/html/blog/index.html @@ -51,6 +51,7 @@ <br/> <article> <pre> +<a href="2021-06-01_getting_started_with_ecs">Getting Started with ECS using Planck ECS</a> <a href="2021-05-31_minigene_and_the_future">Minigene And The Future</a> <a href="2021-01-13_removing_the_us">Removing the US</a> <a href="2021-01-13_planck_ecs">Planck: A Minimalistic Yet Performant ECS Library</a>