From b608d852e4f759d8aa344d7e9a03f6867ce2897b Mon Sep 17 00:00:00 2001
From: Kelwan
Date: Mon, 5 Aug 2024 11:31:41 -0700
Subject: [PATCH 1/3] fix: fixed dead links, rewrote parts of tutorial and
setup
---
.../basic-example.component.html | 121 +++++++++++---
.../moving-block/moving-block.component.html | 148 ++++++++++--------
.../start/unity/start-unity.component.html | 27 +++-
src/assets/BasicExampleScene.png | Bin 0 -> 16899 bytes
src/assets/MovingBlockScene.png | Bin 6325 -> 16661 bytes
src/assets/UnityAssemblyScripts.png | Bin 0 -> 29690 bytes
src/assets/UnityBasicExampleCountdown.webm | Bin 0 -> 33417 bytes
src/assets/UnityFolderHierarchy.png | Bin 0 -> 58309 bytes
8 files changed, 199 insertions(+), 97 deletions(-)
create mode 100644 src/assets/BasicExampleScene.png
create mode 100644 src/assets/UnityAssemblyScripts.png
create mode 100644 src/assets/UnityBasicExampleCountdown.webm
create mode 100644 src/assets/UnityFolderHierarchy.png
diff --git a/src/app/start/tutorials/unity/basic-example/basic-example.component.html b/src/app/start/tutorials/unity/basic-example/basic-example.component.html
index 0d996339..96d39c4b 100644
--- a/src/app/start/tutorials/unity/basic-example/basic-example.component.html
+++ b/src/app/start/tutorials/unity/basic-example/basic-example.component.html
@@ -60,10 +60,11 @@
Creating an Entity
We'll start by declaring the components we'll be adding to our entity
- // Declare an Example component type
- var exampleComponent = new example.Example {{'{'}}
- example_value = 5,
- {{'}'}};
+ void start() {{'{'}}
+ // Declare an Example component type
+ var exampleComponent = new example.Example {{'{'}}
+ example_value = 0,
+ {{'}'}};
var removeComponent = new example.ToBeRemoved {{'{'}}{{'}'}};
@@ -73,7 +74,13 @@
Creating an Entity
Components can be declared from the namespace that is derived from the
name of your package in the .ecsact file. Now that we have
our components, we can add them to a newly created entity using the
- Runner's executionOptions
+ Runner's
+ executionOptions
@@ -96,24 +103,20 @@
Runtime Listeners
- // Added on the class to cleanup event listeners
+ // Declared on the script, added on the class to cleanup event listeners
List<{{'System.Action'}}> cleanUpFns = new();
-
+
+ // Callbacks from the Ecsact Runtime used to change state in Unity
cleanUpFns.AddRange(new[] {{'{'}}
- // Callback invoked when Example component is added
Ecsact.Defaults.Runtime.OnInitComponent<{{'example.Example'}}>((entity, component) => {{'{'}}
- Debug.Log("Example component added");
Debug.Log(component.example_value);
{{'}'}}),
- // Callback invoked when Example component is updated
Ecsact.Defaults.Runtime.OnUpdateComponent<{{'example.Example'}}>((entity, component) => {{'{'}}
Debug.Log(component.example_value);
{{'}'}}),
- // Callback invoked when Example component is removed
Ecsact.Defaults.Runtime.OnRemoveComponent<{{'example.ToBeRemoved'}}>((entity, component) => {{'{'}}
Debug.Log("Example component removed");
{{'}'}}),
- // Callback invoked on the creation of an entity
Ecsact.Defaults.Runtime.OnEntityCreated((entityId, placeholderId) => {{'{'}}
Debug.Log("Entity created");
{{'}'}})
@@ -143,7 +146,8 @@
Now the entity has components, which will cause our system to trigger. Now
we need to implement the system for it to happen. If you haven't setup
your Assembly Definition now is the time.
- If the script is not part of an assembly, it will not be found. We'll name
+ If the script is not part of an assembly, it will not be found. Your
+ system scripts must be in the same hierarchy as the assembly. We'll name
our new system script
BasicExampleSystem.cs
@@ -161,10 +165,10 @@
// Modify and update its value
value.example_value +=1;
- context.Update<{{'example.Example'}}>(value);
+ context.Update(value);
if(value.example_value >= 100) {{'{'}}
- // Remove the component from the context
+ // Remove the component from the entity
context.Remove<{{'example.ToBeRemoved'}}>();
{{'}'}}
{{'}'}}
@@ -173,24 +177,95 @@
- Every tick, which in this example occurs on fixed update the
- example_value is increased by 1. Once it reaches a certain value, the
- component ToBeRemoved is removed. After this the entity no
- longer has the required components to continue being iterated over.
+ Every tick, the example_value that's a field on our component
+ is increased by 1. Once it reaches a value of 100, the component
+ ToBeRemoved is removed. After this the entity no longer has
+ the required components to continue being iterated over.
- Assuming things worked, example_value will increase until it
- reaches 100. Then on its conclusion you should see the
+ On its conclusion, you'll receive the
OnRemoveComponent callback
+
+
Basic Example File
+
+
+ using System.Collections.Generic;
+ using UnityEngine;
+
+ public class BasicExample : MonoBehaviour
+ {{'{'}}
+
+ List<{{'System.Action'}}> cleanUpFns = new();
+ int entityId;
+
+ void Start()
+ {{'{'}}
+ // Callbacks from the Ecsact Runtime used to change state in Unity
+ cleanUpFns.AddRange(new[] {{'{'}}
+ Ecsact.Defaults.Runtime.OnInitComponent<{{'example.Example'}}>(
+ (entity, component) => {{'{'}}
+ Debug.Log(component.example_value);
+ {{'}'}}),
+ Ecsact.Defaults.Runtime.OnUpdateComponent<{{'example.Example'}}>(
+ (entity, component) => {{'{'}}
+ Debug.Log(component.example_value);
+ {{'}'}}),
+
+ Ecsact.Defaults.Runtime.OnRemoveComponent<{{'example.Example'}}>(
+ (entity, component) => {{'{'}} Debug.Log("Component removed");
+ {{'}'}}),
+ Ecsact.Defaults.Runtime.OnEntityCreated(
+ (entityId, placeholderId) => {{'{'}} Debug.Log("Entity created");
+ {{'}'}})
+ {{'}'}});
+
+ // Declare an Example component type
+ var exampleComponent = new example.Example
+ {{'{'}}
+ example_value = 0,
+ {{'}'}};
+
+ // Declare a ToBeRemoved component type
+ var removeComponent = new example.ToBeRemoved {{'{'}}{{'}'}};
+
+ // Create an entity and add an initial components list
+ Ecsact.Defaults.Runner.executionOptions.CreateEntity()
+ .AddComponent(exampleComponent)
+ .AddComponent(removeComponent);
+ {{'}'}}
+
+ void OnDestroy()
+ {{'{'}}
+ foreach (var cleanUpFn in cleanUpFns)
+ {{'{'}}
+ cleanUpFn();
+ {{'}'}}
+ {{'}'}}
+ {{'}'}}
+
+
+
+
+ This example is on the
+ ecsact-examples
+ repository as a reference. You can load the scene
+ BasicExample.unity and you'll see something like this:
+
+
+
Next Steps
- This taught you the basics of using Ecsact with Unity. If you want to
- learn more about Unity Sync and Nested Systems, check out
+ This covers the basics of using Ecsact with Unity. To learn about Unity
+ Sync and Nested Systems, check out
Example Using Unity Sync
Sync can be used create a simple game. If you haven't tried the
basic example
yet, it's recommended to start there first. We'll be using the scene
- EcsactExample in the
- MovingBlock in the
+ unity-example
- repository. You can either use the scene provided, or make your own and
- build it however you like.
+ repository. It's given as a reference point and should look something like
+ this:
+
+
+
+ You can use it as a reference, remove the scripts and GameObjects to
+ follow along, or make your own environment in a scene of your own. You'll
+ see later how it comes into play with collision
Prerequisites
@@ -27,9 +33,8 @@
Prerequisites
Setup
- You'll want to setup your Assembly file,
- get the Ecsact Integration and enable Unity Sync. You can read about how
- to do that.
+ Make sure to setup your Assembly file,
+ get the Ecsact Integration and enable Unity Sync before continuing.
@@ -97,21 +102,21 @@
Adding a GameObject with Unity Sync
- Let's see if our script is working! Let's have an initial entity spawn by
- using
+ Let's see if our script is working! Using
Dynamic Entity. If everything worked correctly, when you enter playmode a gameObject
- should spawn at the position you inputted for the entity.
+ >, spawn an entity with the components we just declared. When you enter
+ playmode a gameObject will instantiate at the position you inputted for
+ the entity.
Generating Blocks
- What if we took this one step further? We've managed to use Unity Sync
- with our Dynamic Entity, and now we can can adapt to changes in entities
- during runtime. Let's go into our .ecsact file.
+ What if we took this one step further? We used Unity Sync with our Dynamic
+ Entity, and now we can can adapt to changes in entities during runtime.
+ Let's go into our .ecsact file.
@@ -123,7 +128,7 @@
Generating Blocks
i32 pos_y;
{{'}'}}
- // An action that will happen on an input
+ // An action that will happen on an input, generates new blocks at runtime
action PerformGenerateBlock {{'{'}}
i32 pos_x;
i32 pos_y;
@@ -150,8 +155,8 @@
Generating Blocks
Now we have the code in place to make a block spawn from an action. Any
newly generated entities will be pooled for Unity Sync. This means the
- Sync script that adds the Block GameObject will be automatically added to
- our new entities. Let's make a way to perform our action.
+ Sync script we wrote will add the Block gameObject to all our
+ new entities. Let's make a way to perform our action.
@@ -197,6 +202,16 @@
Generating Blocks
GameObject to be present during the runtime. With Ecsact and pushing the
action accounted for, all that's left to do is implement the systems.
+
+ Note: Make sure your
+ folder hierarchy
+ is in your Assembly Definition
+
@@ -227,10 +242,10 @@
Generating Blocks
var block = context.Get<{{'example.QueueBlock'}}>();
builder
- .AddComponent<{{'example.Block'}}>(
+ .AddComponent(
new example.Block{{'{'}}{{'}'}}
)
- .AddComponent<{{'example.Position'}}>(
+ .AddComponent(
new example.Position{{'{'}}x = block.pos_x, y = block.pos_y{{'}'}}
)
.Finish();
@@ -240,12 +255,14 @@
Generating Blocks
- When the InputAction is pressed, a new entity will be
- generated with a block the same way our Dynamic Entity does.
- But there's a small difference. When an entity is spawned at runtime, the
- Position of the entity won't be applied to the gameobject itself. We can
- use Unity Sync to solve that
- problem.
+ To reiterate, when the InputAction is pressed a new entity
+ will be generated with a block the same way our first
+ Dynamic Entity does. But there's a small difference. When an
+ entity is spawned at runtime, the Position of the entity won't be applied
+ to the gameobject itself. We can use
+ Unity Sync to solve that
+ problem. Create a script called `SyncMove` that listen to callbacks from
+ the `init` and `update` component events.
@@ -269,19 +286,19 @@
Generating Blocks
- A listener for OnUpdate was added too. This will be used a
- bit later for Movement.
+ The listener for OnUpdate will be important when we add
+ movement.
Movement
- So we can generate entities at will during runtime and manipulate their
- data. The only problem now is we can't interact with them. Let's add
- movement to all the entity blocks that we spawn. We'll add
- Velocity, a move action, and a corresponding system to apply
- changes.
+ We're not generating entities at will during runtime and can manipulate
+ their data. The only thing missing is we can't interact with them. Let's
+ add movement to all the entity blocks that we spawn. We'll add
+ Velocity, a Move action, and a corresponding
+ system to apply changes.
@@ -315,6 +332,14 @@
Movement
Ecsact, any Dynamic Entities and
GenerateBlockSystem to see the effects.
+
+
+
+ // Add to your builder we used for GenerateBlock
+ builder.AddComponent(new example.CanMove {{'{'}}{{'}'}})
+
+
+
Let's move to Unity. In our example, we'll add a script to the same
GameObject that holds our Dynamic Entity called
@@ -372,11 +397,9 @@
Movement
- We've done our Ecsact declarations and added the ability to push our
- Action. Now like before we implement our systems. This might
- start to feel familiar, the workflow of getting to and from Ecsact doesn't
- change very much. We'll make another script we'll drop into our systems
- folder called MoveSystems.
+ Now We'll implement our newly declared systems Move and
+ ApplyVelocity. Drop the new script into our systems folder
+ and call it MoveSystems.
- Since we already added SyncMove earlier, the gameobject
+ Since we already added SyncMove earlier, the GameObject
should be updated whenever the position of the entity is.
Basic Collision
- We have movement, great! We can finally interact with our game in some
- way. The big problem now is we can go off the screen, and there's no way
- to
+ We have movement, great! We can finally interact with our entities. The
+ big problem now is we can go off the screen, and there's no way to
interact with the other entities in our scene. We're going to
cover one of the ways in which you can compare entities and create
interactions with Nested Systems.
@@ -455,11 +477,12 @@
Basic Collision
Nested System
takes each entity that qualifies for CollisionComparer, and
compares each one against all of the qualified entities in
- CheckCollision. You can view it as a nested for loop.
+ CheckCollision.
Note: You need to add the Collider component to any entities
- that you want to be considered.
+ that you want to be considered. Don't forget the
+ GenerateBlock builder from before!
- There's only 1 system implementation. If there's nothing for a
+ CollisionComparer doesn't need an implementation because its
+ components are readonly. If there's nothing for a
system to consider then the system is considered
trivialBasic Collision
{{'}'}}
-
- If you've opened up the project you will see this. There are gameObjects
- called boundaries in the project that will create a little
- game area. You can just as easily make your own in any way you like. The
- last thing left to do is add our new StaticCollider script to
- these gameObjects. Now, we can move, spawn new blocks that can also move,
- and run into game boundaries!
+ The last step is to apply our static colliders to boundaries in our game
+ level. You'll see in the reference example that there are GameObjects
+ called boundaries using this script. If you're making your
+ own, add them to your boundary so the generated blocks collide with them
If the Ecsact package was successfully installed, there will be a new
window under
@@ -58,7 +58,7 @@
The Ecsact Window
-
Assembly
+
Assembly
For systems to be registered into Ecsact, it needs to be part of an
Assembly. In addition, it will need the following dependencies:
@@ -82,7 +82,22 @@
Assembly
-
Unity Sync
+
Folder hierarchy
+
+ Everything done with Ecsact, including system implementations, has to be
+ contained in the hierarchy of the generated
+ Assembly definition. If you're
+ just learning we'd recommend putting everything in
+ Assets arrow_right Scripts
+ arrow_right Ecsact
+
+
You should have a configuration that looks something like this:
Ecsact supports having multiple recipes. You can use other
implementations, or there's one available for EnTT that comes packaged in
@@ -106,7 +121,7 @@