An Entity System Framework
//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;
}
}
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:
//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.
public LogEnemySystem() : base(Aspect.All(typeof(Health)).GetOne(typeof(Koopa),typeof(Goomba),typeof(Magikoopa)).GetExclude(typeof(Ghost))) {}
[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);
}
}
[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.
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");