npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

com.femiroglu.ecs

v1.0.5

Published

ECS Architecture with ScriptableObjects, courages data oriented programming. Check out the documentation.

Downloads

6

Readme

ECS-Like Game Architecture wScriptableObjects

This package is a simple game architecture system promotes data driven programming & C# jobs system, using scriptable objects.

System has three core classes.

  • Engine: Because scriptableobject's doesn't have MonoBehaviour callbacks, we need a wrapper. Engine knows System reference via inspector, and connects their logic to the Unity's callbacks.
  • Component: This is a monobehaviour used for data storage and handling registration/unregistration of a gameobject to the systems. Also stores ENTITY ID.
  • System: A scriptable object contains all the logic for the gameobjects.

note: Remember that systems can also contain logic that will run outside of callbacks. Since they are scriptableobjects, you can call any public method outside of the callback, anywhere you want by binding its references via the inspector. This idea is inspired by Ryan Hipple's ScriptableObject Runtime Sets idea.

Instructions & Demo Explanation

1. Create a monobehaviour class which derives from "SystemComponent" class.
2. Put every data exclusive to that gameobject which will be used in your logic.

using UnityEngine

public class MovementComponent : SystemComponent
{
    [SerializeField] private float speed;
    
    public float Speed
    {
        get
        {
            return speed;
        }
    }        
}

3. Depending on when you want the game object to move, register in the system where the action starts.

private void OnEnable()
{
    RegisterComponent();
}

private void OnDisable()
{
    RemoveComponent();
}

Final version of movement component

public class MovementComponent : SystemComponent
{
    [SerializeField] private float speed;
    public float Speed { get { return speed; } }

    private void OnEnable()
    {
        RegisterComponent();
    }

    private void OnDisable()
    {
        RemoveComponent();
    }
}

4. Create a system class which derives from GameSystem that will contain your logic.

public class MovementSystem : GameSystem
{
    protected override void MaxEntities {get;}
}

5. Add a field declaring maximum entity (gameobject) count.

public class MovementSystem : GameSystem
{
    [SerializeField] private int maxEntities;
    protected override void MaxEntities {get {return maxEntities; } }
}

6. Lets create an array of transforms & components, to maintain objects that registered to the system.

public class MovementSystem : GameSystem
{
    [SerializeField] private int maxEntities;
    protected override void MaxEntities {get {return maxEntities; } }
    
    private MovementComponent[] _components;
    private Transform[] _transforms;
    private float[] _speeds;
}

7. Lets populate these arrays on component register operation by overriding RegisterComponent method Base method will assign unique id's to each component & the id will be used as it's index.

public class MovementSystem : GameSystem
{
    [SerializeField] private int maxEntities;
    protected override void MaxEntities {get {return maxEntities; } }
    
    private MovementComponent[] _components;
    private Transform[] _transforms;
    private float[] _speeds;
    
    public override void RegisterComponent(SystemComponent component)
    {
    base.RegisterComponent(component);
    int id = component.EntityID;

    _components[id] = (MovementComponent)component;
    _transforms[id] = component.transform;
    _speeds[id] = ((MovementComponent)component).Speed;
    }
}

7. You need to clear/redefine these array references at awake, because of how scriptableobjects work.

public class MovementSystem : GameSystem
{
    [SerializeField] private int maxEntities;
    protected override void MaxEntities {get {return maxEntities; } }
    
    private MovementComponent[] _components;
    private Transform[] _transforms;
    private float[] _speeds;
    
    // remember these virtual callbacks will be connected to the unity via Engine.cs
    public override void OnSystemAwake()
    {
        base.OnSystemAwake();

        _components = new MovementComponent[MaxEntities];
        _transforms = new Transform[MaxEntities];
        _speeds = new float[MaxEntities];
    }
    
    public override void RegisterComponent(SystemComponent component)
    {
    base.RegisterComponent(component);
    int id = component.EntityID;

    _components[id] = (MovementComponent)component;
    _transforms[id] = component.transform;
    _speeds[id] = ((MovementComponent)component).Speed;
    }
}

