create references to objects and hold these references in a cache
I would like to create a chache for each system in my Entity-Component-System. Currently each system would loop through all entities and check for required components.
internal class MySystem : ISystem
{
public void Run()
{
for (int i = 0; i < EntityManager.activeEntities.Count; i++)
{
Guid entityId = EntityManager.activeEntities[i];
if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent)) // Get the position component
{
// Update the position of the entity
}
}
}
}
ISystem
just requires to implement a Run
method. I think this approach might get really slow if each system has to check for the right components.
I save all the components to a pool of the component type and these pools get stored to a collection.
private Dictionary<Type, object> componentPools = new Dictionary<Type, object>();
where object
of Dictionary<Type, object>
is always Dictionary<Guid, TComponent>()
.
When running a system it would be better passing in a collection of the required components only.
These are the methods from my EntityManager class that would affect the cache of each system
public Guid CreateEntity()
{
// Add and return entityID
}
public void DestroyEntity(Guid entityId)
{
// Remove entity by ID
// Remove all components from all pools by refering to the entityID
}
public void AddComponentToEntity<TComponent>(Guid entityId, IComponent component) where TComponent : IComponent
{
// Add the component to the component pool by refering to the entityID
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
// Remove the component from the component pool by refering to the entityID
}
public void AddComponentPool<TComponent>() where TComponent : IComponent
{
// Add a new component pool by its type
}
public void RemoveComponentPool<TComponent>() where TComponent : IComponent
{
// Remove a component pool by its type
}
How can I create systems that refer to the required components only and update their cache when calling one of these methods shown above?
I tried to create a pseudo code example to show what I mean
internal class Movement : ISystem
{
// Just add entities with a Position and a MovementSpeed component
List<Guid> cacheEntities = new List<Guid>();
public void Run()
{
for (int i = 0; i < cacheEntities.Count; i++)
{
Guid entityId = cacheEntities[i];
Position positionComponent = EntityManager.GetComponentPool<Position>()[entityId];
MovementSpeed movementSpeedComponent = EntityManager.GetComponentPool<MovementSpeed>()[entityId];
// Move
}
}
}
and maybe it's possible to create collections that require no entityId
, so they only store the reference to the component that should get updated.
c# entity-component-system
add a comment |
I would like to create a chache for each system in my Entity-Component-System. Currently each system would loop through all entities and check for required components.
internal class MySystem : ISystem
{
public void Run()
{
for (int i = 0; i < EntityManager.activeEntities.Count; i++)
{
Guid entityId = EntityManager.activeEntities[i];
if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent)) // Get the position component
{
// Update the position of the entity
}
}
}
}
ISystem
just requires to implement a Run
method. I think this approach might get really slow if each system has to check for the right components.
I save all the components to a pool of the component type and these pools get stored to a collection.
private Dictionary<Type, object> componentPools = new Dictionary<Type, object>();
where object
of Dictionary<Type, object>
is always Dictionary<Guid, TComponent>()
.
When running a system it would be better passing in a collection of the required components only.
These are the methods from my EntityManager class that would affect the cache of each system
public Guid CreateEntity()
{
// Add and return entityID
}
public void DestroyEntity(Guid entityId)
{
// Remove entity by ID
// Remove all components from all pools by refering to the entityID
}
public void AddComponentToEntity<TComponent>(Guid entityId, IComponent component) where TComponent : IComponent
{
// Add the component to the component pool by refering to the entityID
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
// Remove the component from the component pool by refering to the entityID
}
public void AddComponentPool<TComponent>() where TComponent : IComponent
{
// Add a new component pool by its type
}
public void RemoveComponentPool<TComponent>() where TComponent : IComponent
{
// Remove a component pool by its type
}
How can I create systems that refer to the required components only and update their cache when calling one of these methods shown above?
I tried to create a pseudo code example to show what I mean
internal class Movement : ISystem
{
// Just add entities with a Position and a MovementSpeed component
List<Guid> cacheEntities = new List<Guid>();
public void Run()
{
for (int i = 0; i < cacheEntities.Count; i++)
{
Guid entityId = cacheEntities[i];
Position positionComponent = EntityManager.GetComponentPool<Position>()[entityId];
MovementSpeed movementSpeedComponent = EntityManager.GetComponentPool<MovementSpeed>()[entityId];
// Move
}
}
}
and maybe it's possible to create collections that require no entityId
, so they only store the reference to the component that should get updated.
c# entity-component-system
add a comment |
I would like to create a chache for each system in my Entity-Component-System. Currently each system would loop through all entities and check for required components.
internal class MySystem : ISystem
{
public void Run()
{
for (int i = 0; i < EntityManager.activeEntities.Count; i++)
{
Guid entityId = EntityManager.activeEntities[i];
if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent)) // Get the position component
{
// Update the position of the entity
}
}
}
}
ISystem
just requires to implement a Run
method. I think this approach might get really slow if each system has to check for the right components.
I save all the components to a pool of the component type and these pools get stored to a collection.
private Dictionary<Type, object> componentPools = new Dictionary<Type, object>();
where object
of Dictionary<Type, object>
is always Dictionary<Guid, TComponent>()
.
When running a system it would be better passing in a collection of the required components only.
These are the methods from my EntityManager class that would affect the cache of each system
public Guid CreateEntity()
{
// Add and return entityID
}
public void DestroyEntity(Guid entityId)
{
// Remove entity by ID
// Remove all components from all pools by refering to the entityID
}
public void AddComponentToEntity<TComponent>(Guid entityId, IComponent component) where TComponent : IComponent
{
// Add the component to the component pool by refering to the entityID
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
// Remove the component from the component pool by refering to the entityID
}
public void AddComponentPool<TComponent>() where TComponent : IComponent
{
// Add a new component pool by its type
}
public void RemoveComponentPool<TComponent>() where TComponent : IComponent
{
// Remove a component pool by its type
}
How can I create systems that refer to the required components only and update their cache when calling one of these methods shown above?
I tried to create a pseudo code example to show what I mean
internal class Movement : ISystem
{
// Just add entities with a Position and a MovementSpeed component
List<Guid> cacheEntities = new List<Guid>();
public void Run()
{
for (int i = 0; i < cacheEntities.Count; i++)
{
Guid entityId = cacheEntities[i];
Position positionComponent = EntityManager.GetComponentPool<Position>()[entityId];
MovementSpeed movementSpeedComponent = EntityManager.GetComponentPool<MovementSpeed>()[entityId];
// Move
}
}
}
and maybe it's possible to create collections that require no entityId
, so they only store the reference to the component that should get updated.
c# entity-component-system
I would like to create a chache for each system in my Entity-Component-System. Currently each system would loop through all entities and check for required components.
internal class MySystem : ISystem
{
public void Run()
{
for (int i = 0; i < EntityManager.activeEntities.Count; i++)
{
Guid entityId = EntityManager.activeEntities[i];
if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent)) // Get the position component
{
// Update the position of the entity
}
}
}
}
ISystem
just requires to implement a Run
method. I think this approach might get really slow if each system has to check for the right components.
I save all the components to a pool of the component type and these pools get stored to a collection.
private Dictionary<Type, object> componentPools = new Dictionary<Type, object>();
where object
of Dictionary<Type, object>
is always Dictionary<Guid, TComponent>()
.
When running a system it would be better passing in a collection of the required components only.
These are the methods from my EntityManager class that would affect the cache of each system
public Guid CreateEntity()
{
// Add and return entityID
}
public void DestroyEntity(Guid entityId)
{
// Remove entity by ID
// Remove all components from all pools by refering to the entityID
}
public void AddComponentToEntity<TComponent>(Guid entityId, IComponent component) where TComponent : IComponent
{
// Add the component to the component pool by refering to the entityID
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
// Remove the component from the component pool by refering to the entityID
}
public void AddComponentPool<TComponent>() where TComponent : IComponent
{
// Add a new component pool by its type
}
public void RemoveComponentPool<TComponent>() where TComponent : IComponent
{
// Remove a component pool by its type
}
How can I create systems that refer to the required components only and update their cache when calling one of these methods shown above?
I tried to create a pseudo code example to show what I mean
internal class Movement : ISystem
{
// Just add entities with a Position and a MovementSpeed component
List<Guid> cacheEntities = new List<Guid>();
public void Run()
{
for (int i = 0; i < cacheEntities.Count; i++)
{
Guid entityId = cacheEntities[i];
Position positionComponent = EntityManager.GetComponentPool<Position>()[entityId];
MovementSpeed movementSpeedComponent = EntityManager.GetComponentPool<MovementSpeed>()[entityId];
// Move
}
}
}
and maybe it's possible to create collections that require no entityId
, so they only store the reference to the component that should get updated.
c# entity-component-system
c# entity-component-system
asked Jan 17 at 8:30
MHComputechMHComputech
26213
26213
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
The Entity-components-system asks for a specific design.
ECS follows the composition over inheritance principle
Dealing with pools of components, which are in essence raw data, it makes sense to handle this data with reference to the actual component type -- given you'll want to apply specific behaviors for each.
The decorator pattern plays nicely with composition, adding behaviors by wrapping types. It also allows EntityManager
to delegate responsibilities to the component pools, instead of having one massive decision tree that has to handle all cases.
Let's implement an example.
Suppose there is a drawing function. This would be a "System" that iterates through all entities that have both a physical and a visible component, and draws them. The visible component could typically have some information about how an entity should look (e.g. human, monster, sparks flying around, flying arrow), and use the physical component to know where to draw it.
- Entity.cs
An entity will be typically made up of an ID and a list of components that are attached to it.
class Entity
{
public Entity(Guid entityId)
{
EntityId = entityId;
Components = new List<IComponent>();
}
public Guid EntityId { get; }
public List<IComponent> Components { get; }
}
- Component.cs
Starting with the marker interface.
interface IComponent { }
enum Appearance : byte
{
Human,
Monster,
SparksFlyingAround,
FlyingArrow
}
class VisibleComponent : IComponent
{
public Appearance Appearance { get; set; }
}
class PhysicalComponent : IComponent
{
public double X { get; set; }
public double Y { get; set; }
}
- System.cs
Adding a collection for the SystemEntities
.
interface ISystem
{
ISet<Guid> SystemEntities { get; }
Type ComponentTypes { get; }
void Run();
}
class DrawingSystem : ISystem
{
public DrawingSystem(params Type componentTypes)
{
ComponentTypes = componentTypes;
SystemEntities = new HashSet<Guid>();
}
public ISet<Guid> SystemEntities { get; }
public Type ComponentTypes { get; }
public void Run()
{
foreach (var entity in SystemEntities)
{
Draw(entity);
}
}
private void Draw(Guid entity) { /*Do Magic*/ }
}
- ComponentPool.cs
Next, we'll lay the ground work for what's to come. Our component pools should also have a non-generic interface, that we can fall back upon when we can't supply a component type.
interface IComponentPool
{
void RemoveEntity(Guid entityId);
bool ContainsEntity(Guid entityId);
}
interface IComponentPool<T> : IComponentPool
{
void AddEntity(Guid entityId, T component);
}
class ComponentPool<T> : IComponentPool<T>
{
private Dictionary<Guid, T> component = new Dictionary<Guid, T>();
public void AddEntity(Guid entityId, T component)
{
this.component.Add(entityId, component);
}
public void RemoveEntity(Guid entityId)
{
component.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return component.ContainsKey(entityId);
}
}
Next step is the pool decorator. The decorator pattern is implemented by exposing the same interface as the class it wraps, applying any desired behavior in the process. In our case, we want to check if added entities posses all the component types a system requires. And if they do, add them to the collection.
class PoolDecorator<T> : IComponentPool<T>
{
private readonly IComponentPool<T> wrappedPool;
private readonly EntityManager entityManager;
private readonly ISystem system;
public PoolDecorator(IComponentPool<T> componentPool, EntityManager entityManager, ISystem system)
{
this.wrappedPool = componentPool;
this.entityManager = entityManager;
this.system = system;
}
public void AddEntity(Guid entityId, T component)
{
wrappedPool.AddEntity(entityId, component);
if (system.ComponentTypes
.Select(t => entityManager.GetComponentPool(t))
.All(p => p.ContainsEntity(entityId)))
{
system.SystemEntities.Add(entityId);
}
}
public void RemoveEntity(Guid entityId)
{
wrappedPool.RemoveEntity(entityId);
system.SystemEntities.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return wrappedPool.ContainsEntity(entityId);
}
}
As said, you could place the burden of checking, and managing the system collections, on EntityManager
. But our current design tends to diminish complexity and provide more flexibility in the long run. Just wrap a pool once for each system it belongs to. If the system requires non default behavior, then you can create a new decorator specialized for that system -- without interfering with other systems.
- EntityManager.cs
The orchestrator (aka mediator, controller,...)
class EntityManager
{
List<ISystem> systems;
Dictionary<Type, object> componentPools;
public EntityManager()
{
systems = new List<ISystem>();
componentPools = new Dictionary<Type, object>();
ActiveEntities = new HashSet<Guid>();
}
public ISet<Guid> ActiveEntities { get; }
public Guid CreateEntity()
{
Guid entityId;
do entityId = Guid.NewGuid();
while (!ActiveEntities.Add(entityId));
return entityId;
}
public void DestroyEntity(Guid entityId)
{
componentPools.Values.Select(kp => (IComponentPool)kp).ToList().ForEach(c => c.RemoveEntity(entityId));
systems.ForEach(c => c.SystemEntities.Remove(entityId));
ActiveEntities.Remove(entityId);
}
public void AddSystems(params ISystem system)
{
systems.AddRange(systems);
}
public IComponentPool GetComponentPool(Type componentType)
{
return (IComponentPool)componentPools[componentType];
}
public IComponentPool<TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
{
return (IComponentPool<TComponent>)componentPools[typeof(TComponent)];
}
public void AddComponentPool<TComponent>(IComponentPool<TComponent> componentPool) where TComponent : IComponent
{
componentPools.Add(typeof(TComponent), componentPool);
}
public void AddComponentToEntity<TComponent>(Guid entityId, TComponent component) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.AddEntity(entityId, component);
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.RemoveEntity(entityId);
}
}
- Program.cs
Where it all comes together.
class Program
{
static void Main(string args)
{
#region Composition Root
var entityManager = new EntityManager();
var drawingComponentTypes =
new Type {
typeof(VisibleComponent),
typeof(PhysicalComponent) };
var drawingSystem = new DrawingSystem(drawingComponentTypes);
var visibleComponent =
new PoolDecorator<VisibleComponent>(
new ComponentPool<VisibleComponent>(), entityManager, drawingSystem);
var physicalComponent =
new PoolDecorator<PhysicalComponent>(
new ComponentPool<PhysicalComponent>(), entityManager, drawingSystem);
entityManager.AddSystems(drawingSystem);
entityManager.AddComponentPool(visibleComponent);
entityManager.AddComponentPool(physicalComponent);
#endregion
var entity = new Entity(entityManager.CreateEntity());
entityManager.AddComponentToEntity(
entity.EntityId,
new PhysicalComponent() { X = 0, Y = 0 });
Console.WriteLine($"Added physical component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.AddComponentToEntity(
entity.EntityId,
new VisibleComponent() { Appearance = Appearance.Monster });
Console.WriteLine($"Added visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.RemoveComponentFromEntity<VisibleComponent>(entity.EntityId);
Console.WriteLine($"Removed visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
Console.ReadLine();
}
}
and maybe it's possible to create collections that require no
entityId
, so they only store the reference to the component that should get updated.
As mentioned in the referenced wiki, that's actually discouraged.
It is a common practice to use a unique ID for each entity. This is not a requirement, but it has several advantages:
- The entity can be referred using the ID instead of a pointer. This is more robust, as it would allow for the entity to be destroyed without leaving dangling pointers.
- It helps for saving state externally. When the state is loaded again, there is no need for pointers to be reconstructed.
- Data can be shuffled around in memory as needed.
- Entity ids can be used when communicating over a network to uniquely identify the entity.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54231778%2fcreate-references-to-objects-and-hold-these-references-in-a-cache%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The Entity-components-system asks for a specific design.
ECS follows the composition over inheritance principle
Dealing with pools of components, which are in essence raw data, it makes sense to handle this data with reference to the actual component type -- given you'll want to apply specific behaviors for each.
The decorator pattern plays nicely with composition, adding behaviors by wrapping types. It also allows EntityManager
to delegate responsibilities to the component pools, instead of having one massive decision tree that has to handle all cases.
Let's implement an example.
Suppose there is a drawing function. This would be a "System" that iterates through all entities that have both a physical and a visible component, and draws them. The visible component could typically have some information about how an entity should look (e.g. human, monster, sparks flying around, flying arrow), and use the physical component to know where to draw it.
- Entity.cs
An entity will be typically made up of an ID and a list of components that are attached to it.
class Entity
{
public Entity(Guid entityId)
{
EntityId = entityId;
Components = new List<IComponent>();
}
public Guid EntityId { get; }
public List<IComponent> Components { get; }
}
- Component.cs
Starting with the marker interface.
interface IComponent { }
enum Appearance : byte
{
Human,
Monster,
SparksFlyingAround,
FlyingArrow
}
class VisibleComponent : IComponent
{
public Appearance Appearance { get; set; }
}
class PhysicalComponent : IComponent
{
public double X { get; set; }
public double Y { get; set; }
}
- System.cs
Adding a collection for the SystemEntities
.
interface ISystem
{
ISet<Guid> SystemEntities { get; }
Type ComponentTypes { get; }
void Run();
}
class DrawingSystem : ISystem
{
public DrawingSystem(params Type componentTypes)
{
ComponentTypes = componentTypes;
SystemEntities = new HashSet<Guid>();
}
public ISet<Guid> SystemEntities { get; }
public Type ComponentTypes { get; }
public void Run()
{
foreach (var entity in SystemEntities)
{
Draw(entity);
}
}
private void Draw(Guid entity) { /*Do Magic*/ }
}
- ComponentPool.cs
Next, we'll lay the ground work for what's to come. Our component pools should also have a non-generic interface, that we can fall back upon when we can't supply a component type.
interface IComponentPool
{
void RemoveEntity(Guid entityId);
bool ContainsEntity(Guid entityId);
}
interface IComponentPool<T> : IComponentPool
{
void AddEntity(Guid entityId, T component);
}
class ComponentPool<T> : IComponentPool<T>
{
private Dictionary<Guid, T> component = new Dictionary<Guid, T>();
public void AddEntity(Guid entityId, T component)
{
this.component.Add(entityId, component);
}
public void RemoveEntity(Guid entityId)
{
component.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return component.ContainsKey(entityId);
}
}
Next step is the pool decorator. The decorator pattern is implemented by exposing the same interface as the class it wraps, applying any desired behavior in the process. In our case, we want to check if added entities posses all the component types a system requires. And if they do, add them to the collection.
class PoolDecorator<T> : IComponentPool<T>
{
private readonly IComponentPool<T> wrappedPool;
private readonly EntityManager entityManager;
private readonly ISystem system;
public PoolDecorator(IComponentPool<T> componentPool, EntityManager entityManager, ISystem system)
{
this.wrappedPool = componentPool;
this.entityManager = entityManager;
this.system = system;
}
public void AddEntity(Guid entityId, T component)
{
wrappedPool.AddEntity(entityId, component);
if (system.ComponentTypes
.Select(t => entityManager.GetComponentPool(t))
.All(p => p.ContainsEntity(entityId)))
{
system.SystemEntities.Add(entityId);
}
}
public void RemoveEntity(Guid entityId)
{
wrappedPool.RemoveEntity(entityId);
system.SystemEntities.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return wrappedPool.ContainsEntity(entityId);
}
}
As said, you could place the burden of checking, and managing the system collections, on EntityManager
. But our current design tends to diminish complexity and provide more flexibility in the long run. Just wrap a pool once for each system it belongs to. If the system requires non default behavior, then you can create a new decorator specialized for that system -- without interfering with other systems.
- EntityManager.cs
The orchestrator (aka mediator, controller,...)
class EntityManager
{
List<ISystem> systems;
Dictionary<Type, object> componentPools;
public EntityManager()
{
systems = new List<ISystem>();
componentPools = new Dictionary<Type, object>();
ActiveEntities = new HashSet<Guid>();
}
public ISet<Guid> ActiveEntities { get; }
public Guid CreateEntity()
{
Guid entityId;
do entityId = Guid.NewGuid();
while (!ActiveEntities.Add(entityId));
return entityId;
}
public void DestroyEntity(Guid entityId)
{
componentPools.Values.Select(kp => (IComponentPool)kp).ToList().ForEach(c => c.RemoveEntity(entityId));
systems.ForEach(c => c.SystemEntities.Remove(entityId));
ActiveEntities.Remove(entityId);
}
public void AddSystems(params ISystem system)
{
systems.AddRange(systems);
}
public IComponentPool GetComponentPool(Type componentType)
{
return (IComponentPool)componentPools[componentType];
}
public IComponentPool<TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
{
return (IComponentPool<TComponent>)componentPools[typeof(TComponent)];
}
public void AddComponentPool<TComponent>(IComponentPool<TComponent> componentPool) where TComponent : IComponent
{
componentPools.Add(typeof(TComponent), componentPool);
}
public void AddComponentToEntity<TComponent>(Guid entityId, TComponent component) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.AddEntity(entityId, component);
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.RemoveEntity(entityId);
}
}
- Program.cs
Where it all comes together.
class Program
{
static void Main(string args)
{
#region Composition Root
var entityManager = new EntityManager();
var drawingComponentTypes =
new Type {
typeof(VisibleComponent),
typeof(PhysicalComponent) };
var drawingSystem = new DrawingSystem(drawingComponentTypes);
var visibleComponent =
new PoolDecorator<VisibleComponent>(
new ComponentPool<VisibleComponent>(), entityManager, drawingSystem);
var physicalComponent =
new PoolDecorator<PhysicalComponent>(
new ComponentPool<PhysicalComponent>(), entityManager, drawingSystem);
entityManager.AddSystems(drawingSystem);
entityManager.AddComponentPool(visibleComponent);
entityManager.AddComponentPool(physicalComponent);
#endregion
var entity = new Entity(entityManager.CreateEntity());
entityManager.AddComponentToEntity(
entity.EntityId,
new PhysicalComponent() { X = 0, Y = 0 });
Console.WriteLine($"Added physical component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.AddComponentToEntity(
entity.EntityId,
new VisibleComponent() { Appearance = Appearance.Monster });
Console.WriteLine($"Added visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.RemoveComponentFromEntity<VisibleComponent>(entity.EntityId);
Console.WriteLine($"Removed visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
Console.ReadLine();
}
}
and maybe it's possible to create collections that require no
entityId
, so they only store the reference to the component that should get updated.
As mentioned in the referenced wiki, that's actually discouraged.
It is a common practice to use a unique ID for each entity. This is not a requirement, but it has several advantages:
- The entity can be referred using the ID instead of a pointer. This is more robust, as it would allow for the entity to be destroyed without leaving dangling pointers.
- It helps for saving state externally. When the state is loaded again, there is no need for pointers to be reconstructed.
- Data can be shuffled around in memory as needed.
- Entity ids can be used when communicating over a network to uniquely identify the entity.
add a comment |
The Entity-components-system asks for a specific design.
ECS follows the composition over inheritance principle
Dealing with pools of components, which are in essence raw data, it makes sense to handle this data with reference to the actual component type -- given you'll want to apply specific behaviors for each.
The decorator pattern plays nicely with composition, adding behaviors by wrapping types. It also allows EntityManager
to delegate responsibilities to the component pools, instead of having one massive decision tree that has to handle all cases.
Let's implement an example.
Suppose there is a drawing function. This would be a "System" that iterates through all entities that have both a physical and a visible component, and draws them. The visible component could typically have some information about how an entity should look (e.g. human, monster, sparks flying around, flying arrow), and use the physical component to know where to draw it.
- Entity.cs
An entity will be typically made up of an ID and a list of components that are attached to it.
class Entity
{
public Entity(Guid entityId)
{
EntityId = entityId;
Components = new List<IComponent>();
}
public Guid EntityId { get; }
public List<IComponent> Components { get; }
}
- Component.cs
Starting with the marker interface.
interface IComponent { }
enum Appearance : byte
{
Human,
Monster,
SparksFlyingAround,
FlyingArrow
}
class VisibleComponent : IComponent
{
public Appearance Appearance { get; set; }
}
class PhysicalComponent : IComponent
{
public double X { get; set; }
public double Y { get; set; }
}
- System.cs
Adding a collection for the SystemEntities
.
interface ISystem
{
ISet<Guid> SystemEntities { get; }
Type ComponentTypes { get; }
void Run();
}
class DrawingSystem : ISystem
{
public DrawingSystem(params Type componentTypes)
{
ComponentTypes = componentTypes;
SystemEntities = new HashSet<Guid>();
}
public ISet<Guid> SystemEntities { get; }
public Type ComponentTypes { get; }
public void Run()
{
foreach (var entity in SystemEntities)
{
Draw(entity);
}
}
private void Draw(Guid entity) { /*Do Magic*/ }
}
- ComponentPool.cs
Next, we'll lay the ground work for what's to come. Our component pools should also have a non-generic interface, that we can fall back upon when we can't supply a component type.
interface IComponentPool
{
void RemoveEntity(Guid entityId);
bool ContainsEntity(Guid entityId);
}
interface IComponentPool<T> : IComponentPool
{
void AddEntity(Guid entityId, T component);
}
class ComponentPool<T> : IComponentPool<T>
{
private Dictionary<Guid, T> component = new Dictionary<Guid, T>();
public void AddEntity(Guid entityId, T component)
{
this.component.Add(entityId, component);
}
public void RemoveEntity(Guid entityId)
{
component.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return component.ContainsKey(entityId);
}
}
Next step is the pool decorator. The decorator pattern is implemented by exposing the same interface as the class it wraps, applying any desired behavior in the process. In our case, we want to check if added entities posses all the component types a system requires. And if they do, add them to the collection.
class PoolDecorator<T> : IComponentPool<T>
{
private readonly IComponentPool<T> wrappedPool;
private readonly EntityManager entityManager;
private readonly ISystem system;
public PoolDecorator(IComponentPool<T> componentPool, EntityManager entityManager, ISystem system)
{
this.wrappedPool = componentPool;
this.entityManager = entityManager;
this.system = system;
}
public void AddEntity(Guid entityId, T component)
{
wrappedPool.AddEntity(entityId, component);
if (system.ComponentTypes
.Select(t => entityManager.GetComponentPool(t))
.All(p => p.ContainsEntity(entityId)))
{
system.SystemEntities.Add(entityId);
}
}
public void RemoveEntity(Guid entityId)
{
wrappedPool.RemoveEntity(entityId);
system.SystemEntities.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return wrappedPool.ContainsEntity(entityId);
}
}
As said, you could place the burden of checking, and managing the system collections, on EntityManager
. But our current design tends to diminish complexity and provide more flexibility in the long run. Just wrap a pool once for each system it belongs to. If the system requires non default behavior, then you can create a new decorator specialized for that system -- without interfering with other systems.
- EntityManager.cs
The orchestrator (aka mediator, controller,...)
class EntityManager
{
List<ISystem> systems;
Dictionary<Type, object> componentPools;
public EntityManager()
{
systems = new List<ISystem>();
componentPools = new Dictionary<Type, object>();
ActiveEntities = new HashSet<Guid>();
}
public ISet<Guid> ActiveEntities { get; }
public Guid CreateEntity()
{
Guid entityId;
do entityId = Guid.NewGuid();
while (!ActiveEntities.Add(entityId));
return entityId;
}
public void DestroyEntity(Guid entityId)
{
componentPools.Values.Select(kp => (IComponentPool)kp).ToList().ForEach(c => c.RemoveEntity(entityId));
systems.ForEach(c => c.SystemEntities.Remove(entityId));
ActiveEntities.Remove(entityId);
}
public void AddSystems(params ISystem system)
{
systems.AddRange(systems);
}
public IComponentPool GetComponentPool(Type componentType)
{
return (IComponentPool)componentPools[componentType];
}
public IComponentPool<TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
{
return (IComponentPool<TComponent>)componentPools[typeof(TComponent)];
}
public void AddComponentPool<TComponent>(IComponentPool<TComponent> componentPool) where TComponent : IComponent
{
componentPools.Add(typeof(TComponent), componentPool);
}
public void AddComponentToEntity<TComponent>(Guid entityId, TComponent component) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.AddEntity(entityId, component);
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.RemoveEntity(entityId);
}
}
- Program.cs
Where it all comes together.
class Program
{
static void Main(string args)
{
#region Composition Root
var entityManager = new EntityManager();
var drawingComponentTypes =
new Type {
typeof(VisibleComponent),
typeof(PhysicalComponent) };
var drawingSystem = new DrawingSystem(drawingComponentTypes);
var visibleComponent =
new PoolDecorator<VisibleComponent>(
new ComponentPool<VisibleComponent>(), entityManager, drawingSystem);
var physicalComponent =
new PoolDecorator<PhysicalComponent>(
new ComponentPool<PhysicalComponent>(), entityManager, drawingSystem);
entityManager.AddSystems(drawingSystem);
entityManager.AddComponentPool(visibleComponent);
entityManager.AddComponentPool(physicalComponent);
#endregion
var entity = new Entity(entityManager.CreateEntity());
entityManager.AddComponentToEntity(
entity.EntityId,
new PhysicalComponent() { X = 0, Y = 0 });
Console.WriteLine($"Added physical component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.AddComponentToEntity(
entity.EntityId,
new VisibleComponent() { Appearance = Appearance.Monster });
Console.WriteLine($"Added visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.RemoveComponentFromEntity<VisibleComponent>(entity.EntityId);
Console.WriteLine($"Removed visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
Console.ReadLine();
}
}
and maybe it's possible to create collections that require no
entityId
, so they only store the reference to the component that should get updated.
As mentioned in the referenced wiki, that's actually discouraged.
It is a common practice to use a unique ID for each entity. This is not a requirement, but it has several advantages:
- The entity can be referred using the ID instead of a pointer. This is more robust, as it would allow for the entity to be destroyed without leaving dangling pointers.
- It helps for saving state externally. When the state is loaded again, there is no need for pointers to be reconstructed.
- Data can be shuffled around in memory as needed.
- Entity ids can be used when communicating over a network to uniquely identify the entity.
add a comment |
The Entity-components-system asks for a specific design.
ECS follows the composition over inheritance principle
Dealing with pools of components, which are in essence raw data, it makes sense to handle this data with reference to the actual component type -- given you'll want to apply specific behaviors for each.
The decorator pattern plays nicely with composition, adding behaviors by wrapping types. It also allows EntityManager
to delegate responsibilities to the component pools, instead of having one massive decision tree that has to handle all cases.
Let's implement an example.
Suppose there is a drawing function. This would be a "System" that iterates through all entities that have both a physical and a visible component, and draws them. The visible component could typically have some information about how an entity should look (e.g. human, monster, sparks flying around, flying arrow), and use the physical component to know where to draw it.
- Entity.cs
An entity will be typically made up of an ID and a list of components that are attached to it.
class Entity
{
public Entity(Guid entityId)
{
EntityId = entityId;
Components = new List<IComponent>();
}
public Guid EntityId { get; }
public List<IComponent> Components { get; }
}
- Component.cs
Starting with the marker interface.
interface IComponent { }
enum Appearance : byte
{
Human,
Monster,
SparksFlyingAround,
FlyingArrow
}
class VisibleComponent : IComponent
{
public Appearance Appearance { get; set; }
}
class PhysicalComponent : IComponent
{
public double X { get; set; }
public double Y { get; set; }
}
- System.cs
Adding a collection for the SystemEntities
.
interface ISystem
{
ISet<Guid> SystemEntities { get; }
Type ComponentTypes { get; }
void Run();
}
class DrawingSystem : ISystem
{
public DrawingSystem(params Type componentTypes)
{
ComponentTypes = componentTypes;
SystemEntities = new HashSet<Guid>();
}
public ISet<Guid> SystemEntities { get; }
public Type ComponentTypes { get; }
public void Run()
{
foreach (var entity in SystemEntities)
{
Draw(entity);
}
}
private void Draw(Guid entity) { /*Do Magic*/ }
}
- ComponentPool.cs
Next, we'll lay the ground work for what's to come. Our component pools should also have a non-generic interface, that we can fall back upon when we can't supply a component type.
interface IComponentPool
{
void RemoveEntity(Guid entityId);
bool ContainsEntity(Guid entityId);
}
interface IComponentPool<T> : IComponentPool
{
void AddEntity(Guid entityId, T component);
}
class ComponentPool<T> : IComponentPool<T>
{
private Dictionary<Guid, T> component = new Dictionary<Guid, T>();
public void AddEntity(Guid entityId, T component)
{
this.component.Add(entityId, component);
}
public void RemoveEntity(Guid entityId)
{
component.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return component.ContainsKey(entityId);
}
}
Next step is the pool decorator. The decorator pattern is implemented by exposing the same interface as the class it wraps, applying any desired behavior in the process. In our case, we want to check if added entities posses all the component types a system requires. And if they do, add them to the collection.
class PoolDecorator<T> : IComponentPool<T>
{
private readonly IComponentPool<T> wrappedPool;
private readonly EntityManager entityManager;
private readonly ISystem system;
public PoolDecorator(IComponentPool<T> componentPool, EntityManager entityManager, ISystem system)
{
this.wrappedPool = componentPool;
this.entityManager = entityManager;
this.system = system;
}
public void AddEntity(Guid entityId, T component)
{
wrappedPool.AddEntity(entityId, component);
if (system.ComponentTypes
.Select(t => entityManager.GetComponentPool(t))
.All(p => p.ContainsEntity(entityId)))
{
system.SystemEntities.Add(entityId);
}
}
public void RemoveEntity(Guid entityId)
{
wrappedPool.RemoveEntity(entityId);
system.SystemEntities.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return wrappedPool.ContainsEntity(entityId);
}
}
As said, you could place the burden of checking, and managing the system collections, on EntityManager
. But our current design tends to diminish complexity and provide more flexibility in the long run. Just wrap a pool once for each system it belongs to. If the system requires non default behavior, then you can create a new decorator specialized for that system -- without interfering with other systems.
- EntityManager.cs
The orchestrator (aka mediator, controller,...)
class EntityManager
{
List<ISystem> systems;
Dictionary<Type, object> componentPools;
public EntityManager()
{
systems = new List<ISystem>();
componentPools = new Dictionary<Type, object>();
ActiveEntities = new HashSet<Guid>();
}
public ISet<Guid> ActiveEntities { get; }
public Guid CreateEntity()
{
Guid entityId;
do entityId = Guid.NewGuid();
while (!ActiveEntities.Add(entityId));
return entityId;
}
public void DestroyEntity(Guid entityId)
{
componentPools.Values.Select(kp => (IComponentPool)kp).ToList().ForEach(c => c.RemoveEntity(entityId));
systems.ForEach(c => c.SystemEntities.Remove(entityId));
ActiveEntities.Remove(entityId);
}
public void AddSystems(params ISystem system)
{
systems.AddRange(systems);
}
public IComponentPool GetComponentPool(Type componentType)
{
return (IComponentPool)componentPools[componentType];
}
public IComponentPool<TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
{
return (IComponentPool<TComponent>)componentPools[typeof(TComponent)];
}
public void AddComponentPool<TComponent>(IComponentPool<TComponent> componentPool) where TComponent : IComponent
{
componentPools.Add(typeof(TComponent), componentPool);
}
public void AddComponentToEntity<TComponent>(Guid entityId, TComponent component) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.AddEntity(entityId, component);
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.RemoveEntity(entityId);
}
}
- Program.cs
Where it all comes together.
class Program
{
static void Main(string args)
{
#region Composition Root
var entityManager = new EntityManager();
var drawingComponentTypes =
new Type {
typeof(VisibleComponent),
typeof(PhysicalComponent) };
var drawingSystem = new DrawingSystem(drawingComponentTypes);
var visibleComponent =
new PoolDecorator<VisibleComponent>(
new ComponentPool<VisibleComponent>(), entityManager, drawingSystem);
var physicalComponent =
new PoolDecorator<PhysicalComponent>(
new ComponentPool<PhysicalComponent>(), entityManager, drawingSystem);
entityManager.AddSystems(drawingSystem);
entityManager.AddComponentPool(visibleComponent);
entityManager.AddComponentPool(physicalComponent);
#endregion
var entity = new Entity(entityManager.CreateEntity());
entityManager.AddComponentToEntity(
entity.EntityId,
new PhysicalComponent() { X = 0, Y = 0 });
Console.WriteLine($"Added physical component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.AddComponentToEntity(
entity.EntityId,
new VisibleComponent() { Appearance = Appearance.Monster });
Console.WriteLine($"Added visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.RemoveComponentFromEntity<VisibleComponent>(entity.EntityId);
Console.WriteLine($"Removed visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
Console.ReadLine();
}
}
and maybe it's possible to create collections that require no
entityId
, so they only store the reference to the component that should get updated.
As mentioned in the referenced wiki, that's actually discouraged.
It is a common practice to use a unique ID for each entity. This is not a requirement, but it has several advantages:
- The entity can be referred using the ID instead of a pointer. This is more robust, as it would allow for the entity to be destroyed without leaving dangling pointers.
- It helps for saving state externally. When the state is loaded again, there is no need for pointers to be reconstructed.
- Data can be shuffled around in memory as needed.
- Entity ids can be used when communicating over a network to uniquely identify the entity.
The Entity-components-system asks for a specific design.
ECS follows the composition over inheritance principle
Dealing with pools of components, which are in essence raw data, it makes sense to handle this data with reference to the actual component type -- given you'll want to apply specific behaviors for each.
The decorator pattern plays nicely with composition, adding behaviors by wrapping types. It also allows EntityManager
to delegate responsibilities to the component pools, instead of having one massive decision tree that has to handle all cases.
Let's implement an example.
Suppose there is a drawing function. This would be a "System" that iterates through all entities that have both a physical and a visible component, and draws them. The visible component could typically have some information about how an entity should look (e.g. human, monster, sparks flying around, flying arrow), and use the physical component to know where to draw it.
- Entity.cs
An entity will be typically made up of an ID and a list of components that are attached to it.
class Entity
{
public Entity(Guid entityId)
{
EntityId = entityId;
Components = new List<IComponent>();
}
public Guid EntityId { get; }
public List<IComponent> Components { get; }
}
- Component.cs
Starting with the marker interface.
interface IComponent { }
enum Appearance : byte
{
Human,
Monster,
SparksFlyingAround,
FlyingArrow
}
class VisibleComponent : IComponent
{
public Appearance Appearance { get; set; }
}
class PhysicalComponent : IComponent
{
public double X { get; set; }
public double Y { get; set; }
}
- System.cs
Adding a collection for the SystemEntities
.
interface ISystem
{
ISet<Guid> SystemEntities { get; }
Type ComponentTypes { get; }
void Run();
}
class DrawingSystem : ISystem
{
public DrawingSystem(params Type componentTypes)
{
ComponentTypes = componentTypes;
SystemEntities = new HashSet<Guid>();
}
public ISet<Guid> SystemEntities { get; }
public Type ComponentTypes { get; }
public void Run()
{
foreach (var entity in SystemEntities)
{
Draw(entity);
}
}
private void Draw(Guid entity) { /*Do Magic*/ }
}
- ComponentPool.cs
Next, we'll lay the ground work for what's to come. Our component pools should also have a non-generic interface, that we can fall back upon when we can't supply a component type.
interface IComponentPool
{
void RemoveEntity(Guid entityId);
bool ContainsEntity(Guid entityId);
}
interface IComponentPool<T> : IComponentPool
{
void AddEntity(Guid entityId, T component);
}
class ComponentPool<T> : IComponentPool<T>
{
private Dictionary<Guid, T> component = new Dictionary<Guid, T>();
public void AddEntity(Guid entityId, T component)
{
this.component.Add(entityId, component);
}
public void RemoveEntity(Guid entityId)
{
component.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return component.ContainsKey(entityId);
}
}
Next step is the pool decorator. The decorator pattern is implemented by exposing the same interface as the class it wraps, applying any desired behavior in the process. In our case, we want to check if added entities posses all the component types a system requires. And if they do, add them to the collection.
class PoolDecorator<T> : IComponentPool<T>
{
private readonly IComponentPool<T> wrappedPool;
private readonly EntityManager entityManager;
private readonly ISystem system;
public PoolDecorator(IComponentPool<T> componentPool, EntityManager entityManager, ISystem system)
{
this.wrappedPool = componentPool;
this.entityManager = entityManager;
this.system = system;
}
public void AddEntity(Guid entityId, T component)
{
wrappedPool.AddEntity(entityId, component);
if (system.ComponentTypes
.Select(t => entityManager.GetComponentPool(t))
.All(p => p.ContainsEntity(entityId)))
{
system.SystemEntities.Add(entityId);
}
}
public void RemoveEntity(Guid entityId)
{
wrappedPool.RemoveEntity(entityId);
system.SystemEntities.Remove(entityId);
}
public bool ContainsEntity(Guid entityId)
{
return wrappedPool.ContainsEntity(entityId);
}
}
As said, you could place the burden of checking, and managing the system collections, on EntityManager
. But our current design tends to diminish complexity and provide more flexibility in the long run. Just wrap a pool once for each system it belongs to. If the system requires non default behavior, then you can create a new decorator specialized for that system -- without interfering with other systems.
- EntityManager.cs
The orchestrator (aka mediator, controller,...)
class EntityManager
{
List<ISystem> systems;
Dictionary<Type, object> componentPools;
public EntityManager()
{
systems = new List<ISystem>();
componentPools = new Dictionary<Type, object>();
ActiveEntities = new HashSet<Guid>();
}
public ISet<Guid> ActiveEntities { get; }
public Guid CreateEntity()
{
Guid entityId;
do entityId = Guid.NewGuid();
while (!ActiveEntities.Add(entityId));
return entityId;
}
public void DestroyEntity(Guid entityId)
{
componentPools.Values.Select(kp => (IComponentPool)kp).ToList().ForEach(c => c.RemoveEntity(entityId));
systems.ForEach(c => c.SystemEntities.Remove(entityId));
ActiveEntities.Remove(entityId);
}
public void AddSystems(params ISystem system)
{
systems.AddRange(systems);
}
public IComponentPool GetComponentPool(Type componentType)
{
return (IComponentPool)componentPools[componentType];
}
public IComponentPool<TComponent> GetComponentPool<TComponent>() where TComponent : IComponent
{
return (IComponentPool<TComponent>)componentPools[typeof(TComponent)];
}
public void AddComponentPool<TComponent>(IComponentPool<TComponent> componentPool) where TComponent : IComponent
{
componentPools.Add(typeof(TComponent), componentPool);
}
public void AddComponentToEntity<TComponent>(Guid entityId, TComponent component) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.AddEntity(entityId, component);
}
public void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent
{
var pool = GetComponentPool<TComponent>();
pool.RemoveEntity(entityId);
}
}
- Program.cs
Where it all comes together.
class Program
{
static void Main(string args)
{
#region Composition Root
var entityManager = new EntityManager();
var drawingComponentTypes =
new Type {
typeof(VisibleComponent),
typeof(PhysicalComponent) };
var drawingSystem = new DrawingSystem(drawingComponentTypes);
var visibleComponent =
new PoolDecorator<VisibleComponent>(
new ComponentPool<VisibleComponent>(), entityManager, drawingSystem);
var physicalComponent =
new PoolDecorator<PhysicalComponent>(
new ComponentPool<PhysicalComponent>(), entityManager, drawingSystem);
entityManager.AddSystems(drawingSystem);
entityManager.AddComponentPool(visibleComponent);
entityManager.AddComponentPool(physicalComponent);
#endregion
var entity = new Entity(entityManager.CreateEntity());
entityManager.AddComponentToEntity(
entity.EntityId,
new PhysicalComponent() { X = 0, Y = 0 });
Console.WriteLine($"Added physical component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.AddComponentToEntity(
entity.EntityId,
new VisibleComponent() { Appearance = Appearance.Monster });
Console.WriteLine($"Added visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
entityManager.RemoveComponentFromEntity<VisibleComponent>(entity.EntityId);
Console.WriteLine($"Removed visible component, number of drawn entities: {drawingSystem.SystemEntities.Count}.");
Console.ReadLine();
}
}
and maybe it's possible to create collections that require no
entityId
, so they only store the reference to the component that should get updated.
As mentioned in the referenced wiki, that's actually discouraged.
It is a common practice to use a unique ID for each entity. This is not a requirement, but it has several advantages:
- The entity can be referred using the ID instead of a pointer. This is more robust, as it would allow for the entity to be destroyed without leaving dangling pointers.
- It helps for saving state externally. When the state is loaded again, there is no need for pointers to be reconstructed.
- Data can be shuffled around in memory as needed.
- Entity ids can be used when communicating over a network to uniquely identify the entity.
answered Jan 20 at 0:00
FunkFunk
5,4191826
5,4191826
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54231778%2fcreate-references-to-objects-and-hold-these-references-in-a-cache%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown