46 Tips & Tricks for 2D mobile Performance in Unity

来源:互联网 发布:爱宕 犬 数据 编辑:程序博客网 时间:2024/06/06 19:10
If this is your first jump into the world of Unity, my firsttip (this one’s a freebie) is to stop trying to use it like otherlanguages and environments. You will be using GameObjects, you willbe adding multiple script components, and you will have to thinkdifferently. When I first started, my approach was to largelyignore prefabs (or use them like Flash’s display list) and get acopy of Box2D running. Painful as it is to deviate, get ready toput in some work!

Without further ado, let’s get started:

Physics:

-Use the built-in physics.
It might seem like a waste of cycles to have a fully 3Dphysics engine running the show for a 2D game, but bear in mindthat the Nvidia PhysX engine will be running in Unity’s nativecore. We’re talking about a hyper-optimised engine maintained bylarge professional teams, and not a hobbyist 2D engine. Freeze Zposition and X/Y rotation for that 2D feel. 

 -Try to use a 1/1scale. 
By this I mean, 1 unit = 1 meter.  You canuse larger or smaller scales, but you’re likely to encounter someweirdness when it comes to collisions, and the speed at whichobjects fall. Remember, a grand piano without air resistance willfall as fast as a dead baby but if everything is large, it willseem slow. You can fiddle with gravity, but again, you’re runningthe risk of messing with collisions. I like to use a 1/32 scalecompared to my sprites with a cam size of 160.

-Get your object Mass right. 
Just like with scale, if you have a grand piano weighing 2gmor a dead baby weighing 500kg things are going to getunpredictable. Try to keep it realistic-ish.

 -Mesh colliders can be slow compared toprimitive box/sphere colliders. 
While a sphere may have many more verts than say a cube, theuniform distance from the centre would make it massively easier tocalculate than lots of individualtriangles. 

-You can simulate more complex shapes by combining primitivecolliders. 
If you have a parent object with say a Box Collider andRigidbody component, you can add child objects with just a BoxCollider. The entire object will collide like one solid multipartobject. 

-Continuing from the above… 
Rather than having several of these linked together, you canadd more child objects with RigidBodies and Colliders, and useJoints to connect them to the parent object. You could build acompound car for example with a parent body to move the entirething.

 -Multiple basic joints are not supported onone game object… 
…but multiple configurable joints are. Rather than having anetwork of jointed objects you could for example have a spring anda slider from wheel to axle cutting down on a suspension object inbetween. 

-Objects with a collider but no RigidBody are consideredstatic. 
Moving these is expensive, so if you’re creating them withcode, add the collider and physics material (to the collider) afterpositioning.

-While it’s considered good practice to keep your solveriterations constant…
…you might find it beneficial to use the full amount onlyevery 2nd update. I.e.  8,4,8,4,8,4. If thisalleviates some of the processor load the solver for example maynot have to skip iterations, and will actually provide a moreconsistent simulation. I said it was going to becounter-intuitive.

-While the use of Interpolation and Extrapolation onRigidBodies is discouraged en-masse..
…in some cases you might find that turning these on, andreducing the overall solver iterations provides a bettersimulation. Fiddle. 

-Lower your timestep! 
If you’re aiming for an unrealistic 60FPS and the phone isconstantly struggling, you’re best just to settle for a lowerframerate and give it some breathing room. I like to use a 0.03fixed timestep with a maximum of about 0.05. Again, it can beslightly counter intuitive decreasing timesteps to get higherframerates, but give it a shot.

-Timescale scaling.
This could help, depending on the feel your’e going for. Itsimply simulates more time passing between each iteration. Settingthis too high will obviously mess with collisions, especially if anobject has traveled say, a mile in one frame, it’s not going to hita damn thing.

 

Sprites & Textures

 My engine uses a hybrid system of SpriteManager2/EZGUI and RageSpline for sprites, but I’ve used 2D Toolkitand much of the following still applies.

 -Easy on the fillrate! 
It might seem obvious but if you have a 64×64 image, with onlythe top left 32×32 filled, that’s still a 64×64 sprite. Trim yourtransparent images where possible. Lots of libraries will do thisautomatically.

-Hide sprites you’re not using. 
Make a reference to them and set them active = false; Theywon’t be drawn when offscreen anyway, but something has todetermine whether or not they’re visible and chances are you knowbest, especially when one sprite may be fully hidden behind anotherand is still drawn.

