Galandil
Galandil

Reputation: 4249

How to pass a List<Interface> and an implemented method from another class

I have a Game Manager, which is used to manage the execution order of specific Unity callbacks (FixedUpdate, Update and LateUpdate) in all the other scripts.

Specifically, I wrote these 3 interfaces:

public interface IFixedAt {
    bool IsActive { get; }
    void FixedAt(); 
}

public interface IUpdateAt {
    bool IsActive { get; }
    void UpdateAt();
}

public interface ILateUpdateAt {
    bool IsActive { get; }
    void LateUpdateAt();
}

These interfaces are implemented in game objects' scripts, where needed, like this for example:

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour, IUpdateAt, ILateUpdateAt {

    [SerializeField]
    bool isActive = true;
    public bool IsActive {
        get {
            return (isActive && gameObject.activeInHierarchy);
        }
    }

    public void UpdateAt() {
        // Do stuff in Update
    }

    public void LateUpdateAt() {
        // Do stuff in Late Update
    }
}

The Game Manager script gets at Awake the reference to all scripts which implement the interfaces, creating a List<Interface> and then uses the list at runtime to execute the callbacks only where needed:

using UnityEngine;
using System.Collections.Generic;

public class GameManager : MonoBehaviour {

    public List<GameObject> GameObjectsWithScripts;
    List<IFixedAt> fixedAtList { get; set; }
    List<IUpdateAt> updateAtList { get; set; }
    List<ILateUpdateAt> lateUpdateAtList { get; set; }

    private void Awake() {
        PopulateAllLists();
    }

    private void FixedUpdate() {        
        if (fixedAtList != null) {
            for (int i = 0; i < fixedAtList.Count; i++) {
                if (fixedAtList[i].IsActive)
                    fixedAtList[i].FixedAt();
            }
        }
    }

    private void Update() {
        if (updateAtList != null) {
            for (int i = 0; i < updateAtList.Count; i++) {  
                if (updateAtList[i].IsActive)
                    updateAtList[i].UpdateAt();
            }
        }       
    }

    private void LateUpdate() {
        if (lateUpdateAtList != null) {
            for (int i = 0; i < lateUpdateAtList.Count; i++) {
                if (lateUpdateAtList[i].IsActive)
                    lateUpdateAtList[i].LateUpdateAt();
            }
        }
    }

    void PopulateAllLists() {
        fixedAtList = PopulateList<IFixedAt>(GameObjectsWithScripts);
        updateAtList = PopulateList<IUpdateAt>(GameObjectsWithScripts);
        lateUpdateAtList = PopulateList<ILateUpdateAt>(GameObjectsWithScripts);     
    }

    List<T> PopulateList<T> (List<GameObject> goScripts) {
        //Scans the GOs list and adds existent interface elements to the list
        var list = new List<T>();
        for (int i = 0; i < goScripts.Count; i++) {
            if (goScripts[i].GetComponent<T>() != null) {
                list.Add(goScripts[i].GetComponent<T>());               
            }           
        }

        //Returns list (null if list is empty)
        if (list.Count > 0) {
            return list;
        }
        else {
            return null;
        }
    }
}

Now, the question for which I'm having trouble in understanding if it's possible to do, and if yes, how.

As you can see, the code inside FixedUpdate, Update and LateUpdate is basically the same: it iterates on the specific List, checks if the current element is active, and if true it executes the proprietary callback.

What I want to know is if it's possible to create a generic method that can be called from inside the three callbacks, and passing to it the List to iterate on and the specific method to call for that List, something like this in pseudo-code:

private void FixedUpdate() {        
    Method (fixedAtList, FixedAt() );
}

private void Update() {        
    Method (updateAtList, UpdateAt() );
}

private void LateUpdate() {        
    Method (lateUpdateAtList, LateUpdateAt() );
}

private void Method<T> (List<T> list, Action method) {
    if (list != null) {
        for (int i = 0; i < list.Count; i++) {  
            if (list[i].IsActive)
                list[i].method();
        }
    }
}

I've tried different things, to no success, and atm I'm clueless about how to do that. Any help will be very appreciated.

Upvotes: 0

Views: 88

Answers (1)

juharr
juharr

Reputation: 32266

First you need an interface that covers the IsActive method.

public interface IActive {
    bool IsActive { get; }
}

public interface IFixedAt : IActive {
    void FixedAt(); 
}

public interface IUpdateAt : IActive {
    void UpdateAt();
}

public interface ILateUpdateAt : IActive {
    void LateUpdateAt();
}

Then you need to use the generic Action<T> and then you can pass in lambdas

private void FixedUpdate() {        
    Method (fixedAtList, f => f.FixedAt() );
}

private void Update() {        
    Method (updateAtList, u => u.UpdateAt() );
}

private void LateUpdate() {        
    Method (lateUpdateAtList, l => l.LateUpdateAt() );
}

private void Method<T> (List<T> list, Action<T> method) where T : IActive {
    if (list != null) {
        for (int i = 0; i < list.Count; i++) {  
            if (list[i].IsActive)
                method(list[i]);
        }
    }
}

Upvotes: 3

Related Questions