Giter Site home page Giter Site logo

artemis_csharp's Introduction

Information wants to be free.

thelinuxlich thelinuxlich

thelinuxlich

trophy

artemis_csharp's People

Contributors

capnjosh1 avatar coryleeio avatar dogwatch avatar grafseismo avatar hidra1 avatar ilya-kabanov avatar marwes avatar maximusya avatar mephistofeles avatar migueldeicaza avatar mkullich avatar squizzle avatar thelinuxlich avatar tonedev avatar tpastor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

artemis_csharp's Issues

Hierarchy managing

Hi,

is it possible to have a component like:

public class Children : IComponent
{
public Children()
{
EntityIds = new List();
}
public List EntityIds { get; set; }
}
?

I think, there are situation in the core where objects get cloned. Is this algorithm able to handle lists?
Cheers,
KK

Use of bitwise and/or with BigInteger causing heap allocations

There are a handful of places in the engine where the BigInteger type bitwise operators (|, &) are used. Behind the scenes these seem to dynamically allocate UInt32 arrays to perform their operation. While developing my sample game this was these allocations were ranked #1 for memory allocations according to CLR profiler.

image

NuGet Package seems to be glitched

The Process method is never called when using world.Draw().

[ArtemisEntitySystem(GameLoopType = GameLoopType.Draw, Layer = 2)]
public class TriangleRenderSystem : EntityProcessingSystem
{
    public TriangleRenderSystem() : base(Aspect.All(typeof (TriangleComponent)))
    {
    }

    public override void Process(Entity entity)
    {
    }
}

My setup code looks like this:

        world = new EntityWorld();
        world.InitializeAll(true);

        Entity e = world.CreateEntity();
        e.AddComponent(new TriangleComponent(Color.Red));

Downloading the v1.3 release fixed my issue though, and now Process is being called. Strange bug, to say the least.

What is the use case for Hybrid Queue Processing System?

Initially I thought the Hybrid Queue Processor would only process components that had a certain type, turns out it adds that type to the queue processor each frame.

So the Hybrid Queue Processor finds all the items of a certain type, PLUS you can add additional items to its queue. So you can choose it to look at "Health" component all the time, AND add an entity that has "Health" and Stamina. All "Health" components are processed all the time, but you can add additional entities as long as they have the "Health" component.

Problem is, it won't let you add entities without a certain component.

I don't see a point for this. Seems redundant as it is covered by the Interval Entity System Processor.

It should instead block any entity from being added to it unless it has the required components.

Also it may be doing the above, I fixed a bug by changing from Hybrid and maybe I am decoding the System Processor wrong.

License

Hello,

This project does not describe what license the code it is being distributed under. The Java original project was BSD licensed, but it is not clear what your code is under.

Aspects can't be "chained"

This page states that you can do:

Aspect.All(typeof(SomeComponent)).One(typeof(OtherComponent));

However, this produces the following error:

Member 'Artemis.Aspect.One(params System.Type[])' cannot be accessed with an instance reference; qualify it with a type name instead

Added vs OnAdded

I bet this method names should be more uniform. Curently it looks like:

/**
         * Called if the system has received a entity it is interested in, e.g. created or a component was added to it.
         * @param e the entity that was added to this system.
         */
        public virtual void Added(Entity e) { }
        /**
         * Called if a entity was removed from this system, e.g. deleted or had one of it's components removed.
         * @param e the entity that was removed from this system.
         */
        public virtual void OnRemoved(Entity e) { }

        public virtual void OnEnabled(Entity e) { }

        public virtual void OnDisabled(Entity e) { }

In Artemis.java this looks like:

 void   added(Entity e) 

 void   removed(Entity e) 

 void   disabled(Entity e) 

 void   enabled(Entity e) 

Refresh of NuGet packet?

Will you offer a new NuGet packet?
In current 1.3.1 i am not able to add a component without error of null object.

Tag System

