Collection with generic and non-generic UnityEvents - c#

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.

Related

C# - public getter and protected setter interface

Hello i'm working on a Unity game and i would like to create living entities.
To perform this, i want to create an interface for all entities having health
Here is my interface for LivingEntities:
public interface ILivingEntity
{
public float Hp { get; protected set; }
public float MaxHp { get; protected set; }
public float HpRegenPerSecond { get; protected set; }
public event EventHandler Event_died;
protected virtual void Awake()
{
MaxHp = Hp;
}
protected virtual void receiveDamage(IAttack attackSource)
{
Hp -= attackSource.damage;
watchForEntityDeadOrNot();
}
protected abstract void watchForEntityDeadOrNot();
protected void regenHp()
{
Hp += Time.deltaTime * HpRegenPerSecond;
if (Hp > MaxHp)
Hp = MaxHp;
}
}
The point is:
I need the hp to be public in get
I want to give code in my interface for the hp regeneration per second (to not re implement the same code in each living entity)
I want the hp to be set only from the living entities themself
I saw tricks like this:
in interface:
public float Hp{get;}
and in implementation:
public float Hp{
get{code...}
protected set{code...}
}
but in my case, if i define the setter only in the child class implementation, i'm not able to give any code for my 'regenHp' method in interface.
How to perform this ?
Rather than using an abstract base class as was suggested in a comment, you can leverage Unity's built in component design which is the standard way of solving such a problem. Its common for game objects to be composed of many components.
You can define a shared component like:
public class LivingComponent : MonoBehavior
{
...
}
And then depend on it in your main components:
[RequireComponent(typeof(LivingComponent))]
public class SomeLivingThing : MonoBehavior {}
And if its important that you still have a read-only interface, you can do that too:
public interface ILivingEntity {
// Getters only here
}
public class LivingComponent : MonoBehavior, ILivingEntity {
// Implementation
}
// In some other code:
var hp = obj.GetComponent<ILivingEntity>().Hp;

Fill common attribute value from all scripts

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
}
}

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>();
}

How to programmatically assign two different classes to one variable?

I am creating a game in Unity and I have 2 classes, I want to assign that variable with 2 different classes depending on a boolean value, to later use that class how I wish, here is the code I have so far to give an idea what im looking for, is it even possible for it to be done? Thanks
public GeneticController geneticController;
public GeneticDriver geneticDriver;
public Object Genetic;
void Start() {
if (RaceSelect.SelectedRace == 2) {
Genetic = geneticDriver;
} else {
Genetic = geneticController;
}
}
void FixedUpdate() {
float steer = (float)(Genetic.Steering);
Steering(steer);
}
At the moment It just says, that Object doesn't have a variable called "Steering". What type should Genetic be?
I am making some assumption here, that both GeneticController and GeneticDriver implement a Steering property? If so, and especially if they have additional properties and methods in common, the proper way to implement this is to refactor the classes so that they share a common interface.
public interface ISteering
{
float Steering {get; set;}
}
public class GeneticController : ISteering
{
public float Steering{ get; set; }
}
public class GeneticDriver: ISteering
{
public float Steering{ get; set; }
}
For there you can make your variable Genetic a type of ISteering.
public ISteering Genetic;
However, if Steering is the only property they have in common, I recommend you taking a second look at your overall design. If the two classes share no, or very little, common functions, they probably don't need to share a variable.
Have you tried using a base class that is derived into your 2 sub-classes?
Something along the lines of:
public class BaseClass
{
public float Steering {get;set;}
}
public class GeneticController : BaseClass
{
}
public class GeneticDriver : BaseClass
{
}
then you do the following (and you do not need the cast):
BaseClass Genetic
void Start()
{
if (RaceSelect.SelectedRace == 2)
{
Genetic = geneticDriver;
} else
{
Genetic = geneticController;
}
}
void FixedUpdate()
{
float steer = Genetic.Steering;
Steering(steer);
}
You could set the BaseClass as an interface also. It depends if you have common functionalities, or not, between you 2 derived classes.

Categories

Resources