I have an interface which allows me to pass any object which scores points when it’s killed (regardless of the script itself) to a UIManager which displays those points if necessary. It looks like this and works fine:
using System;
using UnityEngine;
public interface ICanScorePoints {
// gameObject is needed for finding the entity's transform position
// This is inherited from MonoBehaviour and doesn't need explicit implementation
GameObject gameObject { get; }
// The PointValue property is set privately by each entity internally,
// based on a public field in the Inspector (declared in the entity).
// We only need a getter here.
int PointValue { get; }
// This boolean property is set by each entity internally,
// based on a public field in the Inspector (declared in the entity).
// This allows selecting, in the prefab, whether the entity
// should show its score value at its position when destroyed.
bool DisplayPointsWhenKilled { get; }
}
I would like to add an Action to this interface, because all objects implementing this interface should be able to fire a “score points” event. I know that interfaces can’t include fields, but I’ve read that I could use a property instead. So I tried adding this to the interface:
Action<ICanScorePoints> OnScorePoints { get; set; }
This doesn’t generate any errors. However, when I try to add this in one of the scripts implementing the interface, like this…
public static Action<ICanScorePoints> OnScorePoints { get; set; }
…I get an error saying that my script “does not implement interface member ICanScorePoints.OnScorePoints.get and the best implementing candidate AsteroidController.OnScorePoints.get is static” (Visual Studio says more or less the same thing, that the object cannot implement an interface member because it is static).
The reason I’m doing this like that is because the objects that can score points have nothing to do with each other conceptually, and can’t use an inheritance system. And I don’t want my UIManager to subscribe to each and every enemy type individually to listen for scoring events.
Is it possible to do what I’m trying to do, is it only a question of syntax or is this fundamentally impossible?
Thanks.
You can’t do that. Interfaces, abstract, etc. cannot apply to static members. If you want to accomplish this, you will have to manually remember to do it on all deriving classes.
Hello buddy,
The problem is:
- You can’t use static keyword on an interface component.
You can solve it two ways:
-
UIManager will get all of objects references, then subscribe to the action
-
OR the other way around
-
Each object that implements ICanScorePoints will look for the UIManager instance, and add itself to a list there
-
//Exemple
public class UiManager : MonoBehaviour{
private void Start(){
var myArray = FindObjectsOfType<ICanScorePoints>();
for(int i = 0; i < lala.Length; i++)
myArray*.myActionVariable += HandleScorePointsObjects; //subscribe here*
}
private void HandleScorePointsObjects(){
//Do what you need
}
}
I assume you have just one static OnScorePoints event which works for all instances of the ICanScorePoints interface.
Both my options below allow access to a ScorePoints() method on any ICanScorePoints instance.
myClassInstance.ScorePoints();
Option 1: Extension Method
Pros: Can be called for all class instances implementing the ICanScorePoints interface.
Cons: Cannot be overridden (since it cannot be virtual).
public interface ICanScorePoints {
GameObject gameObject { get; }
int PointValue { get; }
bool DisplayPointsWhenKilled { get; }
// No method declaration needed!
}
public static class ScoreManager {
// Static event for every class implementing ICanScorePoints.
public static Action<GameObject, int, bool> OnScorePoints;
// Extension method avaliable on every class implemeting ICanScorePoints.
public static void ScorePoints(this ICanScorePoints obj)
{
if (OnScorePoints != null)
OnScorePoints(obj.gameObject, obj.PointValue, obj.DisplayPointsWhenKilled);
}
}
public class MyClass : MonoBehaviour, ICanScorePoints {
public int PointValue { get { return 22; } }
public bool DisplayPointsWhenKilled { get { return false; } }
// No method definition needed!
}
Option 2: Static Function
Pros: Can be (kind of) overridden, because…
Cons: You must define a normal method for every class implementing the interface.
public interface ICanScorePoints {
GameObject gameObject { get; }
int PointValue { get; }
bool DisplayPointsWhenKilled { get; }
// non-static method must be implemented in every class,
// because interfaces cannot have virtual methods.
void ScorePoints();
}
public static class ScoreManager {
// Static event for every class implementing ICanScorePoints.
public static Action<GameObject, int, bool> OnScorePoints;
// This is like a virtual method, but not really, since
// it still needs to be called explicitly in every class implementing ICanScorePoints.
public static void ScorePoints(GameObject gameObject, int points, bool displayPointsWhenKilled)
{
if (OnScorePoints != null)
OnScorePoints(gameObject, points, displayPointsWhenKilled);
}
}
public class MyClass : MonoBehaviour, ICanScorePoints {
public int PointValue { get { return 22; } }
public bool DisplayPointsWhenKilled { get { return false; } }
// implement ICanScorePoints.ScorePoints() for this class.
public void ScorePoints() {
ScoreManager.ScorePoints(this.gameObject, this.PointValue, this.DisplayPointsWhenKilled);
}
// Note: If you need something like an overridden virtual method,
// put it as a new static function in the ScoreManager class.
}