patterncsharpModerate
Entities and the things they do
Viewed 0 times
thethingsandentitiesthey
Problem
So I have an
The
Library:
The
```
public abstract class Entity : ITrackableObject
{
protected bool _isMoving;
protected Size _size = new Size(32, 64);
private PointF _position;
public bool IsMoving { get { return _isMoving; } }
public Guid Id { get; set; }
public abstract EntityType EntityType { get; }
public GenderType Gender { get; set; }
public PointF Position { get { return _position; } set { if (_position != value) { var oldPosition = _position; _position = value; OnTrackableObjectChanged(new TrackableObjectChangedEventArgs(_position, value, Size, Size)); } } }
public Direction Direction { get; set; }
public Size Size { get { return _size; } }
public RectangleF Bounds { get { return new RectangleF(Position, Size); } }
public EntityProperties EntityProperties { get; set; }
public Color NameColor { get; set; }
public Point Home { get; set; }
protected List _instructions = new List();
public List Instructions { get { return _instructions; } }
public Entity()
{
EntityProperties = new EntityProperties();
NameColor = Color.Preset.White;
_instructions = new List();
}
public virtual void Update(UpdateState updateState)
{
foreach (IInstruction instruction in Instructions)
instruction.Update(updateState);
Move(updateState.Force);
}
public virtual void Move(Vector2F vector)
{
if (vector.R > 0)
_isMoving = true;
else
_isMoving = false;
float r = Math.Max(Math.Abs(vector.X), Math.Abs(vector.Y));
Entity, which is responsible for providing an abstract base for other objects to inherit. (Like Actor objects, which are people, Creature objects, which are animals, etc.)The
Entity provides a solid foundation for Actor and Creature to interact with, I'm just curious if it's not gone overboard or if I've missed something.Library:
Framework (Client and Server)The
Entity:```
public abstract class Entity : ITrackableObject
{
protected bool _isMoving;
protected Size _size = new Size(32, 64);
private PointF _position;
public bool IsMoving { get { return _isMoving; } }
public Guid Id { get; set; }
public abstract EntityType EntityType { get; }
public GenderType Gender { get; set; }
public PointF Position { get { return _position; } set { if (_position != value) { var oldPosition = _position; _position = value; OnTrackableObjectChanged(new TrackableObjectChangedEventArgs(_position, value, Size, Size)); } } }
public Direction Direction { get; set; }
public Size Size { get { return _size; } }
public RectangleF Bounds { get { return new RectangleF(Position, Size); } }
public EntityProperties EntityProperties { get; set; }
public Color NameColor { get; set; }
public Point Home { get; set; }
protected List _instructions = new List();
public List Instructions { get { return _instructions; } }
public Entity()
{
EntityProperties = new EntityProperties();
NameColor = Color.Preset.White;
_instructions = new List();
}
public virtual void Update(UpdateState updateState)
{
foreach (IInstruction instruction in Instructions)
instruction.Update(updateState);
Move(updateState.Force);
}
public virtual void Move(Vector2F vector)
{
if (vector.R > 0)
_isMoving = true;
else
_isMoving = false;
float r = Math.Max(Math.Abs(vector.X), Math.Abs(vector.Y));
Solution
Any time you see things like "Size", "Gender", and "Home" in the same class, yes, you've gone overboard. This class violates a number of object-oriented design principles. You want reusable logic that keeps flexibility to throw your monsters like rocks and cast spells on your rocks that turn them into rockmonsters. At least that's what I wanted when I tried to do a very similar design to this, and it devolved into complete chaos, mainly because I was violating the:
Single Responsibility Principle (SRP)
The SRP states that one class/method/module should only have one reason to change. Your Entity class has dozens. You will want to improve your physics beyond "IsMoving", "Position", and "Direction". You will come up with common life-form attributes other than "Gender". You will want to know more about an Entity's habits and lifecycle than just "Home". All of these changes will force Entity to change, bloat, and become unmaintainable. All of these responsibilities should be broken out into separate classes. How? Well, to start:
Favor Composition Over Inheritance
I can see you struggling with your inheritance tree. A Player is an Actor which is a MovingEntity which is an Entity. What about ThrowableEntity? EatableEntity? StorableEntity? KillableEntity? You're enshrining certain aspects of game objects to the detrement of every other aspect you might care about in the future. This shows that inheritence is not the right architecture to represent the relationships between these aspects. Favor composition instead. There are many ways to do this, but a great place to start is:
Entity-Component-System (ECS) Architecture
The ECS architecture was invented in the game development community for this exact purpose: you want to be able to mix and match aspects arbitrarily. Some things are throwable + collectable (rocks), movable + killable (monsters), operable (switches), killable + operable (doors), collectable + edible (mushrooms), movable + killable + edible (mushroom monsters), etc. Each of those aspects gets one devoted System, which operates over some set of Components.
A Component is a concise set of data which describes an entity's state relative to some aspect. For instance, an Entity may have components like "Position"
Components don't have behavior; instead, Systems have the responsibility of operating on specific types of components. The Movement system cares about the Position and Velocity components, the Ecology system cares about Gender and Fertility, the Rendering system cares about Textures and NameColors. Periodically, a system loops through all the components it knows about and acts on them. For instance, the Movement system takes all the positions and velocities, adds velocities to positions, then stores the new positions. When the system fires can vary: the Movement system may fire 60 times per second, but the Ecology system might only fire every time an in-game day elapses.
I strongly recommend you read the Wikipedia article and browse the Entity-systems wiki.
Single Responsibility Principle (SRP)
The SRP states that one class/method/module should only have one reason to change. Your Entity class has dozens. You will want to improve your physics beyond "IsMoving", "Position", and "Direction". You will come up with common life-form attributes other than "Gender". You will want to know more about an Entity's habits and lifecycle than just "Home". All of these changes will force Entity to change, bloat, and become unmaintainable. All of these responsibilities should be broken out into separate classes. How? Well, to start:
Favor Composition Over Inheritance
I can see you struggling with your inheritance tree. A Player is an Actor which is a MovingEntity which is an Entity. What about ThrowableEntity? EatableEntity? StorableEntity? KillableEntity? You're enshrining certain aspects of game objects to the detrement of every other aspect you might care about in the future. This shows that inheritence is not the right architecture to represent the relationships between these aspects. Favor composition instead. There are many ways to do this, but a great place to start is:
Entity-Component-System (ECS) Architecture
The ECS architecture was invented in the game development community for this exact purpose: you want to be able to mix and match aspects arbitrarily. Some things are throwable + collectable (rocks), movable + killable (monsters), operable (switches), killable + operable (doors), collectable + edible (mushrooms), movable + killable + edible (mushroom monsters), etc. Each of those aspects gets one devoted System, which operates over some set of Components.
A Component is a concise set of data which describes an entity's state relative to some aspect. For instance, an Entity may have components like "Position"
{int X, int Y}, "Gender", "Home", "Texture" etc. These are not properties in the Entity class, but separate anemic Value Types that are associated with an entity.Components don't have behavior; instead, Systems have the responsibility of operating on specific types of components. The Movement system cares about the Position and Velocity components, the Ecology system cares about Gender and Fertility, the Rendering system cares about Textures and NameColors. Periodically, a system loops through all the components it knows about and acts on them. For instance, the Movement system takes all the positions and velocities, adds velocities to positions, then stores the new positions. When the system fires can vary: the Movement system may fire 60 times per second, but the Ecology system might only fire every time an in-game day elapses.
I strongly recommend you read the Wikipedia article and browse the Entity-systems wiki.
Context
StackExchange Code Review Q#101584, answer score: 12
Revisions (0)
No revisions yet.