Following scenario:
I have a LevelSystem and a MusicAudioSystem. Both needed to be unique in Usage. I generally would use LevelSystem with base of TagSystem. For each loaded Level i want the Music got started or the song changed, so i also use a TagSystem as base for the MusicAudioSystem. But i am ablte to set only one Tag to an (unique and single) Entity

        /// <summary>Creates the level.</summary>
        /// <param name="entityWorld">The entity world.</param>
        /// <param name="name">The name.</param>
        /// <param name="playerCount">The player count.</param>
        /// <returns>The <see cref="Entity" />.</returns>
        public static void CreateLevel(EntityWorld entityWorld, string name, int playerCount)
        {
            world = entityWorld;
            var entity = world.CreateEntity();
            entity.AddComponent(new Level {Name = name, PlayerCount = playerCount});
            entity.AddComponent(new MusicAudio("Game") { State = EditorSystem.IsActivated ? MusicAudio.StateType.Stop : MusicAudio.StateType.Play });
            entity.Group = "Level";
            entity.Tag = "Level";
            entity.Refresh();
        }

I need to configure the MusicAudioSystem with "Level" instead of "MusicAudio". Should not the Tag System better support more then one entity Tag? Is this a design flaw or do i misunderstand anything?

Cloning an entity

Hi,
It will be very usefull to have a function for cloning an entity.
This function will produce a copied entity, but with a different uniqueId.

What do you think about this feature ?

Best regards.Cyb

EntitySystem: NullReferenceException due to empty constructor

CollisionSystem extends from EntitySystem and does not override the constructor (meaning no Aspect is provided).

EntitySystem fires off this constructor:

71 | protected EntitySystem()
72 | {
73 |     this.SystemBit = 0;
74 |     this.Aspect = null;         // Note that aspect = null
75 |     this.IsEnabled = true;
76 |     this.Types = null;
77 | }

Later on, when a Component is added to an entity, CollisionSystem's OnChange event is fired, and inside it:

194| bool interest = this.Aspect.Interests(entity);

No check is made to see if this.Aspect is null or not, causing a NullReferenceException.

A workaround is to feed Aspect.Empty() to the constructor. Perhaps, instead of null, this.Aspect's default should be Aspect.Empty()?

EntityWorld Delta property not updating

Had an issue where in the OnUpdateFrame method, calling _entityWorld.Update((long) e.Time) did not update the EntityWorld.Delta property so i could not access it in a EntityProcessingSystem.

Resolved by changing it to _entityWorld.Update() (uses internal method to calculate Delta?).

Clone of repository - does not build

Hi,

First of all nice library, I've had a play with the sample game and plan on having a go using this.

Having cloned the git repo locally unfortunately it initially fails to build. If it matters I'm running Windows 7 64 bit and have XNA 4 and XNA phone tools 7.1 installed. All in all I get 48 errors.

For instance these files are referenced by the solution but are not included.

HybridQueueSystemProcessing.cs
QueueSystemProcessing.cs
QueueSystemProcessingThreadSafe.cs

If I delete the missing files from the solution and all the tests that exercise them, then the I'm basically only left with one class of error.

BigInteger (system.numerics) is not found in the xbox and wp7 versions of the project. A quick google seems to suggest that it is not supported in the compact framework.

Have you worked round any of this locally?

Kind regards,

Paul

Build and NuGet Package for .Net 3.5

Hi there,

I was just trying to install this in a solution generated by Unity3d for Mono Develop. Since I'm using NuGet for other projects, I tried installing artemis with NuGet. Unfortunately that failed miserably since the Projects generated are for .Net Version 3.5. but this is not provided by the NuGet package.

I've build my own dlls for now, but wanted to suggest making one available for public use with NuGet.

Would you accept contributions for this or do you have any plans on integrating this on your own?

Greetings,
@kjellski

Call OnRemoved before actually removing a component

When removing a component from an entity causes it to be removed from a system and the system's OnRemoved method is called, you can't access the removed component from the method.
This sounds logical but it's a problem because:

  • By design, systems in an ECS should only be prepared to work with entities that meet their aspect.
  • More importantly, if a system is responsible for doing some kind of cleanup on a component, it can't if the component is removed.

E.g. consider a component that stores an image. A system that requires that component loads the image in the OnAdded method. Naturally, since the system loaded the image, the system is responsible for unloading it when it's not needed anymore. However if the component that stores the image is removed from the entity, the system has no way to access the image to unload it anymore.

