Hello.
We are right now working in performance checking in the game, so we are doing profiling. Usually it works pretty well, and we can see that we are creating new objects, allocating new arrays, triggering UnityEvents, etc etc. However, there are some cases where it’s very hard to figure out what’s allocating memory, and I’m looking at some pointers.
Our configuration is complex so I’ll try to make it as simple as possible. In general:
- We use Action Events quite extensively
- We separate some of our complex code into non-MonoBehaviours so we can unit test them
The code that I’m trying to analyze is doing this (skipping logic like filtering or debugs):
- CollectionTile is in a GameObject that has a Trigger Collider2D on it
- When something that we care about triggers OnEnter, it sends the info to the InteractingObjects MB which manages the objects you can interact with
- InteractingObjects has a non-MB class (Handler) that has most of the logic, and when it adds a new item it sends the info back to InteractingObjects
- InteractingObjects triggers an Event. And CurrentItemInteraction is listening for it. CurrentItemInteraction handles prompts, player using or interacting with objects, etc etc.
- CurrentItemInteraction checks to see if there is anything to do with an object after the event had triggered.
Now my issue: I’m getting some spikes when a new object enters the Collider so I want to see what is the culprit. After stripping some code that I don’t care about I end up with 1.5Kb generated by the logic that I explained above. The problem is that it ends in InteractingObjects method and that’s it. I tried adding ProfileMarkers but it then reports garbage in MonoJit which again reports a high level method, and I’m again in the same place I started. I have tried moving my code around to no avail (triggering methods early, etc etc).
My questions:
- How does Unity Profiler deal with Action Events triggers? Could that be the issue that I cannot go deeper?
- Is the issue that I’m using non-monobehaviours and it stops analyzing?
- If I add ProfilerMarkers to a method that calls 4 other methods, would it add markers to the other methods? or do you need to add ProfilerMarker to each method?
- What happens if the ProfilerMarker.End is not reached because a return was triggered before hand?
- If I want to see whether a piece of code is the one that is generating garbage is there a way that I can surface it more? Assuming is very hard to test in a new empty game. Maybe moving it in an Update, Start, or something similar?
- What defines that you can actually see the last method that is causing the allocation (for example: the word new, resizing arrays when adding items to a list, etc etc)? How can I surface that?
Thanks a lot! and Here is a summarized sample of the code:
public class CollectionTile : MonoBehaviour
{
//...
private void OnTriggerEnter2D(Collider2D collision)
{
//...
interactingObjects.EnterOnPriority(currentObject);
}
//...
}
public class InteractingObjects : MonoBehaviour
{
//...
public static event Action OnChangingInteractables;
private InteractingObjectsHandler interactingObjectsHandler;
private void OnEnable()
{
interactingObjectsHandler.OnChangingInteractingObjects += OnModifyingInteractables;
}
public void EnterOnPriority(IInteractable currentObject)
{
interactingObjectsHandler.SetInteractingObject_Priority(currentObject);
}
private void OnModifyingInteractables()
{
OnChangingInteractables?.Invoke();
}
public class InteractingObjectsHandler
{
public event Action OnChangingInteractingObjects;
//...
public void SetInteractingObject_Priority(IInteractable interactable)
{
//...
interactingObjectsPriority.Add(interactable);
OnChangingInteractingObjects?.Invoke();
}
}
}
public class CurrentItemInteraction : MonoBehaviour
{
private void OnEnable()
{
InteractingObjects.OnChangingInteractables += GenerateUseAndInteractionPrompt;
}
//...
private void GenerateUseAndInteractionPrompt()
{
if (debug)
Debug.Log("CurrentItemInteraction | GenerateUseAndInteractionPrompt");
OnResettingGliphs?.Invoke();
ContextualInputGlyphManager.Instance.ResetGlyphs();
GenerateUsePrompts();
GenerateInteractionPrompts();
CheckForSettingItem();
}
//...
private void GenerateInteractionPrompts()
{
int amountOfInteractables = interactingObjects.HowManyInteractableObjects(false);
interactingObjects.IndexToCheck = -1;
if (amountOfInteractables == 0)
return;
if (debug)
Debug.Log($"CurrentItemInteraction | GenerateInteractionPrompts. AmountOfInteractables: {amountOfInteractables}");
for (int i = 0; i < amountOfInteractables; i++)
{
LocalizedPromptAndStatus prompt = interactingObjects.WhatCanIDo(false, i);
if (Helper.AreTheseLocalizedStringTheSame(prompt.Prompt, Helper.NothingToPromptPlaceholder()) is false)
{
interactingObjects.IndexToCheck = i;
ContextualInputGlyphManager.Instance.ShowInteractionGlyph(prompt.Prompt, interactingObjects.GetObjectPosition(false, i), prompt.CanItBeUsed);
return;
}
}
}
//...
}