
My strange creative path brought me to the development of games. Thanks to an excellent student program from an IT company, the name of which consists of one Greek small letter cooperating with our university, we managed to put together a team, give birth to documentation and establish an Agile game development under the supervision of a high-quality QA engineer (hello Anna!)
Without much thought, Unity was chosen as the engine. This is a great engine, which really quickly and easily can make a very bad game, which, in their right mind, no one will ever play. To create a good game, you still have to shovel documentation, delve into some of the features and gain development experience.
')
Our game used a physics engine in an unexpected way for it, which caused a lot of performance problems on mobile platforms. In this article, on the example of our game, my struggle with the physics engine and all those features of his work that were seen on the way to a viable beta version are described.
A game
GIF with game
A few words about how it is made.
Made with Blender and a couple of python scripts. At the time of the shooting, in the corner of the screen there were 16 small squares, the color of which encoded 32 bits of a floating-point number - the rotation of the phone at a given time. R, G - data, B - parity. 0 - 0, 255 - 1. The video captured on the computer was divided into frames using ffmpeg, each decoded frame was mapped to a decoded angle. This format allowed to survive any compression in the process of shooting and overcame the fact that all programs have slightly different ideas about the passage of time. In reality, the game is played the same way as on the render.
The airplane flies through an endless and unpredictable cave, in which there are bonuses, all sorts of coins and enemies, in which you can shoot homing missiles. Hit the wall - immediately lost.
A distinctive feature of the game is that the level is nailed to the horizon and the control in it is gyroscopic, and moreover, absolute. Tilted the phone at 45 degrees - the airplane flew at a 45 degree angle. You need to make a dead loop - you have to turn the tablet. There is no sensitivity, only hardcore.
We distinguish two main and obvious problems for the developer:
Problem 1: Infinity
Unity stores and processes the coordinates of objects in the form of ordinary 32-bit float, having an accuracy of up to 6 decimal places. The problem is that our game is endless and, if we fly for a long time, various insane bugs will begin, including teleportation through walls. There are several approaches to solving this problem:
- Ignore. In Minecraft, for example, rounding errors only made the game more interesting, giving rise to the “Far Earth” phenomenon .
- Teleportation in (0; 0; 0) with too much distance of the airplane from the origin.
- Change the point of reference. It is not the plane that moves, but the level around it.
In our case, the only acceptable option is the third one, which was implemented. On the implementation - a little later.
The first - ignoring - absolutely unacceptable. Creating a robot that can play our game forever is an interesting (and very simple) task that someone will solve. Yes, and ordinary Korean players should not be underestimated - the airplane is fast, the level is generated unpredictably. And if, before passing through the walls, to fly and fly, then much more accurate shooting will obviously begin to podlyuchivat after 5 minutes of flight.
The second - teleportation of the player and the whole world - puts mobile devices on their knees, in some cases - somewhere for half a second. This is very noticeable, and therefore - unacceptable. But this is an acceptable option for unpretentious endless PC games.
Problem 2: Level Generation

There are several basic approaches to building endless runners:
- Use prefab level segments that are joined randomly. This is done, for example, in Subway Surfers. It's easy to implement, but the player quickly gets used to it and knows what to prepare for, which is boring.
- The level is simply a straight line on which obstacles are randomly placed. This is done in Joypack Joyride and Temple Run. In our case, this would greatly limit the number of maneuvers.
- Everything is generated randomly. The most difficult, unpredictable and interesting option for the player.
Of course, we chose the most difficult option. In his heart is a very complex state machine that performs random transitions on them. But within the framework of this article, it is not the mechanism that is interesting, but the level generation process and its organization, taking into account the chosen point of reference.
Level structure

