We also used dynamic resolution scales throughout the project, too. When there is a lot on screen, the render scale drops, and when there are only a few hero objects on screen, it goes up. This way, when the viewer focuses on one thing, they get that thing at 100% quality. But, when they focus on a large group of objects (i.e., hundreds of jellyfish), they get a real sense of presence and a feeling of being in a big crowd.
In order to draw things like the player hands, player avatars, and a few other VFX objects, we needed access to VFX Graph. This was, in part, because the assets were originally authored using VFX Graph and utilized a few bespoke scripts. Essentially, rebuilding all the VFX would have been a huge time constraint. In order to get VFX Graph working on Open GL 3.1, we had to disable cubemap arrays in the package (a feature unsupported by GLES 3.1). Essentially, we backported the VFX Graph package from Unity 2022 to 2021 LTS and adjusted a few bits to get it working. Once it did, it proved to be very flexible for our needs. A few of the more recently implemented features cut the need for a few scripts and custom-made bits and pieces, which was helpful.
Beyond that, our developers disabled shadows and amended lighting settings to keep dynamic lighting (but not have too many lights affecting one surface at one time) in order to keep things cheap. We also staggered spawn times for things to prevent GPU hitching and went through a list of standard practice for getting games running in-frame on the hardware – reduce texture size, reduce shader complexity and expensive ALU math operations, reduce texture taps, pack textures where possible, use vertex animation in place of skinned meshes, and, where possible, make things simple. A lot of the techniques from the early console era were employed in order to get stuff running on standalone devices.
Overall, we tried to use expensive things sparingly. When we did use them, we made sure they were center stage so that no performance overhead was wasted.