Pull to refresh

My and my girlfriend’s first video game. Development with Unity. Part 1

Development for iOS *Game development *Development for Android *Unity3D *Game design *
If not to take into account releases for Android and a dozen of abandoned projects just before they were ready, then yes, it is our first game appropriate for more than one platform. How did it all start? Very simply. We worked on another project, let’s call it “project A”, and we’d been working on it for a long time when we decided to make a game during a couple of months and use it to train our marketing skills, and then immediately release our “project A” when we would be more experienced in the promotion of games. But the plan failed and “project A” was kept untouched for the whole year. But this story isn’t about “project A”, it’s about a logical game called «Cubicity: Slide puzzle».

The first draft was the following: minimal graphics, minimal UI and also minimum of everything possible, the game had to be in the style of today’s casual games that flood the market as well as Match-3. As a result, our goal looked like this: round tokens are connected into the figure, being moved with a 4-way swipe. Those who have already played Cubicity know that we haven’t fully changed the task but fiercely improved other aspects being a team of only two people.

If some of the readers expect to find here a secret of successful and fast game development, then there is no such secret. Here we don’t share wide experience or knowledge, we just tell the story about one small company’s project. And we don’t know yet, whether it is successful or not. But for many of you, our readers, this is a message from the past sent by the developers.

Let’s go back to the history of how Cubicity was created. Mainly we work only with Unity and here is the standard set of every decent Unity developer: Newtonsoft.json, Zenject, Cinemachine, Dotween etc. As it can be seen on the picture above, the first prototype of the game included just cubes and chips. After a week of thoughts how to improve the game and attract gamers, there was a eureka moment… To look for some cubic or round characters on the Asset store. And so a few packs of characters were bought without hesitation. The same situation happened with the blocks on which characters move now. Also a list of new gameplay elements was written with almost 30 new items from which we firstly chose neutral things such as redirection blocks/arrows, elevator and teleport. We decided to leave the rest for the new levels and introduce them one by one in 30-35 levels.

To be honest, we can’t even recall what inspired us to make so many levels at the first step, but we have what we have, and so the first release included 95 levels. It’s far too many, to tell the truth, and we regretted it. Why regretted? Because the game wasn’t completely ready and many things were changed after the release. It seemed like we were stuck in the Groundhog Day, because we needed to make alterations in every level out of 95. Two months of continuous work were spent on all levels. Still those levels weren’t 100% ready but we were not far from there. In the most productive days 10 levels were easily transferred from the head to the paper and afterwards to the scene. But there were also days when you feel like you’re Hank Moody from Californication, who suffers from writer’s block, and you think you hit the bottom, but the new day brings new ideas.

As regards the visual component, it was a bit harder. Drawing as in most games is carried out on the off-screen surface with a resolution of less than native and blits to the primary surface, but the UI is drawn without any changes in resolution for better clarity and readability. Thus, we get the best of the two worlds — not a blurry UI, and not a performance heavy render in the game. After numerous experiments we chose 2x MSAA + FXAA for antialiasing, because they give the best picture with the least amount of resources. We reasonably concluded, that a logical game doesn’t need 60 frames per second, and decided not to reinvent the wheel and set the frame limit to 30fps (even consoles usually do this). Setting the frame limit has a positive effect not only on energy consumption, but also on the heating of the phone, which prevents the phone from throttling due to overheating.

Next we had to make a difficult decision, and that was Finish points. Each time the level was started, the characters were chosen randomly from those available for the player, that’s why it would be problematic to draw a miniature figure of some character. You may not believe it, but we spent a great deal of time on solving this task after continuously procrastinating on it. Cubes at the finish points didn’t seem such a terrible idea, and paper painting helped to pass the level and bring everyone to the right place. Later it was decided to use the same characters instead of cubes, but in smaller size. It became better, but only for us. A few days later, these characters were turned and highlighted, and it became much clearer who is who, but still not satisfactory. The final version was adopted a month later, by trial and error, and afterwards a couple of weeks was spent on creating icons for the finishes. Goodbye summer, see you soon again!

In our humble opinion, the clouds turned out to be rather pleasant looking. But in fact this is the simplest hack. When we just decided to add clouds, the first thought was to make a 360 background video. This approach failed, as for mobile platforms it is desirable to fit the game in the size limit for downloading via LTE. If we wanted the video to be not overcompressed, we had to give it 10-15 MB. Combined with the presence of night levels in the game with its clouds it is too much (the entire final Android game build takes 61 MB). The second desire was to write our own system for the clouds. It was tempting for a developer, but for a person who wanted to finish the game as soon as possible it was not suitable. The solution came in the form of creating a texture for the cloud and creating a system of particles with an infinite lifetime of the particle, and also a limited number of particles in general. After that, we added random sizes between two constants along with random rotation. The result was really satisfactory, our sky was full of clouds that were pretty and did not make us cry looking at them.