The solution: When it's detected that removing a component would remove an entity from a system, call the system's OnRemove method first, and then remove the component.

Broken link and broken example, is this project alive??

The forum link on the Artemis frontpage is broken.

The sample code on the page is broken, for example:
Velocity velocity = e.getComponent();
should be a capital G in GetComponent.

The sample application code StarWarrior is broken, for example:
public class RenderSystem : EntityProcessingSystem<SpatialFormComponent,TransformComponent>

Creating a class like that doesn't work. Not anymore, I'm guessing it's an old design.

So, stuff needs updating.

Might seem like silly nitpicking, but first impressions are important. And if the showcase sample stuff doesn't work as described, it's going to turn people off.

Cannot unregister tag

Unregister methods of TagManager is internal, meaning that the tag of an entity cannot be changed.

There is no way to remove a tag, and if you attempt to overwrite an entity's tag you receive an ArgumentException ("An item with the same key has already been added.") from the TagManager's entityByTag dictionary.

Also, true to the original code, this method should be public (link)

Collection was modified when creating entities inside system loop.

I cannot create entity in entity system during processing, if that entity has components required by the system. It's caused by

this.Refresh(entity);

in EntityManager.cs while adding or removing components. I don't know if it's a design decision, but if not I suggest changing this to

this.entityWorld.RefreshEntity(entity);

This way it won't modify collection before the next update.

Inconsistent behaviour of EntityManager.RemoveComponent

I created a TestRemoveComponent test in TestGeneral to demonstrate the issue of removing absent components:

/// <summary>Tests removing components</summary>
#if MONO
[Test]
#else
[TestMethod]
#endif
public void TestRemoveComponent()
{
    EntityWorld entityWorld = new EntityWorld();

    Entity entity = entityWorld.CreateEntity();

    // Remove absent component from a world with no components
    entity.RemoveComponent<TestHealthComponent>();
    Debug.WriteLine("OK");

    entity.AddComponent(new TestHealthComponent());
    entity.RemoveComponent<TestHealthComponent>();
    Debug.WriteLine("OK");

    // Remove absent component
    entity.RemoveComponent<TestHealthComponent>();
    Debug.WriteLine("OK");

    int defaultComponentsBagCapacity = 16;

    for (int i = 0; i < defaultComponentsBagCapacity - 1; i++)
    {
        entity = entityWorld.CreateEntity();
        entity.RemoveComponent<TestHealthComponent>();
    }

    Debug.WriteLine("OK");

    entity = entityWorld.CreateEntity();
    Assert.AreEqual(defaultComponentsBagCapacity, entity.Id, "Entity id has unexpected value.");

    // Remove absent component now from Entity{16} (16 is the magic number = defaultComponentsBagCapacity)
    entity.RemoveComponent<TestHealthComponent>();
    Debug.WriteLine("OK");
}

Firstly, test fails with a NullReferenceException when trying to remove absent component from a "fresh" world.
Secondly (if you comment out that faulting first RemoveComponent), you will notice that removing a component twice from the same entity after actually adding the component - succeeds.
Then you see a for-loop with a "magic" defaultComponentsBagCapacity parameter: the loop successfully creates new entities and removes absent components from them.
And then goes BOOM: if you create yet another entity (which receives Id = 16) and try to remove absent component, you will get IndexOutOfRangeException in Bag's

public T Get(int index)
{
    return this.elements[index];
}

when trying to get the component in this.RemovedComponentEvent(entity, components.Get(entityId));

The method in question that I propose to fix is EntityManager's one:

internal void RemoveComponent(Entity entity, ComponentType componentType)
{
    Debug.Assert(entity != null, "Entity must not be null.");
    Debug.Assert(componentType != null, "Component type must not be null.");

    int entityId = entity.Id;
    Bag<IComponent> components = this.componentsByType.Get(componentType.Id);
    if (this.RemovedComponentEvent != null)
    {
        this.RemovedComponentEvent(entity, components.Get(entityId));
    }

    components.Set(entityId, null);
    entity.RemoveTypeBit(componentType.Bit);
    this.Refresh(entity);
}

