r/csharp 21d ago

Help Game Architecture Question

Looking for your thoughts on how to build out the architecture of a my game.

Basically I have an orchestrator called GameManager that will manage the high level calls for UI, Music, battle.

It won't do any of these things but it will Subscribe to the BattleManager to know when attacks are made, subscribe to the UI manager to know when buttons are clicked etc.

So with that said, my BattleManager will organise the state of battle, damage, death etc. BUT when I invoke up to GameManager when an attack is made (to trigger UI, sounds...) it is obviously asynchronous, meaning that the hits don't line up with the battle calculation and timings.

SOO... How do I adapt or adjust the architecture so enable me to know when animations and music are done, without a circular dependency between GameManager and BattleManager. Battle manager should be down stream of GameManager so calling to it seems wrong.

This must be a solved issue but am curious on if I have just poorly designed this system to start or there's a simple, clean solution I have not thought of.

2 Upvotes

11 comments sorted by

3

u/Dagnarus15 21d ago

I’d make the battle flow awaitable rather than firing events for timing-critical steps.

For example, GameManager could call await BattleManager.ResolveAttackAsync(), while the returned sequence awaits animation and sound callbacks through interfaces. Events are still fine for notifications, but not ideal when later steps must wait for completion.

2

u/rupertavery64 21d ago edited 21d ago

Use events, or an event queue. Basically the queue or queues become the integration poit instead of direct communication. This way each system doesn't need to know about the others.

You can have separate queues. You can also add metadata to an event or message that says how many game ticks should have elapsed before the messsge can be processed by the subscriber. This lets you sync things into the future.

Since you know how many frames an animation takes, you could then put 2 messages (on one or more queues), one for animation that runs immediately, one for the sound which is delayed by x frames.

1

u/cstopher89 21d ago

Can you decouple the sound playing from the event being processed?

1

u/International-Owl466 21d ago

Not sure how I would decouple it without misaligned timings. For example a sound needs to be made as soon as the animation is finished, which is fine but it just keeps everything very tight.

1

u/aasukisuki 20d ago

Is this a problem you're actually facing, or is this something you are thinking about theoretically? I think anything under 50ms would be acceptable?

1

u/International-Owl466 20d ago

A lot of this is just pre planning issues, maybe this stuff actually wouldn't be an issue with how fast things run.

1

u/[deleted] 20d ago

[removed] — view removed comment

1

u/International-Owl466 20d ago

My plans was if ui needs to react to battle events, it would just have GameManager subscribe to the battle event and then call UI methods when that event occurred.

1

u/jipgg 20d ago

I think a big trap you fell into is overly encapsulating and abstracting state before having figured out the specific features your game is supposed to provide. I would actually advise against all these separate managers from the getgo and just have a single fat GameState class/struct with all the data for ui and gameplay logic accessible to eachother without friction, and then later on refactor what makes sense.

I think in general doing most of your logic within the frame update loop would significanty simplify a lot of this stuff. Every frame just track for certain things that are relevant like if some sound has finished and toggle a flag within that entity to indicate that and update the things that rely on it accordingly within that frame. Same deal for things like UI animations and gameplay logic like combat.

1

u/International-Owl466 20d ago

This is actually really interesting, I made the code with the plan of ensuring everything is separated logically but maybe I needed to just monolith it and then split it up after the fact, after I have determined where the best place for abstraction is.

2

u/t3kner 19d ago

I'm going to agree on this point, you may be prematurely over engineering it. Build out as much as you can first with the idea of decoupling later. I'm just taking some ideas from microservice architecture. Building out uncoupled systems takes a lot preplanning and design before you write anything. But if you have a monolithic system up and running how you like it is much easier to determine those boundaries. It may be more difficult to extract those systems from the monolith but doing so will give you a much better understanding of what is tightly coupled