Using the Entity-Component-System pattern I want to connect some systems with events. So some systems shouldn't run in a loop, they should just run on demand.
Given the example of a Health system a Death system should only run when a component gets below 1 health.
I thought about having two types of systems. The first type is a periodic system. This runs once per frame, for example a Render or Movement System. The other type is an event based system. As mentioned before a connection between Health and Death.
First I created a basic interface used by both system types.
internal interface ISystem
{
List<Guid> EntityCache { get; } // Only relevant entities get stored in there
ComponentRequirements ComponentRequirements { get; } // the required components for this system
void InitComponentRequirements();
void InitComponentPools(EntityManager entityManager);
void UpdateCacheEntities(); // update all entities from the cache
void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
Further I created the interfaces
internal interface IReactiveSystem : ISystem
{
// event based
}
and
internal interface IPeriodicSystem : ISystem
{
// runs in a loop
}
but I'm not sure if they will be necessary. There is no problem using
foreach (ISystem system in entityManager.Systems)
{
system.UpdateCacheEntities();
}
but I don't want to run a system if not needed.
There are two types of Events, a ChangeEvent and a ExecuteEvent. The first gets triggered when a value from a component has changed. The second one gets triggered when something should be done with a specific entity.
If you Need or want to you can have a look at the EntityManager
https://pastebin.com/NnfBc0N9
the ComponentRequirements
https://pastebin.com/xt3YGVSv
and the usage of the ECS
https://pastebin.com/Yuze72xf
An example System would be something like this
internal class HealthSystem : IReactiveSystem
{
public HealthSystem(EntityManager entityManager)
{
InitComponentRequirements();
InitComponentPools(entityManager);
}
private Dictionary<Guid, HealthComponent> healthComponentPool;
public List<Guid> EntityCache { get; } = new List<Guid>();
public ComponentRequirements ComponentRequirements { get; } = new ComponentRequirements();
public void InitComponentRequirements()
{
ComponentRequirements.AddRequiredType<HealthComponent>();
}
public void InitComponentPools(EntityManager entityManager)
{
healthComponentPool = entityManager.GetComponentPoolByType<HealthComponent>();
}
public void UpdateCacheEntities()
{
for (int i = 0; i < EntityCache.Count; i++)
{
UpdateCacheEntity(EntityCache[i]);
}
}
public void UpdateCacheEntity(Guid cacheEntityId)
{
Health healthComponent = healthComponentPool[cacheEntityId];
healthComponent.Value += 10; // just some tests
// update UI
}
}
How can I create ChangeEvents and ExecuteEvents for the different systems?
EDIT
Is there a way to add event delegates to the components to run a specific system for this entity on change if a change event is listening or on demand if an execute event is listening?
By mentioning ChangeEvent and ExecuteEvent I just mean event delegates.
Currently I could do something like this
internal class HealthSystem : IReactiveSystem
{
//… other stuff
IReactiveSystem deathSystem = entityManager.GetSystem<Death>(); // Get a system by its type
public void UpdateCacheEntity(Guid cacheEntityId)
{
// Change Health component
// Update UI
if(currentHealth < 1) // call the death system if the entity will be dead
{
deathSystem.UpdateCacheEntity(cacheEntityId);
}
}
}
But I was hoping to achieve a better architecture by using event delegates to make systems communicate and share data between each other.
I am not an expert on this design pattern but I read something on it and my advice is: try not to forget the real purpose of this pattern. This time I found the article on Wikipedia really interesting.
It is basically saying (at least it is what I understood) that this pattern has been "designed" to avoid creating too many dependencies, losing the decoupling. Here an example I took from the article:
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. Another system could be collision detection. It
would iterate through all entities that have a physical component, as
it would not care how the entity is drawn. This system would then, for
instance, detect arrows that collide with monsters, and generate an
event when that happens. It should not need to understand what an
arrow is, and what it means when another object is hit by an arrow.
Yet another component could be health data, and a system that manages
health. Health components would be attached to the human and monster
entities, but not to arrow entities. The health management system
would subscribe to the event generated from collisions and update
health accordingly. This system could also now and then iterate
through all entities with the health component, and regenerate health.
I think that you overcomplicated your architecture, losing the advantages that this pattern can give you.
First of all: why do you need the EntityManager? I quote again:
The ECS architecture handles dependencies in a very safe and simple
way. Since components are simple data buckets, they have no
dependencies.
Instead your components are constructed with the EntityManager dependency injected:
entityManager.AddSystem(new Movement(entityManager));
The outcome is a relatively complex internal structure to store entities and the associated components.
After fixing this, the question is: how can you "communicate" with the ISystems?
Again, answer is in the article: Observer Pattern. Essentially each component has a set of attached systems, which are notified every time a certain action occurs.
by what im getting at this, you want to have a repetitive, once every tick type event alongside a once in a year type event (exaggerated but clear), you can do this with a delegate call back function IE:
public delegate void Event(object Sender, EventType Type, object EventData);
public event Event OnDeath;
public event Event OnMove;
public void TakeDamage(int a)
{
Health-=a;
if(Health<1)
OnDeath?.Invoke(this,EventType.PlayerDeath,null);
}
public void ThreadedMovementFunction()
{
while(true)
{
int x,y;
(x,y) = GetMovementDirection();
if(x!=0||y!=0)
OnMove?.Invoke(this,EventType.PlayerMove,(x,y));
}
}
you can implement this into an interface, and then store the object class and only access the needed stuff like events and so on. but tbh i don't quite understand what you're looking for, so if you could elaborate on the exact issue or thing you need to solve, that would be greatly appreciated!
Related
Say, for example, that I have a GameObject A with a Manager script attached, which on start spawns in x amount of GameObjects with B script attached.
A different GameObject with script C is supposed to do something when the GameObject with script B says so.
So the questions is, what would be the best way for these three to communicate?
Obviously, Script B could just call Script C, however I feel like this method lacks structure and organisation.
Script A could also have reference to script C, and script B could tell the Script A to act on Script C.
I feel like there is some sort of rule I am supposed to follow, however I haven't come across it yet. Any help is much appreciated!
Obviously, Script B could just call Script C, however I feel like this
method lacks structure and organisation.
True. This is what the GameObject.SendMessage function is used for. Unfortunately, it is slow and I wouldn't recommend it but it's worth mentioning.
If you have many objects that will need to communicate with other objects, implement an event manager with event and delegate. This is the proper way to do it. You can find full EventManager implementation here.
With it, you can register any amount of function to an event with:
EventManager.StartListening("jump", someFunction);
Un-register any function from an event with:
EventManager.StopListening("jump", someFunction);
From there, you can invoke the event on any object listening to it:
EventManager.TriggerEvent("jump");
If A already has a reference to script C, it can pass on this reference to B, when it is created. Thus, B can communicate with C, without going through A.
i.e
Script A:
// variables
public ScriptC c;
// methods
void SpawnB(){
// spawn B
B.setC(c); // B's variable for script C is passed in from A
}
Script B:
// variables
ScriptC c;
// methods
void setC(ScriptC v){
c = v;
}
Something along those lines.
You could also use the not Unity specific Action delegates. I like to use a static class for that but you could as well implement it in one of your existing classes (as long as you use static members and methods)
E.g.
public static class MyEvents
{
public static event Action SomeEvent;
public static void InvokeSomeEvent()
{
// Make sure event is only invoked if someone is listening
if (SomeEvent == null) return;
SomeEvent.Invoke();
}
}
This makes your classes completely independent (well, ok they share the MyEvents class) and easy to modularize.
In script C add a "listener" e.g.
private void Start()
{
// It is save to remove a listener also if it wasn't there yet
// This makes sure you are not listening twice by accident
MyEvents.SomeEvent -= OnSomeEvent;
// Add the listener for that event
MyEvents.SomeEvent += OnSomeEvent;
}
private void OnSomeEvent ()
{
// Do something if SomeEvent is invoked
}
Then somewhere in script B just call
MyEvents.InvokeSomeEvent();
So class B doesn't have to know or care who listens for that event; it just invokes it and cares for it's own business.
On the other side C or (any other class where you add a listener for the event) doesn't have to know/cares where the invoke came from; it just handles it and does its stuff.
Note however, that this also makes debugging a little bit harder since it is not that easy anymore to tell where the invoke came from ;)
Note: You can also add parameters to an Action e.g.
public static event Action<int> SomeParameterEvent;
In this case ofcourse all methods have to also implement that parameter
public static InvokeSomeParameterEvent(int value)
{
if(SomeParameterAction == null) return;
SomeParameterEvent.Invoke(value);
}
In C (the listener) you also have to receive the parameters
// name can be changed
private void OnSomeParameterEvent(int value)
{
//...
}
And ofcourse also call it with the parameter in B
MyEvents.InvokeSomeParameterEvent(someInt);
And than you can take it even on step further and instead of a value or a reference pass a complete delegate method as parameter. See examples here
Delegates and Events are mostly used for comunication as Programmer wrote.
For better structure and organization I would suggest using a MVC pattern or any other design pattern you like. Here you can find a great example of MVC implementation for Unity3D with simple yet powerful notification system:
Unity with MVC by Eduardo Dias da Costa
In that example you don't need to use delegates/events for communication and you keep everything well organized.
Some of the communication functions used in quoted tutorial in case the link gets deprecated:
1.
// Iterates all Controllers and delegates the notification data
// This method can easily be found because every class is “BounceElement” and has an “app”
// instance.
public void Notify(string p_event_path, Object p_target, params object[] p_data)
{
BounceController[] controller_list = GetAllControllers();
foreach(BounceController c in controller_list)
{
c.OnNotification(p_event_path,p_target,p_data);
}
}
// Fetches all scene Controllers.
public BounceController[] GetAllControllers() { /* ... */ }
2.
// This class will give static access to the events strings.
class BounceNotification
{
static public string BallHitGround = “ball.hit.ground”;
static public string GameComplete = “game.complete”;
/* ... */
static public string GameStart = “game.start”;
static public string SceneLoad = “scene.load”;
/* ... */
}
3.
// Handles the ball hit event
public void OnNotification(string p_event_path,Object p_target,params object[] p_data)
{
switch(p_event_path)
{
case BounceNotification.BallHitGround:
app.model.bounces++;
Debug.Log(“Bounce ”+app.model.bounce);
if(app.model.bounces >= app.model.winCondition)
{
app.view.ball.enabled = false;
app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
// Notify itself and other controllers possibly interested in the event
app.Notify(BounceNotification.GameComplete,this);
}
break;
case BounceNotification.GameComplete:
Debug.Log(“Victory!!”);
break;
}
}
4.
// Callback called upon collision.
void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); }
Of course you can still implement MVC and use Delegates and Events. It is just to show another way of doing things.
Check out this really good article on communicating through Unity's scriptable objects. The removes the need for scripts to have references to each other, facilitating much more modular code. Within that link I shared, I really recommend the links there about Richard Fine and Ryan Hipple's presentations too.
I'm working on a server for a multi-player game that has to control a few thousand creatures, running around in the world. Every creature has an AI with a heartbeat method that is called every few ms/s, if a player is nearby, so they can react.
Currently the AI uses enumerators as "routines", e.g.
IEnumerable WanderAround(int radius)
{
// Do something
}
which are called from "state methods", which are called in foreachs, yielding in the heartbeat so you get back to the same spot on every tick.
void OnHeartbeat()
{
// Do checks, maybe select a new state method...
// Then continue the current sequence
currentState.MoveNext();
}
Naturally the routines have to be called in a loop as well, because they wouldn't execute otherwise. But since I'm not the one writing those AIs, but newbies who aren't necessarily programmers, I'm pre-compiling the AIs (simple .cs files) before compiling them on server start. This gives me AI scripts that look like this:
override IEnumerable Idle()
{
Do(WanderAround(400));
Do(Wait(3000));
}
override IEnumerable Aggro()
{
Do(Attack());
Do(Wait(3000));
}
with Do being replaced by a foreach that iterates over the routine call.
I really like this design because the AIs are easy to understand, yet powerful. It's not simple states but it's not a hard to understand/write behavior tree either.
Now to my actual "problem", I don't like the Do wrapper, I don't like having to pre-compile my scripts. But I just can't think of any other way to implement this without the loops, that I want to hide because of verbosity and the skill level of the people who're gonna write these scripts.
foreach(var r in Attack()) yield return r;
I'd wish there'd be a way to call the routines without an explicit loop, but that's not possible because I have to yield from the state method.
And I can't use async/await because it doesn't fit the tick design that I depend on (the AIs can be quite complex and I honestly don't know how I would implement that using async). Also I'd just trade Do() against await, not that much of an improvement.
So my question is: Can anyone think of a way to get rid of that loop wrapper? I'd be open to using other .NET languages that I can use as scripts (compiling them on server start) if there's one that supports this somehow.
Every creature has an AI with a heartbeat method that is called every few ms/s,
Why not go full SkyNet and have each creature responsible for its own heartbeat?
Such as creating each creature with a timer (the heart so to speak with a specific heartbeat). When each timer beats it does what it was designed to do, but also checks with the game as to whether it needs to shut-down, be idle, wander or other items.
By decentralizing the loop, you have gotten rid of the loop and you simply have a broadcast to subscribers (the creatures) on what to do on a global/basic level. That code is not accessible to the newbies, but it is understood what it does on a conceptual level.
You could try turning to the .NET framework for help by using events in your server and having the individual AIs subscribe to them. This works if the Server is maintaining the heartbeat.
Server
The server advertises the events that the AIs can subscribe to. In the heartbeat method you would call the OnIdle and OnAggro methods to raise the Idle and Aggro events.
public class GameServer
{
// You can change the type of these if you need to pass arguments to the handlers.
public event EventHandler Idle;
public event EventHandler Aggro;
void OnIdle()
{
EventHandler RaiseIdleEvent = Idle;
if (null != RaiseIdleEvent)
{
// Change the EventArgs.Empty to an appropriate value to pass arguments to the handlers
RaiseIdleEvent(this, EventArgs.Empty);
}
}
void OnAggro()
{
EventHandler RaiseAggroEvent = Aggro;
if (null != RaiseAggroEvent)
{
// Change the EventArgs.Empty to an appropriate value to pass arguments to the handlers
RaiseAggroEvent(this, EventArgs.Empty);
}
}
}
Generic CreatureAI
All of your developers will implement their creature AIs based on this class. The constructor takes a GameServer reference parameter to allow the events to be hooked. This is a simplified example where the reference is not saved. In practice you would save the reference and allow the AI implementors to subscribe and unsubsrcibe from the events depending on what state their AI is in. For example subscribe to the Aggro event only when a player tries to steal your chicken's eggs.
public abstract class CreatureAI
{
// For the specific derived class AI to implement
protected abstract void IdleEventHandler(object theServer, EventArgs args);
protected abstract void AggroEventHandler(object theServer, EventArgs args);
// Prevent default construction
private CreatureAI() { }
// The derived classes should call this
protected CreatureAI(GameServer theServer)
{
// Subscribe to the Idle AND Aggro events.
// You probably won't want to do this, but it shows how.
theServer.Idle += this.IdleEventHandler;
theServer.Aggro += this.AggroEventHandler;
}
// You might put in methods to subscribe to the event handlers to prevent a
//single instance of a creature from being subscribe to more than one event at once.
}
The AIs themselves
These derive from the generic CreatureAI base class and implement the creture-specific event handlers.
public class ChickenAI : CreatureAI
{
public ChickenAI(GameServer theServer) :
base(theServer)
{
// Do ChickenAI construction
}
protected override void IdleEventHandler(object theServer, EventArgs args)
{
// Do ChickenAI Idle actions
}
protected override void AggroEventHandler(object theServer, EventArgs args)
{
// Do ChickenAI Aggro actions
}
}
In the program I am creating, I have a large amount of enums
enum kEvents
{
Start = 0,
End,
EntityCreated,
}
In this program, there is a huge list of Entities.
Each entity holds a list of "kEvents" that it is "Registered to"
Every time an object says "Call the Start event" I have to iterate through every single entity in the game, and find out if they are "listening" for that event.
Note: I know that c# has event handlers, but I was hoping to create this enum system from the ground up instead.
This being the case, what is the best way to:
Hold the collection of enums in each entity object
Check if the entity holds the triggered enum
I am wondering if Enums, being basically numbers, have a lower level, quicker way to handle this than a List<T> of objects.
Every time an object says "Call the Start event" I have to iterate through every single entity in the game, and find out if they are "listening" for that event.
You're doing it wrong! Iterating over every single object and checking if they're registered for an event is very inefficient! This is the typical Observer design pattern and there are several ways to go about this, here are a couple:
Have a single event which gets raised and has an enum parameter for event type (every subscriber gets it).
Have a dictionary of enums and corresponding events.
Here is what option 1 might look like:
delegate void OnEventDelegate(kEvents anEvent);
public class MyEventObservable
{
public event OnEventDelegate OnEvent;
}
public class MyEventObserver
{
// Constructors and all
// ...
public void OnEventReceived(kEvents anEvent)
{
switch(anEvent)
{
// switch through all the events and handle the ones that you need
}
}
}
MyEventObserver observer = new MyEventObserver();
MyEventObservable observable = new MyEventObservable();
observable.OnEvent += new OnEventDelegate(observer.OnEventReceived);
Here is option 2:
public class MyEventObservable
{
private Dictionary<kEvents, List<IObserver>> _observers;
MyEventObservable()
{
// Initialize the dictionary with all the events
}
public void RegisterObserver(kEvents event, IObserver observer)
{
_observers[event].Add(observer);
}
}
interface class IObserver
{
void Notify(kEvents anEvent);
}
public MyEventObserver: IObserver
{
// Constructors and all
// ...
// Override the IObserver
public void Notify(kEvents anEvent)
{
switch(anEvent)
{
// switch through all the events and handle the ones that you need
}
}
}
MyEventObserver observer = new MyEventObserver();
MyEventObservable observable = new MyEventObservable();
observable.RegisterObserver(kEvents.Start, observer);
Option two will reduce the number of events each observer has to handle, but it comes at the cost of having to register for every event separately (which adds coding complexity). This means that option 2 will work faster because there are fewer function calls to make. Depending on how "crazy" you want to get with the performance, there might be other options that can help you to speed it up but this should set you on the right track.
P.S. I have not compiled any of the code, but it should give you a general idea of how things ought to work.
It seems to me that you want to implement some kind of publish/subscribe scheme, some centralized event sink that the Entities can subscribe to events and provide a callback when that event happens, then other Entities would Rise the event and the event sink would call all the Entities that are subscribed to that event.... but I may have not understood the question :)
Something like:
How to access controls in other class
Every time an object says "Call the Start event" I have to iterate
through every single entity in the game, and find out if they are
"listening" for that event.
If I have correctly understood what you are doing then you are doing it backwards. the knowledge of which events an entity is listening to cannot be (only) in the event itself. There must be another data structure that holds this information.
That other data structure could be something as simple as a Map from the enum constant to a list of entities.
In trying to design a collision detection component for a game, I came up with the following solution. I define an interface ICollideable that looks something like:
interface ICollideable
{
Sprite Sprite { get; }
int Damage { get; }
void HandleCollision(ICollideable collidedWith);
}
Basically, any game objects that want to participate in collision detection have to implement this interface, then register themselves with the detector, which maintains a list of ICollideables. When it detects a collision, it calls the HandleCollision method on the object and passes in a reference to the object it collided with.
I like this, because it lets me keep all my collision algorithms in one place, and lets the game objects themselves decide how to handle the collision. But because of the latter, I find I am having to check the underlying object type. For example, I don't want Players to collide with each other, so in the Player class there might be something like:
void HandleCollision(ICollideable collidedWith)
{
if (!(collidedWith is Player)) { // do stuff }
}
and so on, and I am wondering if this is telling me that I have a bad design and what the alternatives might be.
Second question, further along the lines of the first. For scoring purposes, if an Enemy is destroyed by a Projectile, someone needs to know the "Owning Player" member of the Projectile class. However, none of my other collideables have or need this property, so I find myself wanting to do (in the Enemy HandleCollision):
void HandleCollision(ICollideable collidedWith)
{
if (collidedWith is Projectile) {
Health -= collidedWith.Damage;
if (Health <= 0) {
Player whoDestroyedMe = (collidedWith as Projectile).FiredBy
// ...
}
}
}
I haven't a clue as to how to handle this with a better design. Any insights would be appreciated.
EDIT: I wanted to pull focus towards the second question, because my gut tells me a way of handling this will solve the first question. As for the first question, I thought of a way to abstract this behavior. I could define an enum:
enum Team
{
Player,
Enemy,
Neither
}
and have ICollideables implement this property. Then the collision detector simply doesn't register collisions between collideables on the same "Team". So, Player and Player Projectiles would be on one team, Enemy and Enemy Projectiles on the other, and the environment (which can damage both) can be on neither. It doesn't have to be an enum, could be an int or a string or anything, with the idea that objects with the same value for this property do not collide with each other.
I like this idea of modeling behavior with a simple attribute. For instance, if I want to turn "allow friendly fire" on, all I have to do is create Projectiles with a Team value other than the Player's Team value. However, I still may have cases where this is not enough. For example, a Player may have shields that are temporarily impervious to projectiles but will not protect against a direct collision with an enemy, and so on.
I think you're going the wrong way altogether in handling the collision inside of the class of one of the colliders. I would put this logic into a third object, outside of the entity objects. You could do all of the checking of the types in this third object, and even handle most of the logic there too. Why should a Ship or a Projectile have a monopoly over the logic that happens when one hits the other?
The following is how I might handle this, although it means using an object for each style of collision (Ship vs Ship, Ship vs Projectile, Ship vs Asteroid, etc.) You might be more comfortable putting all that logic into a single object, or even a single method on that object.
public interface ICollisionHandler
{
bool HandleCollision(Entity first, Entity second);
}
public class PlayerShipVsProjectile : ICollisionHandler
{
private GameOptions options;
public PlayersOwnShipHandler(GameOptions options)
{
this.options = options;
}
public bool HandleCollision(Entity first, Entity second)
{
// Exactly how you go about doing this line, whether using the object types
// or using a Type property, or some other method, is not really that important.
// You have so much more important things to worry about than these little
// code design details.
if ((!first is Ship) || (!second is Projectile)) return false;
Ship ship = (Ship)first;
Projectile projectile = (Projectile)second;
// Because we've decided to put this logic in it's own class, we can easily
// use a constructor parameter to get access to the game options. Here, we
// can have access to whether friendly fire is turned on or not.
if (ship.Owner.IsFriendlyWith(projectile.Shooter) &&
!this.options.FriendlyFire) {
return false;
}
if (!ship.InvulnerableTypes.Contains(InvulnerableTypes.PROJECTILE))
{
ship.DoDamage(projectile.Damage);
}
return true;
}
}
Like this, you can then do...
// Somewhere in the setup...
CollisionMapper mapper = new CollisionMapper();
mapper.AddHandler(new ShipVsProjectile(gameOptions));
mapper.AddHandler(new ShipVsShip(gameOptions));
// Somewhere in your collision handling...
mapper.Resolve(entityOne, entityTwo);
The implementation of CollisionMapper is left as an exercise for the reader. Remember that you might need to have Resolve call the ICollisionHandler's "Handle" method twice, with the second time reversing the entities (otherwise your collision handler objects will need to check for the reverse situation, which might be ok as well).
I feel this makes the code easier to read. A single object describes exactly what will happen when two entities collide, rather than trying to put all this info into one of the entity objects.
For the first case, I would add the following extra method to ICollidable:
bool CanCollideWith(ICollidable collidedWith)
As the name suggests, it would return true or false depending upon whether it can collide with the passed in object.
Your Player.HandleCollision method would just do its stuff because the calling method could do that test and not even call the method if it wasn't required.
How about something like this?
Collidable.cs
abstract class Collidable
{
public Sprite Sprite { get; protected set; }
public int Damage { get; protected set; }
protected delegate void CollisionAction(Collidable with);
protected Dictionary<Type, CollisionAction> collisionTypes = new Dictionary<Type, CollisionAction>();
public void HandleCollision(Collidable with)
{
Type collisionTargetType = with.GetType();
CollisionAction action;
bool keyFound = collisionTypes.TryGetValue(collisionTargetType, out action);
if (keyFound)
{
action(with);
}
}
}
Bullet.cs
class Bullet: Collidable
{
public Bullet()
{
collisionTypes.Add(typeof(Player), HandleBulletPlayerCollision);
collisionTypes.Add(typeof(Bullet), HandleBulletBulletCollision);
}
private void HandleBulletPlayerCollision(Collidable with)
{
Console.WriteLine("Bullet collided with {0}", with.ToString());
}
private void HandleBulletBulletCollision(Collidable with)
{
Console.WriteLine("Bullet collided with {0}.", with.ToString());
}
}
Player.cs
class Player : Collidable
{
public Player()
{
collisionTypes.Add(typeof(Bullet), HandlePlayerBulletCollision);
collisionTypes.Add(typeof(Player), HandlePlayerPlayerCollision);
}
private void HandlePlayerBulletCollision(Collidable with)
{
Console.WriteLine("Player collided with {0}.", with.ToString());
}
private void HandlePlayerPlayerCollision(Collidable with)
{
Console.WriteLine("Player collided with {0}.", with.ToString());
}
}
I think this is a good question #idlewire and I have to say that I don't think there is anything fundamentally wrong with your original solution. In asking whether object Foo should be allowed to cast the ICollideable to a Bar, the important question is only: is undesirable to have Foo knowing anything at all about Bar? If the answer is 'no' because Foo already knows about Bars (for behaviours other than collisions, perhaps) then I see no problem in the cast and, as you say, it allows you to better encapsulate the behaviour of both.
Where you need to be wary is only where this would introduces a dependency between two things you'd like kept apart - which would make re-use of either without the other (in a different game application for example) impossible. There you might want to either have more specific sub-interfaces from ICollideable (e.g. IElastic and IInelastic), or use properties on the interface as you have proposed with the Enum.
In short, I think your original posting shows good evidence of OO thinking, not bad.
Sometimes the simplest method is the best method. Unless you want to separate your collision interactions into numerous subtypes, you could instead place a bool IsPlayer property within the Interface.
The upside here is that you have a cheaper, and type safe method of determination over casting.
If (isplayer == true)
{
Handlethisway;
}
The downside is that you're still having to do some sort of state checking, but this is more efficient.
To avoid any state checks, you'd need to do the following: Make an ICollidablePlayer Interface which accepts generic Icollideable and handles them differently. Since the Icollideable is your injected dependency, the ICollideablePlayer dependencies are inherent. The objects of Icollideable would have no knowledge of this separate process, and interact with each other in the same manner.
ICollideablePlayer:ICollideable
{
//DependenciesHere
HandlePlayerCollision(ICollideable)
{
HandleDifferently
{
}
ICollideable
{
//DependenciesHere
HandleCollision(ICollideable)
}
}
}
In an interaction, the ICollideable will treat the player as any other ICollideable, but the ICollideablePlayer will reject the interaction when it does the check itself.
For things like shields and all that, You're talking about state changes which implies that those such things should be properties within either of those Interfaces such that something like bool ColliderOff to temporarily change the state.
Right now I have six classes:
Listener - manages socket connections
World - a collection of entities and tasks
Ticker - coordinates updating the world
MessageProcessor - receives commands from players
Intelligence - governs the behavior of non-player characters
Tasks - tracking and execution of tasks
But they are like spaghetti with reference to each other all over the place... The World is a data model which the MessageProcessor, Intelligence, and Tasks classes modify. The Ticker coordiates those three classes updating the World. The Listener is used by the MessageProcessor for incomming messages, and by the other classes to push updates.
How can I improve this situation?
I gave a related answer not long ago. The subject was on improving the testability of code, for which the general solution is to loosen coupling. The main focus on that previous answer was on decoupling networking related code from the world and it's logic, because networking code is not unit testable and is a pain to mock too.
The solution given there was to use an interface for incoming messages, such that you decouple the MessageProcessor (named Handler in other post) from the network code, and similarly, decouple the UpdateNotifier from the World.
The dashed line is just an indirect reference handled either by an interface or delegate. There exists no direct relation between the World and networking component now, making it testable. This is really just an application of the Model View Adapter pattern.
This doesn't seem dissimilar to the design you've described having, except perhaps you are missing a few interfaces. With this pattern of interface based UpdateNotifiers used to push updates, I essentially reuse the same architecture for handling NPCs, tasks, or anything else which is processed elsewhere. You cherry pick the event notifiers you need for a particular area, and implement a concrete Notifier class for them, such that you have multiple adapters on the same model.
And that really only looks more complicated than it is. The World object has no direct dependencies on anything else, and each other class has at most one direct dependency. You can also isolate the timer from the World, as it probably isn't needed there - but perhaps one of the biggest hurdles is handling synchronization between the different adapters.
Well, I'm not sure I have a full picture of what the issues you are having are, but I have a few possibilities from what you have laid out so far. (I may be actually suggesting some things that are already done since I'm not sure I have enough from the one-liner descriptions to understand fully.
The Model
I would say from what you've described, the main thing that strikes me is that you'll want to start implementing common functionality in a class model; you will want either interfaces or base classes that you can use to derive your high-level objects from.
This way you can handle things consistently with little extra effort. I think the idea of "architectural layers" can be useful as a first cut of how to think about it, (e.g. low-level hardware stuff, socket handling etc., then middle-layers stuff like what kind of things happen in your game, and the details behind how game mechanics work, etc., and high-level stuff like what can the PC or NPCs do, what's the environment doing, etc.. and also the idea that you never want to "jump" layers). However, when it comes down to it the important thing is to just find the right abstractions for your game, and keep everything organized in such a way as you never feel like the bit of code you're working on is doing two completely different kinds of things.
So, first, let's take the fact that it sounds like (naturally) there are a lot of things interacting with world state. For something like this, it's probably advantageous to factor a lot of the 'stuff' out into a single class, and then mostly only have the one class doing that job. Ideally you implement, say, event communication/message passing, in it's own little group, so that there's no need to pollute your higher-level objects with the nitty-gritty of handling stuff.
e.g., you want to focus on what things are doing at a high level in the higher-level objects: in an AI perhaps "begin movement toward a location", "set my haste", "stop movement"; and in an environment subsystem do "start raining", "increase windspeed", "dim lights"; in a user class "fire weapon", "sleep", "cast spell". But I wouldn't want any of my high-level classes to even know about things like "send message to world", or "reset thirst timer", or "receive socket data", or "health cycle tick". (These are all just elucidations, not suggestions. ;D)
Events
For instance, I think it may be valuable to keep one object in charge of dispatching events to the World, that way you no longer have everyone talking to everyone. I would likely just create a set of stuff to handle events in general. So, maybe EventMain and an enumEvents that you use so that each type of event has a special ID. And then use Event as the base class for particular events that need extra functionality. (I have both the ID as well as a derivation model in mind, so that things thing like the Dispatcher which likely only need to know very basic things about the event don't have to also know about the derived classes. For instance, the dispatcher could take an event in and send it out without ever having to know the internals of a derived event. This may or may not turn out to be useful, but it's good to have the options.) You could also have an EventDispatcher that has a queue of events to be send to other subsystems.
You will want something common for recieving and sending events. You could do EventSourcer and EventSinker standalone classes that can be set up in any class that is generating or receiving events. Or, you could instead do IEventSource and IEventSink so that you could implement a common interface on multiple classes, or perhaps a common class EventSourceAndSink that implements both, and which is part of your base class model, so that anything that might need to handle events can just derive from it.
I would probably make ProtocolEncoder and ProtocolDecoder classes. You can always combine them into a single object, but it may be valuable, and shouldn't cause any issues if done adequately, to have them be two separate pieces of code. You could also have a ProtocolHelper class that factors out anything in common between the two. The encoders sole job is to receive messages from the network and turn them into events for your game, which it will then pass on to the EventDispatcher. The decoder class will take events from the dispatcher that need to go out to the network, and it will take the data from them and send it out.
How to Get Where You're Going
Since you do have working code, I would recommend that you just start doing it wherever seems natural. That could be things that are bogging you down, or things you've noticed to be very similar in different places that you could make regular with a class or some other type of abstraction, then pull out the old and put in the new. Once you have figured out a workable first cut of a class model, that should give you ideas based on what you already have and as you go be constantly reconsidering your model, fixing the things that are a problem, lather, rinse, repeat.
It doesn't have to be a lot of work, in fact, some of the most gratifying moments I've had working on code was when I was able to do a neat refactor that left a formerly hideous mess in much better shape, removing a lot of hard-to-understand code, and replacing it with something that's easier to understand, in fewer lines of code, and that opened up a path toward my next addition being a pleasure instead of another "zomg I don't have to touch that code again do I?" moment.
Best of luck, follows is a nominal guide to the things I was talking about; the first bit is more detailed because the main event class is one of the more important concepts, and then I try to just give a nominal overview of the classes and how they interact. I'm sure I could spend even more hours on this, but at this point I'll just say: ask me if you have questions and I'll do what I can to give you a good answer :)
Ideas in Code
Oh, one more thing of note is I didn't deal at all with the complexities added if you have multiple threads; there are things ranging from simple to intricate to manage it all if you do, but that's another exercise. :)
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// this is internal to the project namespace, say, TimsWorld_o_Hurt
// I'm now resisting calling everything Xxxx_o_Hurt :)
// examples o' hurt
using EventHandlingLibrary;
namespace EventHandlingLibrary
{
// this will provide the base class for all the events, and can
// also have static methods like factory methods, destination
// lookups etc.
// I have the enums set to protected with the intent being that
// specific factory functions should be called by other classes.
// You should change this if it turns out to be too cumbersome.
public class EventOfHurt
{
#region Event Definitions
protected enum EEventType
{
// System Events
SystemInitializing,
SubsystemInitComplete,
FatalErrorNotification,
SubsystemPingReponse,
SubsystemPingRequest,
// Network Events
FrameRateError,
ThroughputData,
ServerTimeout,
ServerPingRequest,
ServerPingResponse,
// User Events
WeaponsFire,
MovementNotification,
FatigueUpdate
// and so forth
}
protected enum ESubsystem
{
System,
Dispatcher,
TickerTimer,
WorldEntity,
WorldTaskManager,
UserMessageProcessor,
NetworkListener,
NetworkTransmitter,
ProtocolEncoder,
ProtocolDecoder,
PlayerCharacter,
NonPlayerCharacter,
EventSink,
EventSource
// and such
}
#endregion
#region Event Information
public Guid EventId { get; protected set; }
public EEventType EventType { get; protected set; }
public ESubsystem SourceSubsystem { get; protected set; }
public ESubsystem DestSubsystem { get; protected set; }
private List<Tuple<EventOfHurt, DateTime>>
myEventReferences;
// the event(s) that triggered it, if any, and when rec'd
public Tuple<EventOfHurt, DateTime>[]
EventReferences
{
get { return myEventReferences.ToArray(); }
}
public DateTime Timestamp { get; private set; }
#endregion
// we'll be using factor methods to create events
// so keep constructors private; no default constructor
private EventOfHurt(
EEventType evt,
ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher
)
{
EventType = evt;
SourceSubsystem = src;
DestSubsystem = dest;
EventId = Guid.NewGuid();
Timestamp = DateTime.UtcNow;
}
// called to create a non-derived event for simple things;
// but keep other classes limited to calling specific factory
// methods
protected static EventOfHurt CreateGeneric(
EEventType evt, ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher,
Tuple<EventOfHurt, DateTime>[] reasons = null
)
{
EventOfHurt RetVal;
if (dest == null)
dest = ESubsystem.Dispatcher;
List<Tuple<EventOfHurt, DateTime>> ReasonList =
new List<Tuple<EventOfHurt,DateTime>>();
if (reasons != null)
ReasonList.AddRange(reasons);
// the initializer after the constructor allows for a
// lot more flexibility than e.g., optional params
RetVal = new EventOfHurt(evt, src) {
myEventReferences = ReasonList
};
return RetVal;
}
// some of the specific methods can just return a generic
// non-derived event
public static EventOfHurt CreateTickerTimerEvent(
EEventType evt, ESubsystem dest
)
{
ESubsystem src = ESubsystem.TickerTimer;
return CreateGeneric(evt, src, dest, null);
}
// some may return actual derived classes
public static EventOfHurt CreatePlayerActionEvent(
EEventType evt, ESubsystem dest,
Tuple<EventOfHurt, DateTime>[] reasons
)
{
PlayerEvent PE = new PlayerActionEvent(42);
return PE;
}
}
// could have some specific info relevant to player
// events in this class, world location, etc.
public class PlayerEvent :
EventOfHurt
{
};
// and even further speciailzation here, weapon used
// speed, etc.
public class PlayerActionEvent :
PlayerEvent
{
public PlayerActionEvent(int ExtraInfo)
{
}
};
}
namespace EntitiesOfHurt
{
public class LatchedBool
{
private bool myValue = false;
public bool Value
{
get { return myValue; }
set {
if (!myValue)
myValue = value;
}
}
}
public class EventOfHurtArgs :
EventArgs
{
public EventOfHurtArgs(EventOfHurt evt)
{
myDispatchedEvent = evt;
}
private EventOfHurt myDispatchedEvent;
public EventOfHurt DispatchedEvent
{
get { return myDispatchedEvent; }
}
}
public class MultiDispatchEventArgs :
EventOfHurtArgs
{
public MultiDispatchEventArgs(EventOfHurt evt) :
base(evt)
{
}
public LatchedBool isHandled;
}
public interface IEventSink
{
// could do this via methods like this, or by attching to the
// events in a source
void MultiDispatchRecieve(object sender, MultiDispatchEventArgs e);
void EventOfHurt(object sender, EventOfHurtArgs e);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSource(IEventSource evtSource);
void DetachEventSource(IEventSource evtSource);
}
// you could hook things up in your app so that most requests
// go through the Dispatcher
public interface IEventSource
{
// for IEventSinks to map
event EventHandler<MultiDispatchEventArgs> onMultiDispatchEvent;
event EventHandler<EventOfHurtArgs> onEventOfHurt;
void FireEventOfHurt(EventOfHurt newEvent);
void FireMultiDispatchEvent(EventOfHurt newEvent);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSink(IEventSink evtSink);
void DetachEventSink(IEventSink evtSink);
}
// to the extent that it works with your model, I think it likely
// that you'll want to keep the event flow being mainly just
// Dispatcher <---> Others and to minimize except where absolutely
// necessary (e.g., performance) Others <---> Others.
// DON'T FORGET THREAD SAFETY! :)
public class Dispatcher :
IEventSource, IEventSink
{
}
public class ProtocolDecoder :
IEventSource
{
}
public class ProtocolEncoder :
IEventSink
{
}
public class NetworkListener
{
// just have these as members, then you can have the
// functionality of both on the listener, but the
// listener will not send or receive events, it will
// focus on the sockets.
private ProtocolEncoder myEncoder;
private ProtocolDecoder myDecoder;
}
public class TheWorld :
IEventSink, IEventSource
{
}
public class Character
{
}
public class NonPlayerCharacter :
Character,
IEventSource, IEventSink
{
}
public class PlayerCharacter :
Character,
IEventSource, IEventSink
{
}
}