summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Entities/EntityManager.cs
blob: 5e1d28a6a1f09e7ddd4bf9acb1f75f619083e1de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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();
        }
    }
}