Game Engine Architecture, Separation of Application, Game Logic,& GameView, to use or not to use EventManager?

Started by
10 comments, last by L. Spiro 11 years, 7 months ago
I have been studying up on the best ways to implement good game architecture, and I came across this in an awesome book, Game Coding Complete. What you do is separate the Game Logic (Physics, Collisions, World States, Actor States, etc.), and GameView (it takes the information passed to it and renders).

The separation of Application simply means that if you were to port from os to os, your GameLogic should run with no changes necessary. If you decided to switch your graphics API, say from DirectX to OpenGl, You again need not change the GameLogic.

The Game Logic is your game, a simulation that would run on and on, and is completely self contained. The states of actors would be changed via messages that are sent from the input translator(translates button presses into commands to manipulate game state) So the logic takes the messages, applies them by the rules of the game world, and then continues to run. Every tick, or cycle through an update, it would then output the state of the world and the actors to the MasterGameView.

The MasterGameView contains all of the game-views (you'll see why there is multiple), and updates them appropriately with the GameState Info that the GameLogic sent it.

The UserGameView would then proceed to render the GameState, play audio, etc.. It would also take in the UserInput and send it to a translator (which again translates it into commands that the GameLogic can use), and send it to the GameLogic.

The AIGameView would receive the GameState and calculate its actions. It would then send it's AIInput to the translator, which would process it and send it as commands to the GameLogic.

You can create any number of UserGameViews, and AIGameViews. So each Actor could have its own AIGameView. So I Suppose the GameLogic could tell the MasterGameView to create more GameViews, or delete them as needed.

The GameViews would each have a unique ID, this ID would connect it to the actor it is running the AI for.

(I think I am following this right, but any input would be much appreciated.)

The big issue I came to is whether to process the input into game commands and apply them immediately? (Here is my take on the design) [attachment=11269:Engine.jpg]

Or should I process the Input into events, and then when updating the State of the actors, ask the Event Manager to see if there was any new commands for my actor. (Here is my take on the design)[attachment=11270:Engine_2.jpg]

I am leaning towards the Event Based one, simply because I only update the objects once, and I think it would be easier to add a more powerful way for the server to send input into the GameLogic.

Any help, insights, or opinions?

I've made some simpler games and released them to android, and they didn't require the complexity of what I would like to tackle now.
I am a CS student and am trying to teach myself about game programming/architecture/design, while building a nice portfolio for when I start sending intern/job applications.

EDIT:
My goal here isn't to build an engine per-say , It is to build an awesome game that I have in mind. This Game will require decent AI, Multiplayer, and decent but not over complicated physics & collision detection. The thing I want to do more than anything else, is make this so that it is easy to continue to add to it for as long as I want, so that even when I have game play down, I can continue to add more skills, power ups, different AI's, characters etc. I want to be able to take this small scale game, and create Huge depth and polish. This just seems like a good place to start as far as designing the architecture of it, and mapping out how the game will communicate with itself/opperate.

Before I have always used Java, OpenGL, and Android, now I am using C++, Direct X, and Windows.
Advertisement
The event-based system is necessary as far as input goes. You can’t update player states at random times within a game loop—that does not play weill with physics, rendering, etc.

On the start of a logical update, read input (or in a general sense, process all events, which will include input), update player states accordingly, update AI states, run physics, poll and process any triggers you may have, and then update sound.
Since rendering is not coupled with logic that is not included here, but a full cycle would perform the logical cycle first and then render.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Thanks!

Yeah I was planning on doing it all in this order

while(gameIsRunning) {
State = Game.UpdateLogic(deltaTime, Input);
Input = MasterGameView.UpdateViews(deltaTime, State);
}

The Logic will process in the following order:

1 Convert the User and AI and Server Inputs to Events
2 Update the world and all the Actors (grabbing their respective events if they are present, and running what needs to be ran against physics/collision detection)
3 Return a GameState pointer to the main loop

Then the GameView Will Process in the order:

1 Send network GameView info to server (store Input)
2 Process AIGameView and update AI (Store the input from AI)
3 Render the UserGameView (Store Input)
4 Return collective input to be sent back to the GameLogic for the next cycle

That is my basic idea anyways.
The reason I have AI and Network update on the GameView side of things, is because you send the AI the GameState, so the AI can measure it, and then respond by sending the same command input that would have been available to any human. You also send the server GameState Info, and receive input, albeit sometimes overriding or special input to keep the games linked and on track, and to make sure no hacking is going on.
Definitely do not link views with network stuff. They are for showing game entities and stuff but nothing else, except for technically listening to player input maybe.

Yeah I was planning on doing it all in this order

while(gameIsRunning) {
State = Game.UpdateLogic(deltaTime, Input);
Input = MasterGameView.UpdateViews(deltaTime, State);
}

I hope your actual plans are not this simple.
Logic should only be updated if a given amount of time has passed. Rendering should happen every cycle.
I provide details and implementations here.


Engines/games that are overly dependent on event- and message- based systems are bound to fail.
Events and messages are good for certain aspects of engine design, but when used to drive the whole game, things quickly become an unmanageable mess.

The Nintendo DS SDK shipped with a sample Yoshi game which was based off this concept.
You would create a task, set a priority on it, register it with the task manager, and forget about it.
The system was design so that you could plug your tasks into the system and let the engine take over instead of trying to manage them yourself. But the problem was that instead of having to manage each task yourself, you have to manage a list of priorities. When you wanted to add a new task you would have to look back through all the task priorities and figure out what priority to set to your new task to make it execute with the right timing. It quickly became task-spaghetti and was a big pain to debug. Easy to find out where the task is executed, but difficult to breakpoint on the specific task you want to study, and it difficult to find out what created it and with what parameters. And since you can’t predict ahead of time how many tasks you will want to register, you may run out of priorities and have to move a whole bunch up or down, careful not to disturb the delicate balance of task priorities.

Deciding on task priorities is non-trivial when working in a team, since every member has to be fully on the same page at all times, and 1 or 2 years later it can be guaranteed that at least some, if not all team members, have forgotten all of the priorities they had assigned to tasks, making long-term maintainability virtually impossible.
Not to mentioned that what other programmers have done is completely transparent to the rest of us. My coworker may have slipped a priority in between 99 and 100, causing him to move a slew of task priorities down to make room, but unless I was fully tracking the priority list I wouldn’t have been aware of that when I added a task at priority 99 and screwed everything up.
The point of the system is to reduce to amount of code that you need to manage yourself, but frankly I would rather be looking at a plate of code that we are managing ourselves where it is clear what is happening at any time in any game state than to just send my task off into oblivion with a given priority that I hope mixes well with what everyone else the team was thinking.

Events and messages suffer from these same problems. If you are not careful, you will end up with all these managers bouncing events and messages back and forth and you won’t be able to debug it, you won’t be able to keep track of all the types of events and messages you have added and avoid conflicts, you won’t remember the reasons behind choices you made years ago because they are all just numbers, etc.


And finally, making bots that move via the same input mechanisms as humans is not the way to go.
As an AI, your first step is to determine where you want to go. This part is generally very easy. Then you have to determine how to get there.
By using the same inputs as humans, you have to reverse-engineer the input system to figure out exactly what to hit to get the desired result, and this can be non-trivial if your input system includes acceleration over a duration of button-holding and other factors.
Then the fact that your input system should always be changing as you play-test and tune your gameplay. If you don’t refine it over time, you are doing it wrong.

It is much simpler to just give the bots their own ways to generate the impulses that will guide them to their desired destinations.

The system you proposed to send input packets back out from the AI’s is overly complex and unnecessary.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I largely agree with L.Spiro; be wary of relying too much on message/event passing. A warning sign is if you find yourself writing lots of manual marshalling/demarshalling code for the parameters contained within the events. Instead I'd recommend defining interfaces, for example IControllable for actors that can be controlled via AI or user input. You might think events are better for avoiding too tight coupling between classes, but in the end you still need understanding of the object that's at the receiving end, so if control happens through an explicit interface, you'll have much easier time debugging, and the compiler can also help you better.

I believe the concepts (like logic and rendering separation) presented by Game Coding Complete are valid, but it should make it more clear that the implementation in the book isn't the only way to do it. Particularly, if it leads beginning game programmers to believe that using messages/events everywhere is essential, it's really doing a disservice.

I've also seen some game engines define their whole public API through messages, for example a Light would respond to a SetLightColor message which in turn would call the internal SetLightColor member function. This can be somewhat justified as it makes it easier to multithread via a thread-safe message queue, but is still IMO misguided as typically the bottlenecks where multithreading helps lie in the mass processing of low-level tasks (such as culling and animation), instead of high-level API calls.

[quote name='rebuke' timestamp='1347687605' post='4980295']
Yeah I was planning on doing it all in this order

while(gameIsRunning) {
State = Game.UpdateLogic(deltaTime, Input);
Input = MasterGameView.UpdateViews(deltaTime, State);
}

I hope your actual plans are not this simple.
Logic should only be updated if a given amount of time has passed. Rendering should happen every cycle.



And finally, making bots that move via the same input mechanisms as humans is not the way to go.
As an AI, your first step is to determine where you want to go. This part is generally very easy. Then you have to determine how to get there.
By using the same inputs as humans, you have to reverse-engineer the input system to figure out exactly what to hit to get the desired result, and this can be non-trivial if your input system includes acceleration over a duration of button-holding and other factors.
Then the fact that your input system should always be changing as you play-test and tune your gameplay. If you don’t refine it over time, you are doing it wrong.

It is much simpler to just give the bots their own ways to generate the impulses that will guide them to their desired destinations.

The system you proposed to send input packets back out from the AI’s is overly complex and unnecessary.


L. Spiro
[/quote]

they are not that simple, that was just a simple as I could present them. While positions and such must be updated along with drawing everything every frame, other things will be updated at separate intervals.
Also thanks for the warning about tasks/events.

As for the AI system, I think I may have misrepresented it, While a human would pull the right trigger to fire, that could have also been done by mashing the "A" button, or the left trigger, or whatever key the "Fire" Command is mapped to. The system I am proposing has a Series of commands that a player can do, such as "Fire" "Jump" "Move Forward" "Turn Left/Right" "Strafe Left/Right", and each of these commands would be mapped to the specified controls that a player could press. The AI does not send its input in as a press of the "A" button, it sends its input in as a command, for example, "Turn Left" "Move Forward" "Fire". The Human Player's input is mapped/converted to the commands.

The way I see this thing working in my head, seems like the simplest thing ever, it all makes complete sense. Perhaps after I am done I will have to share it with everyone :)
Nice diagram, thought i would make a note. In your diagram you separate out the game world and game logic apart but in reality the separation isn't that clean cut. Game logic affects and shapes the game world at all levels. Would u classify logic which determines where plants will grow to be part of the game world or the game logic? What if the user affected the plant growth based upon their positon? Where does the demarcation lie between what is game logic and game world?

In my experience the game logic is a subset of the game world, but that might depend on your game.

Good Luck!

-ddn
Good Points. I was thinking the entire left side is all part of the game logic. The "Game Logic" box is more of a control to keep everything flowing in order. It just runs the game simulation and all the changes. The right side of the diagram can not change the game directly, it can only send commands. The game logic is where everything changes.

Events and messages suffer from these same problems. If you are not careful, you will end up with all these managers bouncing events and messages back and forth and you won’t be able to debug it, you won’t be able to keep track of all the types of events and messages you have added and avoid conflicts, you won’t remember the reasons behind choices you made years ago because they are all just numbers, etc.


I appreciate what you are saying here, but at the same time, you don't offer any alternatives. Games are a complex web of events no matter how you cut it. You can't ever make a game, especially a big one, with perfectly clean logic that will be obvious when you pick it up again in a year. In fact, I don't think you can write ANY program without sometimes forgetting why you did something you coded a year ago. You just have to write good comments, and document your event system so that if you or a new person are looking at it later, you know what's going on.

I will say though, if you have an alternative to an event-based game engine, I would love to hear it because it could only serve to make me a better programmer.

This topic is closed to new replies.

Advertisement