This tutorial game project introduces the concept of a viewport. This is the aspect of our game which handles which part of the game-world is drawn to the screen. First, we need to decide what to draw and then we must convert their “real-world” coordinates to the screen coordinates at which to draw them. If we accept that most games we make will have areas which cannot possibly be squeezed onto the player’s phone or tablet, in one go, then a viewport is essential. If you write games using OpenGL then this viewport idea is handled for you by OpenGL. However, the code for implementing a simple viewport is much simpler than handling the additional overhead required to get started with OpenGL. Therefore, it is often worthwhile building your own viewport.

patreon

 

 

About this project

Skill level 1
Time to complete 1 hour

New Concepts:

  1. A viewport to control where the game “camera” is looking
  2. Scrolling game worlds that are bigger than the player’s screen (i.e. most games you will make)
  3. A look at all the other exciting things you can do with a viewport
  4. Build a simple (but more advanced than previous projects) control pad
  5. Introduction to the problem of a low frame rate

Projects that demonstrate these concepts

Before we get started with the project let’s dig a little deeper into this viewport idea.

What exactly is a viewport?

You can think of a viewport more intuitively if you think about it as a camera that moves to keep the most relevant part of the game world in focus. The most relevant part, in most games, is the area around the player. Therefore, most viewports track the player as its center. This is what we will do in this project. It is more than this, however, because it can also be used to clip, and remove from processing, certain game objects that we deem unnecessary to update in a given frame. This is something OpenGL doesn’t do.

About the game

The game will be fully runnable and playable code but won’t be a fully-structured game. You can rotate and thrust your spaceship above and around a randomly generated city full of skyscrapers. You can shoot your rapid-fire gun at the skyscrapers and destroy/set fire to them. There isn’t any objective to the game or menu screen. The code was too long and sprawling to add all these features. If you have read the other Android game projects, you can easily add your own enemies etc.

The game world will be “fenced in” by a crude barrier because I couldn’t think of anything more exciting that is also very simple. The game won’t actually have any enemies or score etc, just the scrolling world, burning buildings etc. The main point was to demonstrate the viewport.

Just for fun, we will construct all the buildings with tiny “bricks”. This will demonstrate two things. Firstly, the viewport implementation can not only handle hundreds of objects but, actually, is more efficient the higher the number of game objects. The fun comes because we can destroy and set fire to, individual sections of our buildings. This will allow the player’s spaceship to shoot and even fly through damaged buildings. In this project, we will, however, see that drawing and processing will eventually begin to slow down our game loop. We will talk about solutions when we have built the game.

Starting and planning a 2D scrolling shooter project

Create a new project in Android Studio, use the Empty Activity template, and call it Scrolling Shooter 2D. Leave the rest of the settings at their defaults.

 

First I will show you the Viewport class and go into some detail because that is the class that does all the cool stuff.

Next, we will quickly see the code for all the game object classes (Ship, Bullet, Brick, and Star)so you can copy & paste it. I won’t go into much depth because there is little new from previous projects.

Then we will quickly code MainActivity. The last thing we will code is the GameView class which draws, updates, and uses the Viewport. Much of this code I will just leave comments to remind you what it does but I will go into the ins and outs of the control buttons(HUD) and, of course, the viewport bits.

Finally, in this article, we will discuss more exciting things you can do with a viewport and take a look at what would be a good way to develop your Android game development from here.

Coding the Viewport class

As we have already discussed, a viewport can be thought of as the movie camera that follows the action of our game. It defines the area of the game world that is to be shown to the player. Ours will center on the triangle-shaped spaceship.

It also serves the combined function of making our update method more efficient by flagging objects outside of the player’s field of vision(the viewport). There is no point in processing a mountain of Brick instances in each frame if they are not relevant in that frame. Of course, if your game wants the player to shoot and destroy things that are off-screen then you have to make a tactical decision about what to exclude. The point is, the viewport gives us the choice. This will significantly speed up tasks like collision detection by implementing the first phase of detection by removing objects off-screen from the list of objects to check for collisions.
[widgets_on_pages id=”udemy_advert_java_2″]
Furthermore, our Viewport class will have the task of translating game world coordinates into appropriate pixel coordinates for drawing on the screen. The Viewport class really is an all-singing and dancing thing. So let’s get coding.

First, we will declare a whole bunch of useful variables. We have a PointF which will just be used to represent whatever point in the world is currently the central focus in the viewport. Then we have separate int values for pixelsPerMetreX and pixelsPerMetreY.