After looking at the RemoveComponentsOfEntity below I modified the code as follows:

internal void RemoveComponent(Entity entity, ComponentType componentType)
{
    Debug.Assert(entity != null, "Entity must not be null.");
    Debug.Assert(componentType != null, "Component type must not be null.");

    int entityId = entity.Id;
    Bag<IComponent> components = this.componentsByType.Get(componentType.Id);

    if (components != null && entityId < components.Count)
    {
        IComponent componentToBeRemoved = components.Get(entityId);
        if (this.RemovedComponentEvent != null && componentToBeRemoved != null)
        {
            this.RemovedComponentEvent(entity, componentToBeRemoved);
        }

        components.Set(entityId, null);
        entity.RemoveTypeBit(componentType.Bit);
        this.Refresh(entity);
    }
}

Problem solved. As a plus we got rid of calling events, refreshing entity when there is no a component of the type.

Yes, one can say "Use HasComponent before calling RemoveComponent, or try-catch exceptions" - but I simply want to do the same as "DELETE * FROM my_component_table WHERE entity_id = 123" - no errors in case of absent record.

Entities remain active when are disabled and deleted

An entity will remain active if it is disabled and deleted during a single cycle (assuming it was previously enabled and active). Obviously you're not supposed to disable and delete at the same time, but these things do happen sometimes.

ArtemisEntitySystemPcPhoneXboxVS2010 batch build fails

When doing batch build of ArtemisEntitySystemPcPhoneXboxVS2010 solution, some targets fail with errors:

Error 1 The name 'initializeAll' does not exist in the current context Z:\Projects\GitHub\artemis_CSharp\Artemis_XNA_INDEPENDENT\EntityWorld.cs 110 16 Artemis_Xbox360
Error 2 The name 'processAttributes' does not exist in the current context Z:\Projects\GitHub\artemis_CSharp\Artemis_XNA_INDEPENDENT\EntityWorld.cs 111 36 Artemis_Xbox360

Probably Phone project would fail to build with same errors too - it's just that I don't have XNA installed and it fails fast.

Guess something has not been tested when implementing "Automatically initializing EntityWorld on constructor" related code.

Does Entity.Reset() work with non 32int systems

I've been staring at this code for an hour and I can't figure out what is wrong.

public void Reset()
{
    this.SystemBits = 0;
    this.TypeBits = 0;
    this.IsEnabled = true;
}

This function should clear all components away from an entity, however after executing the code and then checking using HasComponent<>(), the component still appears on the entity. Only after removing the component with RemoveComponent<>() does the entity lose that value.

I am not using FORCE32 my entity system (I have more than 32 components).

I am not too familiar with using the BigInt Class and this may be the problem.

Any idea why this wouldn't reset the entity's attached components?

More than 64 processors

I have more than 64 processors in my game and I discovered a bug where the flags aren't set up correctly for the bit system manager.

Super easy fix though. In SystemBitManager.cs you need a replacement of

bit = 1L << this.position;

with

bit = BigInteger.One << this.position;

in the else statement. I guess no one has built a game with so many processors yet?

1L (1 64int) was looping over after it was shifted 64 bits. With the fix, we are using the as-large-as-needed integer to set the correct flag.

My branch requires me to add something to IComponent, so I can't make a pull request. Can someone else make this change?

OnChanged

Hi!

Let me first say this is an amazing library.

I think it would be handy to have an "isDirty" or "hasChanged" flag on individual components. The current method of entity.refresh() will call onChanged on ALL components. However, if I want to only notify one component that the entity has changed this is not possible. Say I only want to notify certain components of a change under certain conditions.

Currently the way to acheive this is to add an isdirty flag to the component instance, and then check it in the process method.

It could be implemented maybe as two overrides, OnEntityChange() and OnComponentChange()

Also, I think the current logic in the base class method OnChanged should be moved to a different (private?) method. As I override OnChanged and didnt call base.OnChanged (like I do with onadded/onremoved etc) but in this instanced it screwed the entity system because of the logic in onchanged on the base class. Just a gotcha that I had to go to the source to find out and wasnt clear from the API and it was not consistent with the other methods.