Shadows in the game (in the mobile version) consist entirely of quads that are simply arranged by hand, since we didn’t want to add real shadows to the mobile version. One of the reasons is the absence of soft shadows on mobile platforms with OpenGLES 2.0, and, of course, performance degradation on weak devices.

As it was mentioned before, we used 2x MSAA + FXAA for anti-aliasing, but that's not all! We also added AmplifyColor to our post processing, as it is a great asset for reasonable money that allows you to apply different Lut-s on the post processing. A properly selected lut makes the picture get better. During the development process we tried different approaches, including the standard unity post processing stack, but in the build its shaders and options took really much. Some solutions were very beautiful, but they worked extremely poorly on not very fresh phones (believe me, if you think that everyone now has at least 'normal' phone, you are mistaken. A great number of people still own $40 Chinese phones and complain to you in the comments that your DOOM isn’t going well on their garbage).

The balance of the game is always not easy to reach, and even now we sometimes think that the levels might be too difficult, that those hard levels might appear too often etc. After balancing as we could, we they decided to introduce tools to make life easier for the player (Moving back, Bomb, Ice Block, Teleport), and yes, it became easier to live, but not for us, just for future players. The amount of work and bugs increased for us.

We got to the game menu, with our powers waning and regged nerves. The creativity hit the brakes. Frankly speaking, we had to get inspiration from other games, for which we thank them so much. And finally we did it, the UI was ready on prior layouts.

We wanted to be fashionable as well. So we decided to add saving on cloud storage and did not regret it. This task wasn’t the easiest, as on different platforms there are different cloud storage providers. On Steam there is Steamworks, for mobile it is GooglePlay and GameRoom. So we had to unify the system of saving so that it could be substituted for the desired platform. At first, we decided to use EasyMobile for these purposes, but soon we abandoned this idea. The plugin is pretty good and has a huge number of possibilities, but we didn’t really like to work with native cloud storages. As a result, we chose Firebase Realtime Database and Facebook authentication. In short, we had to go through the hell to make it all work (and this is not about programming, but rather about 100500 settings that had to be done in 100500 application locations and in Facebook, Firebase etc.). Also in the database there are traffic limits and in order to save it, every time we write, we create a GUID and write it both to the database and on the device. Thus, if we see that the GUIDs on the device and in the cloud match, we can be sure that we don’t need to read all the data from the cloud, but we can use a local copy of the data. As a result, synchronization was added, but… One of the strangest bugs for us was the unobvious behavior of the Firebase Database in some cases. Since we use Json, we serialize classes for storing state, but Firebase sometimes behaves a bit strangely.

If we pass a dictionary object into Firebase, for example:

var  dict = new Dictionary<int, SlotState> { { 0, new SlotState() },  { 1, new SlotState() },  { 2, new SlotState() };

When we read it from the database, we will get not a Json object, but an array of Json (What?)
Well, ok, we will use lists everywhere and won’t have problems, right? But it appeared to be not right.

If we write in Firebase:

var  dict = new Dictionary<int, SlotState> { { 0, new SlotState() },  { 1, new SlotState() }, { 100500, new SlotState() };

Or even:

var  dict = new Dictionary<int, SlotState> { { 0, new SlotState() },  { 1, null },  { 2, new SlotState() };

When we read it from the database, we will get a Json object with keys and values.

Well, the developers’ logic can be understood, but it can lead to bugs that might appear after a while (Remember the GUIDs added for saving that we mentioned above? As a result, rare reading from the database with relatively frequent entries to it).

When is the release? This question was heard most often. But it was necessary to be well-prepared for this day. We had to make a list of markets, choose a release date, avoid big sales, there was quite a lot of nuances that delayed the release for about 2 months. Following the advice of one article, we chose Tuesday and Wednesday for release. We decided to order a review on 4pds, tell about the game on several forums and advertise it in social networks, in Instagram in particular (of course, for a fee). What worked out of all this, you will find out from the second part of this story, but that's later.

What do we have in the end? Creating a game is not always a quick process. And it is possible that the expected time for developing the game will have to be multiplied by 5. Get people who can help you with practical advice in unfamiliar industries. Relax at every opportunity, since creating something, not just games, takes a lot of energy. It is not right to be close to release feeling exhausted and being less useful than at the start of the project. And money, look for money, you'll need it. And from us, thank you for your attention, good luck and see you in the next article.
Total votes 17: ↑16 and ↓1 +15
Views 5.1K
Comments 5
Comments Comments 5