| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- using System;
- using Microsoft.Xna.Framework;
- using MonoGame.Extended.Collections;
- using MonoGame.Extended.ECS.Systems;
- namespace MonoGame.Extended.ECS
- {
- /// <summary>
- /// Represents an Entity Component System (ECS) world that manages entities, components, and system.s
- /// </summary>
- /// <remarks>
- /// The World is the central container for all ECS operations. It manages the lifecycle of entities, coordinates
- /// component storage, and executes registered systems during update and draw cycles.
- /// </remarks>
- public class World : SimpleDrawableGameComponent
- {
- private readonly Bag<IUpdateSystem> _updateSystems;
- private readonly Bag<IDrawSystem> _drawSystems;
- internal EntityManager EntityManager { get; }
- internal ComponentManager ComponentManager { get; }
- /// <summary>
- /// Occurs when an entity is added to the world during the update cycle.
- /// </summary>
- /// <remarks>
- /// This event is raised after the entity has been fully initialized with it initial components.
- /// The entity ID is valid and can be used to retrieve the entity or query its components.
- /// Subscribers can use this even to perform initialization logic such as adding the entity to external
- /// collections, registering it with other systems, or creating associated resources.
- /// </remarks>
- public event Action<int> EntityAdded;
- /// <summary>
- /// Occurs when an entity is removed from the world during the update cycle.
- /// </summary>
- /// <remarks>
- /// This event is raised before the entity is returned to the pool and before its components are destroyed,
- /// allowing subscribers to perform cleanup operations such as removing the entity from external collections,
- /// unregistering it from other systems, or releasing associated resources.
- /// After this event completes, the entity ID becomes invalid and should not be used.
- /// </remarks>
- public event Action<int> EntityRemoved;
- /// <summary>
- /// Occurs when an entity's component composition changes during the update cycle.
- /// </summary>
- /// <remarks>
- /// This event is raised whenever components are attached to or detached from an entity.
- /// Subscribers can use this event to respond to composition changes, such as updating cached component
- /// references, recalculating entity classifications, or refreshing system queries.
- /// The event is not raised for component value modifications, only structural changes to which components are
- /// present on the entity.
- /// </remarks>
- public event Action<int> EntityChanged;
- /// <summary>
- /// Gets the number of active entities in the world.
- /// </summary>
- /// <remarks>
- /// This count reflects entities that have been created and not yet destroyed.
- /// Entities queued for destruction are still counted until the next update cycle completes.
- /// </remarks>
- public int EntityCount => EntityManager.ActiveCount;
- internal World()
- {
- _updateSystems = new Bag<IUpdateSystem>();
- _drawSystems = new Bag<IDrawSystem>();
- RegisterSystem(ComponentManager = new ComponentManager());
- RegisterSystem(EntityManager = new EntityManager(ComponentManager));
- EntityManager.EntityAdded += OnEntityAdded;
- EntityManager.EntityRemoved += OnEntityRemoved;
- EntityManager.EntityChanged += OnEntityChanged;
- }
- private void OnEntityAdded(int entityId)
- {
- if (EntityAdded != null)
- {
- EntityAdded.Invoke(entityId);
- }
- }
- private void OnEntityRemoved(int entityId)
- {
- if (EntityRemoved != null)
- {
- EntityRemoved.Invoke(entityId);
- }
- }
- private void OnEntityChanged(int entityId)
- {
- if (EntityChanged != null)
- {
- EntityChanged.Invoke(entityId);
- }
- }
- 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);
- }
- /// <summary>
- /// Retrieves an entity by its unique identifier.
- /// </summary>
- /// <param name="entityId">The unique identifier of the entity to retrieve.</param>
- /// <returns>
- /// The entity with the specified identifier, or <c>null</c> if the entity has been destroyed or the ID is
- /// invalid.
- /// </returns>
- public Entity GetEntity(int entityId)
- {
- return EntityManager.Get(entityId);
- }
- /// <summary>
- /// Creates a new entity in the world.
- /// </summary>
- /// <remarks>
- /// The entity is created immediately, but the <see cref="EntityAdded"/> event is not raised until
- /// the next <see cref="Update(GameTime)"/> cycle. The returned entity can be used immediately to
- /// attach components.
- /// Entity IDs are reused from a pool after entities are destroyed.
- /// </remarks>
- /// <returns>A new entity with a unique identifier.</returns>
- public Entity CreateEntity()
- {
- return EntityManager.Create();
- }
- /// <summary>
- /// Destroys an entity in the world.
- /// </summary>
- /// <remarks>
- /// The entity is queued for destruction and the <see cref="EntityRemoved"/> event is raised during
- /// the next <see cref="Update(GameTime)"/> cycle, before the entity and its components are removed from
- /// memory.
- /// Calling this method multiple times with the same entity ID has no additional effect.
- /// After destruction completes, the entity ID may be reused for new entities.
- /// </remarks>
- /// <param name="entityId">The unique identifier of the entity to destroy.</param>
- public void DestroyEntity(int entityId)
- {
- EntityManager.Destroy(entityId);
- }
- /// <summary>
- /// Destroys an entity in the world.
- /// </summary>
- /// <remarks>
- /// The entity is queued for destruction and the <see cref="EntityRemoved"/> event is raised during
- /// the next <see cref="Update(GameTime)"/> cycle, before the entity and its components are removed from
- /// memory.
- /// Calling this method multiple times with the same entity ID has no additional effect.
- /// After destruction completes, the entity ID may be reused for new entities.
- /// </remarks>
- /// <param name="entity">The entity to destroy.</param>
- public void DestroyEntity(Entity entity)
- {
- EntityManager.Destroy(entity);
- }
- /// <summary>
- /// Updates all registered systems in the world.
- /// </summary>
- /// <remarks>
- /// This method invokes <see cref="IUpdateSystem.Update(GameTime)"/> on all registered update systems in
- /// registration order.
- /// Entity lifecycle events (<see cref="EntityAdded"/>, <see cref="EntityRemoved"/>,
- /// <see cref="EntityChanged"/>) are raised during this update cycle as entities are processed by the
- /// <see cref="EntityManager"/>.
- /// </remarks>
- /// <param name="gameTime">A snapshot of the timing values for the current cycle.</param>
- public override void Update(GameTime gameTime)
- {
- foreach (var system in _updateSystems)
- {
- system.Update(gameTime);
- }
- }
- /// <summary>
- /// Draws all registered systems in the world.
- /// </summary>
- /// <remarks>
- /// This method invokes <see cref="IDrawSystem.Draw(GameTime)"/> on all registered draw systems in
- /// registration order.
- /// </remarks>
- /// <param name="gameTime">A snapshot of the timing values for the current cycle.</param>
- public override void Draw(GameTime gameTime)
- {
- foreach (var system in _drawSystems)
- {
- system.Draw(gameTime);
- }
- }
- /// <summary>
- /// Releases all resources used by the <see cref="World"/>
- /// </summary>
- public override void Dispose()
- {
- EntityManager.EntityAdded -= OnEntityAdded;
- EntityManager.EntityRemoved -= OnEntityRemoved;
- EntityManager.EntityChanged -= OnEntityChanged;
- foreach (var updateSystem in _updateSystems)
- {
- updateSystem.Dispose();
- }
- foreach (var drawSystem in _drawSystems)
- {
- drawSystem.Dispose();
- }
- _updateSystems.Clear();
- _drawSystems.Clear();
- base.Dispose();
- }
- }
- }
|