Fill common attribute value from all scripts - c#

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?

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

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

Casting to arbitrary version of generic class

I have a class as follows:
public class Impactable<T> : where T : Spawnable<T>
{
protected T spawnable = Spawnable<T>.Instance;
void DoSomethingIndependentOfT(){}
}
The reason I have it implemented like this is because Spawnable is a Lazy extendable Singleton. In Impactable, I have of course methods that utilize spawnable, but also methods that don't that I'd like to access externally by casting.
public sealed class EnemySpawnable : Spawnable<EnemySpawnable>
{
}
public class MyEnemyA : Impactable<EnemySpawnable>
{
}
MyEnemyA enemy = new MyEnemyA();
Impactable x = enemy;
x.DoSomethingIndependentOfT();
Is it possible to achieve a cast like this in C# or will I have to re-engineer my code?
No, its not. The type argument constraint on Impactable (where) prevents it. But the refactoring required is non-breaking and trivial. The solution is to promote the type-independent methods to a base class which does not require specialization, like so:
public class Spawnable<T>
{
public static T Instance;
}
public class Impactable
{
internal void DoSomethingIndependentOfT() { }
}
public class Impactable<T> : Impactable where T : Spawnable<T>
{
protected T spawnable = Spawnable<T>.Instance;
}
public sealed class EnemySpawnable : Spawnable<EnemySpawnable>
{
}
public class MyEnemyA : Impactable<EnemySpawnable>
{
}
public class Program
{
public static void Main()
{
MyEnemyA enemy = new MyEnemyA();
Impactable x = enemy;
x.DoSomethingIndependentOfT();
}
}
Even if what you intended would be possible (or is made possible in future versions of C#), it's still much cleaner to do it this way, because it self-documents the intent (methods that do not use generics, should not reside in a container scoped to a constrained type).

Diffrent types for one variable

I want to set gunS to Shotgun type or AR15 type but dont know how and this code dont work
public Shotgun gunS2;
public AR15 gunS3;
public MonoBehaviour gunS;
private void Start()
{
set();
}
public void set()
{
if(gunT.name == "Shotgun")
{
gunS = gunT.GetComponent<Shotgun>();
}
else
{
gunS = gunT.GetComponent<AR15>();
}
}
Give them a common interface.
interface IWeapon
{
}
class AR15 : IWeapon
{
}
class Shotgun : IWeapon
{
}
If your classes are defined this way (you obviously have to add implementation) then you can write a variable that can contain any IWeapon.
public IWeapon gunS;
public void set()
{
if(gunT.name == "Shotgun")
{
gunS = gunT.GetComponent<Shotgun>();
}
else
{
gunS = gunT.GetComponent<AR15>();
}
}
Either use a base class and/or interface
public interface IWeapon
{
// whatever public properties and methods shall be accessible through this interface
}
public abstract class Weapon : MonoBehaviour //, IWeapon
{
// whatever fields, properties and methods are shared between all subtypes
// if using the interface implementation of it
}
and then
public class ShotGun : Weapon
// or if for some reason you don't want a common base class
//public class ShotGun : MonoBehaviour, IWeapon
{
// whatever additional or override fields, properties and methods this needs
// or if using the interface the implementation of it
}
and
public class AK74 : Weapon
// or if for some reason you don't want a common base class
//public class AK74 : MonoBehaviour, IWeapon
{
// whatever additional or override fields, properties and methods this needs
// or if using the interface the implementation of it
}
Then simply drag the according object/component into the exposed slot in the Inspector in Unity.
There is no need for your gunT field (wherever it comes from)
// Already reference this via the Inspector in Unity
// then you don't need your Start/set method AT ALL!
public Weapon gunS;
If for some reason this is not an option e.g. if using only the interface
public IWeapon gunS;
there still is no need to check the name or specify the type further. GetComponent will return the first encountered component of the given type or a type inheriting from it. You can simply do
void Awake()
{
// as fallback if for whatever reason you can't directly reference it via the Inspector
// (which doesn't seem to be the case since somewhere you get gunT from ...)
if(!gunS) gunS = /*Wherever this comes from*/ gunT.GetComponent<Weapon>();
// or i only using the interface
//if(!gunS) gunS = /*Wherever this comes from*/ gunT.GetComponent<IWeapon>();
}

Collection with generic and non-generic UnityEvents

I am working on some project in Unity. I have:
[Serializable]
public class ItemAction
{
[SerializeField]
private UnityEvent unityEvent;
public void Perform()
{
unityEvent.Invoke();
}
}
[Serializable]
public class ItemAction<T>
{
[SerializeField]
private UnityEvent<T> unityEvent;
public void Perform(T parameter)
{
unityEvent.Invoke(parameter);
}
}
Also I have this class:
public abstract class Item : MonoBehaviour
{
[SerializeField]
private float weight;
[SerializeField]
private [collection of item actions] actions;
public abstract void Use();
public abstract void Grab(Transform transform);
public abstract void Drop();
}
How to create collection with mixed both generic and non-generic ItemAction instances (so some actions may require some parameters)?
For example:
For unequipped weapons, I can only grab them.
For unequipped medkits, I can grab them or use them immediately.
For triggers/switchers, I can only use them.
I could probably use an empty interface, but I don't think it's good solution...
Like you said you can create a empty interface or just use a collection of objects, because you will have to cast anyways or you skip the ItemAction class and only use the version which has a parameter and pass null for actions which don't require a parameter (not a "nice" solution either, but this is how the WPF framework does it with the ICommand interface, for example)
But there is an other problem, no matter if you use a interface or object list, you will not be able to show them in the editor (unless you create a custom editor), because the editor doesn't show generic classes.
Since you want to implement the methods in every specific item class inheriting from Item (at least, I guess so since you declared those methods abstract),
I'd go with another route, and implement interfaces for every specific action.
For example:
public interface IItem {
float Weight { get; set; }
}
public interface IGrabItem : IItem {
void Grab();
}
public interface IDropItem : IItem {
void Drop();
}
public interface IUseItem : IItem {
void Use();
}
public class MedKit : MonoBehaviour, IGrabItem, IUseItem {
[SerializeField]
float weight;
public float Weight {
get { return weight; }
set { weight = value; }
}
public void Grab() {
//Your code
}
public void Use() {
//Your code
}
}
and then you can choose to implement the code directly in every specific item class or use some kind of event to raise them.
Oh, and btw, if possible, avoid using UnityEvent and rely on the standard .NET event, it's faster and creates less overhead.

Setting Custom Values on Extended class

Im not sure if it is possible. I am running into a unique issue dealing with a clients api.
I am needing to extend a class and add a bool property that does not exist in the base class.
below is an example of what I am trying to accomplish.
public class baseClass
{
//.. No Editable Access
}
public class Extended
{
public bool flaggedAsDeleted(this baseClass bc)
{
//Idealy was looking for get; set; but I know that don't work
return true;// Need to know if possible to set property on baseClass or Alternative
}
public void flagAsDeleted(this baseClass bc)
{
flaggedAsDeleted = true;
}
}
public class program
{
public void doit()
{
baseClass bc = new baseClass();
bc.flagAsDeleted();
}
}
If you're trying to actually extend a class, you do it like this:
public class BaseClass
{
//.. No Editable Access
}
public class Extended : BaseClass
{
public bool FlaggedAsDeleted { get; set; }
}
If you're trying to add data to an existing class, you have two options:
Inheritance - as seen above.
Encapsulation - create a new object that holds an instance of the type you're adding to.
C# provides a feature called Extension Methods, which allows you to seemingly add methods to existing classes. However, these are really just syntactic sugar, as you're still constrained to the class's public API.
public class BaseClass
{
public int Value { get; set; }
}
public static class ExtensionMethods
{
public static void Increment(this BaseClass b)
{
b.Value += 1;
}
}
Extension methods do not allow you to add data to an existing class though.
This is not unique. This is a common problem solved using a Design Pattern called decorator.

How to get class constant via .GetType()

I will try to explain.
I have 3 classes : EnemyTeleport, EnemyZigZag, EnemyNormal. They all inherit base class Enemy.
Each of these 3 classes has his own const -> KillPoints.
So in other class. For example Form Class, I need to get these constants.
These EnemyTeleport, EnemyZigZag, EnemyNormal are located in public Enemy EnemyInstance;
For example
EnemyInstance = new EnemyTeleport();
So how can I get Kill Points from EnemInstance? I don't want to to check each possible variant?
Is there smarter way?
Like EnemInstance.GetType().KillPoints (doesn't work)
Again. EnemyInstance is Enemy type which holds EnemyTeleport. Constant keeps in EnemyTeleport
Why don't you expand your base-class Enemy with a property for the KillPoints. In the constructor of a inherited class you can set the KillPoints to the specific value of the certain enemy
But if you don't want to put it into a base-class:
Make the KillPoints as a property in the certain Enemy like:
public class TeleportEnemy : Enemy
{
public int KillPoints{get;private set;}
public TeleportEnemy()
{
this.KillPoints = 666;
}
}
The code to access the Killpoints by an instance of Enemy looks like:
Enemy enemy = new TeleportEnemy();
PropertyInfo propertyInfo = enemy.GetType().GetProperty("KillPoints");
int value = (int)propertyInfo.GetValue(enemy, null);
I suggest you to use
public abstract class Enemy
{
public abstract List<int> KillPoints;
}
For each specific class define the value of KillPoints (You can use TemplateMethod pattern to calculate your KillPoints)
link : http://www.dofactory.com/Patterns/PatternTemplate.aspx
But in your Main you use base class Enemy for your services
So :
Enemy enemy = new EnemyTeleport();
var result = enemy.KillPoints;
Your hierarchy could be defined as follows. Note how Enemy class is abstract, while concrete enemies implement property KillPoints. Your main implementation has to know only about abstract class Enemy, while concrete implementation should be created elsewhere, depending on some flag in your game.
public abstract class Enemy
{
public abstract int KillPoints {get;}
}
public class EnemyTeleport : Enemy
{
public override int KillPoints
{
get{return 6;}
}
}
public class EnemyZigZag : Enemy
{
public override int KillPoints
{
get{return 10;}
}
}
Now you can take advantage of polymorphic call to KillPoints
private Enemy EnemyInstance;
void Main()
{
EnemyInstance = new EnemyTeleport();
Console.WriteLine(EnemyInstance.KillPoints);
}

Categories

Resources