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

Question GetComponent<> consolidation

Discussion in 'Scripting' started by XJonOneX, Jun 11, 2023.

  1. XJonOneX

    XJonOneX

    Joined:
    Dec 19, 2017
    Posts:
    69
    I currently have a script on a GameObject that holds pertinent information to it.

    I have many of these GameObjects, upwards of 1400 of them.

    At any given time, I need to request the data on them, and I use GameObject.GetComponent<DataComponent> to do that.

    So far I've avoided GetComponent inside Update, and it's only called 'on demand' so to speak. But I might have to iterate over hundreds of GameObjects from time to time.

    I've done caching of components before for use in Update, but I'm not sure if I need to do that or not for these objects. Any ideas out there?
     
  2. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    4,362
    Share the full code. GetComponent is decently fast, but using it in update is usually bad code design
     
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    What kind of game needs that many game objects at all times? That sounds like you're using game objects to hold data and everything together, which is a disaster at worst, and impractical at best. Game objects are things that you use only when you need the engine to take care of it for you. You keep these at the absolute minimum. That way you minimize the bandwidth of communication between your state and the engine state, but also the maintenance loops. Even when they want to show hundreds of trees and units and whatnot, people write custom shaders, or access GPU directly to render content, so you absolutely never need game objects in the thousands. If you do, maybe it's ECS time?
     
  4. XJonOneX

    XJonOneX

    Joined:
    Dec 19, 2017
    Posts:
    69
    Well, like I said, it's not in Update. When certain events happen in my code, I need to grab the data off the GameObjects. Here's an excerpt:

    Code (csharp):
    1.  
    2.    private void MoveBlockDataDown()
    3.    {
    4.         MovingBlocks = true;
    5.  
    6.         Debug.Log("Moving Blocks Start");
    7.  
    8.         GameObject block;
    9.         GameObject aboveBlock;
    10.  
    11.         Block blockComponent;
    12.         Block aboveBlockComponent;
    13.  
    14.         //Search right
    15.         for(int x = 0; x < BoardWidth; x++)
    16.         {
    17.             //Search down
    18.             for(int y = BoardHeight - 1; y >= 0; y--)
    19.             {
    20.                 block = ActiveBoard.Blocks[x, y];
    21.                 blockComponent = block.GetComponent<Block>();
    22.                 if(block.name == "GrayCube")
    23.                 {
    24.                     for(int z = y; z < BoardHeight - 1; z++)
    25.                     {
    26.                         block = ActiveBoard.Blocks[x, z];
    27.                         blockComponent = block.GetComponent<Block>();
    28.                         aboveBlock = blockComponent.NeighborBlocks[0];
    29.                         aboveBlockComponent = aboveBlock.GetComponent<Block>();
    30.  
    31.                         if(aboveBlock.name == "GrayCube") break;
    32.  
    33.                         blockComponent.DestinationPosition = aboveBlockComponent.DataPosition;
    34.                         aboveBlockComponent.DestinationPosition = blockComponent.DataPosition;
    35.  
    36.                         swapState = SwapState.Ended;
    37.  
    38.                         SwapBlockData(blockComponent, aboveBlockComponent);
    39.                     }
    40.                 }
    41.             }
    42.         }
    43.     }
    44.  
    I'm still in the early stages of development, and I haven't reached 1400 GameObjects yet, I just want to optimize things so it's not terrible later on. I'm not constantly querying for the data, so maybe I'll be ok. I'll check ECS out.
     
  5. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,833
    Looks like you want a single data structure that holds all of the Block objects (or all of the Blocks in an island of Blocks). Then all of the block-structure-management methods in that class, including the "how to get the Block above this Block" and moving data from Block to Block.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,494
    Well, is there a reason why your "ActiveBoard.Blocks" array / List contains "GameObject"s and not "Block"s?
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,559
    In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects.

    These mechanisms are for extremely-advanced use ONLY.

    More information: https://starmanta.gitbooks.io/unitytipsredux/content/first-question.html

    More information: https://forum.unity.com/threads/why-cant-i-find-the-other-objects.1360192/#post-8581066

    If something is built into your scene or prefab, make a script and drag the reference(s) in. That will let you experience the highest rate of The Unity Way(tm) success of accessing things in your game.

    For dynamic makings of arbitrary things at runtime, use a template adaptor pattern to minimize code scatter. See enclosed example of dynamic UI.
     

    Attached Files:

  8. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,082
    Use an enum that you store inside of the Block component. String comparisons are slower than enums which are just integer comparisons, and this many comparisons is just eating up performance (and opening you up to errors that can be hard to find if you forget to set it). Here's a very rough example of how this would look.
    Code (csharp):
    1. blockComponent = block.GetComponent<Block>();
    2. if (blockComponent.blockColor == BlockColor.Gray)
     
  9. XJonOneX

    XJonOneX

    Joined:
    Dec 19, 2017
    Posts:
    69
    When I first started on my project, I thought I wanted Block data to be stored on the GameObject. I now realize after ya'lls replies it'd be best to have that data stored inside a 'master' class of all the block and their data, which would also include a reference to their GameObjects.

    You ever just start belting out code, and after a few weeks you start to realize how out of control some stuff can get? That's what happened here. :confused:

    RefactorAllTheCode.png

    @Ryiah, good lookin out on the enum suggestion/string info.

    Thank you all for your input on this. :)
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,559
    I've never done gamedev any other way than that.

    I think if that doesn't happen it means your program isn't growing.

    If it's not growing, it is dead.

    EMBRACE THE CHAOS OF THE PROJECT... RAGE HARD!!!
     
    XJonOneX likes this.
  11. XJonOneX

    XJonOneX

    Joined:
    Dec 19, 2017
    Posts:
    69
    Ok, so let's say I move everything to its own class, and cache a reference to the block game object there as well. When I do a raycast and get the block game object, what would be the best way to get the block information? Have a method that returns the info by iterating through the master class, comparing the raycast object to the cached block object? I'd like to keep the data in an array matrix so it lines up with the game board and I can iterate through it like I do in the code I posted.

    I have a touchinput script that does the raycast, but a boardmanager script that manages block data. I don't want touchinput to be coupled with boardmanager. I've got an event system in place that I can utilize. I suppose I can broadcast an event with the raycast hit object.

    Is there a different way to go about this? Ideas welcome. :)
     
  12. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,494
    GetComponent would work just find when you do a raycast. GetComponent does not somehow does something super expensive. Unity stores the component of a gameobject in some kind of C++ vector and in order to get a component it just has to iterate through that list and check the type of the component.

    However when you have an actual spatial relationship with the block, you can of course use the hit point and work out the coordinates and use them to access your grid / array directly. Keep in mind that multidimensional arrays are not that great for the performance, especially when you really do many look ups / accesses. Just using an flattened ordinary one dimensional array is generally faster. A multidimensional array is essentially just a wrapper about a normal array anyways. Though at each access ever coordinate is checked against the dimension limits and the actual flattened offset is calculated. Doing that manually usually saves a lot of redundant checks and depending on what kind of lookups you need to do, calculating the offset may also be simpler. Though if you only have a few lookups you can stick to a multidimensional array.
     
  13. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,111
    Start by doing that and then use the profiler to check if this actually is a bottleneck. If it is then you can start looking into more optimized data structures.

    Before you jump all the way to events try an interface instead. What's the minimal information the board manager needs to find a block? Turn that into an IBlockProvider interface (just an idea).