Search Unity

On the Spotlight Team, we work with the most ambitious Unity developers to try to push the boundary of what a Unity game can be. We see all sorts of innovative and brilliant solutions for complex graphics, performance, and design problems. We also see the same set of issues and solutions coming up again and again.

This blog series is going to look at some of the most frequent problems we encounter while working with our clients. These are lessons hard won by the teams we have worked with, and we are proud to be able to share their wisdom with all our users.

Many of these problems only become obvious once you are working on a console or a phone, or are dealing with huge amounts of game content. If you take these lessons into consideration early in the development cycle, you can make your life easier and your game much more ambitious.

Transform Changed Messages

Every time a GameObject moves, altering its transform, we need to let every game system that might care about this know. Rendering, physics, and every parent and child of the GameObject needs to be informed of this movement in order to match. As the scope of a game increases, and the number of game objects skyrockets, just the overhead of sending out all these messages can become a performance problem.

Take an example from one our recent projects, Shadow Tactics.

This is one of their NPCs with all of its component parts. This screenshot was taken after they had already moved all their rigs over to use Optimize Game Objects, so the original version had all of the NPC model’s bones in addition to all of the gameplay objects and model structure.

This is a completely normal, expected setup for a game. Level designers are placing NPC spawners. Those spawners then, at runtime, create NPC instances as their own children. Enemy_normal is the root of the NPC. This contains the NavAgent that controls the NPC’s movement. Each NPC has a bunch of GameObject children that define their abilities and tuning. Nothing about this is at all wrong.

What this all means, though, is that every frame, when the NPC moves, it must inform all of its children and all of its parents that its transform has changed. Every frame, every NPC is sending hundreds of Transform Changed messages and taking up a good amount of the frame doing so.

Once we saw how much frame time Transform Changed messages were taking, we talked with Mimimi Productions and they altered their spawning in some simple ways. In addition to turning on Optimize Game Objects, they started spawning their NPCs at the root level of their scene. They also moved all of the NPC Ability Game Objects up to the root of the NPC, so they would not be a child of the NavAgent. This left only the visuals and the physics of the NPC under the Nav Agent. On their target hardware this improved performance by ~10 FPS.

Let’s say that again, for those in the back.

10 frames per second.

Without majorly impacting workflow. Without needing to go back and redo a ton of content. Just by taking the content they already had and moving it around a little bit.

Transform Change Dispatch

Staring with Unity 5.4, we have been heavily optimizing all of the code having to do with transforms and the Transform Changed message. We have optimized memory layout and provided the .SetPositionAndRotation API to avoid extraneous changes. We are now allowing systems to register as caring about a specific transform, rather than needing to broadcast the Transform Changed message to every system in the engine.

One of the largest changes, that is still ongoing, is moving over to a delayed and threaded TransformChangeDispatch system. This lets us queue up all of the Transform Change notifications in a bit of the hierarchy that is completely self-contained and then resolve them in a job off of the main thread. We are moving as many systems as we can over to dispatching these notifications, rather than making them on the main thread synchronously.

Even with all these improvements, it is very important that you think about the structure of your hierarchy as you develop your game. With a bit of forethought, you can save yourself frames of execution time, and use that for things your players will care about.

Hierarchy Structure Guidelines

  • If something moves every frame, make sure all its children care about position. Only rendering, physics, audio, or core systems like that should be there.
  • When dynamically creating game objects at runtime, if they do not need to be children of the spawner for the above reasons, spawn things at the root of the scene.
  • You can easily register everything you spawn and pass along the ActiveInHeirarchy state of the spawner using OnEnable and OnDisable.
  • Try to group your moving transforms such that you have around 50 or so GameObjects per root. This lets the underlying system group your TransformChangeDispatch jobs into a fairly optimal amount of work per thread. Not so few that the thread overhead dominates; not so many that you are waiting on thread execution.

Thanks to Mimimi Productions for letting us use their game Shadow Tactics for our examples. We will be back next time with lessons learned working with Realtime Global Illumination and multi-scene editing.