-Batching is your friend. But notalways. 
If you have 40 collectable coins in your level, all using thesame sprite then batching will use the one texture source multipletimes on a  giant mesh, saving on draw calls. Drawcalls=time. In some very rare cases the batch calculations can be ahinderance, depending on how your game’s set up, but if that’s thecase, chances are you’re doin’ it wrong.

 -Resize your sprite’s Quad (the spriteitself) rather than its transform. 
If you have say a sprite component on a GameObject, thenresize the GameObject’s transform, you’re going to break batchingon that sprite. Instead consider the next point. With SM2 forexample, you’d just set the Sprite/PackedSprite’s “Width” and“Height” properties in the inspector.

-If you have a 64×64 sprite, on a 6px widecube… 
…then it’s going to look like a small version of your image,but upon zooming in, you’ll see that the full 64×64 sprite has beenUV mapped to the cube in perfect detail. Remember what I said aboutfill rate. Unless your’e zooming in and out, you might not want touse such a large texture.

 -Use a Sprite Sheet/Atlas wherepossible. 
This one ought to go higher, but what the hell. A sprite sheetwill allow you to use commonly grouped items like your character,coins, platforms, etc in a single image/texture. Why? Less drawcalls! The same part of a texture can be UV mapped to differentparts of a 3D shape multiple times. I.e. if you were modelling ared and white stripy candy cane, you’d draw one white and one redline, then apply them multiple times. This is a similarconcept.

 -Use the rightshaders! 
There’s no point using lit shaders if you’ve no lighting, andthere’s no point using a transparent shader on a solid squaresprite. You can find dedicated mobile shaders in the Unity Store,by googling and doing a little copy-pasta via MonoDevelop or usingthose that come with SM2/Unity. As of 3.5 I believe the defaultshaders do a pretty decent job. Coming from various otherbackgrounds it might be easy to underestimate the importance ofthese even in a 2D environment.

-Do you really need antialiasing/filtering on yoursprites? 
Be sure to check on your target device. Some things will lookpretty horrific scaled up on your monitor, but absolutely fine onthose tiny high-density screens. Give it a shot, and remember toapply changes to your Sprite Atlas where possible.

-Easy on the compression! 
DXT (DirectX) compression will do a fantastic job on your PC,with hardware decoding, but mobile devices lack this hardwaredecoder and will have to do it in software. I.e. Slowly. GenerallyIOS devices will support hardware PVRTC compression and AndroidsETC, and keep in mind what I said in the last point. DXT might befine given that it offers better clarity during say level loads,but you certainly don’t want to be decompressing them duringgameplay.

-Do you need Mip Maps?
Mip maps are scaled down versions of a texture stored withinthe compressed texture itself. So depending on how far away youare, a lower res copy can be used. Obviously this takes more memoryand more decompression time. You probably don’t need ‘em for a 2Dgame. 

-Conversely…
…rather than using giant sprites on a non retina display andtiny sprites on a  retina display, it might beworth your while making a small and large version of textures andusing each accordingly.

 -Read/Write enabled textures generate asecond copy. 
Second copy needs more memory. In most cases, you can justleave this turned off.

-Tinting a sprite will break batching… 
…and create a new copy of the source texture in memory. Avoidwhere possible, or try to pre-make any colors you’ll need! E.g. ifall your Numbers in a text sprite sheet are to be red.. do it inphotoshop.

 

Loading, Saving and Object Access:

 

-Do you really need to recreate your GUI for eachlevel? 
You can hide it and have it persist when loading differentscenes, reducing loading time.

 -GameObject.Instantiate() isslow! 
One common technique (which proved absolutely vital in TruckToss) is to create a pool of objects during loading. E.g. 4 of eachenemy type. When an object’s no longer needed, disable it and shoveit back in the pool instead of recreating it. So you’d have afunction along the lines of MakePrefab(“path/to/prefab”); whichwill only only call Resources.Load() provided there are none in thepool.

-Resources.Load() is even slower! 
This function does not cache, and involves reading from thedevice’s HD or equivalent. Ideally you want a hybrid pool system ifyou’re doing a lot of loading\unloading. I.e. your standard poolsystem which preloads objects,  but when theinstantiate function is called, it keeps a different copy in adifferent list. Whenever instantiate’s called and there aren’tenough in the pool but there’s a copy in the spare list,Instantiate from that rather than doing a Resources.Load freshagain.  This is a balancing act of memory use andprocessor use, so target it for your device.

 -GameObject.Find() andGetCompoenent().. 
…are slow (You saw that one coming, right?). If you’re goingto be using an object or component repeatedly, then it makes senseto create a reference to it in a local variable rather than lookingit up repeatedly.

