2
\$\begingroup\$

I have a basic AI car script which needs to interact with other cars around it. I would like to access variables from the other cars to help one car identify what the other cars are doing so they can avoid jams and crashes better.

One way to do this would be a raycasthit (ignore any errors there, you know what I mean):

if(Physics.Raycast(...))
{
    hitCar = hit.transform.gameObject.GetComponent<Car>();
}

However, I know that GetComponent can be very performance taxing especially if it's done every time there's an intersection with the raycast.

Another way would be to save all of the cars in an array or list before hand, and then test the name of it against all the names in the array or list.

if(Physics.Raycast(...))
{
    for(int i = 0; i < listOfCars.length; i++)
    {
        if(carName == listOfCars[i].name)
        {
            hitCar = listOfCars[i];
        }
    }
}

However, I've heard that string comparisons (again, especially if there's a lot of intersections with the raycast) can also be taxing on performance.

What's the best way to do this?

\$\endgroup\$
  • 3
    \$\begingroup\$ Profile, profile, profile. \$\endgroup\$ – S. Tarık Çetin Jul 11 '17 at 1:00
  • 4
    \$\begingroup\$ It's true that GetComponent isn't blazingly fast, but it's also not atrocious. The warnings you see against it mainly mean we shouldn't call it capriciously (I've seen beginners search for the same component multiple times per frame in a single script, instead of just caching a single reference), but that doesn't mean we need to eliminate every instance of it to reach good performance. In particular, the raycast step can easily be more expensive than GetComponent in a complex scene. So, remember to profile regularly to ensure you're solving the right bottlenecks for your game's situation. \$\endgroup\$ – DMGregory Jul 11 '17 at 3:27
5
\$\begingroup\$

You could use a Dictionary. Store all Transforms as a key with reference to each Car.

In this example, we have a god Game class that holds references to all Cars. (just make sure your Script Execution Order has Game execute before Car does)

Each Car class adds itself to the Game's Car Dictionary upon Awake.

Then in your Raycast, you get the Car directly by using hit.transform as the key for the dictionary.

public class Game : MonoBehaviour
{
    public static Game Global;
    public Dictionary<Transform, Car> dict_transforms_cars = new Dictionary<Transform, Car>();

    void Awake ()
    {
        Game.Global = this;
    }
}

public class Car : MonoBehaviour
{
    void Awake ()
    {
        Game.Global.dict_transforms_cars.Add(transform, this);
    }
}

...

if(Physics.Raycast(...))
{
    hitCar = Game.Global.dict_transforms_cars[hit.transform];
}

But is this faster than using GetComponent? You would need test the timing.

\$\endgroup\$
  • \$\begingroup\$ That's an excellent idea. I will test this out now. I've done some quick googling and it definately seems like the dictionary will be faster than GetComponent, especially because each car has multiple components, and there could be anywhere up to around 40 cars at one time. \$\endgroup\$ – mr-matt Jul 11 '17 at 2:18
  • \$\begingroup\$ It's a good idea, I have seen unity developers using component like Collider2D to cache other component. Though, I am still curious if unity has lazy initialization. If we have hit.transform to get the transform of the hit unity could be using the GetComponent<Transform> on first access to the value. Though, it would be more accurate to believe that they cache it right after creation of Transform. We still need to test if hit transform is a transform of that component we are trying to get (if anyone has tested it already, please, let me know). \$\endgroup\$ – Candid Moon _Max_ Feb 22 '18 at 23:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.