Comentários encerrados.

  1. Seems like this could be fixed fairly easily by adding a StaticTransform component. You already have RectTransform as a subclass of Transform, why not give us a StaticTransform, which can never be moved after initializing, and doesn’t care if it’s parent moves?

  2. This is an older post but I thought I’d share an idea I had regarding the need for organization in the hierarchy during development vs performance at runtime. I created a folder prefab for any area I was using purely for organizational needs and at run-time, when it started, it would decouple itself from its hierarchy and live on the root, remembering where it came from. If I so instructed, it could be returned to its position in the hierarchy if, during runtime, I wanted to see its original position.

    Honestly, it feels a bit silly to need to go this route. I think there should be a way to tell Unity a specific game object is only there for organization. Like, for example say we were to delete the transform component, it would truly be empty and Unity would understand what that means.

  3. Thanks! This got me improvements in less than 30 minutes :)

  4. What about the case where a non moving parent game object is marked as static, but its children are dynamic? Do the transform changed messaged still propagate through/into static transforms?

  5. Can you guys just treat zeroed transforms as pass throughs. So we can still organize with game objects.

  6. I think that this article should be rectified; having all those gameobjects as container for scripts is just wrong and more experienced programmers seems to share this vision in the comments.
    For young programmers that are reading I think this is just misleading.

  7. Protopop Games

    julho 3, 2017 às 7:39 am

    I’ve been with Unity since version 3 – i even switched from PC to Mac for it:) And I appreciate Unity is working hard on optimizations. But I suspect this is why I haven’t been able to run my open world mobile game BrightRidge using Unity 5.4 or higher.

    I’m an artist and think visually, so it’s intuitive for me to use gameobjects as an organizational tool. I get that the new system is better from a coding and performance perspective, but it can penalize visually minded workflows, and it’s difficult to re-architect a game that’s invested several years of development.

    I feel in general that Unity is prioritizing higher end effects and programmer friendly workflows over mobile stability and artist/newbie accessible approaches. This is a positive thing for many people, but some of us came to Unity because of its user friendliness and accessibility to people who aren’t experienced coders. I hope there’s a way to balance these different needs in the future.

  8. Robert Cummings

    julho 1, 2017 às 7:45 pm

    While there has been some discussion about dedicated groups editor side for objects, I think this workflow is just too deeply entrenched to change. It’s a bit like car steering wheels. There’s better things out there but people don’t like them.

    Onto comments about article: 10fps immediately made my balls twitch in how useless a figure it is. 10fps? what if game is running 1000fps? It is meaningless. Please use millisecs going forward.

    Regarding “Use 50 or so” for an optimal number. Don’t understand clearly enough.

    Finally, if these practises aren’t added to the documentation then these practises will get missed by most of Unity users. So please flag the docs team.

    Thanks and keep up the otherwise great work :)

  9. How about instead of telling people to not organize their scenes properly you just add a type of node that does not have a transform? The real issue here is that there are “objects” like “06_background_scripts” that due to Unity’s design decisions must have a transform even though they are obviously meant to be no more than folders or tags to group objects in.

  10. Cool post. I realy like this kind of post that give you the best practices using unity.

  11. William Armstrong

    junho 30, 2017 às 6:48 pm

    For those asking, it was, at worst case, from ~25 fps to ~35 fps.

  12. “On their target hardware this improved performance by ~10 FPS.”

    So, is that jump from 10 FPS to 20 FPS (50ms difference) or from 110 FPS to 120 FPS (0.76ms difference) ?

  13. “Each NPC has a bunch of GameObject children that define their abilities and tuning. Nothing about this is at all wrong.”
    Maybe I am misunderstanding, but how can using gameobjects for abilities and tuning not be wrong in a blog about optimization and performance?
    Unless we are talking of gameobject with attached mesh, particles generators or similar I can’t think of a reason where it is useful to use them.

    1. William Armstrong

      julho 5, 2017 às 6:47 pm

      I try to avoid calling any workflow that works for the people making the game wrong. There were some unexpected performance considerations. The nice thing about having every Ability be its own GameObject is that it is easy to make prefabs for each ability, and that you can search for abilities by name in the Inspector. By re-arranging the hierarchy a bit, they were able to find a good balance between ease of use and performance.

  14. Friedrich Hanisch

    junho 30, 2017 às 11:30 am

    “Let’s say that again, for those in the back.

    10 frames per second.”

    Unfortunately, fps are meaningless without context! A difference of 10 fps can be meaningless (like between 1000 and 1010 fps) or very meaningful (like between 10 and 20 fps). You should rather write the milliseconds saved, as they’re context-free.

  15. Is it normal to have all that children though?
    They have tents of empty object to contains just a script.
    I would say that this choice is made to give a designer a better overlook at what the npc can do; otherwise I would say that is a bit naive.
    They could have just put the scripts into one or more objects (group of 10 monobehaviour per object maybe?) and the problem would have be gone.

  16. Thanks for the useful post. For better clarity, can you post a screenshot of the same Hierarchy after the fixes?

  17. @William,
    1) claiming a 10 FPS gain without stating the initial FPS rate, nor the number of total objects, nor the number of moving objects per frame is useless.

    Any chance you could provide the required additional details..? :-)

    2) Also, it’d be nice to explain why these messages are sent. My guess is that it’s too update the bounding boxes for the physics and renderer. Is there any other use ?
    In case we have this hierarchy : A->B->C . If C moves it’ll send a message to B, or A & B ? If it’s only to B, and C movement doesn’t change the bounding box of B, B won’t send a message to A, right ?

    3) Lastly, why would the enemy inventory be a separate Transform instead of a script attached to enemy_normal ? The evidence would tell the best practice is to avoid having GameObjects for stuff that doesn’t exist in the rendered world nor in the physics world. Is there anything that would go against that and I have missed..?

  18. Anomalous Underdog

    junho 30, 2017 às 7:31 am

    Being able to group objects into others work as an organizational tool (it’s as if they are grouped into folders). But as mentioned here, that comes with the “Transform Changed message” overhead. What I’m getting at is, I hope in the future, (from the perspective of the end-user) you could decouple this “grouping into folders” functionality found in the Hierarchy window, from the actual transform parentage, because grouping into deep subfolders (speaking only in terms of the Hierarchy window) is still a very nice thing to have.

  19. Maulik Kaloliya

    junho 30, 2017 às 6:13 am

    Grate tips and really helpful.

  20. 10 fps doesn’t mean anything without knowing what the base framerate was. Jumping from 190 fps to 200 fps won’t be noticed. This is why, when talking about performance, it’s usually better to give the cost in ms, and in a blog, show us the profiler, you then get that *gasp*.

  21. I really like the Good Practice guides! Just because I always ask myself, what will be the best way to do this task? And it is difficult to find the answer. This type of post makes things easier for self-taught people.

  22. Do you know what the FPS was before the optimization? For instance, 20 before going to 30 fps after is far more significant than 90 going to 100 fps. Thanks :)

  23. Are there any benefits to also set the objects that don’t move as static or it’s only beneficial for static batching? Also does static state improve performance for the 2D sprites?

  24. seems a great series. Thanks for the info

  25. This sort of blog post is gold, but more details are needed. Why are the messages even sent? Why does every parent always need to know if its child has changed position? Couldn’t it be an opt-in thing? Playdead gave a talk at Unite last year about optimizing Inside and they saw a major performance increase from making this change – it would be great if those of us without source code could do it too.

    “…it must inform all of its children and all of its parents that its transform has changed…” – do you mean only its direct children and direct parents? Or does the parent also pass along the message to its other children – the original objects’ siblings – and so eventually to every object under the same root object?

    Are there any other messages sent that behave like this? Or are Transform Changed messages unique? I know changing a transform’s parent has a similar sort of cost, related to the number of siblings and children the GameObject has.

    Does changing localPosition also send these messages?

    1. William Armstrong

      julho 5, 2017 às 6:52 pm

      Transform Changed messages are unique in this regard. They are sent to all parents, and all children so that position and bounding boxes can be updated, any changes to the shape of a RigidBody can be detected, and a few other systems can be informed in less common cases. Setting local position will also cause this behavior, as that can still alter the bounds and the position of children. Siblings are not informed by default.

  26. Wow, interesting how some changes in transform hierarchy can make a difference in performance.
    I really like best practices stuff, so keep em coming!

  27. Nice, This highlights a desperate need for Folders in the hierarchy. Using gameObjects as containers for the simple purpose of reducing clutter should not be considered best practice. But it’s all we have.

  28. Øyvind Strømsvik

    junho 29, 2017 às 7:09 pm

    Seems stupid that we have to sacrifice readability and cleanliness in the Editor for performance at runtime, Unity should handle this for us automatically. Seems stupid that we have to think about how we structure the hierarchy at all to be honest.

  29. How about putting objects under a root object that never moves, will that have same performance as putting the object as a root?

  30. Are there any plans to add additional organizational tools to help people keep a scene organized without using the transform hierarchy to do so?

  31. John Rockefeller

    junho 29, 2017 às 5:08 pm

    > Once we saw how much frame time Transform Changed messages were taking

    This is a really important note to make: before optimizing, it’s best to profile your app or game to see what exactly is causing the slow-down. Otherwise, we’re prematurely optimizing and can fail to optimize the right thing.

    Are you able to talk a bit about how you discovered that the Transform Changed messages were so noisy? I’m assuming you used the built-in Profiler, but it would be great to call it out so that people who read the article don’t rush out to move all their children to the root but instead take a few minutes to profile first.

  32. I love blog posts like these. Keep them coming!

  33. Thanks for the post and changes in Unity to improve performance!

    In future posts, you might want to use milliseconds rather than frames-per-second:

  34. An increase of 10FPS may sound like an impressive improvement, but it depends relative to what. A bump from 50FPS to 60FPS is a bigger deal than 100FPS to 110FPS for example, and the former would indicate a frame time savings of over 3ms vs just under 1ms in the latter case.