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..a891860f 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 @@ -7,7 +7,7 @@

Introduction

This will cover the basics of using Ecsact with Unity. We will create entities, add components and implement systems using C#. The scene Basic Example in - unity-example has all the contents of this tutorial. @@ -55,15 +55,19 @@

Creating an Entity

Great! We have a simple .Ecsact file we can build on. Now we can make a C# script in Unity, for our use it will be called BasicExample.cs. This script will allow us to - create an entity with some components + create an entity + with some components

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 +77,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

@@ -88,32 +98,29 @@

Creating an Entity

Runtime Listeners

- The Runtime gives you access to - everything used during Unity's runtime. For example, listeners can be - added for changes to component state or when an entity is created or - destroyed. + The + Runtime + gives you access to everything used during Unity's runtime. For example, + listeners can be added for changes to component state or when an entity is + created or destroyed.


-				// 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");
 					{{'}'}})
@@ -137,13 +144,15 @@ 

Runtime Listeners

- Implementing a System + Implementing a + System

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 +170,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 +182,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 based 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

diff --git a/src/app/start/tutorials/unity/moving-block/moving-block.component.html b/src/app/start/tutorials/unity/moving-block/moving-block.component.html index 3fc5db77..86843842 100644 --- a/src/app/start/tutorials/unity/moving-block/moving-block.component.html +++ b/src/app/start/tutorials/unity/moving-block/moving-block.component.html @@ -6,14 +6,24 @@

Introduction

This tutorial will take a look at how Ecsact, the Unity API, and Unity Sync can be used create a simple game. If you haven't tried the - basic example + 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 +37,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.

    @@ -37,9 +46,12 @@

    Setup

    Adding a GameObject with Unity Sync

    We're going to start by using a - Unity Sync script to add a GameObject - to the scene. But first we'll need to create some components to filter our - entities. We will always use example as our ecsact file name. + Unity Sync + script to add a GameObject to the scene. But first we'll need to create + some components to filter our entities. We will always use + example as our ecsact file name.

    
    @@ -97,21 +109,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 +135,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 +162,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 +209,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 +249,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 +262,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 +293,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 +339,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 +404,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.


@@ -391,10 +421,10 @@ 

Movement

var velocity = context.Get<{{'example.Velocity'}}>(); var move = context.Action<{{'example.Move'}}>(); - velocity.x_value = move.dir_x; - velocity.y_value = move.dir_y; + velocity.x_value += move.dir_x; + velocity.y_value += move.dir_y; - context.Update<{{'example.Velocity'}}>(velocity); + context.Update(velocity); {{'}'}} [Ecsact.DefaultSystemImpl(typeof(example.ApplyVelocity))] @@ -411,22 +441,21 @@

Movement

position.x += velocity.x_value; position.y += velocity.y_value; - context.Update<{{'example.Position'}}>(position); + context.Update(position); {{'}'}} {{'}'}}

- 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 +484,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!


@@ -487,27 +517,19 @@ 

Basic Collision

position.x = position.prev_x; position.y = position.prev_y; - if(position.x < otherPos.x) {{'{'}} - velocity.x_value = -0.5f; - {{'}'}} else {{'{'}} - velocity.x_value = 0.5f; - {{'}'}} + velocity.x_value = -velocity.x_value * 0.5f; + velocity.y_value = -velocity.y_value * 0.5f; - if(position.y < otherPos.y) {{'{'}} - velocity.y_value = -0.5f; - {{'}'}} else {{'{'}} - velocity.y_value = 0.5f; - {{'}'}} - - context.Update<{{'example.Velocity'}}>(velocity); - context.Update<{{'example.Position'}}>(position); + context.Update(velocity); + context.Update(position); {{'}'}} {{'}'}} {{'}'}}

- 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

Conclusion

@@ -564,6 +583,6 @@

Conclusion

diff --git a/src/app/start/unity/start-unity.component.html b/src/app/start/unity/start-unity.component.html index f6ae38f5..1bb45695 100644 --- a/src/app/start/unity/start-unity.component.html +++ b/src/app/start/unity/start-unity.component.html @@ -1,7 +1,7 @@

Setting Up Unity

-

Ecsact Integration Package

+

Ecsact Integration Package

Unity Ecsact Integration is provided as a Ecsact Integration Package >.

-

The Ecsact Window

+

The Ecsact Window

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:

+ +
+
+

Unity Sync

To enable Unity Sync

-

Recipes

+

Recipes

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 @@

Recipes

-

Learning about the Unity Integration

+

Learning about the Unity Integration

To learn more about the Ecsact Integration for Unity, you can follow our