A while back, our teams at Gett had an awesome in-house Hackathon, so we had a chance to explore some interesting ideas to improve Gett’s ride-hailing app and rider experience.
My team’s idea was to improve the rider experience while they wait to be matched with an available taxi, as well as during the ride itself, by developing a super simple “time-spender” single-tap game to amuse our users.
The idea for the game was a simple 2D game showcasing the player (a taxi) on the open road, with the goal of collecting as many riders as possible while avoiding COVID-19 obstacles.
Doesn’t sound too difficult, right?
Well, turns out that if you are not an iOS developer, using something like SpriteKit, and you want to develop a dead-simple Android game without having to pay for a game engine, that is not an easy task.
Although we‘re not game developers, I want to share the approach we took.
Basic game development guidelines
Since none of us is a game developer or an NDK developer, our strategy was trying to use our tools as mobile developers, and build the animations by hand our self.
As we started developing the game, we realized that we have to reduce the amount of moving parts on the screen, and this is how our first guideline was born:
1.Divide the animation process into two main parts:
First, create the assets you want to use and set them up in their initial position. We create a method named
gameInit() for that.
gameInit() has finished, you can start moving the various parts. We’ll call this method
Now, As we learned the hard way, you need to:
2.Reduce the number of elements to the minimum that is necessary on the screen.
We started developing the naive way: drawing all the elements on top of each other and moving all of them vertically down the screen.
As you might have guessed, that didn’t go too well. Drawing so many assets and moving them resulted in a major performance hit, and we ended up with an extremely laggy game experience :(
This situation called for a bit of creative thinking. And then, we had an idea. For this sort of top-scrolling game, all we really needed are about 7 elements every frame (3 dashed lines, 3 obstacles, 1 car). So why not use only 7 elements, and reuse them when they are out of the screen bounds?
We took this approach, and the lagging was almost gone.
Although the game was still not silky smooth as we hoped, it is mostly sufficient, especially compared to our initial approach.
We moved on to develop this game in our own simple engine that could drive simple games with relatively simple performance requirements. Let’s take a few moments to go through our findings.
A way to think of animating objects on screen, is thinking like actual real-life animators. We’ll simply draw the entirety of the scene, frame by frame. Meaning, if an object moves top to bottom, we will draw the movement on a frame-by-frame basis, moving its Y-axis, little by little.
Doing so for all of your objects on screen while moving across frames fast enough, will create the illusion of animation, and there you have it, a moving game.
Hitting the Road
Let’s see some code:
In the code above we just build the dashed line. No animation, just put them on the screen by adding the first one to the bottom of the layout and then loop over the rest of them one above the other.
Since we build upon
ConstraintLayout, we use helper methods to align the views. The code for this is a bit long but you can browse through the main pieces quickly:
One note here: we hold a reference to the topmost dashed line
mostHighDashedLine for animation purposes. When the dashed line crosses the screen bounds sight, we reset it above the topmost dashed line. This way we are recycling views and reducing the amount of generated views.
This should look similar to this:
Next, let’s make them move. To do that, we initiated a
Timer, and scheduled it to run
gameLoop() every 3 milliseconds and refresh the frame:
gameLoop() is in charge on refreshing the state of the moving parts on the screen, let's dive into it:
In this piece of code, we’re iterating all of our dashed lines. On each iteration, we:
1. Move a dashed line 2px down the Y-axis.
2. Check whether the view is positioned below the screen and currently out of sight. In this case, we are repositioning the view to be above the highest dashed line (
Also, note the use of
runOnUiThread, since we have to use the main thread to work on the UI.
By now, the code is creating and moving the dashed lines, which makes the illusion of a moving road. When we will add a car, down the screen, that would make the impression of a driving car.
Here is what we have so far (Feel free to run the full code on your device for better performance than this GIF):
Grab a car
At this stage, we’ll add an
ImageView to the bottom of the screen. We can add it by editing the layout:
Now, since we want this to be a single-tap game, we are going to add some touch detection. When we detect that the user’s finger touched the screen, we’ll change its position from left to right or vice-versa.
Since we want to provide the best possible experience, we‘ll catch the touch event as soon as possible. That could be achieved by using
dispatchTouchEvent method (For a very nice explanation of touch events flow, please refer to this post).
endXare variables that hold the right and left car positions to toggle between. Take a look here to see how to initialize them, and run the code. You can finally start playing a bit!
Adding some obstacles
Our game will be quite boring without some objective. For example, collecting some passengers to gain points, and add some obstacles for the player to avoid, just to make it a bit more interesting.
We need to do two things before we can start playing:
1. Create and animate the obstacles.
2. Build a collision mechanism.
Since you already know how to create and animate views as part of the frame-based system, I’m not going to dwell on this subject, I’ll just leave you the code that creates and animates the obstacles here, and move on to the second matter of business: collision.
Up until now, we have a nice animation with the addition of a response to tapping on the screen. If we want to turn it into a game, we’ll need a way to determine whether or not the car has hit an obstacle.
The event of hitting an obstacle is called a collision.
The way to detect collision in a frame-based game is to check for each obstacle whether the car and the object hit each other, for each game frame. For that, we can check the following:
1. The top of the car is above (or equals to) the bottom of the obstacle (Y-axis).
2. Part (or all) of the car’s X-axis is overlapping part (or all) of the obstacle.
Here is the code that checks for these conditions:
Once you detected that a collision has occurred, you can choose whatever you want to do with that. You can, for example, run some cool confetti animation or display the user’s points on a scoreboard.
But wait a minute, don’t forget we have two kinds of “obstacles” — good ones that increase the player’s score, and bad ones that end the game play.
This means that dealing with collisions will probably be the place where you spend most time, since its the core of the business logic of your game.
A simple and effective trick is changing the color of the various obstacles when a collision is detected. You can find the source code for this here.
It has been an extremely cool and fun experience working on this tiny project, especially since we (as Android developers) don’t usually get to build game experiences on a daily basis. Playing around with various ideas around these concepts is very interesting. It could be pretty amazing to take up the gauntlet and try to create a library that wraps these pieces of logic up for the next developers who will want to build an Android game by using simple tools.
I hope you’ve enjoyed this quick blog post, and ff you have any questions, feel free to leave a comment below or reach out to me on Twitter @maozgal! 🎉