Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Adding a method to a game object

Discussion in 'Scripting' started by ThySpektre, Apr 5, 2019.

  1. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Good morning. I have a related question here https://forum.unity.com/threads/gam...llection-can-be-added-to.656236/#post-4395004 but this is an attempt to get help by making the problem more specific and less general.

    I have a scene with hundreds of GameObjects, a few dozen of which have scripts attached with properties that affect its behavior in game.

    At certain points of the game, I’d like to reset these few dozen objects to their original states. To make it more concrete let’s state that the few dozen objects each are either a Door, Window, Ball, or Chair, each of which have prefabs made with scripts attached to them holding these properties.

    What I’d like is a variable array set up in the editor which the game’s designer can fill with objects that need this rest ability. Armed with this array of ResettableGameObjects in my GameManager, when the time comes to reset the GameObjects, I’d like to call a method of the GameObjects in a loop such as:


    for (int i = 0; i <= ResettableGameObjects.Length - 1; i++)
    {
    ResettableGameObjects.Reset()
    }


    Unfortunately, you cannot extend the GameObject to add such a method.

    I could add a Component script to each prefab that contains a Reset function, however as each type of game object(Door, Window, Ball, Chair) has different things that needs reset I cannot use a generic component script named “Reset.cs” unless I want to make the costly calls of finding out which type of object I am attached to. So, I cannot seem to make a generic loop as shown above. I’d need differently named component scripts for each type of object.

    Is there a simply way to achieve the type of reset loop I am looking for above in which a “GameObject’s” reset function is called without needing to know exactly what type of GameObject we have?
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Components can implement interfaces no problem.

    Code (csharp):
    1.  
    2. interface IResetable { void OnReset(); }
    3.  
    4. public DoorReset : MonoBehaviour, IResetable
    5. {
    6.       // fill it in
    7. }
    8.  
    9. public WindowReset : MonoBehaviour, IResetable
    10. {
    11.       // fill it in
    12. }
    13.  
    Then you can use your loop with IResetable types and call OnReset() on them. Defining all of those reset scripts may be cumbersome depending on your setup, but you can always use inheritance to simplify things if there's common elements among each resetable object.

    Perhaps another cheeky way to do it would be to instantiate a clone of your objects on Start, and then on reset, destroy the original object and replace it with your cloned object and then re-clone them again for the next time you need to reset.
     
  3. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Interfaces are not something I am familiar with, could you help explain how that relates here?

    edit: OK I did a quick read on interfaces and I'm not sure how this helps. Each GameObject would still have a differently named component script attached correct?
     
    Last edited: Apr 5, 2019
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    You can fetch interfaces through GetComponent.

    Code (csharp):
    1.  
    2. IResetable resetable = gameObject.GetComponent<IResetable>();
    3.  
    Then it doesn't matter what the "real" component is, just that it has an IResetable implementation. They're very powerful.
     
  5. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    So within any component script (named whatever they are named) attached to the various GameObject I can place an interface with the definition given above:

    Code (CSharp):
    1. interface IResetable { void OnReset(); }
    and then for the one attached to doors just have the public DoorReset function, the one attached to the chairs have the ChairReset function, etc?
     
  6. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Watching this:
    Trying to learn. Will report back.
     
  7. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Well, this is not working so well yet.

    I have created an IRESET interface:

    Code (CSharp):
    1. public interface IReset
    2. {
    3.     void Reset();
    4. }
    5.  
    Then in each object I need to reset, I implement a reset method on a component script attached to it, and add the IReset interface to the class.

    An example for an object who has a reset behavior of simply changing position back to where it started...

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PositionReset : MonoBehaviour, IReset
    6. {
    7.     private Vector3 ResetPosition;
    8.     private Quaternion ResetRotation;
    9.     private Vector3 ResetVelocity;
    10.     private Vector3 ResetAngularVelocity;
    11.     private Rigidbody rb;
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.         ResetPosition = gameObject.transform.position;
    17.         ResetRotation = gameObject.transform.rotation;
    18.         rb = gameObject.GetComponent<Rigidbody>();
    19.         ResetVelocity = rb.velocity;
    20.         ResetAngularVelocity = rb.angularVelocity;
    21.     }
    22.  
    23.     public void Reset()
    24.     {
    25.         gameObject.transform.position = ResetPosition;
    26.         gameObject.transform.rotation = ResetRotation;
    27.         rb.velocity = ResetVelocity;
    28.         rb.angularVelocity = ResetAngularVelocity;
    29.     }
    30. }
    And finally, loop through all the Resetable Objects in my array of objects that need reset.

    Code (CSharp):
    1.        for (int i = 0; i <= ResetableGameObjects.Length - 1; i++)
    2.         {
    3.             IReset resetee = ResetableGameObjects[i].GetComponent<IReset>();
    4.             resetee.Reset();
    5.         }

    Unfortunately, I am getting null reference errors on resetee. (Second line of the script) Indicating none were found.

    Any help?
     
  8. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Everything looks fine, so one (or more) of the objects you've added to your array lacks a component that implements the IReset interface. You can add a null check to figure out which one(s).

    Code (csharp):
    1.  
    2. IReset resetee = ResetableGameObjects[i].GetComponent<IReset>();
    3.  
    4. if(resetee != null)
    5.    resetee.Reset();
    6. else
    7.    Debug.Log(ResetableGameObjects[i].name + " lacks an IReset interface");
    8.  
     
  9. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    The issue...

    Reset is a reserved keyword :(

    Changed Reset() to Resetable() and all OK. Thank you for your help GroZZler. Now I know a bit about Interfaces!
     
    GroZZleR likes this.
  10. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Oh duh! Good catch. :)
     
  11. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    So, if more than one component contains a Resetable() script, could one use GetComponents<IResetable>()?
     
  12. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    I honestly do not know, I've never tried. I'm sure you've tested it by this point... so does it work? :)
     
  13. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Turns out SendMessage works even better. No more messy interfaces, and the GameObjects work more similar to normal objects.
     
  14. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,952
    According to the Unity 4.6 release notes a non-generic GetComponents was added that supports interfaces. I'm guessing it's the following entry which is second from the bottom of the GetComponents reference page.
    https://docs.unity3d.com/ScriptReference/GameObject.GetComponents.html

    Here is the release notes entry and link.
    https://unity3d.com/unity/whats-new/unity-4.6
     
  15. ThySpektre

    ThySpektre

    Joined:
    Mar 15, 2016
    Posts:
    362
    Instead of all the kludgy GetComponents and Interface definitions, SendMessage seems to be doing fine.