Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Resolved Noticable stuttering when I attack for the first time

Discussion in 'Scripting' started by anszwa, Jul 28, 2023.

  1. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    Hello, I have the problem that I feel a noticeable stuttering in my game the first time I hit with a weapon.
    In the frame that I attack, a few things come together, a Collider.GetContacts call, I play a sound, and I spawn a weapon trail.

    When I instantiated all this, the stutter was even bigger, but now I'm accessing already existing objects and on Collider.GetContacts I'm using the variant that shouldn't produce garbage.
    Nevertheless, I have a clear rash in the profiler and some things are allocating memory. As I said, this only happens the first time.

    Do you know if there is a way to get rid of this? Do I have to call it all up somewhere in advance, or can it be prewarmed somehow?

    Here is a picture of the profiler:
    Profiler.png
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    You can click on that frame in the Profiler and get the list of functions shown below - see what's taking up all the time there.
     
    anszwa likes this.
  3. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    thanks, I know. the profiler's picture is from this exact frame.
    I've tried to optimize everything as best as I can, but some things do allocate memory the first time I use them over which I have no control, e.g.:

    AudioSource.Play
    Collider2D.GetContacts
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    No it isn't. The biggest item in the bottom panel is 10 milliseconds, the frame you should be seeing is almost 1000 ms. Plus, if you were on that frame, there'd be a white line at that point in the graph.
     
  5. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    581
    Perhaps it's the AudioSource.Play?. What happens if you don't play a sound?

    I've never had stutters when playing sounds in Unity and so I've always assumed it must preload any samples that are referenced in a scene. Perhaps your sound isn't referenced anywhere?.

    If playing the sound is the problem and for some reason the sound isn't being preloaded when the scene first loads then you can force the sound to preload by playing it very quietly in a Start().
     
    anszwa likes this.
  6. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    yes it is :)
    sorry for the confusion, but this is the same frame. I just put two screenshots together so that you can see the spike better and the selection doesn't hide it.

    I'm now on my home computer (slightly better system) and no longer at work, but I'll take another screenshot, this time with the selection too:
    Profiler.png

    as you can see, the values are slightly better overall, but the result is the same.

    I only displayed the scripts in the profiler. when I monitor everything, the game runs constantly and takes 4ms for one frame, but the first time I attack it takes 10ms. that's still over 60FPS, but the stuttering is still noticeable.
     
  7. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,905
    As @zulo3d suggests, divide and conquer to identify the root cause.

    Turn off the AudioSource.Play. See the impact on timing.

    Turn off the Particle System / Trail Renderer. See the impact on timing.

    My money is on audio loading which is quite common. On the sound(s) involved, look at their import settings. By default, they will be Preload Audio Data: True, but Load in Background: False. If your AudioSource comes from a prefab you Instantiate upon attack, that means the loading process of the prefab will block the main thread to load the audio file asset at that time, before the rest of the attack code can continue.
     
    anszwa likes this.
  8. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    thanks for the answer. I think the bad thing is that I need several things in the same frame that add up.
    I've also never had a problem when I play sound in unity, but I checked again and the first time I play an AudioSource, 48B are actually allocated. the same applies to GetContacts and the sprite renderer that I get from the object pool.

    I've also thought about dividing it all up into more frames, but I need it all at the same time and, as I said, it only causes a problem the first time.

    I've also thought about loading/playing everything in the start or in the menu scene. Before I do that, I just wanted to see if anyone knows a simpler solution or is struggling with the same problem.
     
  9. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    it all has about the same weight.

    1. Audio
    2. Sword Trail
    3. Contact detection

    if only one of them is active, everything is fine. 2-3 things cause a slightly noticeable jerk.
    As for the audio settings, I'm not sure right now. I'll take a look and let you know. thanks for the hint.
     
  10. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,196
    GameObjects and Components are mostly native objects. Only if a managed script needs access to one, Unity creates a managed shell object for it. Subsequent access to that object reuses the same shell. So first time access will allocate managed memory that will stay around for as long as that native object still exists (and beyond that if managed memory still references the shell).

    Caveat: MonoBehaviour and ScriptableObject based components have both their native object and managed object as soon as they spring to life.

    The same as with Scene Objects (GOs and Components) applies to assets, though if you assigned them to an MB or SO via the inspector, they have their shells on Awake/Deserialization as well. If it is assigned to other components (e.g. an AudioSource), which handle their references mostly in native code, that does not happen. So it might just be that on first usage of such components, some part of your or their code initializes the shell for the clip (or your Get component<AudioSource>() is what initializes the shell for the source, rather than the issue being the shell for the clip.


    Lastly, some of that code had not been JITted yet so JIT allocates a bit for it as it runs for the first time.

    That all said, I doubt the 264Bytes of allocations here are the cause for the hickups.

    The AudioSource might also need to initialize a native buffer for the playback or load the clip into RAM (depending on the settings). The particle/trail effect might need to initialize stuff as well but there should be API to prewarm it at a more convenient time. As suggested before, if the settings don't help address this, you could also prewarm the audio.

    If all that doesn't resolve the hickup and it still looks like the GC.Alloc for getting the contacts is the only difference between it taking too much vs an acceptable amount of time, you could also prewarm the shell creation with something like FindAllObjectsOfType<Collider> or something more targeted to only get you those of the enemies and your player character.
     
    anszwa likes this.
  11. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,184
    Have you tried seeing if it happens in a build?
     
    anszwa likes this.
  12. anszwa

    anszwa

    Joined:
    Jul 30, 2012
    Posts:
    39
    Thanks for all the input, I think I've just discovered and been able to solve the biggest problem for the stuttering.

    When attacking, I used Collider2D.GetContacts, as described above, and to filter the contacts I wrote a static class that saves a few ready-made contact filters, at least that's what I thought. What I didn't think of was that the constructor of the class is only called when it is used. In the constructor was the following code:
    Constructor.png
    Apparently, the biggest problem was the string operation.
    Now I changed that and made sure that the class is initialized directly when loading the scene:
    Initialize.png
    The stutter seems to be gone.
    I tested it in editor, as windows build and android with profiler attached and the result is the same everywhere.
    I was able to reduce it from 264B to 182B in said frame. It seems to be running smoothly, but the rest of the allocation looks like it came from JIT compilation:
    Profiler.png
    As long as it doesn't cause more problems now, I'd move on to other features, but do you know if switching to IL2CPP would prevent the Mono.JIT calls?

    Thanks in any case for the many replies and inputs. I wish you the best!
     

    Attached Files:

    MartinTilo likes this.
  13. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,196
    Yes, IL2CPP is AOT, not JIT.
     
    anszwa likes this.