
Sonic Dream Team is a platformer developed by Hardlight and published by SEGA. The game, an installment in the Sonic the Hedgehog series, has Sonic and friends speeding through the evil Doctor Eggman’s twisted dreamscapes to thwart his quest for world domination.
Apple Arcade demands that studios hit the same performance targets across all supported devices, which puts more pressure on the team to optimize so the game looks great on everything from an iPhone 6s Plus to an iPhone 16. Here’s how they hit the required frame rate on both low-end and high-end devices.
How does a team optimize to hit the required frame rate on both low-end and high-end devices?
After 10 years with the hedgehog, Hardlight wanted a new challenge by amping up the storyline, involving more characters, and blending action and adventure styles with high-fidelity visuals – that looked great on the full span of devices where Apple Arcade games can be played. While working towards these goals, the team encountered CPU- and GPU-related performance issues that drove them to continue optimizing.
This content is hosted by a third party provider that does not allow video views without acceptance of Targeting Cookies. Please set your cookie preferences for Targeting Cookies to yes if you wish to view videos from these providers.
Reduced CPU frame times from 52 ms to 16 ms on medium- and high-powered devices
Halved the build size on iOS from 4 GB to 2 GB
Reduced shader variant runtime memory from 1 GB+ to under 100 MB
To analyze performance, the team made use of the various analysis tools Unity offers – in particular was the Profiler, Frame Debugger, and Memory Profiler. These helped the team to better understand where issues came from to hit 30 fps and 60 fps across devices.
Rendering took a large part of the total frame time budget, especially on low-end devices. The team relied on SRP batching to help reduce this cost.
“We chose the Universal Render Pipeline (URP) due to its modern rendering approach and its continued support into the future from Unity,” says Fraser Hutchison, a technical artist at Hardlight. The team prioritized features like SRP Batching and Shader Graph to allow for new ways to push their visuals on mobile devices. “The SRP batcher did a lot of heavy lifting for us, and gave our artists the flexibility to not worry about material constraints like with standard static batching,” Hutchison continues. “We averaged 100-200 batches, primarily being in the opaque queue, which worked well.”

The team also reduced reflection probes and used a material sorting queue and baked lighting to increase batching efficiency and reduce their rendering cost.
Due to the size of their levels, they implemented a custom hierarchical volume culling system to reduce the rendering overhead. “Each level was split into large chunks, which we call ‘islands,’ and was disabled or enabled depending on the player’s position,” says Hutchison. “This also meant we could disable animators and particle effects for those islands too. Overall, this was a lightweight solution that fit our needs well.”

To ensure consistent gameplay across all iOS devices, the Hardlight team set its physics frequency to 60 Hz, rather than the default 50 Hz. They used FixedUpdate to enforce an expected time-step between a particular object’s updates to enhance determinism.
When frame time increases significantly due to performance issues, multiple FixedUpdate calls within a single frame are made in Unity to maintain the desired physics update rate.
“Eventually, the simulation was unable to process all the required updates in time, leading to even longer frame times,” says Louis Macan, Hardlight’s chief software engineer. The impact of having the same physics time-step led to an increased amount of per-frame FixedUpdate calls on low-end devices, which run the game at 30 fps.

To maintain a good frame rate on older devices like the iPhone 6s Plus, the team optimized every FixedUpdate. While profiling an individual FixedUpdate call, they noted that the vast majority of the FixedUpdate time was held by update calls from hundreds of instances of scripts. Even though these scripts quickly exited via an early-out condition, the FixedUpdate calls still executed.
“Even if it looks like the FixedUpdate isn’t doing anything if a condition isn’t met, every Unity event function that is declared in a script will cause an overhead,” says Macan. “While the overhead is small by itself, the thousands of Update, FixedUpdate, and Late Update calls executed by scripts added during level creation had a large impact.”
Fixing this required a combination of approaches. Firstly, a manager was added for the scripts to register themselves to, and the manager would tick each object in turn at the required update frequency. This meant that all Unity event functions could be removed and were no longer taking up time on the CPU. Finally, objects that didn’t interact with the player’s physics were set to tick on Update, halving the computational cost on low-end devices.

The team also noticed that the excessive amount of particle systems in a scene affected CPU performance. As part of a particle pooling system, the team enabled or disabled particle systems using the ParticleSystemRenderer API through a custom ParticleEffectsWrapper component. With more than 500 particle system updates being processed on the main and worker thread, this took about 5.2 ms of CPU frame time.
“Unity’s Visual Effect Graph was not an option for us on this project, as many of our low-end devices do not support GPU compute which that system relies on,” says Hutchison. “If we could have used it, we would have benefited from its better culling and instancing tech.”
Particle systems in Unity can either be procedural or non-procedural. A procedural particle system can be freely rewound or fast-forwarded to any moment in time.
“With procedural, Unity can safely cull the particle system and all its associated processing when it’s offscreen, and then simply fast-forward it to the correct state when it becomes visible again,” says Hutchison. “This can generate considerable performance gains. However, this is often an overlooked part of particle effect creation and it’s very easy to accidentally make the effect non-procedural.”

