This is going to be a very busy and varied chapter. We will learn the theory of the Singleton design pattern.
We will be introduced to another of the classes of the Java Collections, HashMap
in which we will see how we can more efficiently store and make available the wide variety of bitmaps that are required for this project. We will also get started on our new and improved Transform
class, code the first of the component-based classes, code the all-new Camera
class and make a significant start on some of the more familiar classes, GameState
, PhysicsEngine
, Renderer
, GameEngine
and more besides.
Here is a list of what to expect and the order to expect it.
BitmapStore
Transform
GameObjectFactory
GameState
SoundEngine
PhysicsEngine
Renderer
Camera
classHUD
UIController
Activity
GameEngine
LevelManger
By the end of this chapter we will see the game in its first runnable state!
Throughout the course of this chapter there will be loads of errors in Android Studio because so many of the classes are interconnected and we can't code them all simultaneously. Note also that when there is an error in an interface file (and there will be) attempting to implement that interface will also cause an error in the implementing class. Stick with it until the end of this chapter as it all comes together, and we will see the first results on our screens. Be sure to read the information box at the top of chapter 22 as well as the one below for the quicker copy and paste approach you might like to take.
If you want to copy and paste the code read this next information box.
Feel free to copy and paste these classes if you prefer. All classes go in the usual folder except where I specifically point it out (for Level
based classes). Be aware that my domain name is used in all the package names in the download bundle. The first package declaration in all the files will probably auto-update to your package name when you paste them into your project. However, when the import
code refers to the packages that we have created ...GOSpec
(in the previous chapter) and …Level
(later in this chapter) you will probably need to change the domain name and possibly the project name manually in some or maybe all of the files that import
...GOSpec
and …Level
.
If you want to type everything then you will just need to add a new class of the correct name by right-clicking the appropriate folder and selecting New | Java Class.
Let's talk about another pattern.
The Singleton pattern is as the name suggests is a pattern that is used when we want only one of something. And more importantly, that we need to absolutely guarantee we only have one of something. The something I refer to is an instance of a class. Furthermore, the Singleton pattern is also used when we want to allow global access to some of its data or methods. The class can then be used from anywhere within a project and yet is also guaranteeing that all parts/classes of the project that access the class are using the exact same instance.
Part of the Singleton conundrum is simple. To make parts of it available to any other class you simply make the methods public
and static
.
See Chapter 8: Object-Oriented Programming for a reminder about static
variables.
But how do we guarantee that only one instance can ever be created? We will look at the code next but as a look-ahead what we will do is create a class which has a private
constructor. Remember that a private
method can only be called from within the class itself. And a public
and static
method that creates an instance then returns a reference- provided an instance has not already been created- and if it has then a reference to the existing instance is returned. This implies that the class will hold a reference to its own instance. Let's look at this with some sample code.
Here is the class declaration and it contains just one variable. A private
and static
instance of an object that is the same type as the class itself.
class SingletonClass { private static SingletonClass mOurInstance; }
The name of the class is not relevant just that the name of the class is the same as the type being declared.
Next, we can look at the constructor method.
private SingletonClass() {
// This is only here to prevent instantiation
}
It has the same name as the class as do all constructor methods but note it is private
. When a method is private it can only be called by code within the class. Note that depending upon the needs of the project, it is perfectly acceptable to have some code in the constructor, just as long as the constructor is private
.
This next method is default access and returns an object of type SingletonClass
. Take a look over the code.
// Calling this method is the only way to get a SingletonClass static SingletonClass getInstance(Context context) { if(mOurInstance == null) { mOurInstance = new SingletonClass(); } return mOurInstance; }
As the class is default access, any code in the same package can call the method. In addition, because it is static
, it can be called without an instance of the class. Now look at the code in the body. The first line checks whether the private
member, mOurInstance
has been initialized with if(mOurInstance == null)
. If the object has not been initialized (is null
) then the private
constructor is called, and the instance is initialized. The final line of code returns a reference to the private
instance. Also note as will be the case in the platform game, it might not even be necessary to return an instance.
Now we add a method that actually does something. After all, a class must have a purpose.
static void someUsefulMethod() { // Do something useful }
Note the previous method, someUsefulMethod
is static
so it too, like getInstance
can be called without a reference to the class. This is why I said the getInstance
method doesn't necessarily have to return an instance of the class.
Next, we add another method. Note that it is not static
.
void someOtherUsefulMethod() { // Do something else useful }
As someOtherUsefulMethod
is not static
, an instance of the class would be needed in order to call it. So, in this specific case getInstance
would need to return a reference to the object.
Now let's see how we could use our new class in some other class of our project. First, we declare an instance of SingletonClass
but as we cannot call the constructor we initialize it by calling the static getInstance
method.
SingletonClass sc = SingletonClass.getInstance();
The getInstance
method will check whether the member SingletonClass
object needs to be initialized and if required calls the constructor. Either way, getInstance
returns a reference to the private
member instance of SingletonClass
which initializes sc
.
Now we can use the two methods that actually do something useful.
// Call the static method SingletonClass.someUsefulMethod(); // Call the other method sc.someOtherUsefulMethod();
The first method, someUsefulMethod
is called without using a reference because it is static
. The second method is called using the sc
reference we initialized using getInstance
.
It might surprise you to learn that the Singleton pattern is controversial. It's very use is discouraged even banned in some situations. There are a number of reasons for this but in one-person or small-team projects many of the objections are either not relevant or much less relevant. Android Studio even has a way to auto-generate a singleton. If you are interested in a discussion about the use of the Singleton pattern then a quick Google will bring up strong condemnations, spirited defences and even heated arguments about its use. I suggest you read this article because it gives a fairly balanced view on Singleton and from a game development perspective. http://gameprogrammingpatterns.com/singleton.html. Note that the article discusses Singleton in the context of a different programming language, C++ but the discussion is still useful.
Next, we will learn about the Java HashMap
class and then we will get to code a Singleton for real.
3.14.135.107