We are flying in a cave, it has a floor and a ceiling - a couple of blocks, elementary building units. Blocks are combined into segments that seamlessly fit together. The segments, as a whole, revolve around the aircraft and move along its velocity vector, creating the illusion of flight. If a segment goes out of sight of the camera, it is cleared of blocks, docked to the last level segment and filled with new blocks, according to the instructions of the generator. The combination of such segments is the level.
Experienced Unity developers could reasonably wince, having estimated the amount of work and all possible pitfalls. But in words everything is simple, but I had no development experience ...
Basic Laws of Physics in Unity
For a month of development, experimentation and reading documentation, I outlined three basic laws of physics in Unity. They can be broken, but the charge for the violation is performance. The engine will not warn you about an error, and without a profiler you can never find out about them. Failure to comply with these laws can slow down your game
dozens of times. As I understand it, the violation of any law leads to the fact that the physical engine marks the violator-violator as incorrect and re-creates it on the object, with subsequent recalculation of physics:
1. Colliders should not move, rotate, turn on / off and resize.
As soon as you add a collider to an object - forget about any impact on it or the objects in which it is contained. A normal collider is an exclusively static object. A tree, for example, can be with one collider. If a tree can fall on a player - the tree will fall along with performance. If this tree grows from a magical nutrient cloud, which the collider does not have, but can move, this will be accompanied by a drop in performance.
2. If the object moves or rotates - it must be a solid have component rigidbody.
About this is written in the documentation, yes. Which it is not obligatory to read thoughtfully to begin to make game therefore Unity is very simple and intuitively clear.
Rigidbody change the attitude of the physics engine to the object. External forces begin to affect him, he can have linear and angular velocities, and most importantly, a solid body can move and rotate by means of a physical engine without causing a complete recalculation of physics.
There are two types of solids - ordinary and kinematic. Ordinary bodies interact with each other and ordinary colliders - one body can not pass through another. Kinematic bodies follow the simplified rules of simulation - they are not affected by any external forces, gravity - including. They can freely pass through each other and colliders, but they push ordinary solid bodies away, as if they have infinite mass.
If the objects are not sorry to give control of the physics engine - use ordinary solid bodies. For example, if you need to beautifully roll the stones off a cliff. If your scripts or animators control the object directly - use kinematic bodies, so you will not have to constantly deal with the engine and random collisions of objects. For example, if you have an animated character or a guided rocket that explodes on contact with something.
3. If an object is a solid body, it must move and rotate through solid-state methods.
Forget about direct access to the Transform object immediately after adding a collider to it. From now and forever, Transform is your enemy and killer of performance. Before writing transform.position = ... or transform.eulerAngles = ..., utter the phrase "I now absolutely clearly understand what I am doing, I am satisfied with the brakes that will be caused by this line." Do not forget about hierarchical relationships: if you suddenly move an object containing solid bodies, physics will be recalculated.
There are three levels of solid control:
- The highest and, therefore, natural level is through strength. These are the AddForce and AddTorque methods. The physics engine will take into account body weight and correctly calculate the resulting speed. All interactions of bodies occur at this level.
- Medium - change speeds. These are velocity and angularVelocity properties. On their basis, the forces affecting the body during their interaction, as well as, obviously, their position at the next moment of time, are calculated. If a solid has a very small speed, it “falls asleep” to save resources.
- The lowest level - directly the coordinates of the object and its orientation in space. These are the MovePosition and MoveRotation methods. At the next iteration of the physics calculation (this is important, since each subsequent method call within one frame replaces the previous one), they teleport the object to a new position, after which it lives as before. In our game, this is the level used, and only he, because he gives full control over the object.
What is left behind? Turn on / off the object and scale. I do not know if there is a way to resize the object without confusing the engine. It is possible that not. Turning off an object is painless, and turning on ... yes, it causes a physics recalculation in the vicinity of the included object. Therefore, try not to include too many objects at the same time, stretch this process in time so that the user does not notice.
There is a law that does not affect performance, but affects performance: a solid cannot be part of a solid. The parent will dominate, so the child will either stand still relative to the parent, or behave unpredictably and incorrectly.
There is another feature of Unity that is not related to physics, but worthy of mention: the dynamic creation and deletion of objects through the Instantiate / Destroy methods is MUCHLY a slow process. I'm afraid to even imagine what is happening under the hood during the creation of the object. If you need to create and delete something dynamically - use factories and fill them with the necessary objects during game loading. Instantiate should be called as a last resort - if the factory suddenly ran out of free objects, and forget about Destroy forever - everything created should be reused.
The application of laws in practice
(this section contains the line of reasoning when creating a game and its features)