Thoughts? :)

Not an Issue, but rather a thanks

Just wanted to thank @thelinuxlich for porting this to C# many years ago. As some of you may know, Unity has really jumped on the Entity Component System bandwagon and without using this port of Artemis, I would be a lot more lost as to how to use it. So just wanted to say thanks!

Entity.Id publicity

Public class EntityManager got 2 public methods:

        /// <summary>
        /// Check if this entity is active, or has been deleted, within the framework.
        /// </summary>
        /// <param name="entityId">entityId</param>
        /// <returns>active or not.</returns>
        public bool IsActive(int entityId) {
            return activeEntities.Get(entityId) != null;
        }
        /// <summary>
        /// Get the entity for the given entityId
        /// </summary>
        /// <param name="entityId">Desired EntityId</param>
        /// <returns>Entity</returns>
        public Entity GetEntity(int entityId) {
            System.Diagnostics.Debug.Assert(entityId >= 0);

            return activeEntities.Get(entityId);
        }

Which utilize Int value entityId, but same time, as i understand, you cannot obtain entityId any public way, cause Entity.Id is internal property:

        /**
         * The internal id for this entity within the framework. No other entity will have the same ID, but
         * ID's are however reused so another entity may acquire this ID if the previous entity was deleted.
         * 
         * @return id of the entity.
         */
        internal int Id {
            get { return id;}
        }

And i believe something wrong here.

Possible bug in EntityWorld.Clear() ??

i don't know if this is a bug, is possible that is working as expected but due to lack of a more general documentation i'm missunderstanding it.

The problem is when you call EntityWorld.Clear() after some Entities had been added and processed artemis crash.

To check it simply clone the repo put the line entityWorld.Clear(); before close brace of TestSimpleSystem() in TestGeneral.cs and run the test.

The code will look like this:
...
const float Expected2 = 100.0f;
Assert.AreEqual(Expected2, entity2.GetComponent().Points);
Assert.AreEqual(Expected2, entity2.GetComponent().Power);
entityWorld.Clear(); // >>> NEW LINE
}

Tracing it the problem seems to be this call secuence :

1.EntityWorld.Clear() > delete all entities in EntityManager.ActiveEntities > ok
2.EntityWorld.Clear() > calls EntityWorld.Update() > ok
3.EntityWorld.Update() > delete all marked entities in deleted bag and removes it from EntityManager > ok
4.EntityWorld.Update() > calls SystemManager.Update() > ok
5.SystemManager.Update() > calls SystemManager.Process for updatelayers > ok
6.SystemManager.Process() > calls ProcessBagSynchronous() > ok
7.ProcessBagSynchronous() > calls each one of entity systems Process() > ok
8.EntitySystem.Process() > calls ProcessEntities with this.actives > error, actives has entities despite of beign cleared before > raise exception

is and error or i'm misusing EntityWorld.Clear() and is not supposed to be called in this way.

thanks in advance.

Entities not refreshing in certain situations

I have a case in my code where I create and entity from a template during OnAdded in an EntitySystem. If you do this the new entity that is create never gets refreshed because it is currently in the middle of refreshing entities and afterwards it will clear the refresh list.

Simple Example

[Artemis.Attributes.ArtemisEntitySystem(ExecutionType = ExecutionType.Synchronous,GameLoopType = GameLoopType.Update, Layer = 1)]
public class SomeSystem : EntitySystem
{
        public override void OnAdded(Entity entity)
        {
            base.OnAdded(entity);
            GetEntityWorld().CreateEntityFromTemplate("SomeEnt", null); // this entity never gets refreshed
        }
}

Regarding UniqueID and GetEntityByUniqueID

I was using UniqueID earlier only to notice that while UniqueID is flagged as an long in the Entity object, inside EntityManager, the GetEntityByUniqueId method only takes an int and not a long.

    public Entity GetEntityByUniqueID(int entityUniqueId)
    {
        Debug.Assert(entityUniqueId != -1, "Id must != -1");
        Entity entity = null;
        this.uniqueIdToEntities.TryGetValue(entityUniqueId, out entity);
        return entity;
    }

    public long UniqueId
    {
        ...
    }