This point is key! Every game object will have dimensions and a position. Unlike every other Android project(on this site) that came before this one, these dimensions and positions no longer relate to a coordinate on the screen but instead, now relate to a coordinate in the world! That is why we have all these variables with names containing ...metres... etc. Of course, if you sit back and work out the size in meters of any of the game objects, they are tiny compared to say a real skyscraper. You could change the units and therefore the variable names to something like pixelsPerWorldUnit,. I thought meters made the distinction between the game world we are simulating and the screen’s pixels more clear and distinct.

In addition, we also have the resolution of the screen in both axes. The variables   screenXResolution and screenYResolution are passed in when the constructor is called. We then have screenCentreX and screenCentreY which are basically the two previous variables divided by 2 to find the middle. Next in our list of declared variables we have metresToShowX and metresToShowY which will be the number of meters we will squash into our viewport. Changing these values will show more or less of the game world on screen.

Create a new class called Viewport and declare the variables and code the constructor.

Note that in a more advanced implementation, you could dynamically choose metresToShow... values based on things like resolution, screen size, and the ratio of width to height of the screen. In addition, the amount you divide screenResolutionX and ...Y by determining the number of virtual meters displayed on the screen. Adjusting this to get good results on your screen might be necessary and setting them up dynamically based on the specifications of the screen would be appropriate in a real game.

Next, we have a simple method setWorldCentre that does as the name suggests. The value held in this PointF each frame will be significant in every single calculation the Viewport class makes. Our game loop will call each and every frame(at the beginning) based on the position of the spaceship. Add the setWorldCentre method.
[widgets_on_pages id=”udemy_advert_java_3″][widgets_on_pages id=”udemy_code_details”]

And now we fulfill one of the primary roles of the Viewport class with the worldToScreen method. As the name suggests this is the method that converts the locations of all the objects currently in the visible viewport from world coordinates to pixel coordinates that can actually be drawn to the screen. It returns our previously prepared rectToDraw object as the result.

This is how worldToScreen works. It receives the horizontal and vertical world locations of an object along with that object’s width and height. With these values, each in turn, it subtracts the objects world coordinate multiplied by the pixels per meter for the current screen from the appropriate current world viewport center (x or y) . Then, for the left and top coordinates of the object, the result is subtracted from the pixel screen center value and for the bottom and right coordinates it is added. These values are then packed into the correct variable ( left, top, right and bottom ) of convertedRect and returned to the draw method of GameView. This simple calculation gives the screen pixel coordinates to draw the object.

Note that the code has not only calculated the screen coordinates but has also scaled the object in size(up or down as required).

Add the worldToScreen method to the Viewport class.

This next method does exactly the same as the last but with a single point. The spaceship points will be converted using this method. If you’re wondering how the scaling would work on a single point then consider that we will be drawing lines between the three points in the draw method. Therefore, just by moving the points into position the scaling has occurred.

This next method examines each point of a rectangle (in our case a Brick instance) and if its world position is far enough away (in metres, not pixels) from the centre of the viewport true is returned and the object being acted upon will clip itself internally, thereby excluding itself from the next round of update calls. We will see the simple way this occurs when we code the game loop.

More fun than coding the class is seeing it in action.

Coding the game object classes

These next classes are only described briefly because they are well commented and if you have completed the prerequisite projects and tutorials you will already understand much of the code.

Ship

Create a new class called Ship and add the following code. Be sure to read the comments as the code is not discussed. Almost the exact same code is fully explained in the series on calculating heading and the project, Android rotation and heading demo.

Bullet

Create a new class called Bullet and add the following code.

The Bullet class is even simpler than the Space Invaders’ Bullet class. The constructor sets it up and the update method moves the bullet each frame. The class also has a method so that the main game loop can tell whether or not a particular instance is currently active. If the math in the shoot method looks a bit weird then it is explained in the tutorial series that starts with lCalculating heading.

Star

In Android Studio, create a new class called Star and add the following code. All this class does is hold a single point which is randomly generated. The update method then infrequently and randomly switches on and off which creates a twinkling effect when viewed with hundreds of Star instances.

Brick

Add a class called Brick to the project. Brick is simple but there are some things in here that other tutorials(on this site) haven’t covered. In the constructor, as well as positioning the current brick relative to the previous brick and deciding whether it is an edge brick and therefore needs an edge line drawn, an alpha level is randomly generated. This has the effect of some bricks looking like they are lit windows. The varying alpha makes different brightnesses of light. The update method infrequently and randomly varies these values further looking like lights are being turned on and off.

What is most interesting in the context of this project is that the class holds a variable called clipped, which we will see working in conjunction with the Viewport class. When a Brick instance is clipped, there is no need to process each frame or attempt to draw it either.

 Coding the MainActivity