The level must obviously rotate and move.
Let us ease our life forever by placing the axis of rotation of the level - a plane - at the origin. Now we can calculate the distance from the point to it, calculating the length of the vector of coordinates of the point. Trifle, but nice.
The joint movement of objects is easily realized through the hierarchy of objects in Unity, because children are part of the parent. For example, the described level structure is logically implemented as follows:
-
- - \
- - - \ 1
- - - - \ 1 (Collider)
- - - - \ ...
- - - - \ N
- - - \ 2 ...
- - - \ 3 ...
- - - \ 4 ...
(You can even do without a level object)
The script on the axis receives data from the gyroscope and sets an appropriate angle for it ... And immediately violates many rules, because the rotation will be transmitted along the hierarchy to the colliders, which will make the physics engine crazy. You have to make the axis a solid body and rotate it through the appropriate method. But what about the level movement? Obviously, the axis of rotation and the level object will not move, each segment must be moved individually, otherwise we are faced with the problem of infinity. This means that solid bodies must be segments. But we already have a solid higher in the hierarchy and a solid cannot be part of a solid. The logical and elegant hierarchy does not fit; you have to do everything with your hands - both rotation and movement, without using the object for the axis of rotation. Be prepared for this, if you have unique gameplay features.
If you had to move the segments directly, you would have to rotate them. The main difficulty is that in the Unity physics engine there is no method “to rotate an object around an arbitrary point” (Transform has it, but do not be tempted). There is only "rotate around its center." This is logical, because the rotation around an arbitrary axis is both a rotation and a movement, and these are two different operations. But it can be imitated. First, rotate the segment around its axis, then rotate the coordinates of its axis around the plane. Due to the fact that the plane is at the origin, we don’t have to remember even the school geometry and go to Wikipedia, everything is already in Unity. It is enough to translate the rotation angle into a quaternion and multiply it by the coordinates of the point. By the way, I found out about this right at the time of writing this article, before that the rotation matrix was used.
We have enemies who push the plane into the wall, hoping to kill. There is a shield that pushes the plane from the walls, helping to survive. This is implemented trivially - there is an offset vector, which each frame is added to the coordinates of each segment and reset after that. Anyone who wants to kick the plane, through a special method, can leave a vector of his kick, which will be added to this displacement vector.
In the end, the real coordinates of the segment, each frame, are calculated by the control center of the level movement in some way:
Vector3 position = segment.CachedRigidbody.position; Vector3 deltaPos = Time.deltaTime * Vector3.left * settings.Speed; segment.truePosition = Quaternion.Euler( 0, 0, deltaAngle ) * ( position + deltaPos + movementOffset );
After all the calculations and crutches necessary to work the exact joining of the segments during regeneration, segment.truePosition is sent to the MovePosition method of the solid body of the segment.
findings
How fast does all this work? On the old flagships - Nexus 5 and LG G2 - the game flies at 60 FPS, with a barely noticeable drawdown during the inclusion of new colliders during the generation of the segment (this is inevitable and does not do) and pushing the worms out of the ground to get around this, but now there is a deliberate violation of the third law). 40 stable FPS gives any device with a gyroscope, which we came across. Without knowledge and consideration of all laws, the performance was, to put it mildly, unsatisfactory and the phones overheated. So much so that I thought to write my own simple specialized engine for 2D physics. Fortunately, the physics in Unity turned out to be flexible enough so that all the problems could be circumvented and create a unique game, it was enough just a couple of weeks of experiments.
Now, knowing all the main pitfalls of the Unity physics engine, you can quickly slope our game, destroying the dreams, lives and faith of three poor students in humanity. I hope this article will save you a lot of time in the future and help you find not quite obvious violations of the laws of productive physics in your projects.
Read the documentation and experiment, even if you use simple and intuitive tools.