8
\$\begingroup\$

I'm quite new to game development (but not to programming) and I'm trying to figure out what would be the best way to handle inter-world communication. What I mean is this:

I've been reading about entity component systems (ECS) and how people suggest using different worlds/spaces (http://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091) for a subsection of a game. For example a HUD, inventory or combat/movement each get a seperate world/space (because they have different graphics and underlying logic.

However, I was wondering how the inventory, or the HUD knows about the health of a player when the health is handled by a different space/world, for example when in combat?

This also applies to game progression in general, for example dialog with NPC (a dialog would be a separate space since it's a popup screen) but how would you convey the choices made in (or the state of) the dialog to other spaces/worlds. Or basically any other type of event that influence the game progression in different spaces/worlds (health, mana, quests, dialog, combat, inventory, hud, etc)

How would one handle this kind of design? Does it need a (in implementation) singleton object that holds all this kind of information? That would be weird because then the components need to convey each change to this singleton object which feels like doing things twice (going against the main DRY of programming)...

I'm kind of at a loss here in terms of design, any pointers?


---EDIT---

So I've read a few other posts suggested by comments and got a general idea about possibilities, however each of them seems to have one major downside which makes them just not right. It's is very possible that I'm overseeing details that would solve these downsides so feel free to correct me. I'll try to give an overview as well as some answers to some questions.

I'm seeing three major options to 'share' data between spaces. Even though most posts are about sharing data between systems, I feel like the same can be applied to sharing data between systems.

1. Querying

Example: If the HUD world needs to know the current health of the player it can query another world and ask for the current health.

Downside: Worlds need to know about each other which is a major dependency issue and goes against decoupling.

2: Direct messaging (sync and async)

Example: If during combat the health of a player changes it can send messages (sync and async, whatever is needed) to other worlds that need to know about this change.

Downside: Still the decoupling issue: worlds need to know about eachother.

3: Indirect messaging (sync and async) <-- best option

Example: If during combat the health of a player changes it can send messages (sync and async, whatever is needed) to general message hub. Other worlds/systems that need to know about this change are subscribed to the particular message channel and read the messages.

Upside: Completely decoupled, easily manageable and extendable.

Downside/unclear: When does the message channel know that the messages need to be deleted? Or maybe the system that is subscribed marks (only for itself) the message as read and waits for new messages -> messagebox becomes enormous after a while. How do worlds/systems handle order? For example during a frame: if the HUD already polled the health message and after that the health changes, the next frame the HUD gets updated. For some applications this might not be the right way.

Q: A single game object can exists in multiple spaces

I'm using Artemis ECS framework which comes with build-in spaces (called worlds). Each entity (and with it, the data in the form of components) is created on a world and thus cannot be shared between worlds.

\$\endgroup\$
  • \$\begingroup\$ Messaging is the standard approach here: gamedev.stackexchange.com/questions/23834/… \$\endgroup\$ – MichaelHouse May 16 '16 at 15:22
  • \$\begingroup\$ From what I can read in the linked article a single game object can exist under multiple spaces. If you have different graphics or logic between spaces separate the data from graphics and logic. Share the data game object across spaces and aggregate it with different graphics and logic game objects. \$\endgroup\$ – Andreas May 16 '16 at 16:47
  • \$\begingroup\$ This answer I gave about messaging systems may help you out as well: gamedev.stackexchange.com/questions/7718/… \$\endgroup\$ – James May 16 '16 at 23:51
  • \$\begingroup\$ I have implemented all three solutions (querying, direct and indirect) in my gamedev life. And I can say that third option works best for me. You can easily decouple systems and run theirs logic in parallel. The only downside is that you have to do 9 function call to route every single message/event from one system to another. Obviously you can optimize it and the big plus is that you do not need mutexes or singletons in this approach. \$\endgroup\$ – Gregory May 17 '16 at 18:20
  • \$\begingroup\$ @Gregory Thanks for your imput, I expected indirect messages to be the best option. I wasn't aware of the 9 function calls but when planning this messagehub I realised that indeed it would be quite a bit of calls. Did you ever find a good solution/alternative to the deletion of messages when no system needs them anymore? \$\endgroup\$ – Tim May 18 '16 at 9:55
1
\$\begingroup\$

One way to look at it is that you're possibly putting too much into your game objects.

There's no reason that code that actually hooks up the HUD to your in-world game needs to be in a component/system that lives in some particular space. That code will perhaps be better off living in a central manager or global script that has access to all spaces and all objects, and can then interact with the code that knows when to actually create a space and what to put in them (e.g. the code that spawns the player, saves its state between levels, etc.).

You could also just have a "master space" that holds game objects with logic or data that needs to persist past or manipulate the spaces used for levels and UI. That approach is common in engines that force developers to put all scripts/logic onto components/objects (e.g., in Unity, you'd make a global Main object and set it to persist across scene unloading; if Unity actually had spaces, you'd use those instead of the flag).

Remember, abusing your ECS is the same as abusing design patterns; just because you have some nifty new tool doesn't mean that you're supposed to use it to solve every problem you encounter. Evaluate your problem space and select the best-fit solution, even if it's the old dingy thing your ancestors used back in the dark ages of the 90's. :p

\$\endgroup\$
0
\$\begingroup\$

I did create a few prototypes but nothing too big and the way I used to handle multiple spaces was simply create a game object which contains, world, player etc and than I'd setup some properties that are required by some other spaces for example health, in the game object

Whenever called it will get the player's health. that way I could send it to the HUD and display the health bar.

It is not the cleanest but it gets the job done, I did some performance testing back in 2013 and everything seemed to work smoothly. to avoid such dependencies you could always drop the health bar when player health is null.

Usually when player doesn't exist it means that the user is in either a menu or a cut-scene.

Code Example:

public float PlayerHealth {
  get {
    if (player !+ null) 
      return player.Health;
    return -1;
  }
}

Hope this is what you were looking for.

\$\endgroup\$
0
\$\begingroup\$

this is something that I'm actually working on in the past couple of weeks. I'm working on my own ECS library (wanted to do that for experience and just to try it out, cause I've wanted to do so for quite some time).

This is the github link: https://github.com/gioragutt/xna-ecs

For your problem, I've always written a small pubsub library, which you can see here

Basically, I have an EmsClient class, which stuff can derive from. Currently, my components don't do that, but the more higher level classes, although there's not reason not to. I subscribe to Names of messages, and provide a callback with the following signature: Action<JObject>. As you've already understood, I'm using Json Objects as means to transfer messages. I've done this after I've before used just byte[]'s, and I found that I needed something more general, and since I'm used to something like that from my workplace (we have an IPCD that works similarly, except the callback method is always the same, since usually we separate responsibility to different handlers).

There's an EmsServer (one on the server, and one on each client) that's responsible for moving messages between EmsClient on it's realm (EmsServer on the server side moves messages between EmsClients on the server side, vice versa for the client side).

For messaging between the Client and the Server, I created an EmsServerEndpoint which is an EmsClient himself, he just does the logic of buffering the sent messages on it's realm, and flushing them to other realms (F.E the client sends the message to the server, whereas when the server transfers each message to all clients connected.

You can see usage in a lot of places, f.e: ClientGameManager, ServerGameManager.

Whereas, for your example, if I want to add a GUI component for a player, you can look HERE, at the BeginAllocateLocal and BeginAllocateRemote methods, which are responsible for building the GameObjects of the players. Each GameObject contains an Entity (from the ECS lib) and an IComponentContainer (which is too, from the ECS lib). Each GameObject automatically gets a transform (like in Unity, from which I took inspiration).

My code pretty much speaks for itself, and if you get to it, I'm looking for criticism for it, so I would like some constructive criticism :)

Hope that my code would help you with some ideas!

\$\endgroup\$
0
\$\begingroup\$

Consider observer/subject pattern components for your game components and ui components. You plug them together upon creation/load up and then forget about them. If the character's health changes, it notifies all observers, which can do whatever they want with the information.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.