To avoid this, the ParticleEffectsWrapper component not only culled particles based on camera frustum, but also defined custom render bounds so that even if a particle was marked as procedural, manually set bounds could still cull the system.
“With a large number of particle systems in a scene, it became important to make sure that as many of the systems as possible were procedural, and, if not, had their render bounds set in the ParticleEffectsWrapper component,” says Macan. “This prevented unnecessary computations and draw calls, which significantly improved performance.”
Alongside culling, the team prioritized good creation practices for effects. They used only as many particle systems as necessary and clamped max particles to reduce alpha overdraw and memory allocation. They also focused on working with their particle effects’ texture atlas and avoiding excessive duration times. They also enabled dynamic batching through the ‘Show All Hidden Properties’ toggle in the URP render settings, and this minor change helped them to reduce the draw calls of particles and speed up rendering.

The vision for Sonic Dream Team required advanced graphics and quick gameplay. For customization, they used render features for certain effects like Screen Space Ambient Occlusion, custom fullscreen effects, and screen space decals. This created rendering delays and memory issues.
“During development, we discovered that when using depth-based screen space decals for character and enemy blob shadows, the render feature was causing an unnecessary depth-normal prepass,” says Hutchison. “This meant a large portion of rendering time was spent on unnecessary work and increased render target memory, as we didn’t require the normal buffer. We worked with the Unity team to resolve this, and the fix was added in an engine patch release.”

Shader prewarming was also a concern, with significant CPU/GPU spikes throughout levels causing stutters. “In order to effectively prewarm shaders on Metal, shaders must contain all required keywords and have the specific vertex layout group of each mesh they will render on. This meant that Editor-only caching was not possible,” says Hutchison.
To combat this, Hardlight’s engineers created a system to “flash render” all objects in front of the camera at load time, behind the loading screen.
“Our island culling tech provided easy access to lists of all renderers in the scene. Doing this on-device meant we had all the correct pipeline state data and keywords from the quality settings before the player officially entered the level,” says Hutchison. “This did slightly increase level load times, however, it was worth it for the player experience.”
While happy with their results overcoming shader prewarming, Hutchison is looking into other options moving forward. “Since the game’s release, Unity has released PSO caching and prewarming through GraphicStateCollections, which we want to use in the future.”

During development, the game used 1.35 GB of memory at runtime. Considering the iPhone 6s Plus had only 2 GB of physical memory, this could have led to issues, including the risk of the operating system terminating the application.
“Asset duplication was a big hurdle. The game had over 3,000 duplicated assets, which bloated its size and caused the same assets to be loaded multiple times during runtime,” says Macan.
At the time, the Unity Addressable System only managed a small fraction of the game assets. This led to cases where assets were being referenced both in the binary and through Addressable Groups, causing duplication.
Early in development, the team used a single Addressable Group and set it to pack separately. This meant that each asset in the group had its own separate AssetBundle. While useful in specific cases, this granular approach also led to a large amount of duplicated assets. Ensuring that all assets were Addressable and having a structured approach to Addressable Groups halved the build size on iOS from 4GB to 2GB.

Shader variants were a big area of focus for the team throughout the game’s production, with runtime shader memory ballooning to over 1 GB. The number of variants also increased build times. They tackled the problem using various methods – mainly shader stripping with IPreprocessShaders, Unity’s pre-filtering, Unity’s dynamic shader loading, and reducing shader branching.
“We used our own custom IPreprocessShaders stripping implementation, which globally removed shader keywords from all shaders and a local approach through Shader Control from the Unity Asset Store,” says Hutchison. “Being more intentional about when to use a keyword in shaders was also a big win.”
Their shaders used Shader Graph, which inherently includes keywords predefined by Unity, and adding extra keywords to these had an exponential effect. Swapping some logic to dynamic branches or completely removing keywords significantly reduced the total variants.
“With all of these methods combined, we decreased runtime shader variant memory to an average of 100 MB or less,” says Hutchison. “We also used Unity’s Project Auditor, which provides a global view of all assets and settings in the build. Using this in combination with the Memory Profiler, we identified textures and meshes that benefited from improved import presets. This further reduced memory pressures from art and audio assets.”

Over more than a decade, Unity and Hardlight have built a shared layer of tech that the studio counts on. “We’re still supporting games we made in Unity 10 years ago,” says Macan.
Unity has added so much over the years, he noted, that the studio simplified its maintenance by dropping some native code for Unity’s API. “Unity handles so much of the low-level device support, we can focus on making the actual game. As tech evolves, I'm confident that Unity will help us keep up,” he exclaims.
Sonic Dream Team proves you can still teach an old hedgehog new tricks. And, just like Sonic, Hardlight and Unity have a strong track record and a bright future.
Start creating games that compete with, and surpass the quality and success of big studio releases with help from powerful tools, support, verified partners, and a vibrant community.