Crave For Games

The text below is one of the notes from the StoneDrop development log. If you want a list of only technical articles you can go to the Articles page.
Performance optimizations in the game Assassin's Creed Identity

Key points from Ubisoft Blue Byte team talk at GDC about their way to overcome performance limitations on mobile devices.

Video 1. Assassin's Creed Identity: Creating a Benchmark Mobile Game. August 2015, GDC

Project overview:

- made with Unity

- 450k+ lines of code

- project build time ~30 minutes (iOS) (twice if profiler attached)

- one environment asset bundle takes up to 9 hours of build time

- self-imposed runtime memory budget: 40 Mb mesh and 60 Mb texture data on one level

- used Unity built-in features: static batching, LightMaps, LightProbes, NavMesh (OffMeshLinks for climbing).


Customized rendering pipeline for characters:

- glossy surfaces, AO and specular maps, LODed models

- crowd: low-poly characters are mixed from different parts (heads, body parts, accessories...) and colors to increase diversity, limited animations

- enemies: med-poly, only heads and some accessories randomized, animations based on enemy type

- main characters: high-poly, customizable color, outfit, weapons and faces; 800 animations, 2.4k animation transitions.




- environment art, character art and gamedesighn are separated and delivered to a client via asset bundles

- level art project is a separate project for artists and game designers

- game logic separated from Unity logic: ECS rebuild for game logic, implemented in separate .Net 3.5 compiled DLL


- EntityManager holds a list of Entities, Entities have Components

- TurnHandler keeps a distance buckets (entities based on player distance) and a visible bucket

- TurnHandler distributes available turns within a frame to all components based on a bucket the entity is in: visible entities never skip updates, distant buckets may be updated once a second

- pros: full control over entities; custom game loop entry points; OOP approach and heavy usage of interfaces

- cons: code complexity; impossible to debug on device because of app size; il2cpp problems with generics and interfaces

- AI is driven by behavior trees

Character pooling:

- all entities are prioritized based on gameplay relevance and only visible ones are processed in rendering

- there is a HumanoidCache - collection of body parts baked in large meshes within GameObjects

- logic pooling configured to max 10 visible gameplay-relevant entities + 5 cached


- switched to Protobuf serialization - better than Unity serialization both in terms of memory and speed


- 100+ gameplay-relevant entities in a mission (+crowd)

- 4 ms simulation per frame (on iPad 4) +8 ms for turn updates each 100 ms.


Editor extensions:

- custom node-based behavior tree editor with debug function (visualization during gameplay)

- mission editor; missions are set as a hierarchy of GameObjects with components describing objectives and its data

- mission graph flow and data visualization written to debug mission in runtime

- crowd editor: footsteps (probability of crowd walking here) map drawn with a brush, then it converted to node-like structure with weights like Voronoy diagram which is used by crowd agents; all editing is possible without game restart in the editor

- editor preview of possible humanoid parts combinations.


C# for Unity:

- Unity mono is not equals to Xamarin mono - features almost frozen at v2.6 (mid 2010)

- Unity mono source code available at[1]

- everything that is not in Unity's micro corlib brings a lot of code to a project

- beware interfaces and generics with AOT on iOS/IL2CPP

- boxing overhead may be noticeable to performance

- avoid object parameters - in il2cpp builds every possible combination of types is generated in code, so try to restrict generic type parameters as much as possible

- avoid foreach

- allocation differs on device - use remote profiling

- avoid using reflection, but if you do - use link.xml to add linker exceptions to enforce the code to be compiled into player (when code stripping enabled)

- avoid value-type generics

- keep in mind any texture take twice memory while loading.


Link 1:

This article in social networks: