Hidden order in color chaos

I was lucky at work to do what I love in a strong team with good people. We built and destroyed castles in the air, fought with windmills, implemented, supported and did not worry. Once I wanted to build my castle. Reasoning from different angles, I decided that it would be small, and I will build it myself, it will be a hobby project. There were several ideas, I chose one and started developing, it was a game.

Idea and implementation

The inspiration was Rubik's Cube and V.L.Rvachev 's R-functions . What is the idea: on the playing field there are elements that are linked into lists. Chains of related items can be moved within these lists. An important part of the implementation and gameplay is the geometric arrangement of the elements. In the case of the Rubik's Cube, they are located in a three-dimensional matrix

image alt

Now attention. List nodes do not have to be in a rectangular matrix. You can arrange them arbitrarily, you can make chains of different lengths, impose different conditions on this graph and move around it. It sounds simple until we try to visualize such a model. Here we face the first interesting task.

We are used to the fact that game objects have a clear and well-defined size. However, if we set arbitrary trajectories of the movement of objects, then when visualizing puzzle elements collisions will be inevitable, they will overlap each other. The solution is to calculate the size and shape of the elements depending on their relative position.

In general, it would be interesting to get an imitation of drops, for example, this:
image alt

Looks good. You won’t get such a result with R-functions, but I got inspiration from them. Now we complicate the model to see how it may look as a whole. The drops had to be made more “stiff”, otherwise the whole image walked with a shake.

It turned out like this:
image alt

And again it looks good, and even good. We’ve finished checking the starting model, now we need to extract useful artifacts from it. After the experiments, he settled on this option.

  • To reduce memory consumption, graphic elements (now I began to call them blobs) will be stored in a vector format in the form of polygons.
  • To give them a rounded shape, polygons will be rendered with interpolation by splines, as in vector fonts.
  • All the vertices and indices of a single playing field will lie in one continuous buffer, which would reduce the number of rendering passes.

Still a small amount of memory for service structures for describing frames. The result was a data structure ranging in size from 0.5 to 1 MB per playing field. Size depends on the desired accuracy of the geometric description of blobs and the smoothness of their movement. There is a model, there is data, it remains to revive them. After some effort, the initial idea materialized in such a gameplay:

image alt

In the article from the idea to the implementation a couple of paragraphs fit. In practice, the distance traveled was longer, and, of course, it was already many times passed down to me. Therefore, I will focus only on a few episodes of the work done, which seem interesting to me.

Platform and SDK selection

I approached the choice very far. I decided that this should be something cross-platform (or easily portable) and the first implementation should certainly be on a mobile platform, for example, Android.

My professional activity is connected with enterprise development mainly in the .net stack, and the words “mobile” and “game” were something far for me. Based on the above, it would be logical to choose Xamarin or a mono-based implementation of other common libraries as the main development tool. Therefore, after some thought, I decided to write in c ++ and use it as a framework. As a result, 99.9% of the program code was written on it. Development and debugging was carried out in Visual Studio for the native Windows OS. For the mobile platform, only the final assembly and minor tests were performed.

The main thing is not to give up

The smooth flow of blobs is an important part of the gameplay, I needed FPS 60 and enough optimization to keep this frame rate on the maximum number of devices. What happened during the implementation: the OpenGL ES2 API was used, with minimal graphics settings in the application (disabled: anti aliasing, highlighting selected blob chains, etc.) the main screen is drawn in 3 glDrawElements calls, with maximum settings from 6 to 9 calls. In general, it turned out quite economically, we could still work on optimization, but here disappointment lied. Under no circumstances did I manage to get 60 FPS from my old Galaxy Ace (I thought it would work on it - it would work everywhere). After many experiments and considerable time spent, it finally dawned on me. All the time I tried to optimize the shaders and the number of draw-cols, and the solution to the problem with Galaxy Ace was in a completely different plane. I excluded this device from my checklist, after that the need to continue work on optimizing performance disappeared. The main thing is not to give up, there is a solution!

A couple of buttons on the interface

Another interesting task is the user interface. Getting down to it, I did not expect at all that the implementation of a “pair of buttons” would take so much time. It was a very, very big surprise. To make the “buttons” work, several render-s were implemented: for text, icons, sprites, rectangles, alignment of elements relative to the screen and each other was added. Objects from more primitives are compiled more complicated, the behavior is added to them: text labels, buttons, dialogs and pop-ups, etc. Of course, I was concerned about performance and couldn’t just draw “buttons”, it had to be done quickly. And if you need to quickly, then there is only one option - we minimize draw-cola. As a result, UI elements were grouped so that all objects in the same group are drawn in one call. The bike turned out to be like a micro library for the UI. For instance,

std::unique_ptr  button(new Icon(
   IconType::MenuSolid, // вид иконки
   SDFStyle::Default,  // способ вывода SDF изображения иконки
   vec2(0, 0.5f),  // смещение при относительном выравнивании
   size,  // размер
   rect::up, // выравниваем верх
   rect::down, // относительно низа
   parent_button->GetRenderObject(),  // вот этого объекта
   [](IControl*)->void {Locator::Resolve(true)->OpenActivity("Levels"); }

Developer Feedback

As the release approached, I thought about journaling. The application was ported to Android and tested on a pair of physical devices and emulators. Obviously not enough to press the “publish” button with a clear conscience. We need a centralized error log that will help in the first days after the release (or the next update) to fix something critical. Raising your servers is not at all what I would like. Ready-made solutions for mobile applications did not like, at least free. Went to the clouds. In general, everyone is the same. By coincidence, which are not at all fundamental, I settled on Azure with its blob repository. According to my expectations, a solution with a remote log in the cloud should be cheap, because minimum is written to the log and during normal operation of the application the logs should be empty.

In conclusion about the main

Looking back at the work done and the decisions made, we can definitely say that the resources, already limited, were spent chaotically and terribly inefficient, but on the other hand, they were very correct. A business model, planning, product practices with hypotheses and pivots are very good tools when your project = work. But if you suddenly have a desire to “compose” something beyond the usual, the metrics stop working, the goal is not a result, but a process. And the most difficult task in it is to keep this strange and unsteady motivation. Only with it you can climb into the most inaccessible places. This was my main challenge during the project. Probably like everyone who walked along this road.

All this time I worked on the product, and this is much more than a code or an idea, work on it can never stop, but today I put one more checkmark: “The lock is ready”.

Also popular now: