Wywołanie metody w klasie po przechwyceniu zdarzenia

0

Witam,
Tworzę do swojej gry system celów misji do wykonania na danym poziomie. Mam stworzoną fabrykę która przechowuje ID konkretnych celów misji oraz Action.
Problem polega na tym że nie wiem jak zrobić by w momencie wywołania pewnego eventu, na obiektach którego ID celu misji zgadza się ze zdarzeniem, wywołana została metoda z tej klasy.

Próbowałem w konstruktorze przekazać Action i potem dać Invoke ale chyba nie dokończa dobrze to działa.

Niestety słabo ogarniam te eventy i delegaty i jeśli ktoś ma rady jak to zrobić lepiej i elastyczniej lub wyjaśni jakoś przystępniej to będę wdzięczny za radę.

Fabryka:


public abstract class ObjectiveNameFactory
{
    public static readonly Dictionary<string, Action> objectiveHandlers = new Dictionary<string, Action>()
    {
        {"BuildBuilding", delegate { EventsManager.Instance.BuildingComplete += TestBuild;  }}, 
        {"GatherResource", delegate { EventsManager.Instance.GatherComplete += TestGather;  }},
        {"ResearchTechnology", delegate { EventsManager.Instance.ResearchedTechnology += TestResearch;  }}
    };

    private static void TestResearch(GameObject obj)
    {
        Debug.Log("Work research! " + obj);
    }

    private static void TestGather(GameObject obj)
    {
        Debug.Log("Work gather! " + obj);
    }
    
    
    private static void TestBuild(GameObject obj)
    {
        Debug.Log("Work build! " + obj);
    }
}

Objective class:

public class Objectives
{
    public string objectiveID {get;}
    public int goalAmount {get;}
    public int currentProgress {get; private set;}
        
    public Objectives(string objectiveID, int goalAmount, int currentProgress, Action myDelegate)
    {
        this.objectiveID = objectiveID;
        this.goalAmount = goalAmount;
        this.currentProgress = currentProgress;
        
        myDelegate.Invoke();
    }

    public void IncreaseProgress()
    {
        currentProgress++;
    }
}

Objective manager


public class ObjectivesManager : MonoBehaviour
{

    [SerializeField] private TextMeshProUGUI objectiveListTextMeshPro;

    void Awake()
    {
        foreach (var handler in ObjectiveNameFactory.objectiveHandlers.Values)
        {
            handler();
        }
        
        CreateNRandomObjectives();
    }
    

    private void CreateNRandomObjectives()
    {
        for (int i = 0; i < 5; i++)
        {
            int rnd = Random.Range(0, ObjectiveNameFactory.objectiveHandlers.Count);
            Objectives objectives = new Objectives(ObjectiveNameFactory.objectiveHandlers.Keys.ToList()[rnd], Random.Range(1, 20), 0, ObjectiveNameFactory.objectiveHandlers[ObjectiveNameFactory.objectiveHandlers.Keys.ToList()[rnd]]);
            objectiveListTextMeshPro.text += (objectives.objectiveID + " " + objectives.currentProgress + "/" + objectives.goalAmount) + Environment.NewLine;
        }
    }
}

Events manager


public class EventsManager : MonoBehaviour
{
    public static EventsManager Instance;
    
    public event Action<GameObject> BuildingComplete;
    public void OnBuildingComplete(GameObject building) { BuildingComplete?.Invoke(building); }
    
    public event Action<GameObject> GatherComplete;
    public void OnGatherComplete(GameObject sourceDeposit) { GatherComplete?.Invoke(sourceDeposit); }
    
    public event Action<GameObject> ResearchedTechnology;
    public void OnResearchedTechnology(GameObject labBuilding) { ResearchedTechnology?.Invoke(labBuilding); }
    
    private void Awake()
    {
        Instance = this;
    }
0

Problem polega na tym że nie wiem jak zrobić by w momencie wywołania pewnego eventu, na obiektach którego ID celu misji zgadza się ze zdarzeniem, wywołana została metoda z tej klasy.

Bez zagłębiania skupie się na tym.
Jeśli dobrze rozumiem, to masz powiedzmy na scenie budynek, a na nim skrypt np. Building.

public event Action<GameObject> BuildingComplete;

możesz zmienić na:

public event Action<Building> BuildingComplete;

i w skrypcie budynku, w momencie wykrycia zbudowania, możesz zawołać:

EventsManager.Instance.BuildingComplete?.Invoke(this);

Teraz, skoro event przyjmuje Building, a nie GameObject, to możesz wywołać na nim metode.

możesz przekazywać też wiele argumentów:

public event Action<GameObject> GatherComplete;

możesz zmienić według potrzeb na:

public event Action<Unit, Resource> GatherComplete;

i mieć dostęp zarówno do jednostki, która zbiera, jak i zasobu, który został zebrany.
Wszystko zależy jak tam reszta wygląda.

Nie do końca rozumiem z ID, ale możesz dodać już w konkretnych skryptach pole tego typu i porównywać wartość w reakcji na event

0

Z ID chodzi o to że jak jest klasa Objectives, ona ma właściwość public string objectiveID {get;} I te ID brane są z fabryki ObjectiveNameFactory.objectiveHandlers . I chce by w momencie wywołania eventu np. EventsManager.Instance.BuildingComplete?.Invoke(this); w obiektach klasy Objectives które mają objectiveID == "BuildBuilding" wywołana została metoda z tej klasy o nazwie IncreaseProgress.
Próbowałem do obiektu przekazać przez konstruktor typ Action public Objectives(string objectiveID, int goalAmount, int currentProgress, Action myDelegate)
Action które przekazywane jest w konstruktorze również brane jest z fabryki ObjectiveNameFactory.objectiveHandlers delegate { EventsManager.Instance.BuildingComplete += TestBuild; }

Tylko nie bardzo wiem co dalej z tym zrobić by w momencie wywołania eventu ta metoda IncreaseProgress się wywołana na obiektach których to powinno dotyczyć.

1

Najprościej:
Gdzieś trzymasz List<Objectives> i dodajesz do niej te stworzone. Pózniej w TestResearch przelatujesz przez nie, porównujesz ID i wywołujesz IncreaseProgress na prawidłowych.
Ew. bardziej uniwersalnie, zamiast List<Objectives> zrobić Dict<string, List<Objectives>>, gdzie kluczem będzie konkretne ID - wtedy nie musisz sprawdzać ID, tylko przelatujesz po tym, które jest odpowiednie.

1

Hm, myślałem nad takim rozwiązaniem, może faktycznie tak zrobię. Acz bardziej planowałem zrobić to tak bardziej profesjonalnie na zdarzeniach. Ewentualnie na razie zrobię tak jak radzisz a potem spróbuję na zdarzeniach jakoś.

1 użytkowników online, w tym zalogowanych: 0, gości: 1