Can't unregister from a system while processing it

I didn't investigate this much but apparently, if you remove a component required by a system during its Process method, you get a System.InvalidOperationException.

The following should be enough to trigger the exception:

public class TestComponent : IComponent
{
}

public class TestSystem : EntityProcessingSystem
{
    public TestSystem()
        : base(Aspect.All(typeof(TestComponent)))
    {
    }

    public override void Process(Entity entity)
    {
        entity.RemoveComponent<TestComponent>();
    }
}

And then run:

EntityWorld world = new EntityWorld();
world.SystemManager.SetSystem<TestSystem>(new TestSystem(), Artemis.Manager.GameLoopType.Update);
world.InitializeAll(true);
Entity test = world.CreateEntity();
test.AddComponent(new TestComponent());
world.Update();

Creating better Summary tags for Processing Systems (and other items)

I am pretty new to Artemis, but I am loving it so far. Very easy to implement new features and items without breaking a lot of things.

However, the summaries for each entity processing system aren't clear at all. I have to keep inspecting the code to figure out what is going on.

I'd like to do update all the summaries and lay out some uses cases directly in the descriptions so future Artemis C# programers can implement faster.

I'd like to spearhead this. How do I do it and who do I need to talk to? I am fairly new to contributing to open source repos.

ComponentTypeManager is not thread-safe

Yes, I am aware that Artemis is not supposed to resolve any threading issues by itself - it better remain light and simple. But it already has Asynchronous systems support. So I am confused.

Anyway here is the issue:

Static ComponentTypeManager works like singleton:

public static class ComponentTypeManager
{
    /// <summary>The component types.</summary>
    private static readonly Dictionary<Type, ComponentType> ComponentTypes = new Dictionary<Type, ComponentType>();

It has a factory-like method, that is used throught out the Artemis:

public static ComponentType GetTypeFor(Type component)
{
    Debug.Assert(component != null, "Component must not be null.");

    ComponentType result;
    if (!ComponentTypes.TryGetValue(component, out result))
    {
        result = new ComponentType();
        ComponentTypes.Add(component, result);
    }

    return result;
}

When program execution adds a brand new component to an entity, new ComponentType is created and is added to Type-ComponentType dictionary.

In a classical scenario - single EntityWorld instance - all is good. But what if we want to run several instances of EntityWorld? For simplicity each instance is single-threaded - but all the instances are run on a ThreadPool or granted a dedicated thread. Can you see the danger coming?

ArgumentException An element with the same key already exists in the Dictionary<TKey, TValue>.

Artemis ComponentType system is not thread-safe despite the fact that each world is run on a single thread! This exception masks another mt-bug: same Type may get different autogenerated Id and Bit.

You can get this exception with a single EntityWorld instance - by misusing Asynchronous systems - just Add same Component to any entity in systems' Process method.

How to repeat

  1. Edit ComponentTypeManager.GetTypeFor(Type component) by adding a Thread.Sleep:
public static ComponentType GetTypeFor(Type component)
{
    Debug.Assert(component != null, "Component must not be null.");

    ComponentType result;
    if (!ComponentTypes.TryGetValue(component, out result))
    {
        result = new ComponentType();

        global::System.Threading.Thread.Sleep(1000);

        ComponentTypes.Add(component, result);
    }

    return result;
}
  1. Run the code (I ommited irrelevant EntityWorlds creation):
public void TestComponentTypeManagerMultithreaded()
{
    var t1 = Task.Factory.StartNew(() =>
    {
        ComponentTypeManager.GetTypeFor(typeof(TestHealthComponent));
    });
    var t2 = Task.Factory.StartNew(() =>
    {
        ComponentTypeManager.GetTypeFor(typeof(TestHealthComponent));
    });
    Task.WaitAll(t1, t2);
}

Workarounds/Solutions

