void UpdateFilters(int entity, EcsComponentMask oldMask, EcsComponentMask newMask) { for (var i = _filtersCount - 1; i >= 0; i--) { var filter = _filters[i]; var isNewMaskCompatible = newMask.IsCompatible(filter); if (oldMask.IsCompatible(filter)) { if (!isNewMaskCompatible) { #if DEBUG var ii = filter.EntitiesCount - 1; for (; ii >= 0; ii--) { if (filter.Entities[ii] == entity) { break; } } EcsHelpers.Assert(ii != -1, string.Format("Something wrong - entity {0} should be in filter {1}, but not exits.", entity, filter)); #endif filter.RaiseOnRemoveEvent(entity); } } else { if (isNewMaskCompatible) { filter.RaiseOnAddEvent(entity); } } } }
/// <summary> /// Gets exist one or adds new component to entity. /// </summary> /// <param name="entity">Entity.</param> /// <param name="isNew">Is component was added in this call?</param> public T EnsureComponent <T> (int entity, out bool isNew) where T : class, new () { EcsHelpers.Assert(entity >= 0 && entity < _entitiesCount, string.Format("Invalid entity: {0}", entity)); var entityData = _entities[entity]; EcsHelpers.Assert(!entityData.IsReserved, string.Format("\"{0}\" component cant be added to removed entity {1}", typeof(T).Name, entity)); var pool = EcsComponentPool <T> .Instance; for (var i = 0; i < entityData.ComponentsCount; i++) { if (entityData.Components[i].Pool == pool) { isNew = false; return((T)entityData.Components[i].Pool.GetExistItemById(entityData.Components[i].ItemId)); } } var link = new ComponentLink(pool, pool.RequestNewId()); if (entityData.ComponentsCount == entityData.Components.Length) { Array.Resize(ref entityData.Components, entityData.ComponentsCount << 1); } entityData.Components[entityData.ComponentsCount++] = link; AddDelayedUpdate(DelayedUpdate.Op.AddComponent, entity, pool, link.ItemId); #if DEBUG var component = pool.Items[link.ItemId]; for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentAdded(entity, component); } #endif isNew = true; return(pool.Items[link.ItemId]); }
/// <summary> /// Gets filter with specific include / exclude masks. /// </summary> /// <param name="filterType">Type of filter.</param> public EcsFilter GetFilter(Type filterType) { EcsHelpers.Assert(filterType != null, "filterType is null"); EcsHelpers.Assert(filterType.IsSubclassOf(typeof(EcsFilter)), string.Format("Invalid filter-type: {0}", filterType)); var i = _filtersCount - 1; for (; i >= 0; i--) { if (this._filters[i].GetType() == filterType) { break; } } if (i == -1) { i = _filtersCount; var filter = Activator.CreateInstance(filterType, true) as EcsFilter; filter.SetWorld(this); #if DEBUG for (var j = 0; j < _filtersCount; j++) { EcsHelpers.Assert(!_filters[j].IncludeMask.IsEquals(filter.IncludeMask) || !_filters[j].ExcludeMask.IsEquals(filter.ExcludeMask), string.Format("Duplicate filter type \"{0}\": filter type \"{1}\" already has same types in different order.", filterType, _filters[j].GetType())); } #endif if (_filtersCount == _filters.Length) { Array.Resize(ref _filters, _filtersCount << 1); } _filters[_filtersCount++] = filter; } return(_filters[i]); }
/// <summary> /// Removes component from entity. /// </summary> /// <param name="entity">Entity.</param> /// <param name="noerror">Suppress error if component not exists.</param> public void RemoveComponent <T> (int entity, bool noError = false) where T : class, new () { EcsHelpers.Assert(entity >= 0 && entity < _entitiesCount, string.Format("Invalid entity: {0}", entity)); var entityData = _entities[entity]; var pool = EcsComponentPool <T> .Instance; ComponentLink link; link.ItemId = -1; var i = entityData.ComponentsCount - 1; for (; i >= 0; i--) { link = entityData.Components[i]; if (link.Pool == pool) { break; } } if (noError && i == -1) { return; } EcsHelpers.Assert(i != -1, string.Format("\"{0}\" component not exists on entity {1}", typeof(T).Name, entity)); AddDelayedUpdate(DelayedUpdate.Op.RemoveComponent, entity, pool, link.ItemId); entityData.ComponentsCount--; Array.Copy(entityData.Components, i + 1, entityData.Components, i, entityData.ComponentsCount - i); }
/// <summary> /// Removes exists entity or throws exception on invalid one. /// </summary> /// <param name="entity">Entity.</param> public void RemoveEntity(int entity) { EcsHelpers.Assert(entity >= 0 && entity < _entitiesCount, string.Format("Invalid entity: {0}", entity)); if (!_entities[entity].IsReserved) { AddDelayedUpdate(DelayedUpdate.Op.RemoveEntity, entity, null, -1); } }
/// <summary> /// Creates new entity and adds component to it. /// Faster than CreateEntity() and multiple AddComponent() calls sequence. /// </summary> /// <param name="c1">Added component of type T1.</param> /// <param name="c2">Added component of type T2.</param> /// <param name="c3">Added component of type T3.</param> /// <returns>New entity Id.</returns> public int CreateEntityWith <T1, T2, T3> (out T1 c1, out T2 c2, out T3 c3) where T1 : class, new () where T2 : class, new () where T3 : class, new () { EcsHelpers.Assert(typeof(T1) != typeof(T2), string.Format("Cant create entity with multiple components of same type \"{0}\"", typeof(T1).Name)); EcsHelpers.Assert(typeof(T1) != typeof(T3) && typeof(T2) != typeof(T3), string.Format("Cant create entity with multiple components of same type \"{0}\"", typeof(T3).Name)); var entity = CreateEntityInternal(); var pool1 = EcsComponentPool <T1> .Instance; var pool2 = EcsComponentPool <T2> .Instance; var pool3 = EcsComponentPool <T3> .Instance; var entityData = _entities[entity]; while ((entityData.ComponentsCount + 3) > entityData.Components.Length) { Array.Resize(ref entityData.Components, entityData.ComponentsCount << 1); } ComponentLink link; link.Pool = pool1; link.ItemId = pool1.RequestNewId(); c1 = pool1.Items[link.ItemId]; entityData.Components[entityData.ComponentsCount++] = link; AddDelayedUpdate(DelayedUpdate.Op.AddComponent, entity, pool1, link.ItemId); link.Pool = pool2; link.ItemId = pool2.RequestNewId(); c2 = pool2.Items[link.ItemId]; entityData.Components[entityData.ComponentsCount++] = link; AddDelayedUpdate(DelayedUpdate.Op.AddComponent, entity, pool2, link.ItemId); link.Pool = pool3; link.ItemId = pool3.RequestNewId(); c3 = pool3.Items[link.ItemId]; entityData.Components[entityData.ComponentsCount++] = link; AddDelayedUpdate(DelayedUpdate.Op.AddComponent, entity, pool3, link.ItemId); #if DEBUG for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentAdded(entity, c1); } for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentAdded(entity, c2); } for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentAdded(entity, c3); } #endif return(entity); }
/// <summary> /// Gets all components on entity. /// </summary> /// <param name="entity">Entity.</param> /// <param name="list">List to put results in it. if null - will be created.</param> /// <returns>Amount of components in list.</returns> public int GetComponents(int entity, ref object[] list) { EcsHelpers.Assert(entity >= 0 && entity < _entitiesCount, string.Format("Invalid entity: {0}", entity)); var entityData = _entities[entity]; var count = entityData.ComponentsCount; if (list == null || list.Length < count) { list = new object[entityData.ComponentsCount]; } for (var i = 0; i < count; i++) { var link = entityData.Components[i]; list[i] = link.Pool.GetExistItemById(link.ItemId); } return(count); }
public T GetComponent <T> (int entity) where T : class, new () { EcsHelpers.Assert(entity >= 0 && entity < _entitiesCount, string.Format("Invalid entity: {0}", entity)); var entityData = _entities[entity]; EcsHelpers.Assert(!entityData.IsReserved, string.Format("\"{0}\" component cant be obtained from removed entity {1}", typeof(T).Name, entity)); var pool = EcsComponentPool <T> .Instance; for (var i = 0; i < entityData.ComponentsCount; i++) { if (entityData.Components[i].Pool == pool) { return(pool.Items[entityData.Components[i].ItemId]); } } return(null); }
/// <summary> /// Destroys all registered external data, full cleanup for internal data. /// </summary> public void Dispose() { #if DEBUG EcsHelpers.Assert(!_isDisposed, "World already disposed"); _isDisposed = true; for (var i = _debugListeners.Count - 1; i >= 0; i--) { _debugListeners[i].OnWorldDestroyed(this); } #endif if (this == Active) { Active = null; } for (var i = 0; i < _entitiesCount; i++) { // already reserved entities cant contains components. if (_entities[i].ComponentsCount > 0) { var entity = _entities[i]; for (var ii = 0; ii < entity.ComponentsCount; ii++) { entity.Components[ii].Pool.RecycleById(entity.Components[ii].ItemId); } } } // any next usage of this EcsWorld instance will throw exception. _entities = null; _entitiesCount = 0; _filters = null; _filtersCount = 0; _reservedEntities = null; _reservedEntitiesCount = 0; _delayedUpdates = null; _delayedUpdatesCount = 0; }
/// <summary> /// Manually processes delayed updates. Use carefully! /// </summary> public void ProcessDelayedUpdates() { if (_delayedUpdatesCount > 0) { for (var i = 0; i < _delayedUpdatesCount; i++) { var op = _delayedUpdates[i]; var entityData = _entities[op.Entity]; _delayedOpMask.CopyFrom(entityData.Mask); switch (op.Type) { case DelayedUpdate.Op.RemoveEntity: EcsHelpers.Assert(!entityData.IsReserved, string.Format("Entity {0} already removed", op.Entity)); while (entityData.ComponentsCount > 0) { var link = entityData.Components[entityData.ComponentsCount - 1]; var componentId = link.Pool.GetComponentTypeIndex(); entityData.Mask.SetBit(componentId, false); #if DEBUG var componentToRemove = link.Pool.GetExistItemById(link.ItemId); for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentRemoved(op.Entity, componentToRemove); } #endif UpdateFilters(op.Entity, _delayedOpMask, entityData.Mask); link.Pool.RecycleById(link.ItemId); _delayedOpMask.SetBit(componentId, false); entityData.ComponentsCount--; } ReserveEntity(op.Entity, entityData); break; case DelayedUpdate.Op.SafeRemoveEntity: if (!entityData.IsReserved && entityData.ComponentsCount == 0) { ReserveEntity(op.Entity, entityData); } break; case DelayedUpdate.Op.AddComponent: var bit = op.Pool.GetComponentTypeIndex(); EcsHelpers.Assert(!entityData.Mask.GetBit(bit), string.Format("Cant add component on entity {0}, already marked as added in mask", op.Entity)); entityData.Mask.SetBit(bit, true); UpdateFilters(op.Entity, _delayedOpMask, entityData.Mask); break; case DelayedUpdate.Op.RemoveComponent: var bitRemove = op.Pool.GetComponentTypeIndex(); EcsHelpers.Assert(entityData.Mask.GetBit(bitRemove), string.Format("Cant remove component on entity {0}, marked as not exits in mask", op.Entity)); #if DEBUG var componentInstance = op.Pool.GetExistItemById(op.ComponentId); for (var ii = 0; ii < _debugListeners.Count; ii++) { _debugListeners[ii].OnComponentRemoved(op.Entity, componentInstance); } #endif entityData.Mask.SetBit(bitRemove, false); UpdateFilters(op.Entity, _delayedOpMask, entityData.Mask); op.Pool.RecycleById(op.ComponentId); if (entityData.ComponentsCount == 0) { AddDelayedUpdate(DelayedUpdate.Op.SafeRemoveEntity, op.Entity, null, -1); } break; } } _delayedUpdatesCount = 0; } }
/// <summary> /// Removes external event listener. /// </summary> /// <param name="observer">Event listener.</param> public void RemoveDebugListener(IEcsWorldDebugListener observer) { EcsHelpers.Assert(observer != null, "observer is null"); _debugListeners.Remove(observer); }
/// <summary> /// Adds external event listener. /// </summary> /// <param name="observer">Event listener.</param> public void AddDebugListener(IEcsWorldDebugListener observer) { EcsHelpers.Assert(observer != null, "observer is null"); EcsHelpers.Assert(!_debugListeners.Contains(observer), "Listener already exists"); _debugListeners.Add(observer); }