In my last post, I talked about the type of architecture I wanted to approach the game with. Next up is to explain how exactly I am going to implement parts of it. Again, most of these concepts are ones I took from Game Programming Complete, 3rd edition. I really recommend all beginners to buy this book. Though my lack of C++ has stopped me from benefiting from the provided source code, the extensive explanations and excellent presentation has really taught me a lot, and the concepts not thoroughly explained, are have explanations abound on the internet.
Threading
As you’ve probably noticed in the previous post, I am putting a heavy emphasis on distinct separation between components of the game. In Tetris, I followed the default methods set out for me by XNA upon creating a project. Throw all your updating in one method, all your drawing in another etc. While this was a very easy concept to grasp conceptually, it was horrible from a code perspective. It became hard to track the progress of the code as the game runs.
It’s much simpler to divide up the game into different Objects. All that would be required to track the progress of the game would be to watch the state of specific objects as the game runs. I decided to think of all these objects as a game within your game. They will all run independently and communicate with each when required. If any of the other objects decided that they didn’t want to play anymore and just sat there, all the other objects could go about their merry way. Though they might require communication between that non-responsive object, they will still function nonetheless.
Another feature of threading that I found immediately useful while reading up on it is the ability to freeze a thread while it calls another thread and waits for it to finish. I immediately thought back to how overcomplicated my pause menu system was in Tetris. A simple thread to interrupt the main game code would have sufficed.
To summarize, threading allows for me to separate parts of my code for ease of access, it allows for me to really push this type of architecture and see where it can take me. And lastly, threading’s a topic I would have to deal with a t some point or another if I want to be an avid coder, might as well throw myself into the heat of it, best way to learn.
Actor System
So after all that talk about separating game and player, how do we actually go about doing it? First, I concentrated on how graphics would work in this game. Based on the previously explained architecture, it was easy to decide that the player Actor would be responsible for printing the players own character onto the screen. But then I realized, what if I wanted to put local multiplayer in the game (I definitely do). Then this becomes more complicated. The two players could draw their own players sure, but who would draw the enemies? The game background? I decided that I would make the two Users objects that belong to a higher actor class. This higher class would be responsible for managing the two users. Two would be the maximum number of players I’m going to cap it at. This type of system could extend to as many players as needed. One thing to note about XNA is that game performance is heavily dependent on how many times you call spriteBatch.Begin(). Ideally, you only want this called in one place. So it would be prudent to leave all the graphics code inside the higher actor class, rather than distributing it out amongst the players. All players will receive exactly the same view and audio regardless of its implemented anyway, since this is a single screen local multiplayer.
Another dilemma was whether to put the viewport properties inside the Actor class, or to put it in the Game logic. On one hand, it’s readily obvious that the viewport directly affects what an actor see, so obviously it belongs to an actor individually. But on the other hand, perhaps more important hand, the viewport is actual a critical part of the gameplay design. When I say viewport, I mean the section of the map that is currently accessible to players and what is displayed on the screen. While it is true that the viewport is the basis for which the graphics processes depend, the viewport is also independent of the player. It moves through the level at a constant pace, in fact the viewport affects what the player can and cannot do. It is more of a gameplay mechanic than a graphics property.
Another thing that the viewport dilemma brought to my attention would be whether or not I want to allow multiple resolutions of my game as an option to the player. Though I may choose not to implement such a feature, it would be prudent to keep the viewport data in such a way that different players playing on network multiplayer can each view the same game on different resolutions. While this is also definitely not going to be implemented in the game, it never hurts to think about best practice when coding.
Messaging system
Since the game is designed to keep the Actors separate, the Actors must have a way to communicate, to this end, I decided to use event driven programming. In this way, actors only communicate with other actors when they decide that there is an event of significance that the recipient should know about. Such events in this game might be that the user pressed a movement key. The player actor is the one who detects that this has occurred, through polling of the keyboard state. Once this event has been detected, a message object is created, containing details of the event and perhaps what actions might be taken. A method from the recipient actor takes this object in as a parameter and adds it to an appropriate queue in the recipient object. This is set up for a central event handler in the recipient object. The event handler thread will process the event queue as the events line up and decide where to send this message object so that it can be dealt with.
The use of a queue is important because of the heavy use of threading in this game. The threads will not all run at the same time, so consequently the event handler in the central object may not be able to run fast enough to handle all the incoming event real-time. As a failsafe, if the queue really starts growing, a second event handler thread could be created whose entire purpose is to fast track through the queue to enable the event handler to catch up to the point where gametime matches real time.
No comments:
Post a Comment