So, I finally got off my ass and built something in Unity (and also got off my ass and blogged something new!) This is not a comprehensive tutorial on building a mobile app, but rather a high-level conceptual separation of development lifecycle for my first mobile game.
I knew C#, I knew games, and I wanted to build something to see how difficult (or not) it was to build and ship a mobile game to the Google Play Store and Apple App Store with one codebase! This article is going to highlight a few important phases of development. I’m not going to dive into specifics of every step I took (perhaps yet,) but rather I’ll describe the overall process. Maybe I’ll update this post with new articles if I do a deeper dive into each segment.
Total dev time is probably 24 hours, give or take, depending on how much research I did on a particular Unity topic. The time estimate was greatly helped by my existing experience with the C# language, and there are probably a couple hundred lines of code depending on how horrid you feel my code practices were.
Almost all game assets were free (Creative Commons 0, etc.) The one exception is a set of progress bars I purchased for a $1.98 on the Unity Asset Store:
This felt like a pretty good deal considering I am not very graphically inclined as you’ll observe in the design process and final product. The asset package came with a bunch of different style bars, so I hope to use others in the future. I highly recommend the Asset Store as a starting point for prototype-ish projects like this! Otherwise, everything else I used for materials, fonts, etc were found free online at various places like Open Game Art, etc.
An idea
I think the most valuable decision I made was to keep it simple: build the simplest game I could think of. Let’s bounce a ball around a box. Start moving a ball at a random angle, clicking the ball increases its speed and subsequently a power meter (this feature actually came in a subsequent iteration,) and the game is won when the power meter fills up. Then, the user/score are logged to a leaderboard and presented with an option to play again. Simple, right? How hard could bouncing a ball around a box be?
The out-of-the-box Unity allows you to quickly drag geometric shapes and UI elements (text, buttons, etc) right into your scene view as soon as it opens. I snagged a few materials online, including a space theme background and a couple metal-ish styles for the ball and walls to make up the box. The walls are just cube game objects stretched to form a frame, and the ball is just a sphere.
I guess I should mention now that this game is technically built in 3D, but the vast majority of work is only in the X-Z axis.
Building a frame
The figure above should literally take you a couple minutes to achieve from starting a new project:
- Create new cube game object
- Scale it (I think my z-scale is 10 for the wall objects)
- Position it
- Repeat for the other three walls
- Create new sphere game object
Now, we need to get the ball moving when someone clicks it:
void FixedUpdate () { if (Input.GetMouseButtonDown(0)) { var direction = rb.velocity; if(rb.IsSleeping()) { // randomize direction, move it var x = Random.Range(-1f, 1f); var z = Random.Range(-1f, 1f); // constrain y to 0 rb.AddForce(new Vector3(x, 0, z)); } rb.AddForce(direction * thrust); } }
This initially applies two forces, but it gives you an idea of how to quickly prototype the mechanics of the game. For reference, rb
is a RigidBody
member of this script, which is attached to the ball–remember the sphere we created earlier? Also, thrust
is just a float
on the script as well, something we can play with while debugging to get the feel of the initial speed right.
Coding it
To clarify, this wasn’t exactly a single phase in the classic definition of the word. I didn’t have all the Unity bits in place and spend a single block of time on code. There were lots of mini sessions.
The main components/features are:
- Main menu: this consists of a start button and a few other buttons e.g. toggle music/sound effects, open leaderboard, and a reset button
- Leaderboard: this is initially a hidden panel that opens when the user fills the power meter
- Username input: also initially hidden–acts as a modal with an input for name with validation rules
- Various UI: the timer, the reset button, the power meter
Again, this isn’t comprehensive or broken out into really obvious areas–this was my first game, and I’m summarizing some of the areas in which I worked, since I didn’t document this along the way!
“Main Menu”
This is in quotations, because there really isn’t much of a menu–it’s nothing more than a start button, and some management in a game manager script:
Once the user clicks Start, the game is on! The rest of the aforementioned buttons aren’t necessarily menu-worthy, since they’re available most of the time e.g. toggling music on and off.
The one button that I disabled during game play is the leaderboard, since it makes a web call, and I never really considered how to pause the game!
The Leaderboard
Update: At the moment, an overzealous co-worker of mine has overtaken the entire leaderboard, because I initially opted to display overall best scores instead of a per-user basis. Unfortunately, App42/Shephertz/whatever they call themselves literally don’t support sorting in ascending order (because lower times are better in Super Bouncy Box) by user. The only way I could make it work is by loading every score across the wire and doing my own sorting and filtering. It shouldn’t take a simpleton more than a couple seconds to figure out this is inefficient as more people play and log scores.
In any event, it works something like this:
On game over (user fills power meter,) save the score and pass a callback including some leaderboard parameters so it can do its thing when the first API call comes back:
var cb = new GameOver(user, scoreText panel); svc.SaveUserScore(game, name, score, cb);
After the score is submitted, the App42 API can return a properly ranked leaderboard. Here’s a snipper from the GameOver callback:
// get top 10 scores var sbs = GameManager.GetScoreboardService(); // setup sort by ascending headers per API Dictionary<string, string> meta = new Dictionary<string, string>(); meta.Add("orderByAscending", "score"); sbs.SetOtherMetaHeaders(otherMetaHeaders); var cb = new LeaderboardCb(name, text, panel); // call API endpoint sbs.GetTopNRankings(gameName, 10, cb);
Once the scores are back, parse them and display the leaderboard. Here’s a snippet from the LeaderboardCb callback:
var nameBuilder = new StringBuilder(); var scoreBuilder = new StringBuilder(); // get the response from the API Game game = (Game) response; //...loop scores in response and assemble text nameText.text = nameBuilder.ToString(); scoreText.text = scoreBuilder.ToString(); // show leaderboard leaderboardPanel.SetActive(true);
So, I’m in the process of testing out PlayFab and GameSparks for sort by ascending functionality. Unfortunately, I got a response on PlayFab’s Slack that this isn’t supported. Apparently, I’m the first person to ever author an app that needs sort-by-ascending and doesn’t want to write their own leaderboard logic. Maybe PlayFab and GameSparks are just overkill for this project?? Or maybe I should just pass on this feature, since this was a contrived example of a game simply to go through the app deployment process!
Username input
I toyed with how best to implement this for a contrived, proof-of-concept game. Ultimately, I decided on keeping is simple: if a user has never entered their username before, pop this modal when the game loads and store it in PlayerPrefs and never see it again. The username would only be used for leaderboard scores.
I poked around on the asset store and asked around on a couple Unity channels for a “starter” set of assets to do something like a modal and eventually just made it up with colored and translucent panels. It would function similarly to the leaderboard “modal” without all the callback logic to load content since this was a simple input:
Caveat emptor: During a later App Store release, Apple insisted that I add a privacy policy to the game before being approved for release. The username input existed in the previous build and must have gone uncaught.
The Timer
The first iteration of the timer didn’t include the little “+1” second that flashes when missing the ball, rather it tracked misses in the background and just ended up requiring a few more lines of code to pass that count along until the end when it would be silently added to the score (bad UX!)
Eventually, I made the timer a double, and increased it by Time.deltaTime on each frame update. Simultaneously, upon a miss, I fired the “+1” animation and added 1000 milliseconds to the time and moved on with my life. Doing it this way simply allowed me to say “get the double and convert it to seconds and submit score” when the game was won!
Power Meter
This thing was weird–I’m not proud of how it was implemented (probably because there are better ways,) but it’s pretty sexy to watch! The asset package I purchased came with different colored bars in addition to the frame outside the “meat” of the progress bar. Yeah, that’s a technical term.
The frame, anchored to the bottom middle of my Canvas UI
The progress bar, anchored to the left of the frame
The HexagonMask hides the extra part of the progress bar when the size changes. I think I may have even gone down this path, then figured out another animation path. The important part is that it works, and I hope I never touch it again!
Publishing it
As the theme of this article goes, I’m not going into much detail about this process. It’s not as complex as I expected. For a game like this–that is, not much to worry about in game purchases, advertising, targeting demographics, etc–the ratings I was given were probably the lowest possible. I don’t believe I’m limited to publishing in any particular countries, etc.
In summary, the Android/iOS processes are similar (Android definitely a little easier,) but the iOS process requires you to wait for approval of the game.
Android
I was able to roll my Android APK (the package Unity builds for you) out to production in minutes. Well, the process of publishing it took minutes, and Google suggested it would take ~24 hours to roll it out to Play Store services across the globe. Some takeaways:
- Get screenshots ready in advance i.e. know what dimensions and types you need
- Have your marketing material ready
- Remember your damn key store password when you sign your build, otherwise you’ll have to publish another app (note the BouncyBox2) in the bundle ID
iOS
Their process and UI were a lot easier to understand (go figure from Apple,) but again, the waiting process sucked. I was somewhat fortunate to have a handful of friends/testers that were able to link straight to the Android build once it was published and test pretty quickly, but other than using TestFlight, which I haven’t fully figured out, I was up a creek to wait for production Apple builds to release. Some takeaways:
- Similarly to Android, have your screenshots and marketing material ready in advance
- I have no idea as to the accuracy of this URL, but it estimates the turnaround time for App Store reviews. They tweet in the morning around 7/8am ET with updates, so I added a Zap (zapier.com) to pop this tweet into my personal Slack channel
Happy coding! Please feel free to leave questions in the comments!