Simply edit your MainActivity class to look like this code. There is nothing new here. If anything looks unusual check back to the Breakout or SpaceInvaders  projects. If you have done any Android projects on this site before then this code will look very familiar. It simply creates an  GameView instance and passes the screen resolution to its constructor. The onPause and onResume methods call corresponding methods from the GameView instance to start and stop the thread that runs the main game loop.

 

Coding the GameView

This is the longest and most complicated class. I have heavily commented it. If I don’t explain something, that is because the details were given in one of the prerequisite projects. The list of these projects is at the top of the page. I will go into detail when we use the Viewport class and when we code the HUD inner class.

The code that follows is quite lengthy but follows in order. Note that I have broken it up into chunks with a bit of discussion and I don’t always show the full level of indentation, to make the code more readable.

Create a new class called GameView. First, the class declaration, into which all the rest of the code that follows will go.

The previous code declares all the game objects, Stars, Bricks and a Ship called player. Notice also we declare an instance of Viewport called vp.

The previous code is the GameView constructor. It sets up the Paint and Holder objects ready for drawing. It also gets the sound effects ready to play. In this code, we also call the vp constructor passing in the screen resolution as is required. The code also creates a new HUD instance, again passing in the screen resolution. We will see the HUD inner class soon. “Release your inner HUD”. Too much? Here is the prepareLevel code.

In the prepareLevel method, most of the code initializes the Brick instances. Also notice, however, we call the setWorldCentre method on our vp object and pass in the center of the player ship. The Viewport instance is now fully configured and ready to convert all world coordinates to screen coordinates, relative to the center of the player’s spaceship.

The run method above simply calls update and draw while keeping track of the frames per second.

The next method  update is quite huge(really it should be split up a bit). I encourage you to read the code thoroughly paying special attention to the use of vp.clipObjects which sets any Brick instances that are outside the viewport to clipped = true. Notice that the way we handle collision detection is unaffected by the fact we are using a viewport. If two objects collide in the game world it doesn’t matter if they are at x one million or x 10 – they collide. The viewport has not complicated this task in any way except that we only update and check for collisions on unclipped bricks.

This next method called  draw is where the Viewport class does its most interesting work and calls worldToScreen and worldToScreenPoint.

The next three methods handle starting and stopping the thread when prompted to by the MainActivity class. The onTouchEvent method, however, is surprisingly short. It calls hud.handleInput and passes in motionEvent. All the input handling as well as the preparation of the on-screen buttons will be handled by the inner HUD class which we will discuss next.

The HUD class is simple and effective. It handles the screen presses as well as defining the position of all the transparent buttons the player will be able to press. Each button is defined by a RectF and the position and size of each calculated based on the resolution of the screen. The .contains method is used with each of the RectF objects to determine when any of the buttons are pressed and then they can be responded to.

You can now run the game. Note that the button in the top-right is a pause button and you need to press it to make the other buttons work. But there’s more!

What else can you do with a viewport?

We could easily add a “map screen” to our game by having the screen presses call these methods (while in “map mode”) instead of moving the ship.

We could output debugging to the screen regarding the number of clipped game objects. We could internally clip new game objects with a simple isClipped variable. We could then monitor the number of clipped game objects by calling these two methods, once each per frame:

All we need to do is vary the metresToShow... variables and we can instantly zoom our game world in and out. Furthermore, if we have multiple instances of Viewport, we can also pass in different resolutions for each instance. Consider passing the full-screen resolution as we did in this project and then a smaller, say 100 x 100-pixel resolution but with larger values for the metresToShow variables. You could then make two passes through the draw method and you have a map of the game world drawn on top of the current view around the player.

We could even use the same math we use to rotate the spaceship, on each and every pixel via the Viewport class. We could then rotate our entire game world with ease. Or, in a more likely scenario, we could rotate a shrunken view of a game world, perhaps drawn in the top corner, and we will have ourselves a rotating mini-map without breaking a sweat.

Problems with the design

Anyone with a device that is more than a few years old will probably notice the frame rate drops lower as there are more objects on the screen. My 4-year-old Samsung Note tablet drops as low as 30 frames per second at times. There are a number of things we could do to improve the efficiency of the code. We could optimize our collision detection. We could find a totally new way to represent the buildings. The bricks were just a bit of fun. We don’t really need to construct each building out of dozens of game objects! But what if we really do need vast numbers of game objects?

Quite soon we are going to need to adopt a new development paradigm. We can achieve this by using a game library like LibGDX or we can stick with the Android API and optimize our game code, at the same time as moving to OpenGL ES to do all our drawing. The next series of Android game projects will introduce the basics of making a game, with OpenGL ES doing all the drawing and eventually lead up to a full, 2D, OpenGL ES game project.

patreon