-Reflective functions can be noticeablyslower. 
Reflection is the ability for a language\code to look withinitself and get method names\types\scope etc and potentially alterthem. I.e. calling a function by string name, or using delegates.Try to avoid this kinda of behaviour for performance criticalcode.

 -The garbage collector is sloweryet. 
It has to scan trees of objects looking for orphaned classesand objects, and ‘islands’ of objects with references only to eachother, determine how long they’ve been that way, and then free upthe memory. Sure you can call it manually, but that’s generallymore of a hint to it than a command, and shouldn’t generally beused during gameplay.

 -Using too much memory…
…will cause IOS devices to crash\quit and your app won’t beaccepted to the app store. That’s actually secondary to the pointthat when memory’s low, your game will slow down dramatically,especially during garbage collections and when you’re trying toinstantiate new objects.

 

Sounds: 

-Set your BG music to decompress onload. 
Anything you’ll be using a lot should probably be decompressedon load rather than streaming from the disk (which is slow and canbe especially troublesome on Android.) This is another tradeoffsituation however, given that decompression can take time andmemory. Balance it!

-Infrequently used clips… 
..may be left compressed in memory, especially if there arelots of them.

 -Force to mono? 
Yes yes! Unless you really want stereo and you’ve got somebitchin’ music or sound effects, you’ll save yourself some space onthis one. Remember the phone has a mono speaker

-Hardware decoding is faster. 
Well, that’s a generalisation, but faster isbetter. 

UI: 

-Unity’s UI system is slow. 
Where possible try to use a plugin or sprite package thatrenders to meshes in 3D space.

-OnGUI is slow! 
Even in a blank scene you can see it spike. Try to have nomore than one of these in your code, and centralise branches outfrom it. It’s fairly easily done.

-Try to keep your UI animations in FixedTimestep()functions.
This way they’ll stay consistent across multiple devices andframerates. You might benefit from having all of your game logicfor every class branch out form a single base call toFixedTimeStep();

-Does your UI have to update *every* frame?
You might gain a massive performance boost by deferring it toevery 2nd or third frame. With truck toss, the game runs muchsmoother with the entirety of the game logic running on everysecond FixedUpdate. I know, right?

-Moar Cameras! 
If your game does a lot of scaling\zooming, then why have toscale the UI and risk breaking batching? Add another camera for theUI, and set UI objects’ layers to that of the camera. Again, itsounds like a lot of extra blitting, but could potentially speedyour game up with very little in the way of changes.

Build Options:

 -Disable theaccelerometer! 
In older Unities, that was done via #definekaccelerometer_frequency 0 in the AppControler class via XCode.Nowadays you can disable it from within Unity itself, and can freeup 2-3 FPS.

-In some cases OpenGL ES 1.0 or 2.0…
…will offer better performance on your device. This seems tovary between devices and Android\iOS. Try on as many devices as youcan.

-Strip things down! 
When your game’s running nice and stable, switch compatibilityto the .NET 2.0 subset, Stripping Level to micro mscorlib andScript Call Optimisation to Fast but no Exceptions. This willgenerate a smaller binary with less redundant code and debugsymbols. I successfully navigated that one without a stripperreference. Jugs.

-Target iOS 5 where possible. 
There really is no discernible speed difference. However, makesure your XCode project’s settings match, or uploading to iTunesmight fail. Perhaps it’s worth exporting another project.

 

Bonus Content – Building Faster!:

-Symlink Unity Libraries…
…from the build settings where possible, this will savecopying more files over to xCode, by effectively creating ashortcut. Much love for symlinks.

Set your “Debug Information Format”
From within Xcode (Build Settings) to STABS, otherwise havefun watching DWARF taking ages. Stabs..dwarf… Lols…

-Sometimes using “Build for “…
…within XCode will build and run faster. Delete the previousversion on your device and give it a shot. I have no idea why thisis so, but you’re welcome.

 

Bonus Content 2 – Faster physics modelling:

 This is a fun little technique I use toconvert edge chains to solid blocks!

Create a fake edge chain, where each link between 2 points isactually a stretched out, rotated cube parented to something,forming the outline of shape of your choice(cloud\star\car\whatever). Hit play, and while the game is running,drag your chain back into the editor to make a prefab. You now havea group of parented objects you can fill in and make solid. Thislets you use your own code to generate physics shapes.

 

And finally, 
Break every rule. In the spirit of hacking away at things, youmust fiddle and see what’s going on.If something seems like a sillyidea at first, you might just not have thought about it from allangles. 

Let me know if you’ve anything interesting to add. Happyhacking!
0 0