Developing The Crew 2 came with its own set of challenges, challenges that emerged both from creating a follow up title to the original online open-world of The Crew and from introducing a suite of new features into the Ubisoft-published racing game.
Like The Crew before it, The Crew 2 offers its players the ability to race from sea to shining sea in a slightly modified version of the United States. The open-world map isn’t a 1:1 recreation of the USA as we know it, however, the team at Ivory Tower instead created a unique version of the country built around the USA’s actual geography but with the scale of states and landmarks morphed to offer players a traversable but easily recognizable spin on The States.
Of course, creating any open world comes with challenges that developers have to address, especially when players are moving through that world at breakneck speeds. Building the streaming technology to handle a massive world where the environments just fly by required the team to pay special attention to how objects were rendered in both the distance and near the player through a number of tools and graphics engine considerations.
Gamasutra recently sat down with David Guillaume and Carl Pedimina of Ivory Tower to dig the technical aspects of creating an online world that is both massive and detailed. The full Q&A below explores lessons learned by the team during development, ranging from how and why devs zoned environments into areas and sectors to lighten the computational load to tips on how fellow game developers can address the issues that creep up during development of their own open world games.
David Guillaume, Art Director
Carl Pedimina, Lead Engine Programmer
Guillaume: Our very first endeavor was to learn everything there is to know about the country’s geography, through an impressive collection of books, documentaries, maps, and other documents. Once we studied all these resources, it was clear for us that we needed to make a selection of places and environments in order to deliver the best experience, one that would be our own take on the US.
We carefully picked the cities, natural wonders, and iconic landmarks that we would recreate in the game, according to four major criteria:
"Every choice we made focused on one result: giving players the best experience of the USA, [gamepad] in hand."
One final step consisted [of] organizing research trips to the US in order to study the terrain up close and capture its essence. With such a rich database to inspire ourselves from, we could recreate an interpretation of the USA that would fit our needs and player needs.
Guillaume: This wasn’t an easy task, indeed, and we had to proceed with a method and according to a consistent set of rules. We started defining our world’s size, with the objective to enrich it with as much content as possible. Doing this, we had to make sure we were respecting some core constraints:
Every choice we made focused on one result: giving players the best experience of the USA, [gamepad] in hand. That’s why our interpretation of the country isn’t a simple copy-paste of reality: you have to get behind the wheel to truly discover it. For instance, the Grand Canyon in The Crew 2 has been scaled down ten times compared to its proportions in real life, and we decided to recreate only the most iconic section of the huge formation.
Pedimina: In the very beginning of the project, we tried to look at middleware like the Unreal Engine. It quickly appeared that this kind of game engine would not do. Our world is huge (approximately 120 km by 75 km) with a lot of different places and moods. Furthermore, the player can drive vehicles that can go over 600 km/h!
However, we did not want to reinvent the wheel. So we decided to take the basis of the Dunia Engine (used at Ubisoft for the Far Cry series) and revamp it to our needs. Eventually, we rewrote a lot of things like the world editor, the game editor, the world management, the streaming strategy, the graphics engine… and we used only 10-15 percent of the original engine but it allowed us to quickly prototype and build our own engine by providing solid foundations.
In such a project, you must quickly decide how your world must be partitioned and I think that there’s no good recipe that fits every game's needs. The core dev team was already experienced in creating and managing big game maps. The majority of us had previously worked on a racing game, so we did not start from a blank page. Our experience allowed us to quickly choose the best techniques for our project.
The world is subdivided in what we call “areas” and each area is subdivided into sectors. As you might expect, areas and sectors contain some kind of level of detail of the whole world, an area contains the low version of objects in a sector. A limited number of sectors and areas are always loaded around the player.
Because The Crew map is very big, we had to use low-resolution meshes for the far view of the world. These meshes were generated from the heightmap and optimized to have the least number of vertices. It worked surprisingly well and the player is not able to see the seams between the dynamic heightmap system used in areas and sectors and the traditional mesh used for the far view.
For The Crew 2, we added more level of details but it’s more an extension of the area and sector system to improve the view quality when using planes.
We also decided to divide the USA in “ecosystems”. An ecosystem can be seen as a library that contains the resources used by the specific places of the map. For example, you’ve got one ecosystem specific to New York, another one for New England… A few ecosystems can be loaded in memory at the same time. Ecosystems are hierarchical: a mesh in a child ecosystem can use the material of its parent ecosystem. But the opposite is not possible. The ecosystem system allowed us to have a lot of diversity by keeping the memory footprint as low as possible and [allowing] the reuse of resources without the need to load it several times.
We also extensively use object instancing: most of our buildings are made of basic blocs. It reduces the streaming pressure and the graphics engine can use hardware instancing to reduce the number of draw call and increase performances.
Pedimina: One challenge when managing a world this big is what we call floating-point number precision: the further you go from the origin of your world, the bigger the numbers you have to manage. There are guidelines and benchmarks we needed to follow in order to make sure to deliver a solid experience for players on all platforms, so this was something we needed to both prioritize in terms of attention and optimization. In practice, your objects start to “shake”, the different pieces of your vehicle do not join properly anymore…
"On a level-based game, it's rarely a problem. But when you stream a continuous world, your object's creation and destruction must be as light as possible to avoid stuttering and other annoying side effects."
There are a few solutions to this problem, but what we decided to do is compute the loss of precision each time we do mathematical operations on an object's position. It means that a position is no more stored in a single 3D vector but in two vectors: one for the position and the other one stores the loss of precision when doing mathematical operations. These two vectors are used by the game engine but also in different parts of the graphics engine, like vertex transformation for instance. We encapsulated all of this in a specific class to keep it user-friendly; most of the programmers use it like a simple vector class. To keep the computation overhead low, we use it only on dynamic objects. All static object positions are always relative to a sector or an area where we do not have a precision problem.
Another challenge, that is a bit less obvious when you first start to work on this kind of project, is object initialization and termination time. On a level-based game it’s rarely a problem. But when you stream a continuous world, your object's creation and destruction must be as light as possible to avoid stuttering and other annoying side effects. In fact, you have to twist your mind and think more data-oriented design than object-oriented design. Bonus: the use of data-oriented design [leads to] more efficient object updates, making your CPU happy.
And with The Crew 2, we had to step up our game once more! The addition of planes, the possibility to travel across mountains and oceans, and to navigate on lakes and rivers [requires] the graphics engine to go more and more physically based rendering, meaning more data to load. Furthermore, with the Fast Fav system the player can switch from one vehicle to another on the go and instantly. Last but not least, the support of 4K display involves specific hi-res resources. All of this combined had the streaming system quickly reaching its limits. We had to split big textures into several files: we now first load the low-resolution mip map, and load the hi-resolution mip map when we have time. We also had to use a streaming priority system to be sure to load mandatory resources first. We can also boost the streaming system and the resource post load processing in some places to avoid loading screens as much as possible and make the player’s experience smooth and seamless.
Pedimina: First, you definitely have to create tools that help artists and designers to quickly check if they are doing the things right from the very beginning of the project. Also, those tools need to allow artists & designers to work simultaneously in the game’s world.
Then there are basic things you need to determine:
Another [bit of] advice would be to avoid monolithic data. For example, split your texture mip map chain into several files: load the low-resolution mips in priority and load the high-resolution mip when you have time. The same technique can be used for mesh and other kind of data.
"All in all, I think the most important point is the tools. [...] Computers are here to do automatic tasks, so build as many tools as possible to help you out."
Design a streaming priority system as soon as possible, to make it as flexible as possible and allow the shortest loading time possible. Use and abuse instancing and data reuse. Try to use “dynamic” techniques at runtime. For example, you can create systems that spawn animals, litters, pollen, […] around the player. It gives a vivid and living world without impacting too much the streaming.
A few things you need to keep in mind and that apply to all games:
All in all, I think the most important point is the tools: managing a big map can be very tedious and hard to tackle. Computers are here to do automatic tasks, so build as many tools as possible to help you out: checkers, automated processes… There are so many challenges in game dev, so every bit of help is welcome!