C# Entity Component System

#1
Hello, I need help with understanding the C# entity component system implementation. I have searched the docs for documentation and found little to no of it. This is the only thing remotely close that I could find: http://docs.cryengine.com/pages/viewpag ... d=25531348 which cannot be used as it is outdated as of 5.3, I cannot find a BaseEntity class and hence cannot follow the tutorial.

The only thing I know to do currently is create an entity component from this official Cryengine tutorial: https://www.youtube.com/watch?v=-zlCg3bTbBQ , so I have these questions:
  • How do I create a base entity.
    How do I attach components to said entity.
    How will the different components interact with each other and the base entity.
    How is it possible to place entity components in the level, I thought they had to be attached to an entity, hence why they are called entity components.
I am pretty much in the dark with this so any help is grandly appreciated! Thanks :)

Re: C# Entity Component System

#2
The BaseEntity is the class that represented the Entity in C# before 5.3 (if I remember correctly). The BaseEntity class was very confusing, since it was both an Entity and an EntityComponent at the same time. That's why since 5.3 we fully implemented the component system in C#, which caused that the BaseEntity to be refactored to the Entity class. The Entity class is now sealed, so it cannot be inherited from.
To modify the behaviour of the Entity you add components to it, which are classes that inherit from EntityComponent. In 5.3 the only way to add a component to an Entity is by calling AddComponent<TypeOfComponent>() on the Entity, or by adding the EntityClass-attribute to the class so it is exposed in the Create Object menu under the Entity category in the Sandbox. An example of such an attribute can be seen in the Rolling Ball template on the Player class.
To interact with other components on an entity you can get them by calling GetComponent<TypeOfComponent>() on the target Entity. This returns the instance of the component so you can immediately use it for interactions.

For 5.4 we've made the component system a lot better with for example a button in the sandbox that lets you add components directly to existing entities in the scene. On top of that we've made it more stable and better to work with from code as well. Also the EntityClass attribute is not anymore necessary to expose components in the Sandbox, all classes that inherit from EntityComponent will automatically be selectable in the Sandbox. I've also updated the whole C# documentation so it's up to date and hopefully less confusing for all of our users. All of this will of course be released once 5.4 is released.

The most up to date C# documentation for 5.3 is the template documentation. It gives a broad explanation of how the C# templates work. If you have more questions about C# feel free to ask of course.

Hopefully this answers your questions. :D

Re: C# Entity Component System

#3
Thanks, it answered most of my questions :) I have tried to put this to work and I have failed, basically I created a class that will be the entity and a class that is derived from EntityComponent. I tried using Entity.AddComponent<Component>() on the entity and it does not work. So how can I properly add a component to an entity?

Re: C# Entity Component System

#4
You don't need to create an Entity class. That should also be impossible since the Entity class is sealed. You only need to create a class that inherits from EntityComponent, like this:

Code: Select all

