Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
TruckSimulation 16 is a truck simulation for mobile devices in which players can roam around in a massive open game world. Christoph Ender explains how his team met this technical challenge with Unity and which problems they had to solve along the way.
This article was first published in the german print magazine "making games" 02/16. At this time Unity 4.7 was current. In the year 2017 the version 5 is current, but all technical problems and solutions remain the same, so the article is still up-to-date. You can find more infos and videos of the game at the buttom.
For a truck simulation like »TruckSimulation 16« the gameplay is clearly defined: The player will be driving. For that reason the surroundings should be appealing and not be repetitive. The defined playground is the motorway network of Central Europe. Every street was to offer a certain level of reality in terms of course, landscaping and distinctive landmarks. Such a big, non-procedural game world requires considerably more space than the memory of a smartphone has to offer. That’s why it is divided into smaller bits that are dynamically loaded as required during the game.
Image 0: Ingame screenshots
Dynamic loading
In an ideal case, these landscape patches are completely created in a 3D modeling program. That way you have full control of the degree of detail and the visibility of the various areas and you can, for example, create tidy breakoff edges at road ditches. Unfortunately, this approach is too time-consuming for the size of our world and thus too costly. One should also keep in mind that the Level Designers have to be familiar with a 3D modeling program.
Despite justified performance warnings we decided for the terrain that’s integrated in Unity. These terrains are defined through a heightmap and can be quickly created, amended and automated through scripts in the editor. Besides, the render quality of geometry (pixelerror) and textures (basedistance) can be easily adjusted to the performance of the device. The compromise between quality and memory usage meant a resolution of one data point per unit and meter.
Image 1: The landscape scenes are drawn into the vectorized map of the game. The red area is loaded into the Unity editor:
Image 1: The terrain of several landscape scenes can be edited at the same time.
The world with its landscape, houses, streets etc. was divided into square tiles sized 1024 x 1024 meters (image 1). A square kilometer is big enough to comfortly accommodate a huge city or a motorway junction, and not too small to make life hard for the Level Designers. In hindsight though slightly smaller tiles would have been better to reduce the memory usage and the load times. In an open game world it’s common to load and display three or eight adjacent tiles. After running a few tests though, we confined ourselves to only one adjacent tile. This is possible since we can limit the camera position through our road course. The player won’t notice it in practice, and it saves a lot of effort and memory. We set the visibility range in the highest visual quality to 600 meters, which is quite remarkable for a mobile game.
To drive through a landscape tile the player only needs about 20 seconds using the 200 km/h (124 mph) racetruck. A mobile device of a lower performance class (like the iPhone 4s or Samsung S3) can only just manage to load and display the next tile in the background in time. If there is a massive loss of performance due to other apps, the truck would drop through the map. In order to not interrupt the gameflow, tiles must be loaded in the background – asynchronously. Unity supports this by saving each tile in a scene of its own. In each of these scenes there is only one root node to easily unload it. Alternatively, we could also work with a pool of instantiated objects and a separate load system. Unfortunately, when setting a heightmap of a terrain, a Unity application always stops for a few milliseconds. That’s why slower devices experience choppy playback from time to time when reloading scenes. And we forgot to factor in »Mr. Physics«. Most scenes include hundreds, if not thousands of static colliders for guardrails, street lanterns, houses etc. The internal integration into the room partitioning procedure of PhysX takes roughly about several milliseconds.
The landscape
Unfortunately, it’s slightly cumbersome to edit the terrain; fortunately, smaller tools from the Asset Store can help here. We tried out various terrain generators like »Terrain Composer«, but none of them were convincing. The floor consists of a mixture of small, repeatable textures (splat maps). The use of satellite photos is technically feasible and was also tested, however, it would take a lot of memory, even more media storage and way too much time to load.
In the course of the project we discovered several flaws of Unity Terrain:
The virtual camera in Unity Editor has a fixed setting based on which the terrain renders artifacts which make an exact placement of objects more difficult.
The built-in support of trees isn’t usable on mobile devices. If such a tree is placed on a terrain, the frame rate on the device is halved when rotating the camera.
Only a few floor textures per tile can be used. When there are more than three, the performance drops.
On some Android devices with Maali chips the floor texture is rendered incorrectly as a rough chessboard pattern. The workaround is to only use odd splat map indices. We moved the grass texture that’s used practically everywhere to second position.
The terrain offers a performant collision detection which prevents the truck from dropping through the ground. In rare cases, this became only active after a few frames on mobile devices, so that the truck dropped down nonetheless.
Image 2: The Unity Terrain as a heightmap can be automatically converted to a polygon-reduced mesh.
As a test, the finished Unity Terrain was transformed to static geometry in a complex automated process (image 2). The number of polygons was considerably reduced using the mesh simplification tool »Cruncher«; for optimization purposes, we tried to leave the triangles close to the street untouched and removed any triangles below streets. Altogether, the rendering of this approach was more efficient, but it also increased the compressed size of the app from approximately 2 MB per tile to 3 MB. In an uncompressed state, this corresponds to 5 MB and 7.5 MB. With further development work, one could thin the polygon mass through occlusion culling in a more targeted manner in order to make this approach the preferred procedure. Unfortunately, the Cruncher API doesn’t have enough impact on the process. In the distance, Unity displays its Terrain with increasingly less triangles (dynamic level of detail, ROAM), which may result in penetration flaws with the street. That’s not the case with a static mesh.
For tunnel entrances, no visual or physical holes can be drilled into the Unity Terrain, even if some assets from the store say so. In order to compensate for that, we drew the Unity Terrain in the tunnel below the street and replaced the area above the street with the terrain converted to the mesh. Meshes in Unity can comfortably be edited with plugins like »Mesh Toolkit«.
The street geometry lies 4 cm above the terrain. It’s the lowest value where no penetrations and no Z-buffer fighting occur on the mobile device. It turned out that the adjustment of the terrain to the street takes a long time and could not be performed manually with the same precision. A lot of effort was put into developing a tool which ensures a nice adjustment of the roadside as well (image 5).
Terrains in general take up a big part of the computing power on mobile devices and therefore have to be considered thoroughly.
Automate
In a six-minute driving mission with an average speed of 100 km/h the player covers ten tiles. As a long-term motivation we created a road network with 1,300 streets on 180 tiles, which took several man-years to do! I would like to take this opportunity to thank the Level Designers for courageously hanging in there during this almost endless task. A manual correction, for example exchanging a certain type of tree, would be prone to errors and would take days if not weeks! That’s why scripts are required to work through all tiles. One example is the above-mentioned conversion of the Unity Terrain into static geometry. A set internal structure of the tile scenes is always helpful for scripts. By the way, only a few dozen scenes can be loaded into the editor at a time because the maximum of approximately 64,000 phys-ics objects in Unity 4 is rapidly reached.
Unfortunately, Unity 4 can only edit one terrain at a time. A tool has been written for comfortably and simultaneously displaying and working with several tiles (image 1 and 3). The common boundary of two terrains was smoothed semi-automatically (stitching). When saving tiles a lot of integration tests are run, for example, to check if streets are connected correctly. It’s important to detect potential errors immediately. To completely drive and test all routes in both directions takes at least one day. Unity 5 now supports multiscene-editing.
Image 3: the internal Tool Map Editor offers comfortable management of all landscape scenes.
Light and shadow
A real-time shadow requires considerable performance from mobile devices. In terms of a driving simulation, the limited depictive space of the shadows can be noticed, for example,
if in a parkway only the next two trees cast shadows, but not the other ones that are further away. Lightmaps for the terrains and all objects look great, but they require creation effort, computing power and memory. These two known shading techniques prevent Draw Call Batching (see below). That’s why we used some tricks here.
In the country, the terrain under each tree was darkened. A script or a Level Designer increased the weighting for the darkest floor texture around this spot. The measurement is free of charge; it requires no extra service. The result is similar to the simple blobs under the vehicles. A lot of houses and other objects in the countryside are often far away and require no shadows.
For the »City« scenario, a shadow through interpolation of the floor textures (blending) or multiplication with a factor per geometry node (vertex coloring) of the terrain isn’t sufficient. The one-meter resolution of the terrain limits the resolution of the shadow. The terrain is covered by the street, and bridges and houses etc. also cast shadows onto the street. For each city a lightmap was calculated and placed on a big transparent layer in the middle of the city (image 5). Even details like lanterns cast a correct shadow onto the floor. They don’t leave shadows on walls, but this situation doesn’t occur anyway. The lightmap is like an IGeL service and involves extra costs, e. g. one frame on slow mobile devices. One restriction is that the sun can’t be too low and can’t move around, and cities must largely be built on one layer. All this won’t really be noticed by the player. In terms of the terrain version with static geometry, we calculated the shading (not shadows) offline and wrote them into the geometry nodes in order to relieve the actual terrain shader.
One way to implement shadows, which is useful for barren landscapes but rarely applied, is the use of precalculated shadow flags. Since objects can be placed facing any given direction, the shadow of e.g. a house can be precalculated in eight directions and projected on transparent geometry. In many cases, a number of generic shadow forms is enough, and through rotation and scaling of the geometry the shadow can be fine-tuned. However, the shadow flag approach already failed due to the creation effort, selection and arrangement and especially due to the positioning in regards to the mountains. Altogether we’re happy with the shadow, even if our approach doesn’t take special cases like tunnels into account.
Image 5: An intersection with traffic lights and car AI.
Streets and traffic
There have been some assets and software for the simulation of car traffic; however, none of them were designed for a massive game world and dynamic reloading. Programming dynamic and credible car traffic took several months and should not be underestimated! The implementation of the road network and the car AI was the subject of numerous discussions and meetings.
Streets are defined through control points which – in the correct order – form a curved line or space curve (spline) and thus constitute the center line of the street. This logic information later becomes important for the car traffic, the map and the path finding. For each individual lane a space curve is derived with a distance of a few meters (image 5).
The original idea to create geometry and crash barriers via control points whilst driving soon turned out to be impracticable. The integration of the »EasyRoad« asset for the static creation of streets and their geometry proved to be a catastrophe only after production had already started. We fixed bugs in hard-to-edit source code (obfuscated code) and had to live with bugs in the libraries (dll).
In hindsight we would write the required functions for the street creation, the adjustment of the terrain, the creation of crash barriers, noise barriers, lanterns etc. ourselves. Due to their number of polygons and colliders, the crash barriers proved to be a real performance killer, especially in curves. Another idea that was tested and rejected was to replace the many colliders by an invisible strap that would pull the truck to the center of the street. The result were very funny physical effects which, however, didn’t do justice to the term »simulation«.
Intersections and motorway exits were built using a 3D modeling tool. In order to avoid having to form the terrain for bridges in a way that it fits exactly the streets, the terrain surrounding the bridge was created as a geometry. To place it, a script would press the Unity Terrain into the form from below, just like making a sand cake, and the form would then be removed.
The first implementation of the car AI was physically realistic, but it overstrained the mobile processor. Even when a car slipped into the ditch, it often managed to make it back onto the street. For the second implementation, the cars were pulled by an imaginary string, but it failed due to one little detail: In each frame for every vehicle the position is calculated on a parameterized space curve (spline interpolation), which considerably strains the CPU. We therefore had to limit the number of cars to 25. A linear interpolation between precalculated close control points on the curve would be an improvement. The constant assigning of the road section that’s closest to the truck strains the processor, too. It is required both for the cars to brake in time and for the path finding. Algorithms from scientific articles were analyzed and programmed for the optimal calculation of the shortest distance of a point to several space curves.
What’s really disadvantageous is the often demanded option to nest prefabs in Unity. With regard to the »Intersection« prefab, one would love to set up the traffic lights as well as the actual lights as prefabs. In my opinion, that’s the only real disadvantage of Unity Engine.
Performance
A tile has 500 to 5,000 objects on average. The system for display accuracy depending on the camera distance (level of detail) included in Unity 4 is too slow in this case. Objects that were close to each other were logically grouped in a particular system and switched on or off together in regular intervals, depending on the camera distance. A minor disadvantage is that the objects’ popup distance slightly varies. Unity 5 has improved this system.
The multitude of game objects has a negative impact on the performance, especially in cities.
Unity can precalculate object occlusions for different camera locations offline (occlusion culling), which almost doubles the frame rate in cities! Much to our regret, this function can’t be applied to asynchronously loaded scenes. The Asset Store offers several systems for occlusion culling, which, regrettably, aren’t sufficient for our purpose.
The models and textures were created with foresight. All houses in the country, in industrial areas and in the cities only use a relatively small texture atlas (image 4). This allows for fast rendering and keeping the memory usage low. Some texture areas are used by various objects. Depending on the area the player is currently in, ideally only one to two of these atlases are in the cache at any given time. In addition, there is an atlas for all traffic signs. A high-rise could easily be coated with small tileable textures. Such textures were only exceptionally used for crash barriers and in order to keep the number of textures to a minimum. For performance reasons, native or complex shaders were only used for vehicles and water surfaces.
Image 4: Even without tiling, a lot of different houses can be textured by only one texture.
For performance optimization reasons, Unity can combine the geometry of several objects in one big object (vertex buffer combination) offline (draw call batching), distinguishing between two variants: The first one where objects aren’t moved during runtime or amended in visibility status (static batching), and the second one where this doesn’t apply (dynamic batching). The first one is recommended for the largely unchangeable objects of a landscape, however, only under certain conditions by Unity: All objects in a batch must use the same material and mustn’t have more than 300 vertices. The graphic designers weren’t quite fond of this threshold; yet it’s important. If necessary, a complex object must be divided into two small objects. Transparent objects practically aren’t batched and should generally be used sparingly on mobile devices. Further conditions are stated in the documentation. Activated »static batching« increases the frame rate significantly. One step further can be taken when combining the geometry offline. This would increase the frame rate even more, but counters the selection of objects located outside the camera (frustrum culling) and interferes with the mentioned adjustment to the camera distance and device performance. A performance factor of 4 lies between the slowest and the fastest supported smartphone. That’s why we offer the player three performance settings.
TruckSimulation 16 is not only just a driving simulation. What wasn’t addressed is that the players can build their own logistics company by establishing branches and hiring drivers.
In conclusion it can be said that open game worlds on mobile devices are still an adventure and increase the development time significantly.
Christoph Ender
TruckSimulation 16
In TruckSimulation 16, the player takes on the role of a logistics operator. Players can do deliveries themselves or later, in a management part, hire employees who also do deliveries. The game includes seven trucks and major parts of Central and Western Europe with over 20 cities.
- Platforms: iOS and Android
- First publication: November 25, 2015
- Publisher: Astragon
- Facebook: /TruckSimulationMobile
- Website: www.trucksimulation16.de
Read more about:
BlogsYou May Also Like