Wywołanie metody w klasie po przechwyceniu zdarzenia

Wywołanie metody w klasie po przechwyceniu zdarzenia
M1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 29
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:

Kopiuj

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:

Kopiuj
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

Kopiuj

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

Kopiuj

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;
    }
Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
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.

Kopiuj
public event Action<GameObject> BuildingComplete;

możesz zmienić na:

Kopiuj
public event Action<Building> BuildingComplete;

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

Kopiuj
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:

Kopiuj
public event Action<GameObject> GatherComplete;

możesz zmienić według potrzeb na:

Kopiuj
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

M1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 29
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ć.

Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
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.

M1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 29
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ś.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.