Artemis

An Entity System Framework

View the Project on GitHub thelinuxlich/artemis_CSharp

Description

Artemis is a high performance Entity System framework for games, originally written in Java by Arni Arent and Tiago Costa, now ported to C#.
Its goal is to provide a clean API to abstract your game world organization into entities, components and systems.
Artemis has no dependencies (for PC, in Xbox and Windows Phone 7 we have one) and can be used with any game framework or library, 2D or 3D, and even multiplatform, if you use it with Mono/MonoTouch/Mono4Android.

Differences from the original version

We support all the features included in the original Java version, but we've also added the following features:

Getting Started

There is much more to tell about this paradigm and we already have rich articles which inspired this framework. Some of them: And many more can be found here.

Your components must inherit from the interface IComponent or ComponentPoolable if you want it to use the Artemis Component Pool. Example:
//Add this Attribute and extend ComponentPoolable if you want your Component to use Artemis Component Pool
[Artemis.Attributes.ArtemisComponentPool(InitialSize=5,IsResizable=true, ResizeSize=20, IsSupportMultiThread=false)]
class Velocity : ComponentPoolable
{
        private float velocity;
        private float angle;

        public Velocity() { }

        public Velocity(float vector)
        {
            velocity = vector;
        }

        public Velocity(float velocity, float angle)
        {
            this.velocity = velocity;
            this.angle = angle;
        }

        public float Speed {
            get { return velocity;}
            set { velocity = value; }
        }

        public float Angle
        {
            get { return angle; }
            set { angle = value;}
        }

        public void AddAngle(float a)
        {
            angle = (angle + a) % 360;
        }

        public float AngleAsRadians
        {
            get { return (float)Math.PI * angle / 180.0f; }
        }

        //obligatory for poolable Components
        public void Cleanup()
        {
             coords = Vector3.Zero;
        }
}
A sample for entity assembling:
Entity e = world.CreateEntity(); // you can pass an unique ID as first parameter.
e.AddComponent(new Transform(200,400));
e.AddComponentFromPool<Velocity>(); // use AddComponentFromPool if the Component extend from ComponentPoolable
Your systems should inherit from one of the following templates: And here is a system example, using Velocity and Transform components to create the Movement behavior:
//Add this attribute so the EntityWorld knows the systems it should execute, use the Layer to determine execution order
[Artemis.Attributes.ArtemisEntitySystem(ExecutionType = ExecutionType.Synchronous,GameLoopType = GameLoopType.Update, Layer = 1)]
public class MovementSystem : EntityProcessingSystem {

    public MovementSystem() : base(Aspect.All(typeof(Transform), typeof(Velocity))) { }

    public override void Process(Entity e) {
        Velocity velocity = e.getComponent<Velocity>();
        float v = velocity.Speed;

        Transform transform = e.getComponent<Transform>();

        float r = velocity.AngleAsRadians;

        float xn = transform.X + (TrigLUT.Cos(r) * v * world.Delta);
        float yn = transform.Y + (TrigLUT.Sin(r) * v * world.Delta);

        transform.SetLocation(xn, yn);
    }
}
On your game initialization, create a new EntityWorld:
var world = new EntityWorld();
Update or Draw the World:
world.Update();
world.Draw(); // do this on a different loop, e.g: every 60 frames.
And you are good to go. The Entity object has some intuitive methods like Delete(), GetComponent(), RemoveComponent(), which you can see in action on the example game.

Aspects

Aspects are used in the constructor of your entity systems to tell them which components they should be interested. At the moment we have three methods to build Aspects: You can also compose your Aspect utilizing these methods together, example:
public LogEnemySystem() : base(Aspect.All(typeof(Health)).GetOne(typeof(Koopa),typeof(Goomba),typeof(Magikoopa)).GetExclude(typeof(Ghost))) {}

Extending Systems

It's quite simple to extend the EntitySystem with your own logic, an example can be found here, which gives us less setup on systems which only uses Aspect.All(), example:
      [Artemis.Attributes.ArtemisEntitySystem(ExecutionType = ExecutionType.Synchronous,GameLoopType = GameLoopType.Update, Layer = 1)]
public class MovementSystem : EntityComponentProcessingSystem<Transform,Velocity> {
    public override void Process(Entity e,Transform transform, Velocity velocity) {
        float v = velocity.Speed;
        float r = velocity.AngleAsRadians;

        float xn = transform.X + (TrigLUT.Cos(r) * v * world.Delta);
        float yn = transform.Y + (TrigLUT.Sin(r) * v * world.Delta);

        transform.SetLocation(xn, yn);
    }
}
    

Templates

Create your entity templates to avoid manually creating and configuring entities:
[Artemis.Attributes.ArtemisEntityTemplate("BulletExplosion")]
public EnemyTemplate : Artemis.IEntityTemplate {
	public Entity BuildEntity(Entity e,EntityWorld entityWorld, params object[] args) {
		e.AddComponent(new Transform(200,400));
		e.AddComponent(new Velocity(2.4f,0.9f));
	}
}
Create your entities with the template applied:
var enemy = world.CreateEntityFromTemplate("BulletExplosion",array_of_parameters); // you can also use an alternative signature passing a custom unique id as the first parameter.

Blackboard

You can easily share common objects between systems using the blackboard, here is a XNA example, on the game initialization:
EntitySystem.BlackBoard.SetEntry<ContentManager>("ContentManager", Content);
EntitySystem.BlackBoard.SetEntry<GraphicsDevice>("GraphicsDevice", GraphicsDevice);
EntitySystem.BlackBoard.SetEntry<SpriteBatch>("SpriteBatch", spriteBatch);
Then you can retrieve the objects inside systems like this:
this.device = EntitySystem.BlackBoard.GetEntry<GraphicsDevice>("GraphicsDevice");
this.spriteBatch = EntitySystem.BlackBoard.GetEntry<SpriteBatch>("SpriteBatch");
this.contentManager = EntitySystem.BlackBoard.GetEntry<ContentManager>("ContentManager");

Games created with Artemis C#

Magnetic by Nature - by Tripleslash Studios. They also have a Kickstarter running for the game, support it!

Discussion, issues, suggestions

We have a forum section here.