Home > database >  Fill common attribute value from all scripts
Fill common attribute value from all scripts

Time:12-03

I have multiple scripts that have one common boolean variable with the same name. As shown below:

Script1.cs

public bool isTrigger = false;

Script2.cs

public bool isTrigger = false;

... ...

Now I have a master script, that should find all the scripts with the common boolean name and change it. Here in the example allScripts would be the list of all scripts that have the isTrigger boolean.

MasterScript.cs

for(int i = 0; i<allScripts.Length; i  ){
  allScripts[i].isTrigger = true;
}

How do I achieve this? How do I find all the scripts that have the same boolean variable and add it to a list?

CodePudding user response:

As said there are basically two options:

Common Base class

Use a common base class for your components like e.g.

public abstract class MyBase : MonoBehaviour
{
    public bool IsTrigger;

    // more common fields, properties and methods
}

and then instead inherit your other classes from that one

public class ClassA : MyBase
{
    // additional A specific stuff
}

public class ClassB : MyBase
{
    // additional B specific stuff
}

Since ClassA and ClassB aready inherit the members from MyBase you can then directly use

public class Master : MonoBehaviour
{
    // Use the common base type!
    // Either populated via the Inspector
    public MyBase[] allInstances;

    // Or on runtime
    private void Awake()
    {
        // e.g. find everything of the base type in your scene
        allInstances = FindObjectsOfType<MyBase>(true);

        foreach(var instance in allInstances)
        {
            instance.IsTrigger = true;
        }
    }
}
  • Advantages
    • Every subclass already has the field IsTrigger and there is no more to do
    • The base class can already implement common behavior in general and can be extended using virtual, abstract and then override in the subclasses
  • Disadvantages
    • You can only inherit from one single class so extending this with another base class is very inflexible

Interface

Instead of a common base class you can use an interface. An interface does not bring any own implementation but is rather a member template.

As in an interface you can't define fields you will have to use properties

public interface IMyThing
{
    bool IsTrigger { get; set; }
}

and then

public class ClassA : MonoBehaviour, IMyThing
{
    // example using a serialzed auto-property
    [field: SerializeField] public bool IsTrigger { get; set; }
}

public class ClassB : MonoBehaviour, IMyThing
{
    // Example using a backing field
    [SerializeField] private bool isTrigger;

    public bool IsTrigger
    {
        get => isTrigger;
        set => isTrigger = value;
    }
}

and then in your master

public class Master : MonoBehaviour
{
    // reference via Inspector
    // Using the SeriaizeReferenceAttribute enables to serialize and reference any instances of 
    // classes inheriting from UnityEngine.Object and implementing the interface
    [SeriaizeReference] public IMyThing[] allInstances;

    private void Awake()
    {
        // since FindObjectOfType can not be sued on interfaces here i gets a bit more complex
        // find all instances from all scenes
        var list = new List<IMyThing>();
    
        for (var i = 0; i < SceneManager.sceneCount; i  )
        {
            var scene = SceneManager.GetSceneAt(i);
            var roots =  scene.GetRootGameObjects( );
            foreach (var root in roots)
            {
                list.AddRange(root.GetComponentsInChildren<IMyThing>(true));
            }
        }

        allInstances = list.ToArray();

        foreach(var instance in allInstances)
        {
            instance.isTrigger = true;
        }
    }
}
  • Advantage
    • Solves the limitation of the base class: You can implement as many interfaces as you wish
  • Disadvantage
    • No guarantee anymore that a class implementing IMyThing is a MonoBehaviour! => FindObjectsOfType can not be used and GetComponent, etc might fail on runtime
    • Every class has to implement according members itself
    • Unity doesn't directly serialize interfaces

CodePudding user response:

You can create an interface in which you define your property. You then have your scripts inherited from this class.

When instantiating your objects you need to add them to a list.

public interface IExampleA 
{
public bool isTrigger { get; set;}
}

public class B : IExampleA 
{

}
class masterscript
{
    public void function() 
    {
     // Create objects that has class B
     // if object has class B add it to a list
     // do your loop
     }


}
  • Related