Saturday, February 15, 2014

Cross Platform Game Engine Series - Basic Objects

Why C#
One of the first decisions before starting to program a game engine is always what programming language and technology to use in the creation of that engine.
An easy way of determining what language to choose is to list the platforms the engine is intended to support and the programming languages that support them. The decision then comes down to which language supports the largest number of target platforms. If there is a tie, the developer might want to consider only going with languages that are supported natively – without the need for 3rd party code.
Native support is important because relying on 3rd party code can put in a bad position if the 3rd party choose to stop supporting the platform.
Our ideal PlayStation Vita engine, as described in the previous post would have a diagram as follows:

Windows
Mac
Android
iOS
PSVita Mobile
C#
OpenTK1 or SharpDX2
Mono3 or Xamarin.Mac4
Xamarin.Android5
Xamarin.iOS6
Native7
C/C++
Native8
Native9
Android NDK10
Native9
No7
Obj-C
Yes11
Native12
Apportable13
Native12
No7
Java
Yes
Yes
Native14
No
No7

Clearly, C# is the only language that meets our requirements – mostly because the PlayStations Mobile developer program only supports C#. With the language for the engine chosen, let’s continue to one of the most important parts of a game – its game loop.
Game Loop
Most software only responds to user input - meaning that a program simply sits idle until the user provides some input, but games need to run continuously regardless of what the user does.
The first requirement of a game loop, running continuously, is easily accomplished with the following:
  1 while(GameRunning) { /* Do Game Logic */ }
But this loop would quickly crash the system – since it never gives control back to the system. A good example of why passing control back to the system is important can be found in the MSDN documentation15 :
“Unlike MS-DOS-based applications, Windows-based applications are event-driven. They do not make explicit function calls (such as C run-time library calls) to obtain input. Instead, they wait for the system to pass input to them.”
“If a top-level window stops responding to messages for more than several seconds, the system considers the window to be not responding.”
Unfortunately, the code to pass control back to the system is heavily platform dependent. This is where the Platform class comes in. The Platform class, that I will discuss shortly, will allow the engine to pass control back to the system in a platform independent manner.
  1 while(GameRunning) 
  2 { 
  3     Platform.ListenForEvents(); 
  4     // Do Game Logic 
  5 }
So the next step is to actually put some game logic in the loop. Traditionally, this logic is rendering scenes, updating the scene, updating physics, and handling input. So the loop becomes:
  1 while(GameRunning) 
  2 { 
  3     Platform.ListenForEvents(); 
  4     RenderScene(); 
  5     UpdateScene(); 
  6     UpdatePhysics(); 
  7     HandleInput(); 
  8 }
This loop still has problems – as every piece of game logic runs every iteration. This is unnecessary as not every system needs to be updated every frame - a common example may be that the physics are updated 30 times a second, the scene is updated 15 times a second, and input and rendering are updated as fast as the computer can.
A simple solution for this is to use a Regulator which is an object that will determine when specific code can be ran. This changes the loop into a more complicated, but still understandable, form:
  1 Regulator sceneRegulator = new Regulator(15.0); 
  2 Regulator physicsRegulator = new Regulator(30.0); 
  3 while(GameRunning) 
  4 { 
  5     Platform.ListenForEvents(); 
  6     RenderScene(); 
  7     HandleInput(); 
  8     if(sceneRegulator.IsReady()) { UpdateScene(); } 
  9     if(physicsRegulator.IsReady()) { UpdatePhysics(); } 
 10 }
Note: Physics really shouldn't be updated in this matter – but I will address that topic in a separate post later. For a quick discussion on how physics should be implemented view Glenn Fielder’s Fix Your Timestep Article16
Now the game has a main loop that updates different systems at different rates! I will now discuss some of the classes used to make this loop possible.
Platform Class
The platform class is responsible for hiding platform specific code from the rest of the engine’s code base. Its definition is as follows:

As seen above the platform class uses the partial17 and static18 keywords. The static keyword allows the Platform class methods to be used anywhere inside the engine without requiring an instance, and the partial keyword allows the Platform class to split up into multiple files. One downside of the partial method is that, as stated in the partial methods documentation17, they can only be private and cannot return values.
Regulators
The regulators used to control when specific code sections should update are heavily inspired by the Regulators used in Programming Game AI by Example by Matt Buckland on page 329.

As seen above the regulator needs to generate a random number, and also needs to keep track of the time.
Random Number Generation
Generating a random number is a very common method needed in game development so it makes sense to have a single common source of random numbers. The implementation used in this engine is based on a MSDN article19 ensuring thread safe random numbers.

Time
Getting the time that has passed since the game started is also a very common method required in game development. This is handled by the following static Time class:

In addition the engine has access to the following Timer class that allows monitoring any time frame desired.

Memory Management
As seen above, many of classes derive from an ObjectBase class. This class is there to help with XML serialization and memory management. XML serialization will be discussed later – but memory management is very important to games. In games performance matters a lot and poor memory management can negatively impact that performance.
For example, imagine that you just unloaded a level of the game. C# uses Automatic Memory Management20 so it could be a while until the level is actually unloaded from memory. By using IDisposable the engine can provide a common method to inform the level that it should prepare to be unloaded – the memory still won’t be free but it will signal to the garbage collector that the memory can be freed.
This leads to the ObjectBase implementation inspired by this Stack Exchange post21:

Conclusion
This blog series will continue next week with Cross Platform Game Engine Series – File IO.
References
  1. http://www.opentk.com/
  2. http://sharpdx.org/
  3. http://www.mono-project.com/Main_Page
  4. https://xamarin.com/mac
  5. http://xamarin.com/android
  6. http://xamarin.com/ios
  7. https://en-support.psm.playstation.net/app/answers/detail/a_id/147
  8. http://msdn.microsoft.com/library/windows/apps/hh452744.aspx
  9. https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/ObjectiveC.html
  10. https://developer.android.com/tools/sdk/ndk/index.html
  11. http://stackoverflow.com/questions/56708/objective-c-for-windows
  12. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html
  13. http://www.apportable.com/
  14. http://developer.android.com/tools/index.html
  15. http://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx
  16. http://gafferongames.com/game-physics/fix-your-timestep
  17. http://msdn.microsoft.com/en-us/library/6b0scde8.asp
  18. http://msdn.microsoft.com/en-us/library/79b3xss3.aspx
  19. http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx
  20. http://msdn.microsoft.com/en-us/library/aa691138(v=vs.71).aspx
  21. http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238%20

No comments:

Post a Comment