How much I remember developing games for mobile phones, I always had to invent some tricks to make everything work properly. They came across this 15 years ago, when they wrote games still under black and white phones, and we still face it now. I am sure that a huge set of similar tricks exists in the development of games for desktops and, especially, consoles. But I am engaged in mobile phones, therefore it will be a question of them.
The dirtiest trick in J2ME development is to put one common try-catch inside the main game loop.
public void run() { while(isGame) { try() { gameField.Update(); } catch (Exception ex) { } } }
As a result, any exception is intercepted inside the loop. The frame is not processed to the end, but the game does not fall - as if nothing had happened, the next frame begins to be cheated. No flights.
A side effect is the hang of the game, if the problem is very serious and occurs on every frame.
I repent, once had to use this method. As it happens with everyone, deadlines were tight, and the bug refused to be.
The trick that had to be resorted to in almost every project is the challenge of two updates of logic for one drawing.
We tried not to use object updates depending on the frame time, since fleet operations took a lot of CPU time. Therefore, at that moment, when the complexity of logic and the number of objects in the frame grew so that the phone could not cope, we launched two updates in a row. Most often this happened before the final balancing of the game, so it did not really affect anything.
Why in such cases just do not change the balance by increasing the distance traveled by objects per frame? First of all, probably because of a collision. To move objects over long distances, we would have to write a more complex collision logic, which would have an impact on performance. Also, the logic when driving over long distances sometimes faltered, and it would have to be complicated. Well, in general, drawing took much longer than processing the logic even in double volume, so this was the easiest thing.
Another problem that J2ME has had to deal with is the size of the memory. And as RAM, and the delivery of the build. It was established experimentally that quite a lot can be saved by reducing the number of classes. As a result, the whole game logic lay in one class. All data were decomposed into arrays. In one of them lay the type of object, in the other - the type of its logic, in the third - the state of logic, in the fourth and fifth - X and Y coordinates, etc. Included was a cycle with a large switch on the type of object that twisted it all. No dynamic selections and deletions, fewer cache misses. Worked pretty fast. Another couple of classes for the menu, sprites, tile background, the main game cycle. As a result, the whole game takes less than ten java-classes. Fortunately, none of my acquaintances, the "right" Java developer, have ever seen this code.
In earlier versions of BREW, there was absolutely no API for working with the screen directly. And those functions for rendering that were provided by the system were very slow. I had to find the screen buffer myself. To do this, we filled the entire screen in red, created an empty sprite (or whatever they called it) and scanned the memory from its address in both directions. Moved to the number of bytes a little less than the screen area. If you found a similar combination, filled the screen with other colors and with the help of a couple of calculations found the pixel format, the beginning of the screen buffer and whether there is an indent after each line. It took less than a second, the player did not have time to notice anything. It's clear that we did not include the update of this whole process on the screen.
The toughest thing you have to deal with in iOS is the need to clear the memory while starting the game. Due to some circumstances, we did not use our memory pool with custom allocators for all allocations. But our memory consumption at times was approaching the 300 mb mark, which did not have a very good effect on the stability of the application on weak devices. The real problems started with the release of iOS 7 or 8. The axis began to sometimes “make mistakes” and close Blitz at the moments of peak memory load, although it was clear that other, less priority applications remained to hang (Skype or mail client). After additional research, we found out that iOS really dislikes allocating a large amount of memory in one tick. But if you allocate memory gradually, you can pass far beyond our limits without fear of being closed.
To redistribute the memory allocation on a project that hundreds of thousands of players are already playing into is long and very dangerous. Therefore, we made a slightly different decision. At the start of the game, we gradually, in chunks of 10 mb each, allocate memory to the volume required by the game at peak load. And then we clean it all at once. It takes less than a second. And in the log you can see how the axis closes other applications. In such a tricky way, we fixed the fall of the game during peak memory load.
One of the serious drawbacks of mobile GPUs is the slow rendering of semi-transparent geometry. And the game desperately needs bushes and trees. And as much as possible. The matter was complicated by the fact that we have a sniper mode, in which the player observes everything that happens right from the sight of the tank, in good approximation. At times, the fill rate in the bushes and trees passed over 10 screens of translucent geometry.
For bushes, the solution was simple enough, and it was not even programmers who invented it, but artists. We introduced a near lod, which turned on almost close to the bush and consisted of one billboard, the plane always turned to the player. This made it possible to sit down in the bushes relatively painlessly and even to heal enemy tanks.
Trees were a little more difficult, as they can be knocked over to the ground in any direction. For them, we created a special shader that turned on when the player was very close to the tree. This gardener shader cut off all the extra branches of the tree, leaving only the three closest to the camera. Due to the fact that near branches block almost the entire review, the player does not notice the absence of the others. But the GPU very much even notices.
I would not say that these solutions completely saved us from problems, but they definitely allowed us to add at least some vegetation in an acceptable amount to the map.
We encountered the same problem in the sniper mode when creating effects. Sniper mode plus some particle effects near the camera - and the FPS counter is guaranteed to freeze at around 10 (the engine simply won't allow less). The solution was similar to the bushes, although technically it was much more difficult. We introduced detail levels for particles. And at the very closest one, they got rid of all the excess (for example, a shot looks like just a flash). As a result, at the maximum approximation, only the most necessary effects remain. It is not paradoxical, but our far-off effects lodge is more detailed than the closest one.
An interesting story came out with the user registration service. It was written for the elder brother and sent scraps of JavaScript without a twinge of conscience in the hope that the client would start all this, and already this JavaScript would form the next http request. It turned out that mobile web view is not very suitable for this purpose. In our case, it needs to be done hidden, and on internal test services, he asked the user for confirmation of the connection. In addition, it was all necessary to twist in a separate thread. There was little time, and it was almost unrealistic to order a rework of the registration service in the required time frame. And we had to parse the scripts coming from the server on our side, form the following requests based on them, send them to the service. Later there were a couple of fixes that allowed a bit to standardize this process. It sounds weird, but it still works.
While working on the Android version, we had to look for many non-trivial solutions, but they are hardly worthy of an article. There is one really fun thing that we had to use for performance testing. The fact is that during the battle, or several fights, many Android devices have time to warm up, the processor frequency is automatically reduced. Which leads to a decrease in FPS. And we really wanted to see the results of daily performance tests more or less stable. After examining the situation, we came to the conclusion that after each test it is necessary to reboot the device. The phone has time to cool down and think again, and we get fairly predictable results. This does not in any way cancel the playtests, on which QA checks the playability and quality of the assemblies, but makes it possible to notice any changes in the performance of the device and take action.
We also have our own Skype bot, which builds builds, updates servers, monitors the status of the trunk and unit of tests, and assigns programmers to review the code. But that's another story.
If you have any questions, or you are ready to share your mobile life hacking - write comments, discuss.
Source: https://habr.com/ru/post/303686/
All Articles