Search Unity

Resolved Is there a way to call the function of the object hit by RayCast without using GetComponent?

Discussion in 'Scripting' started by chealin, Feb 10, 2023.

  1. chealin

    chealin

    Joined:
    Sep 10, 2017
    Posts:
    76
    hello

    I want to fire a RayCast on the screen and run a script on the hit object.

    But the way I thought was to keep calling GetComponent with Update.
    Calling GetComponent repeatedly is not good for optimization, so I want to use another method.
    -
    So it seems that OnMouseOver can also be used for 3D objects, so I tried that too, but it didn't seem to work,
    so I gave up.


    Is there a way to call the script attached to the hit object without using GetCompent?

    Code (CSharp):
    1.     private RaycastHit hit;
    2.     private float maxDistance = 300f;
    3.  
    4.     private void Update()
    5.     {
    6.         if (Physics.Raycast(transform.position, transform.forward, out hit, maxDistance))
    7.         {
    8.             if (hit.collider.GetComponent<ValueInfo>())
    9.             {
    10.                 hit.collider.GetComponent<ValueInfo>().OnValue();//I don't want GetCompent to be called on Update......
    11.             }
    12.         }
    13.     }
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,859
    One script running
    GetComponent<T>
    maybe once per frame isn't going to affect performance. Always use the profiler to determine where your performance issues are.

    Also don't forget
    TryGetComponent(out T)
    exists.

    The monobehaviour callbacks only work with the old input system. With the new system you need to use the Event system interfaces like these: https://docs.unity3d.com/Packages/c...Engine.EventSystems.IPointerEnterHandler.html

    Otherwise, via raycasts, you will have to use
    GetComponent<T>
    . That's just a fundamental principle of Unity.
     
  3. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,433
    In practice, GetComponent is WAY less of a hit than other searches like FindObjects. Even if it's not optimized, you're doing, what, 2 to 20 Type.Equals() calls, and you're done. If you know you'll need to do it often, move the types you want to GetComponent higher in the object's component list.
     
  4. chealin

    chealin

    Joined:
    Sep 10, 2017
    Posts:
    76
    thank you
    You are truly an angel!


    I used IPointerEnterHandler
    Then, I added the [Physics Raycaster] component to the Camera, and masked the Layer with the Canvas.

    so it works fine
    Thank you very much.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class MouseOverInfo : MonoBehaviour, IPointerEnterHandler
    7. {
    8.     public void OnPointerEnter(PointerEventData pointerEventData)
    9.     {
    10.         Debug.Log("Name:"+gameObject.name);
    11.     }
    12. }
    13.  
    upload_2023-2-10_14-25-13.png



    upload_2023-2-10_14-25-32.png
     
    spiney199 likes this.
  5. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    OnPointerEnter will still rely un an underlying GetComponent<IPointerEnterHandler>() after a raycast (done by the engine internally). So you're not going to be gaining any performance.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,859
    Agreed.

    And depending on the system, it's probably better to be happening all in once place (ergo, one system raycasting each frame) rather than written across a bunch of components.
     
    chealin likes this.
  7. chealin

    chealin

    Joined:
    Sep 10, 2017
    Posts:
    76

    I didn't know that OnPointerEnter also checks with GetComponent.

    It would be easier to proceed by checking with TryGetComponent(out T) in the existing code.

    Thanks for letting me know how to do it.
     
  8. SF_FrankvHoof

    SF_FrankvHoof

    Joined:
    Apr 1, 2022
    Posts:
    780
    How else would it know that it hit an IPointerEventHandler?
    A Raycast is always done against colliders, so its result will always be a collider. Anything after that will involve GetComponent and/or casting.
     
  9. chealin

    chealin

    Joined:
    Sep 10, 2017
    Posts:
    76

    I tried to use TryGetComponent(out T) in the if statement part of this code
    Code (CSharp):
    1.     private RaycastHit hit;
    2.     private float maxDistance = 300f;
    3.     private void Update()
    4.     {
    5.         if (Physics.Raycast(transform.position, transform.forward, out hit, maxDistance))
    6.         {
    7.             if (hit.collider.GetComponent<ValueInfo>())//change here
    8.             {
    9.                 hit.collider.GetComponent<ValueInfo>().OnValue();
    10.             }
    11.         }
    12.     }
    OnPointerEnter also depends on GetComponent<IPointerEnterHandler>(), so
    I understood that the degree of optimization of the method of writing code to determine by Raycast in Update() and the method of using OnPointerEnter is similar.
     
  10. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,859
    And? Did it not work?

    It'd just look like this:
    Code (CSharp):
    1. if (hit.collider.TryGetComponent(out ValueInfo valueInfo))
    2. {
    3.     valueInfo.OnValue();
    4. }
    It also most likely doesn't matter. Don't speculate on potential performance issues. 99.99% chance it won't ever be an issue.

    The only real concern here is an design/architectural one.
     
    chealin likes this.
  11. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,411
    one (messy) alternative:
    caching the getcomponent for object(s) at start or at first raycast detection,
    into some dictionary/hashset, by object name or so (if set to uniques)..
     
  12. chealin

    chealin

    Joined:
    Sep 10, 2017
    Posts:
    76

    Code (CSharp):
    1.     private RaycastHit hit;
    2.     private Ray ray;
    3.  
    4.     private void Update()
    5.     {
    6.         ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    7.  
    8.         if (Physics.Raycast(ray, out hit))
    9.         {
    10.             if (hit.collider.TryGetComponent(out MeshInfo meshInfo))
    11.             {
    12.                 meshInfo.PlayValue();
    13.             }
    14.         }
    15.     }
    I wrote hit.collider.TryGetComponent and it works fine.

    thank you!
     
  13. TzuriTeshuba

    TzuriTeshuba

    Joined:
    Aug 6, 2019
    Posts:
    185
    i think a fair balance between optimization and cleanliness could be to cache only the last collider detected and its corresponding component. This avoids most of the unnecesary computations (i.e. mouse remaining on the same collider for a while).

    As mentioned, check the profiler to understand if and what to optimize.
     
  14. LOSTSOUL86

    LOSTSOUL86

    Joined:
    Apr 17, 2017
    Posts:
    10
    I got exactly into same problem I hit a gameObject with a raycast.
    Then I need to run the script withing the object but all I have is just the gameObject reference or name...
    To run the script in this gameObject I need to use getComponent.
    I have many possible objects being hit with the raycast, so I could possibly build an array of the script references, but then I need to search trough them to see which one got hit by name...
    At least if I can extract the object index in the array from this raycast I could maybe build an array of the scripts, but for this I still need to get the script...

    Using sendMessage is I think slower than getComponent...
    I think in this situation the fastest solution will be to use the getComponent.

    I think maybe to avoid the getComponent there would have to be an update to the raycastHit to contain a custom data...
     
  15. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,859
    You should never have to search for anything by name. RaycastAll for all the results, run down the collection and TryGetComponent on each element and call the necessary method. This method could be composed via an interface too. You can use LayerMasks to ensure you're iterating through the smallest number of objects.

    Don't speculate on what is more performant. Use the profiler if you have a performance issue.