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 How to call one class from another elegantly?

Discussion in 'Scripting' started by anilsayar, Jul 31, 2023.

  1. anilsayar

    anilsayar

    Joined:
    Oct 14, 2018
    Posts:
    14
    Hello,

    My question would be about how to transfer data between different classes elegantly. Let's say that I would like to draw an object. If this object is clicked, then it must be removed. The call must be done from the so-called class named Caller to Drawer, which draws the object, detects mouse input, then removes object. The problem is that mouse detection must be performed each frame, which means that must be called inside Update function of the Drawer class. So, everytime I call the draw function, the rest of the code must be executed inside Drawer's Update and other remaining functions. I have to send all the variables to Drawer to perform other tasks. This brings lots of mess. Considering I have lots of duties to perform, this brings many small classes, data transfer back and forth... Are there any better ways for doing this simple "call+detect+remove" operation? I am trying to keep my code elegant and neat. Is there a way for checking mouse position without needing Update function? Do I need a class with huge Update function for this kind of operations (Caller is being inherited from elsewhere tho)?

    I am writing a simple code for demonstration purposes.

    Thanks

    Code (CSharp):
    1. public class Drawer : MonoBehaviour
    2. {
    3.     void Update()
    4.     {
    5.        
    6.         if (Input.GetMouseButtonDown(0))
    7.         {
    8.               //-Detect mouse position
    9.               //-Remove object
    10.         }
    11.  
    12.     public void draw() {
    13.            //-Draw object
    14.     }
    15.  
    16.     public void checkmousepos() {
    17.          
    18.     }
    19.     public void remove() {
    20.          
    21.     }
    22. }
    Code (CSharp):
    1. public class Caller : InheritedFromElseWhere
    2. {
    3.        public Caller() { }
    4.  
    5.        void callDrawer() {
    6.                 Drawer ar = ...getComponent<Drawer>();
    7.                 ar.draw();
    8.       }
    9.  
    10. }
    This is what I want:
    Code (CSharp):
    1. public class Caller : InheritedFromElseWhere
    2. {
    3.        public Caller() { }
    4.  
    5.        void callDrawer() {
    6.                 Drawer ar = ...getComponent<Drawer>();
    7.                 ar.draw();
    8.                 ar.checkmousepos();
    9.                 ar.remove();
    10.       }
    11.  
    12. }
     
  2. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
  3. anilsayar

    anilsayar

    Joined:
    Oct 14, 2018
    Posts:
    14
    Unfortunately, that function works with Colliders that I don't have any. I cannot add it either.
     
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    You're using the Unity engine, so sooner or later you're going to have to break-down and use some of the hooks they provide. Polling mouse input has to be done every frame if you want it to feel responsive. This is the exact sort of thing that Update is for so I don't see why you think this is a problem.
     
    Ryiah and Kurt-Dekker like this.
  5. anilsayar

    anilsayar

    Joined:
    Oct 14, 2018
    Posts:
    14
    I believe that a small collider attachment won't hurt anybody. After some tests, I came up with the idea that I need to create another script to pile up all mouse actions under your OnMouseDown() function in this newly created script. Then don't touch other scripts. OnMouseDown() function will at least provide neat Update functions. I appreciate for your prompt help.


    Code (CSharp):
    1. public class Caller : InheritedFromElseWhere
    2. {
    3.        public Caller() { }
    4.  
    5.        //moved function to other script
    6. }
    Code (CSharp):
    1. public class MouseInterractions : MonoBehaviour
    2. {
    3.        void Start {}
    4.        void Update {}
    5.        void callDrawer()
    6.        {
    7.                 Drawer ar = new Drawer();
    8.                 ar.draw();
    9.       }
    10.  
    11.       void OnMouseDown()
    12.       {
    13.              Drawer ar = new Drawer();
    14.              ar.checkmousepos();
    15.              ar.remove();
    16.      }
    17. }
     
  6. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    The most elegant way is to use event, because more than one class can suscribe to it. I didn't get what exactly you want to do with checkmousepos and draw() if you are going to remove it so I leave it to you, you can pack the code in the same function or use separate events.

    Code (csharp):
    1.  
    2.     public delegate void RemoveDrawerEventHandler(Drawer drawer);
    3.                                      
    4.    public class Drawer : MonoBehaviour
    5.     {
    6.        public static event RemoveDrawerEventHandler removeDrawerEvent;
    7.            
    8.         void Update()
    9.         {
    10.            
    11.             if (Input.GetMouseButtonDown(0))
    12.             {
    13.                   //-Detect mouse position
    14.                   //-Remove object
    15.                  removeDrawerEvent.Invoke(this);
    16.             }
    17.      
    18. ...      
    19.  
    20.     }
    21.  
    and then:

    Code (csharp):
    1.  
    2.    public class Caller : InheritedFromElseWhere
    3.     {
    4.            public Caller()
    5.           {
    6.               Drawer.removeDrawerEvent += removeDrawer;
    7.           }
    8.      
    9.            void removeDrawer(Drawer ar) {
    10.                     ar.remove();
    11.           }
    12.      
    13.     }
    14.  
     
  7. anilsayar

    anilsayar

    Joined:
    Oct 14, 2018
    Posts:
    14
    I tried your example. It works with multiple events, which is great. I realized that the called function (void removeDrawer in this case) is executed twice (consecutively). There must be a problem. Am I wrong?
     
  8. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    If I understand you correctly, I had a similar issue with not wanting my cursor(raycast) position in relation to world space read every frame. And also not have everything associated keep running it's update checks and calls every frame.

    So the raycast I limited to a timer, basically shoot every 6 frames, and declare it's hit position as a global Vector3.

    Then since my map was basically tiles 10x10 units, I rounded that position to be a multiple of 10. And set a static Vector3 as this value.

    Then each class that needed this Vector3, would check against it's own variable of lastCursorPosition versus the static value, if and when changed by the cursor, and would only run once if they weren't equal. Then of course set their last vector to the static vector, to not read again unless that value changed.

    So I'm not particularly sure how your operation works, or else I could explain what I mean better. But there are ways to make code "chill out" until something of note changes.
     
  9. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    If it's called twice then it is because it has been registered twice. You can surely sort it out with a Debug.Log(). Are you using multiple instances of the Caller class?
     
  10. anilsayar

    anilsayar

    Joined:
    Oct 14, 2018
    Posts:
    14
    You are right. Sorry, my bad. Thanks for giving an example about Events. I am sure that everybody would benefit from it.
     
  11. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    Yeah what's great about it is that if tomorrow you want to make a pop sound when the item is removed your audio controller just have to suscribe to the event.