  1. Make all public\internal static methods thread-safe.
    • mostly unneccessary synchronization overhead
  2. Make all public\internal static methods thread-safe conditionally i.e. by using compilation symbol and #if define's.
    • may be a universal solution, but the code gets polluted with conditionals
  3. Manually assign Ids and Bits to your Components.
    • requires Artemis modifictions; losing IComponent simplicity.
  4. Implement "Entity Universe" initialization - run it once at startup

Artemis Component Type system initialization example:

foreach (Type type in assembly.GetTypes())
{
    if (typeof(Artemis.Interface.IComponent).IsAssignableFrom(type))
    {
        Artemis.Manager.ComponentTypeManager.GetTypeFor(type);
    }
}

RandomSelector misses last behavior always

RandomSelector never selects last behavior.

This is the cause

switch (_Behaviors[_Random.Next(0, _Behaviors.Length - 1)].Behave())

If _Behaviors length is 4, this turns to (0,3) and because random min, max bounds work stupidly, this means that we are selecting number between 0 and 2, never 3.

So, in order to fix this, remove the -1

switch (_Behaviors[_Random.Next(0, _Behaviors.Length)].Behave())

https://msdn.microsoft.com/en-us/library/2dx6wyd4.aspx

The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.

When calling Entity.Delete(), the entity passed to EntitySystem.OnRemoved does not contain any components.

Sample code:

entity.AddComponent(new TestHealthComponent());
world.Update();
entity.Delete();

// in system, using Aspect.All(typeof(TestHealthComponent))

public override void OnRemoved(Entity entity) 
{
    Debug.Assert(entity.GetComponent<TestHealthComponent>()!=null);  <- fails
}

The components should be accessible during the call to OnRemoved, so that the system can have a chance to clean up.

I think this is related to Issue #49.

In EntityWorld.Update, the following code is called:

                    this.TagManager.Unregister(entity);
                    this.GroupManager.Remove(entity);
                    this.EntityManager.Remove(entity);

before this

this.EntityManager.Refresh(entity);

Thus, OnRemove is called with an empty Entity.

I have tried coming up with a solution, but my knowledge of Artemis' internals is still too limited.

Thank you!

Async

Integration of ASYNC/AWAIT functionality. It really is the "shit". With Artemis you typically don't process entities in sequential order anyway. By splitting the workload through threads you could increase peformance but only with LOTS of entities (10,000?).

My suggestion, create async methods with obvious naming to show they are Async. World#ProcessAsync. Keep the original synchronous side for sanity. Granted Bag would need to be thread safe. Good idea? Bad idea?

KeyNotFoundException in IntervalEntityProcessingSystem.ProcessEntities

Artemis_XNA_INDEPENDENT/System/IntervalEntityProcessingSystem.cs:

protected override void ProcessEntities(IDictionary<int, Entity> entities)
{
    for (int index = 0, s = entities.Count; s > index; ++index)
    {
        this.Process(entities[index]);
    }
}

Should be a foreach loop over entities.Values - just like in other systems' ProcessEntities methods. Otherwise you'd likely get KeyNotFoundException.

Artemis_XNA_INDEPENDENT/System/EntityProcessingSystem.cs

protected override void ProcessEntities(IDictionary<int, Entity> entities)
{
    foreach (Entity entity in entities.Values)
    {
        this.Process(entity);
    }
}

Defer AddComponent until the end of the current entity system.

Basically the same as RemoveComponent and Delete for Entity.

If a component is added to an entity, and that component is used on the current system, the processing crashes since the enumeration changed.

I'll see if I can fix it and make a pull request.

Incorrect use of GUID as a UniqueId

Entity.UniqueId is generated by EntityManager using System.Guid:

BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0);

Guid is 16 byte. We truncate it by half to 8 bytes.
Guid is not guarantied to be unique, all the more so its half.
While internally to Artemis UniqueIds are not actually required to be unique, they might be required to be for Client-Server applications. But Guid implementations might differ on Client and Server - thus cannot be relied on, strictly speaking. For reassurance Entities birth place can be resticted to one side - Server.

Well, creating an Entity with a predefined UniqueId is supported, so workarounds can be used.

Design issue with SystemManager's SetSystem<T>, GetSystem<T>

There is a design issue that leads to a bug.

I added two tests into TestGeneral.TestAttributes() that demonstrate the issue.