using System; using CryEngine; namespace CryEngine.RollingBall { [EntityClass("Player", "Game", null, "User.bmp")] public class Player : EntityComponent { // Your player code goes here } }
By adding the EntityClass-attribute to it, it will also be exposed in the in the sandbox. So that way you can drag it into the scene from the Entities menu in the Create Object window. The second parameter of the EntityClass defines in which category the component will be shown, so in this case the "Game" category.

Now that you have an EntityComponent you can also spawn an Entity with that component from code.

Code: Select all

Vector3 position = Vector3.Zero; Quaternion rotation = Quaternion.Identity; Player player = Entity.SpawnWithComponent<Player>(position, rotation);
If you already have a reference to an Entity, for example by searching for it by name, you can also add a component to this entity.

Code: Select all

Entity entity = Entity.Find("PlayerEntity"); entity.AddComponent<Player>();

Re: C# Entity Component System

#7
The player class is inheriting from the EntityComponent. Ok I have updated the FirstPersonPlayer and the InputManager classes. They are both EntityComponents. How would I go about adding the InputManager as a component to the FirstPersonPlayer. Here is the code:

FirstPersonPlayer Class:

Code: Select all

using CryEngine.Common; using System; namespace CryEngine.SampleApp.Data.Player { [EntityClass(name: "First Person Player", category: "Player")] public class FirstPersonPlayer : EntityComponent { //Variables [EntityProperty(description:"The speed of the player when walking.")] public float WalkSpeed { get; set; } = 1.0f; [EntityProperty(description: "The speed of the player when walking.")] public float RunSpeed { get; set; } = 3.0f; [EntityProperty(type: EntityPropertyType.Object,description: "The speed of the player when walking.")] public string PlayerGeometry { get { return Geom; } set { Geom = value; Reset(); } } private string Geom; [EntityProperty(description: "The speed of the player when walking.")] public float PlayerMass { get { return Mass; } set { Mass = value; Reset(); } } private float Mass; //Initialize public FirstPersonPlayer() { } //Begin Play public override void OnEditorGameModeChange(bool enterGame) { base.OnEditorGameModeChange(enterGame); if (enterGame) { //InputM = Entity.GetOrCreateComponent<InputManager>(); Reset(); } } //Physics Tick public override void OnPrePhysicsUpdate(float frameTime) { base.OnPrePhysicsUpdate(frameTime); if (!Global.gEnv.IsEditorGameMode()) { return; } //Walking Entity.Physics.Action((Common.pe_action_move action) => { action.dir = InputM.MoveDirection * WalkSpeed * frameTime; // iJump needs to be 2 for normal movement. You can also set it to 1 which will make the movement instant instead of adding it to velocity. action.iJump = 2; }); } public void Reset() { //Load new geometry Entity.LoadGeometry(slot: 0, url: Geom); //Initialize Physics PhysicalizeLiving(); } void PhysicalizeLiving() { var entity = Entity; var physics = entity.Physics; if (physics != null) { var parameters = new Common.SEntityPhysicalizeParams(); parameters.type = (int)Common.pe_type.PE_LIVING; parameters.mass = Mass; //PE_LIVING requires player-dimensions and player-dynamics to physicalize itself. var playerDimensions = new Common.pe_player_dimensions(); var playerDynamics = new Common.pe_player_dynamics(); #region Player dimensions // Set this to 0 for a cylinder shaped collider, or 1 for a capsule shape. playerDimensions.bUseCapsule = 1; // Change this to specify the size of your cylinder/capsule playerDimensions.sizeCollider = new Common.Vec3(0.45f, 0.45f, 0.45f); // Keep pivot at the player's feet (defined in player geometry) playerDimensions.heightPivot = 0.0f; // Offset collider upwards playerDimensions.heightCollider = 1.0f; playerDimensions.groundContactEps = 0.004f; parameters.pPlayerDimensions = playerDimensions; #endregion #region Player dynamics playerDynamics.kAirControl = 0.0f; playerDynamics.mass = Mass; parameters.pPlayerDynamics = playerDynamics; #endregion physics.Physicalize(parameters); } } } }
InputManager Class:

Code: Select all

using System; using CryEngine.Common; namespace CryEngine.SampleApp.Data { public class InputManager : EntityComponent { //Variables public float KForward; public float KBack; public float KLeft; public float KRight; public Vector3 MoveDirection; public override void OnPrePhysicsUpdate(float frameTime) { base.OnPrePhysicsUpdate(frameTime); if (!Global.gEnv.IsEditorGameMode()) { KForward = Input.GetInputValue(EKeyId.eKI_W); KBack = Input.GetInputValue(EKeyId.eKI_S); KLeft = Input.GetInputValue(EKeyId.eKI_A); KRight = Input.GetInputValue(EKeyId.eKI_D); MoveDirection = new Vector3(KRight - KLeft, KForward - KBack, 0); } } } }

Re: C# Entity Component System

#8
Your InputManager doesn't need to be an EntityComponent. In your FirstPersonPlayer you can create an instance of the InputManager, and in the OnPrePhysicsUpdate of the FirstPersonPlayer you can update the InputManager before you need the input values.
For example the FirstPersonPlayer would be like:

Code: Select all

namespace CryEngine.SampleApp.Data.Player { [EntityClass(name: "First Person Player", category: "Player")] public class FirstPersonPlayer : EntityComponent { private InputManager _inputManager = new InputManager(); // ... your other properties and methods ... //Physics Tick public override void OnPrePhysicsUpdate(float frameTime) { base.OnPrePhysicsUpdate(frameTime); if (!Global.gEnv.IsEditorGameMode()) { return; } _inputManager.Update(); //Walking Entity.Physics.Action((Common.pe_action_move action) => { action.dir = InputM.MoveDirection * WalkSpeed * frameTime; // iJump needs to be 2 for normal movement. You can also set it to 1 which will make the movement instant instead of adding it to velocity. action.iJump = 2; }); } // ... the rest of your code ... } }
and your InputManager would be like:

Code: Select all

namespace CryEngine.SampleApp.Data { public class InputManager { //Variables public float KForward; public float KBack; public float KLeft; public float KRight; public Vector3 MoveDirection; public void Update() { KForward = Input.GetInputValue(EKeyId.eKI_W); KBack = Input.GetInputValue(EKeyId.eKI_S); KLeft = Input.GetInputValue(EKeyId.eKI_A); KRight = Input.GetInputValue(EKeyId.eKI_D); MoveDirection = new Vector3(KRight - KLeft, KForward - KBack, 0); } } }

Re: C# Entity Component System

#10
It's because in the Entity.cs class that the AddComponent is in does not exist in 5.3, (release branch) but exists in the 5.4 (main branch).
You are completely right. I forgot that it was added in 5.4. Even worse, I was the one that requested it for 5.4. :P Instead you can use the GetOrCreateComponent<>() method. The only drawback of that method is that you cannot add a second component of a type to an Entity.

Who is online

Users browsing this forum: No registered users and 2 guests