Entities / Netcode for Entities

Introduction

The Status Effect Framework now supports Unity's Entities and Netcode for Entities packages. Similar to the Netcode for GameObjects support the system is server authoritative. No additional setup is needed between Entities and Netcode for Entities, but most of the work that will need to be done for additional functionality will be with how to interact with StatusVariables and handle Modules.

This section is a lot more in-depth than others. If you do not have at least a basic understanding of ECS I would recommend playing around with Unity samples and learning how that works before tacking this section.

Getting Started

Baking your player or characters will only change slightly as the way StatusVariables are setup in Entities is a little bit different.

For everything that contains a StatusVariable you will need to implement the IEntityStatus interface on the class. Then in OnBake() append all of the StatusVariables to the baked Entity.

using StatusEffects.Entities;
using Unity.Entities;
using System;
using UnityEngine;
// Note that there is a UnityEngine.Hash128 
// which we do not want to use here.
using Hash128 = Unity.Entities.Hash128;

public class ExamplePlayerAuthoring : MonoBehaviour, IEntityStatus
{
    public Hash128 ComponentId => m_ComponentId;
    private Hash128 m_ComponentId = new Hash128(Guid.NewGuid().ToString("N"));

    public void OnBake(Entity entity, StatusManagerBaker baker)
    {
        baker.DependsOn(this);
        baker.AppendToBuffer(entity, new StatusFloats(ComponentId, StatusMaxHealth));
        baker.AppendToBuffer(entity, new StatusFloats(ComponentId, StatusSpeed));
        baker.AppendToBuffer(entity, new StatusInts(ComponentId, StatusCoinMultiplier));
        baker.AppendToBuffer(entity, new StatusBools(ComponentId, StatusStunned));
    }
    
    // Example variables
    public StatusFloat StatusMaxHealth = new StatusFloat(100, true);
    public StatusFloat StatusSpeed = new StatusFloat(5, true);
    public StatusInt StatusCoinMultiplier = new StatusInt(1, true);
    public StatusBool StatusStunned = new StatusBool(false);
}

When baking using a Baker, directly convert the StatusVariables. For example StatusEffects.StatusFloat will convert to StatusEffects.Entities.StatusFloat.

using Unity.Entities;
using StatusEffects.Entities;

public struct ExamplePlayer : IComponentData
{
    // This is important to look up StatusVariable
    // data from the dynamic buffer.
    public Hash128 ComponentId;
    
    public StatusFloat MaxHealth;
    public StatusInt CoinMultiplier;
    public StatusBool Stunned;
}

public class ExamplePlayerBaker : Baker<ExamplePlayerAuthoring>
{
    public override void Bake(ExamplePlayerAuthoring authoring)
    {
        var entity = GetEntity(TransformUsageFlags.Dynamic);
        AddComponent(entity, new ExamplePlayer
        {
            ComponentId = authoring.ComponentId,
            MaxHealth = authoring.StatusMaxHealth,
            CoinMultiplier = authoring.StatusCoinMultiplier,
            Stunned = authoring.StatusStunned
        });
    }
}

That is all you need to do to setup functional StatusVariables for ECS. The reason there is a two part baking system is because of the way StatusVariables needed to be dynamically updated by ISystems.

Here is an example of how you can lookup those dynamic variables from within a job.

partial struct DebugPlayerMaxHealthJob : IJobEntity
{
    [ReadOnly]
    public BufferLookup<StatusFloats> StatusFloatsLookup; // from SystemAPI.GetBufferLookup<StatusFloats>()

    public uint LastSystemVersion; // from state.LastSystemVersion

    public void Execute(Entity entity, in ExamplePlayer player)
    {
        var statusFloatBuffer = StatusFloatsLookup[entity];
        // This is a check to see if the StatusFloats buffer was changed 
        // post baking. Unless you are doing something that modifies this 
        // buffer you may not need to check this and can exclude it from 
        // the GetValue() call.
        bool structuralChange = StatusFloatsLookup.DidChange(parentEntity, LastSystemVersion);
        player.MaxHealth.GetValue(player.ComponentId, statusFloatBuffer, out var maxHealth, structuralChange);
        UnityEngine.Debug.Log($"Max Health: {maxHealth}");
    }
}

Additional Info

Please checkout the pages for:

Please also check out the Entities specific code in the sample for more examples on how to implement the Status Effect Framework in ECS.

Thats it! If you have other questions or find any bugs feel free to send me a message on Discord or add it to the issue tracker on GitHub.

Last updated