public void TestAttributes()
{
    ...
    entityWorld.InitializeAll(true); 
    ...
    // TestNormalEntityProcessingSystem2 and TestNormalEntityProcessingSystem3 are autoloaded (marked with ArtemisEntitySystem attribute)
    List<TestNormalEntityProcessingSystem2> listOfTestNormalEntityProcessingSystem2 = entityWorld.SystemManager.GetSystem<TestNormalEntityProcessingSystem2>();
    Assert.IsNotNull(listOfTestNormalEntityProcessingSystem2, "Failed to retrieve autoloaded TestNormalEntityProcessingSystem2 system.");
    Assert.AreEqual(1, listOfTestNormalEntityProcessingSystem2.Count, "Invalid count of TestNormalEntityProcessingSystem2 systems");
    Debug.WriteLine("OK");

    List<TestNormalEntityProcessingSystem3> listOfTestNormalEntityProcessingSystem3 = entityWorld.SystemManager.GetSystem<TestNormalEntityProcessingSystem3>();
    Assert.IsNotNull(listOfTestNormalEntityProcessingSystem3, "Failed to retrieve autoloaded TestNormalEntityProcessingSystem3 system.");
    Assert.AreEqual(1, listOfTestNormalEntityProcessingSystem3.Count, "Invalid count of TestNormalEntityProcessingSystem3 systems");
    Debug.WriteLine("OK");
    ...
}

Those tests fail. Below is explained why.

In SystemManager class:

/// <summary>The systems.</summary>
private readonly IDictionary<Type, IList> systems;

public T SetSystem<T>(T system, GameLoopType gameLoopType, int layer = 0, ExecutionType executionType = ExecutionType.Synchronous) where T : EntitySystem
{
    return (T)this.SetSystem(typeof(T), system, gameLoopType, layer, executionType);
}

public List<T> GetSystem<T>() where T : EntitySystem
{
    IList system;
    this.systems.TryGetValue(typeof(T), out system);
    return (List<T>)system;
}

internal void InitializeAll(bool processAttributes, IEnumerable<Assembly> assembliesToScan = null)
{
    ...
    EntitySystem instance = (EntitySystem)Activator.CreateInstance(type);
    this.SetSystem(instance, pee.GameLoopType, pee.Layer, pee.ExecutionType);
    ...
}

As you can see in case of autoloading Systems marked with ArtemisEntitySystem attribute they are added being casted to base EntitySystem class.
So the generic SetSystem<T> method is invoked as SetSystem<EntitySystem>. And then typeof(T) (which is typeof(EntitySystem) now) is passed further to non-generic SetSystem method - where system instance is added to a List under EntitySystem-Type key.
Calling GetSystem<TestNormalEntityProcessingSystem2>() later tries to lookup typeof(TestNormalEntityProcessingSystem2) key - which is absent in this case.

Workaround:
do not make use of autoloading feature. Call SetSystem<MyEntitySystem>() manually to later retrieve it by GetSystem<MyEntitySystem>().

Possible fix:
in SetSystem<T>() replace typeof(T) with system.GetType() to work on concrete runtime type.
Further possible (breaking change) fixes:
change the semantics of system retrieval:

public List<T> GetSystems<T>() where T : EntitySystem
{
    IList system;

    this.systems.TryGetValue(typeof(T), out system);

    return (List<T>)system;
}

public T GetSystem<T>() where T : EntitySystem
{
    IList systems;

    this.systems.TryGetValue(typeof(T), out systems);

    if (systems != null && systems.Count > 1)
    {
        throw new InvalidOperationException(string.Format("System list contains more than one element of type {0}", typeof(T)));
    }

    return (T)systems[0];
}

Deleting an entity with ComponentPoolable raises KeyNotFound exception

I'm calling delete in my system processor

    public class ExpiredObjectSystem : EntityComponentProcessingSystem<Components.DistanceExpireComponent, Components.PositionComponent>
    {
        public override void Process(Entity entity, DistanceExpireComponent expire, PositionComponent position)
        {
            if (expire.CheckExpired(entity, position.MapPosition))
                entity.Delete();
        }
    }

I get this error: {"The given key was not present in the dictionary."}

What am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.