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. Dismiss Notice

Discussion Most efficient way to calculate whether object is within cone

Discussion in 'Scripting' started by Ts1nd, Sep 25, 2023.

  1. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    Hello,
    I have following problem:
    1. Assume each character has 8 engage slots (each is 45° cone with 1.5f radius)
    2. Another nearby character is standing next to it with given size and distance
    3. What is the most efficient way to calculate which engage slots are occupied by that character?

    See visual example below:
    upload_2023-9-25_13-39-24.png

    I am doing the following:
    upload_2023-9-25_13-56-52.png

    It works however this treats each character as a point. If characters are close to eachother or when one target has bigger radius, the expectation would be to occupy more than 1 slot. Was thinking of calculating the angle between "two ends" of character and adding it below:
    check = angle < (45f + characterAngle) / 2f;
    However that involves a lot of inefficient math. Is there a better way?
     
    Last edited: Sep 25, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    Is it actually a problem?

    DO NOT OPTIMIZE "JUST BECAUSE..." If you don't have a problem, DO NOT OPTIMIZE!

    If you DO have a problem, there is only ONE way to find out. Always start by using the profiler:

    Window -> Analysis -> Profiler

    Failure to use the profiler first means you're just guessing, making a mess of your code for no good reason.

    Not only that but performance on platform A will likely be completely different than platform B. Test on the platform(s) that you care about, and test to the extent that it is worth your effort, and no more.

    https://forum.unity.com/threads/is-...ng-square-roots-in-2021.1111063/#post-7148770

    Remember that you are gathering information at this stage. You cannot FIX until you FIND.

    Remember that optimized code is ALWAYS harder to work with and more brittle, making subsequent feature development difficult or impossible, or incurring massive technical debt on future development.

    Don't forget about the Frame Debugger either, available right near the Profiler in the menu system.

    Notes on optimizing UnityEngine.UI setups:

    https://forum.unity.com/threads/how...form-data-into-an-array.1134520/#post-7289413

    At a minimum you want to clearly understand what performance issues you are having:

    - running too slowly?
    - loading too slowly?
    - using too much runtime memory?
    - final bundle too large?
    - too much network traffic?
    - something else?

    If you are unable to engage the profiler, then your next solution is gross guessing changes, such as "reimport all textures as 32x32 tiny textures" or "replace some complex 3D objects with cubes/capsules" to try and figure out what is bogging you down.

    Each experiment you do may give you intel about what is causing the performance issue that you identified. More importantly let you eliminate candidates for optimization. For instance if you swap out your biggest textures with 32x32 stamps and you STILL have a problem, you may be able to eliminate textures as an issue and move onto something else.

    This sort of speculative optimization assumes you're properly using source control so it takes one click to revert to the way your project was before if there is no improvement, while carefully making notes about what you have tried and more importantly what results it has had.

    "Software does not run in a magic fairy aether powered by the fevered dreams of CS PhDs." - Mike Acton
     
  3. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    Can you PLEASE stop spamming this generic post in all my threads? It has nothing to do with the question.
    Mods, can you remove this post?
     
  4. tomfulghum

    tomfulghum

    Joined:
    May 8, 2017
    Posts:
    69
    Inefficient math is fine unless you're getting bad performance. If you have a solution that works, profile it and find out if it's performant enough for your use case. If not, you can start optimizing.

    That said, a few seconds of googling led me to this stackoverflow post.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    I was hoping you might respond with something more along the lines of:

    "The profiler tells me my code is spending too much time on computation X. Is there a way I can get computation X to either happen faster or with fewer instructions?"

    Either that or perhaps instead of calling it "lots of inefficient math" you could say "Computation X is mathematically inefficient based on these specific measurement metrics. How can I improve it?"

    Alas.

    Here's another generic post since you failed to read the one already present at the top of the forum:

    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/

    - Do not TALK about code without posting it.
    - Do NOT post unformatted code.
    - Do NOT retype code. Use copy/paste properly using code tags.
    - Do NOT post screenshots of code.
    - Do NOT post photographs of code.
    - ONLY post the relevant code, and then refer to it in your discussion.
     
    Bunny83 and tomfulghum like this.
  6. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    I am getting bad performance that's why I ask for different options. Thanks.
     
  7. tomfulghum

    tomfulghum

    Joined:
    May 8, 2017
    Posts:
    69
    You probably should have led with that. Can't blame people for their answers if you're omitting important information.
     
    Bunny83 likes this.
  8. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,860
    Your
    new List<Occupant>()
    is definitely taking more CPU time than the math equation. Frankly, I see a nightmare of overwrought object soup in need of simplification and refactoring; expressions like
    baseinfo.mainManager.combatantsInfo.combatants[baseinfo.ID].distanceTo
    just scream Web2.0 Java to me.
     
  9. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    It was included in the title where I asked what the most efficient way to make that calculation.

    Actually not, the math part is calculated up to 20 times for each initialization of this list and takes vast majority of the CPU time in this method.
    How so? It doesn't bother me as long as it's performant.
     
  10. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    537
    Code (CSharp):
    1.         if (Vector3.Dot((monsterPosition-transform.position).normalized,transform.forward)>0.7f) // approx 90 degree cone
    2.             Debug.Log("I see a monster!");
     
    Last edited: Sep 25, 2023
  11. Saniell

    Saniell

    Joined:
    Oct 24, 2015
    Posts:
    167
    The math is the least inefficient part about this code. Amount of pointer chasing and apparently GC allocations (is 4th line is called every frame?) is what makes it slow
     
  12. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    All this bit is called once every few frames depending on the number of characters in combat. Why would it have any impact at all? It's very cheap to do it even once per frame. How would you code it?
     
  13. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    284
    If you have code that allocates new memory on the heap every few frames it will trigger sweeps of the garbage collector to occur more often. The garbage collector can impact performance, but it is somewhat decoupled from the lines that allocate. So, you won't see the performance impact on those lines that allocate the new objects, but it will cause more garbage collection cycles to trigger some time later on. That being said, instantiating once and caching a reference to the necessary objects will be both faster than allocating new memory, and put less pressure on the garbage collector.
     
    Ts1nd likes this.
  14. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    Would caching the reference and using some .Reset() method be significantly faster? How about resetting list of objects, would it be better to use list.Clear()?
     
  15. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    284
    Underlying each List is an array. The default length of the array is 4, after adding the first item. When you add a new item beyond the the length of the underlying array, the List allocates a new array that is twice as long, copies the contents, adds the new item, and clears and discards the old array for garbage collection. You can provide an initial capacity argument to the List constructor when you instantiate it the first time to give it some specific internal size for the starting array length. When you call List.Clear() it sets the Length value to 0, and calls Array.Clear() on the internal array to zero out the memory values that were stored. So, yes just calling List.Clear() will set the list back to a state as if nothing has been added, but it will maintain the same internal array allocated in memory. Not to mention you will avoid allocating the List object that wraps around that internal array. So, you can keep using that same List object across multiple frames.

    You don't want to call .Reset() on the list. That is an IEnumerator method and it is not for resetting the actual contents of the list. You would have to cast the list as ((IEnumerator)List).Reset(), and it wouldn't do anything really. It's for resetting the process of enumerating over the collection, like in a foreach loop. It's not for resetting the List, it's for resetting that enumeration operation.

    I was about to consider writing a BenchmarkDotNet test for you to compare the real nano or microsecond times it takes to call .Clear() versus new List(), without considering garbage collection, just to answer your question more precisely. However, Clear() is not really testable in that way. Instantiating a new list can be easily benchmarked, but testing the clearing of a populated list is much trickier. Those benchmark tests really need to be like one line of code that can be run over and over billions of times in a row, and you don't know ahead of time how many iterations there will be per the various cycles and across the whole test. So, pre-allocating billions of populated lists isn't really feasible. If you try to populate the lists between each iteration of the benchmarking process it messes with the tests, and if you try to populate the lists in the test it obviously affects the results. I supposed I could make a control test that just populates a list, then another test that populates a list exactly the same way, then clears it. Then, you could subtract the results to find the difference and get a roundabout figure. If you really want to know the precise timings of new List() versus List.Clear() I could probably figure something out, but I would just re-use the list across multiple frames personally.
     
    Ts1nd likes this.
  16. Ts1nd

    Ts1nd

    Joined:
    Jan 24, 2021
    Posts:
    84
    That's very useful, thank you! If I know in advance the maximum possible number of elements in the list, can I just turn it into an array and always have it populated then just flag each element whether it's used or not with something like
    Code (csharp):
    1. slot.occupants[i].isUsed = True
    However I will then have to manually reset each used element (object Occupant) before it gets changed which was done automatically with creating new list. Is it possible that garbage cleaning it could be faster than reseting it with a method such as:
    Code (csharp):
    1. slot.occupants[i].Reset()
    Or it's just always better to reset regardless of complexity of the class? Asking mainly for other bits of code in my project.
    That's super helpful info and will definitely consider it during my next optimization.
     
    Last edited: Sep 25, 2023