8. Lets create a struct to define our movement logic via C# jobs system

    [BurstCompile]
    public struct CalculateMoveVector : IJobParallelFor
    {
        public NativeArray<Vector3> MoveVector;
        public NativeArray<float> Speeds;
        public NativeArray<Vector3> Directions;
        public float Delta;

        public void Execute(int index)
        {
            MoveVector[index] += Directions[index] * (Speeds[index] * Delta);
        }
    }

9. Define our Movement logic inside MovementSystem.cs and call the function on Update

public class MovementSystem : GameSystem
{
    [SerializeField] private int maxEntities;
    protected override void MaxEntities {get {return maxEntities; } }
    
    private MovementComponent[] _components;
    private Transform[] _transforms;
    private float[] _speeds;
    
    // remember these virtual callbacks will be connected to the unity via Engine.cs
    public override void OnSystemAwake()
    {
        base.OnSystemAwake();

        _components = new MovementComponent[MaxEntities];
        _transforms = new Transform[MaxEntities];
        _speeds = new float[MaxEntities];
    }
    
    public override void RegisterComponent(SystemComponent component)
    {
    base.RegisterComponent(component);
    int id = component.EntityID;

    _components[id] = (MovementComponent)component;
    _transforms[id] = component.transform;
    _speeds[id] = ((MovementComponent)component).Speed;
    }
    
    public override void OnSystemUpdate()
    {
        MoveForward();
    }

    private void MoveForward()
    {
        float deltaTime = Time.deltaTime;
               
        NativeArray<Vector3> moveVectors = new NativeArray<Vector3>(ActiveEntityIDs.Count, Allocator.TempJob);
        NativeArray<float> speeds = new NativeArray<float>(_speeds, Allocator.TempJob);
        NativeArray<Vector3> directions = new NativeArray<Vector3>(ActiveEntityIDs.Count, Allocator.TempJob);
               
        for (int i = 0; i < ActiveEntityIDs.Count; i++)
        {
            Transform t = _transforms[ActiveEntityIDs[i]];
            directions[i] = t.forward;
            speeds[i] = _speeds[i];
        }
               
        CalculateMoveVector job = new CalculateMoveVector
        {
            MoveVector = moveVectors, Speeds = speeds, Directions = directions, Delta = deltaTime
        };      
        JobHandle jobHandle = job.Schedule(ActiveEntityIDs.Count, 64);      
        jobHandle.Complete();       
        speeds.Dispose();
        directions.Dispose();       
        for (int i = 0; i < ActiveEntityIDs.Count; i++)
        {
            Transform t = _transforms[ActiveEntityIDs[i]];
            t.position += moveVectors[i];
        }       
        moveVectors.Dispose();
    }
}

10. Add Asset Menu Attribute to the movement system

[UnityEngine.CreateAssetMenu]
public class MovementSystem : GameSystem

11. Create an empty gameobject in scene, and add Engine.cs component.
12. Create an instance of MovementComponent via Create asset menu via right click => create in projects window.
13. Add MovementComponent.cs to the gameobjects that needs to move, adjust speed field & populate the system reference via inspector."
14. Populate the system reference via Engine.cs inspector.

Developer Notes

  • The amount of performance gained through this approach is directly related to your knowledge of the C# jobs system and the multithread efficiency of your algorithm.
  • Do not forget that Burst Compiler gives very different performance results in editor than build, and always test your applications on the target platform.
  • Since you can easily inject the references of GameSystem scriptableobjects into monobehaviours, feel free to add the methods to be called outside of Unity callbacks as public to your systems and call the added method anywhere you want.
  • Changing Engine.cs execution order to just before default execution order is highly recommended.
  • This was just an idea about scriptableobjects. I don't think it's production ready yet.

How To Install

This package uses the [scoped registry] feature to resolve package dependencies. Open the Package Manager page in the Project Settings window and add the following entry to the Scoped Registries list:

1. Open Edit / Project Settings in Unity

Project Settings

2. Go to Package Manager

Package Manager

3. On Scoped Registries Window add the keys below

Scoped Registry

  • Name: FEmiroglu
  • URL: https://registry.npmjs.com
  • Scope: com.femiroglu

4. Open Window / Package Manager in Unity

Open Package Manager

5. Select My Registries Option

My Registries

Now you can install the package from My Registries page in the Package Manager window.

My Registries