mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-25 08:22:15 +00:00
Move source projects into source directory
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class Aspect
|
||||
{
|
||||
internal Aspect()
|
||||
{
|
||||
AllSet = new BitVector32();
|
||||
ExclusionSet = new BitVector32();
|
||||
OneSet = new BitVector32();
|
||||
}
|
||||
|
||||
public BitVector32 AllSet;
|
||||
public BitVector32 ExclusionSet;
|
||||
public BitVector32 OneSet;
|
||||
|
||||
public static AspectBuilder All(params Type[] types)
|
||||
{
|
||||
return new AspectBuilder().All(types);
|
||||
}
|
||||
|
||||
public static AspectBuilder One(params Type[] types)
|
||||
{
|
||||
return new AspectBuilder().One(types);
|
||||
}
|
||||
|
||||
public static AspectBuilder Exclude(params Type[] types)
|
||||
{
|
||||
return new AspectBuilder().Exclude(types);
|
||||
}
|
||||
|
||||
public bool IsInterested(BitVector32 componentBits)
|
||||
{
|
||||
if (AllSet.Data != 0 && (componentBits.Data & AllSet.Data) != AllSet.Data)
|
||||
return false;
|
||||
|
||||
if (ExclusionSet.Data != 0 && (componentBits.Data & ExclusionSet.Data) != 0)
|
||||
return false;
|
||||
|
||||
if (OneSet.Data != 0 && (componentBits.Data & OneSet.Data) == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using MonoGame.Extended.Collections;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class AspectBuilder
|
||||
{
|
||||
public AspectBuilder()
|
||||
{
|
||||
AllTypes = new Bag<Type>();
|
||||
ExclusionTypes = new Bag<Type>();
|
||||
OneTypes = new Bag<Type>();
|
||||
}
|
||||
|
||||
public Bag<Type> AllTypes { get; }
|
||||
public Bag<Type> ExclusionTypes { get; }
|
||||
public Bag<Type> OneTypes { get; }
|
||||
|
||||
public AspectBuilder All(params Type[] types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
AllTypes.Add(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AspectBuilder One(params Type[] types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
OneTypes.Add(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AspectBuilder Exclude(params Type[] types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
ExclusionTypes.Add(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Aspect Build(ComponentManager componentManager)
|
||||
{
|
||||
var aspect = new Aspect();
|
||||
Associate(componentManager, AllTypes, ref aspect.AllSet);
|
||||
Associate(componentManager, OneTypes, ref aspect.OneSet);
|
||||
Associate(componentManager, ExclusionTypes, ref aspect.ExclusionSet);
|
||||
return aspect;
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterTypeCanBeEnumerable.Local
|
||||
private static void Associate(ComponentManager componentManager, Bag<Type> types, ref BitVector32 bits)
|
||||
{
|
||||
foreach (var type in types)
|
||||
{
|
||||
var id = componentManager.GetComponentTypeId(type);
|
||||
bits[1 << id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public static class BitArrayExtensions
|
||||
{
|
||||
public static bool IsEmpty(this BitArray bitArray)
|
||||
{
|
||||
for (var i = 0; i < bitArray.Length; i++)
|
||||
{
|
||||
if (bitArray[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ContainsAll(this BitArray bitArray, BitArray other)
|
||||
{
|
||||
var otherBitsLength = other.Length;
|
||||
var bitsLength = bitArray.Length;
|
||||
|
||||
for (var i = bitsLength; i < otherBitsLength; i++)
|
||||
{
|
||||
if (other[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
var s = Math.Min(bitsLength, otherBitsLength);
|
||||
|
||||
for (var i = 0; s > i; i++)
|
||||
{
|
||||
if ((bitArray[i] & other[i]) != other[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Intersects(this BitArray bitArray, BitArray other)
|
||||
{
|
||||
var s = Math.Min(bitArray.Length, other.Length);
|
||||
|
||||
for (var i = 0; s > i; i++)
|
||||
{
|
||||
if (bitArray[i] & other[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Collections;
|
||||
using MonoGame.Extended.Entities.Systems;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public interface IComponentMapperService
|
||||
{
|
||||
ComponentMapper<T> GetMapper<T>() where T : class;
|
||||
}
|
||||
|
||||
public class ComponentManager : UpdateSystem, IComponentMapperService
|
||||
{
|
||||
public ComponentManager()
|
||||
{
|
||||
_componentMappers = new Bag<ComponentMapper>();
|
||||
_componentTypes = new Dictionary<Type, int>();
|
||||
}
|
||||
|
||||
private readonly Bag<ComponentMapper> _componentMappers;
|
||||
private readonly Dictionary<Type, int> _componentTypes;
|
||||
|
||||
public Action<int> ComponentsChanged;
|
||||
|
||||
private ComponentMapper<T> CreateMapperForType<T>(int componentTypeId)
|
||||
where T : class
|
||||
{
|
||||
// TODO: We can probably do better than this without a huge performance penalty by creating our own bit vector that grows after the first 32 bits.
|
||||
if (componentTypeId >= 32)
|
||||
throw new InvalidOperationException("Component type limit exceeded. We currently only allow 32 component types for performance reasons.");
|
||||
|
||||
var mapper = new ComponentMapper<T>(componentTypeId, ComponentsChanged);
|
||||
_componentMappers[componentTypeId] = mapper;
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public ComponentMapper GetMapper(int componentTypeId)
|
||||
{
|
||||
return _componentMappers[componentTypeId];
|
||||
}
|
||||
|
||||
public ComponentMapper<T> GetMapper<T>()
|
||||
where T : class
|
||||
{
|
||||
var componentTypeId = GetComponentTypeId(typeof(T));
|
||||
|
||||
if (_componentMappers[componentTypeId] != null)
|
||||
return _componentMappers[componentTypeId] as ComponentMapper<T>;
|
||||
|
||||
return CreateMapperForType<T>(componentTypeId);
|
||||
}
|
||||
|
||||
public int GetComponentTypeId(Type type)
|
||||
{
|
||||
if (_componentTypes.TryGetValue(type, out var id))
|
||||
return id;
|
||||
|
||||
id = _componentTypes.Count;
|
||||
_componentTypes.Add(type, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
public BitVector32 CreateComponentBits(int entityId)
|
||||
{
|
||||
var componentBits = new BitVector32();
|
||||
var mask = BitVector32.CreateMask();
|
||||
|
||||
for (var componentId = 0; componentId < _componentMappers.Count; componentId++)
|
||||
{
|
||||
componentBits[mask] = _componentMappers[componentId]?.Has(entityId) ?? false;
|
||||
mask = BitVector32.CreateMask(mask);
|
||||
}
|
||||
|
||||
return componentBits;
|
||||
}
|
||||
|
||||
public void Destroy(int entityId)
|
||||
{
|
||||
foreach (var componentMapper in _componentMappers)
|
||||
componentMapper?.Delete(entityId);
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using MonoGame.Extended.Collections;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public abstract class ComponentMapper
|
||||
{
|
||||
protected ComponentMapper(int id, Type componentType)
|
||||
{
|
||||
Id = id;
|
||||
ComponentType = componentType;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public Type ComponentType { get; }
|
||||
public abstract bool Has(int entityId);
|
||||
public abstract void Delete(int entityId);
|
||||
}
|
||||
|
||||
public class ComponentMapper<T> : ComponentMapper
|
||||
where T : class
|
||||
{
|
||||
public event Action<int> OnPut;
|
||||
public event Action<int> OnDelete;
|
||||
|
||||
private readonly Action<int> _onCompositionChanged;
|
||||
|
||||
public ComponentMapper(int id, Action<int> onCompositionChanged)
|
||||
: base(id, typeof(T))
|
||||
{
|
||||
_onCompositionChanged = onCompositionChanged;
|
||||
Components = new Bag<T>();
|
||||
}
|
||||
|
||||
public Bag<T> Components { get; }
|
||||
|
||||
public void Put(int entityId, T component)
|
||||
{
|
||||
Components[entityId] = component;
|
||||
_onCompositionChanged(entityId);
|
||||
OnPut?.Invoke(entityId);
|
||||
}
|
||||
|
||||
public T Get(Entity entity)
|
||||
{
|
||||
return Get(entity.Id);
|
||||
}
|
||||
|
||||
public T Get(int entityId)
|
||||
{
|
||||
return Components[entityId];
|
||||
}
|
||||
|
||||
public override bool Has(int entityId)
|
||||
{
|
||||
if (entityId >= Components.Count)
|
||||
return false;
|
||||
|
||||
return Components[entityId] != null;
|
||||
}
|
||||
|
||||
public override void Delete(int entityId)
|
||||
{
|
||||
Components[entityId] = null;
|
||||
_onCompositionChanged(entityId);
|
||||
OnDelete?.Invoke(entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
//public class ComponentType : IEquatable<ComponentType>
|
||||
//{
|
||||
// public ComponentType(Type type, int id)
|
||||
// {
|
||||
// Type = type;
|
||||
// Id = id;
|
||||
// }
|
||||
|
||||
// public Type Type { get; }
|
||||
// public int Id { get; }
|
||||
|
||||
// public bool Equals(ComponentType other)
|
||||
// {
|
||||
// if (ReferenceEquals(null, other)) return false;
|
||||
// if (ReferenceEquals(this, other)) return true;
|
||||
// return Id == other.Id;
|
||||
// }
|
||||
|
||||
// public override bool Equals(object obj)
|
||||
// {
|
||||
// if (ReferenceEquals(null, obj)) return false;
|
||||
// if (ReferenceEquals(this, obj)) return true;
|
||||
// if (obj.GetType() != GetType()) return false;
|
||||
// return Equals((ComponentType) obj);
|
||||
// }
|
||||
|
||||
// public override int GetHashCode()
|
||||
// {
|
||||
// return Id;
|
||||
// }
|
||||
|
||||
// public static bool operator ==(ComponentType left, ComponentType right)
|
||||
// {
|
||||
// return Equals(left, right);
|
||||
// }
|
||||
|
||||
// public static bool operator !=(ComponentType left, ComponentType right)
|
||||
// {
|
||||
// return !Equals(left, right);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class Entity : IEquatable<Entity>
|
||||
{
|
||||
private readonly EntityManager _entityManager;
|
||||
private readonly ComponentManager _componentManager;
|
||||
|
||||
internal Entity(int id, EntityManager entityManager, ComponentManager componentManager)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
_entityManager = entityManager;
|
||||
_componentManager = componentManager;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public BitVector32 ComponentBits => _entityManager.GetComponentBits(Id);
|
||||
|
||||
public void Attach<T>(T component)
|
||||
where T : class
|
||||
{
|
||||
var mapper = _componentManager.GetMapper<T>();
|
||||
mapper.Put(Id, component);
|
||||
}
|
||||
|
||||
public void Detach<T>()
|
||||
where T : class
|
||||
{
|
||||
var mapper = _componentManager.GetMapper<T>();
|
||||
mapper.Delete(Id);
|
||||
}
|
||||
|
||||
public T Get<T>()
|
||||
where T : class
|
||||
{
|
||||
var mapper = _componentManager.GetMapper<T>();
|
||||
return mapper.Get(Id);
|
||||
}
|
||||
|
||||
|
||||
public bool Has<T>()
|
||||
where T : class
|
||||
{
|
||||
return _componentManager.GetMapper<T>().Has(Id);
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
_entityManager.Destroy(Id);
|
||||
}
|
||||
|
||||
public bool Equals(Entity other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((Entity) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||
return Id;
|
||||
}
|
||||
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Entity left, Entity right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Collections;
|
||||
using MonoGame.Extended.Entities.Systems;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class EntityManager : UpdateSystem
|
||||
{
|
||||
private const int _defaultBagSize = 128;
|
||||
|
||||
public EntityManager(ComponentManager componentManager)
|
||||
{
|
||||
_componentManager = componentManager;
|
||||
_addedEntities = new Bag<int>(_defaultBagSize);
|
||||
_removedEntities = new Bag<int>(_defaultBagSize);
|
||||
_changedEntities = new Bag<int>(_defaultBagSize);
|
||||
_entityToComponentBits = new Bag<BitVector32>(_defaultBagSize);
|
||||
_componentManager.ComponentsChanged += OnComponentsChanged;
|
||||
|
||||
_entityBag = new Bag<Entity>(_defaultBagSize);
|
||||
_entityPool = new Pool<Entity>(() => new Entity(_nextId++, this, _componentManager), _defaultBagSize);
|
||||
}
|
||||
|
||||
private readonly ComponentManager _componentManager;
|
||||
private int _nextId;
|
||||
|
||||
public int Capacity => _entityBag.Capacity;
|
||||
public IEnumerable<int> Entities => _entityBag.Where(e => e != null).Select(e => e.Id);
|
||||
public int ActiveCount { get; private set; }
|
||||
|
||||
private readonly Bag<Entity> _entityBag;
|
||||
private readonly Pool<Entity> _entityPool;
|
||||
private readonly Bag<int> _addedEntities;
|
||||
private readonly Bag<int> _removedEntities;
|
||||
private readonly Bag<int> _changedEntities;
|
||||
private readonly Bag<BitVector32> _entityToComponentBits;
|
||||
|
||||
public event Action<int> EntityAdded;
|
||||
public event Action<int> EntityRemoved;
|
||||
public event Action<int> EntityChanged;
|
||||
|
||||
public Entity Create()
|
||||
{
|
||||
var entity = _entityPool.Obtain();
|
||||
var id = entity.Id;
|
||||
Debug.Assert(_entityBag[id] == null);
|
||||
_entityBag[id] = entity;
|
||||
_addedEntities.Add(id);
|
||||
_entityToComponentBits[id] = new BitVector32(0);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void Destroy(int entityId)
|
||||
{
|
||||
if (!_removedEntities.Contains(entityId))
|
||||
_removedEntities.Add(entityId);
|
||||
}
|
||||
|
||||
public void Destroy(Entity entity)
|
||||
{
|
||||
Destroy(entity.Id);
|
||||
}
|
||||
|
||||
public Entity Get(int entityId)
|
||||
{
|
||||
return _entityBag[entityId];
|
||||
}
|
||||
|
||||
public BitVector32 GetComponentBits(int entityId)
|
||||
{
|
||||
return _entityToComponentBits[entityId];
|
||||
}
|
||||
|
||||
private void OnComponentsChanged(int entityId)
|
||||
{
|
||||
_changedEntities.Add(entityId);
|
||||
_entityToComponentBits[entityId] = _componentManager.CreateComponentBits(entityId);
|
||||
EntityChanged?.Invoke(entityId);
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
foreach (var entityId in _addedEntities)
|
||||
{
|
||||
_entityToComponentBits[entityId] = _componentManager.CreateComponentBits(entityId);
|
||||
ActiveCount++;
|
||||
EntityAdded?.Invoke(entityId);
|
||||
}
|
||||
|
||||
foreach (var entityId in _changedEntities)
|
||||
{
|
||||
_entityToComponentBits[entityId] = _componentManager.CreateComponentBits(entityId);
|
||||
EntityChanged?.Invoke(entityId);
|
||||
}
|
||||
|
||||
foreach (var entityId in _removedEntities)
|
||||
{
|
||||
// we must notify subscribers before removing it from the pool
|
||||
// otherwise an entity system could still be using the entity when the same id is obtained.
|
||||
EntityRemoved?.Invoke(entityId);
|
||||
|
||||
var entity = _entityBag[entityId];
|
||||
_entityBag[entityId] = null;
|
||||
_componentManager.Destroy(entityId);
|
||||
_entityToComponentBits[entityId] = default(BitVector32);
|
||||
ActiveCount--;
|
||||
_entityPool.Free(entity);
|
||||
}
|
||||
|
||||
_addedEntities.Clear();
|
||||
_removedEntities.Clear();
|
||||
_changedEntities.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using MonoGame.Extended.Collections;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
internal class EntitySubscription : IDisposable
|
||||
{
|
||||
private readonly Bag<int> _activeEntities;
|
||||
private readonly EntityManager _entityManager;
|
||||
private readonly Aspect _aspect;
|
||||
private bool _rebuildActives;
|
||||
|
||||
internal EntitySubscription(EntityManager entityManager, Aspect aspect)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_aspect = aspect;
|
||||
_activeEntities = new Bag<int>(entityManager.Capacity);
|
||||
_rebuildActives = true;
|
||||
|
||||
_entityManager.EntityAdded += OnEntityAdded;
|
||||
_entityManager.EntityRemoved += OnEntityRemoved;
|
||||
_entityManager.EntityChanged += OnEntityChanged;
|
||||
}
|
||||
|
||||
private void OnEntityAdded(int entityId)
|
||||
{
|
||||
if (_aspect.IsInterested(_entityManager.GetComponentBits(entityId)))
|
||||
_activeEntities.Add(entityId);
|
||||
}
|
||||
|
||||
private void OnEntityRemoved(int entityId) => _rebuildActives = true;
|
||||
private void OnEntityChanged(int entityId) => _rebuildActives = true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_entityManager.EntityAdded -= OnEntityAdded;
|
||||
_entityManager.EntityRemoved -= OnEntityRemoved;
|
||||
}
|
||||
|
||||
public Bag<int> ActiveEntities
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rebuildActives)
|
||||
RebuildActives();
|
||||
|
||||
return _activeEntities;
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildActives()
|
||||
{
|
||||
_activeEntities.Clear();
|
||||
|
||||
foreach (var entity in _entityManager.Entities)
|
||||
OnEntityAdded(entity);
|
||||
|
||||
_rebuildActives = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>An Entity Component System to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame ecs entity component system</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MonoGame.Extended\MonoGame.Extended.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303">
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public interface IDrawSystem : ISystem
|
||||
{
|
||||
void Draw(GameTime gameTime);
|
||||
}
|
||||
|
||||
public abstract class DrawSystem : IDrawSystem
|
||||
{
|
||||
public virtual void Dispose() { }
|
||||
public virtual void Initialize(World world) { }
|
||||
public abstract void Draw(GameTime gameTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public abstract class EntityDrawSystem : EntitySystem, IDrawSystem
|
||||
{
|
||||
protected EntityDrawSystem(AspectBuilder aspect)
|
||||
: base(aspect)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Draw(GameTime gameTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public abstract class EntityProcessingSystem : EntityUpdateSystem
|
||||
{
|
||||
protected EntityProcessingSystem(AspectBuilder aspectBuilder)
|
||||
: base(aspectBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
Begin();
|
||||
|
||||
foreach (var entityId in ActiveEntities)
|
||||
Process(gameTime, entityId);
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
public virtual void Begin() { }
|
||||
public abstract void Process(GameTime gameTime, int entityId);
|
||||
public virtual void End() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using MonoGame.Extended.Collections;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public abstract class EntitySystem : ISystem
|
||||
{
|
||||
protected EntitySystem(AspectBuilder aspectBuilder)
|
||||
{
|
||||
_aspectBuilder = aspectBuilder;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_world != null)
|
||||
{
|
||||
_world.EntityManager.EntityAdded -= OnEntityAdded;
|
||||
_world.EntityManager.EntityRemoved -= OnEntityRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly AspectBuilder _aspectBuilder;
|
||||
private EntitySubscription _subscription;
|
||||
|
||||
private World _world;
|
||||
|
||||
protected virtual void OnEntityChanged(int entityId) { }
|
||||
protected virtual void OnEntityAdded(int entityId) { }
|
||||
protected virtual void OnEntityRemoved(int entityId) { }
|
||||
|
||||
public Bag<int> ActiveEntities => _subscription.ActiveEntities;
|
||||
|
||||
public virtual void Initialize(World world)
|
||||
{
|
||||
_world = world;
|
||||
|
||||
var aspect = _aspectBuilder.Build(_world.ComponentManager);
|
||||
_subscription = new EntitySubscription(_world.EntityManager, aspect);
|
||||
_world.EntityManager.EntityAdded += OnEntityAdded;
|
||||
_world.EntityManager.EntityRemoved += OnEntityRemoved;
|
||||
_world.EntityManager.EntityChanged += OnEntityChanged;
|
||||
|
||||
Initialize(world.ComponentManager);
|
||||
}
|
||||
|
||||
public abstract void Initialize(IComponentMapperService mapperService);
|
||||
|
||||
protected void DestroyEntity(int entityId) => _world.DestroyEntity(entityId);
|
||||
protected Entity CreateEntity() => _world.CreateEntity();
|
||||
protected Entity GetEntity(int entityId) => _world.GetEntity(entityId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public abstract class EntityUpdateSystem : EntitySystem, IUpdateSystem
|
||||
{
|
||||
protected EntityUpdateSystem(AspectBuilder aspectBuilder)
|
||||
: base(aspectBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Update(GameTime gameTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public interface ISystem : IDisposable
|
||||
{
|
||||
void Initialize(World world);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Entities.Systems
|
||||
{
|
||||
public interface IUpdateSystem : ISystem
|
||||
{
|
||||
void Update(GameTime gameTime);
|
||||
}
|
||||
|
||||
public abstract class UpdateSystem : IUpdateSystem
|
||||
{
|
||||
public virtual void Dispose() { }
|
||||
public virtual void Initialize(World world) { }
|
||||
public abstract void Update(GameTime gameTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Collections;
|
||||
using MonoGame.Extended.Entities.Systems;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class World : SimpleDrawableGameComponent
|
||||
{
|
||||
private readonly Bag<IUpdateSystem> _updateSystems;
|
||||
private readonly Bag<IDrawSystem> _drawSystems;
|
||||
|
||||
internal World()
|
||||
{
|
||||
_updateSystems = new Bag<IUpdateSystem>();
|
||||
_drawSystems = new Bag<IDrawSystem>();
|
||||
|
||||
RegisterSystem(ComponentManager = new ComponentManager());
|
||||
RegisterSystem(EntityManager = new EntityManager(ComponentManager));
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
foreach (var updateSystem in _updateSystems)
|
||||
updateSystem.Dispose();
|
||||
|
||||
foreach (var drawSystem in _drawSystems)
|
||||
drawSystem.Dispose();
|
||||
|
||||
_updateSystems.Clear();
|
||||
_drawSystems.Clear();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
internal EntityManager EntityManager { get; }
|
||||
internal ComponentManager ComponentManager { get; }
|
||||
|
||||
public int EntityCount => EntityManager.ActiveCount;
|
||||
|
||||
internal void RegisterSystem(ISystem system)
|
||||
{
|
||||
// ReSharper disable once ConvertIfStatementToSwitchStatement
|
||||
if (system is IUpdateSystem updateSystem)
|
||||
_updateSystems.Add(updateSystem);
|
||||
|
||||
if (system is IDrawSystem drawSystem)
|
||||
_drawSystems.Add(drawSystem);
|
||||
|
||||
system.Initialize(this);
|
||||
}
|
||||
|
||||
public Entity GetEntity(int entityId)
|
||||
{
|
||||
return EntityManager.Get(entityId);
|
||||
}
|
||||
|
||||
public Entity CreateEntity()
|
||||
{
|
||||
return EntityManager.Create();
|
||||
}
|
||||
|
||||
public void DestroyEntity(int entityId)
|
||||
{
|
||||
EntityManager.Destroy(entityId);
|
||||
}
|
||||
|
||||
public void DestroyEntity(Entity entity)
|
||||
{
|
||||
EntityManager.Destroy(entity);
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
foreach (var system in _updateSystems)
|
||||
system.Update(gameTime);
|
||||
}
|
||||
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
foreach (var system in _drawSystems)
|
||||
system.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using MonoGame.Extended.Entities.Systems;
|
||||
|
||||
namespace MonoGame.Extended.Entities
|
||||
{
|
||||
public class WorldBuilder
|
||||
{
|
||||
private readonly List<ISystem> _systems = new List<ISystem>();
|
||||
|
||||
public WorldBuilder AddSystem(ISystem system)
|
||||
{
|
||||
_systems.Add(system);
|
||||
return this;
|
||||
}
|
||||
|
||||
public World Build()
|
||||
{
|
||||
var world = new World();
|
||||
|
||||
foreach (var system in _systems)
|
||||
world.RegisterSystem(system);
|
||||
|
||||
return world;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user