Files
MonoGame.Extended/Source/MonoGame.Extended.Entities/EntityManager.cs
T
Geir Sagberg c7410c9a9e Don't add entity to _removedEntities if it is already there (#658)
* Gitignore .vscode/

* Fix casing of Installation.md

* Make Destroy Entity idempotent
2020-02-29 20:48:49 +10:00

119 lines
4.0 KiB
C#

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);
}
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();
}
}
}