Programowanie gier eventy Dalton & Cartman KNTG Polygon, 21 grudnia 2015 r.
Przykład z życia
Jasełki
Jasełki 1. Narodziny Jezuska 2. Pojawienie się gwiazdy 3. Przybycie pastuchów i mędrców (x: 100, y: -50) 4. Przekazanie prezentów (mirra) 5. Jezusek otrzymał prezent (mirra, true) 4. Przekazanie prezentów (kadzidło) 5. Jezusek otrzymał prezent (kadzidło, true) 4. Przekazanie prezentów (złoto) 5. Jezusek otrzymał prezent (złoto, false) 6. Kurtyna, wszyscy idą spać
Eventy w C# (Unity)
Delegacje (C#) delegate void DelegateType(int a); public void Demo() DelegateType deleg = Foo; deleg(1); // wynik: // foo: 1 void Foo(int x) Console.WriteLine("foo: " + x); deleg = Bar; deleg(2); // wynik: // bar: 2 void Bar(int y) Console.WriteLine("bar: " + y); deleg = null; deleg += Foo; deleg += Bar; deleg(3); // wynik: // foo: 3 // bar: 3
Eventy (C#) class EventInvoker public delegate void DelegateType(int x); public event DelegateType SomethingInterestingHappened; private void DoStuff() SomethingInterestingHappened(5); class EventListener private EventInvoker eventinvoker; public EventListener(EventInvoker ei) eventinvoker = ei; // eventinvoker.somethinginterestinghappened = EventHandler; // niedozwolone // eventinvoker.somethinginterestinghappened = null; // niedozwolone eventinvoker.somethinginterestinghappened += EventHandler; // ok // eventinvoker.somethinginterestinghappened(10); // niedozwolone private void EventHandler(int param) Console.WriteLine("EventListener received an event that something " + "interesting happened in EventInvoker. Parameter: " + param);
Eventy w Unity public class EventManager public static /* some public static /* some public static /* some public static /* some type type type type */ */ */ */ ScreenChanged; UserTapped; GamePaused; MainMenuShown; public class UserTappedEventArgs : EventArgs public readonly int X; public readonly int Y; public UserTappedEventArgs(int x, int y) X = x; Y = y; class Foo public Foo() EventManager.UserTapped += OnUserTapped; private void OnUserTapped(UserTappedEventArgs args) Console.WriteLine("User tapped. Coords: (" + args.x + ", " + args.y + ")"); class UserInterface void HandleInput() //... EventManager.UserTapped( new UserTappedEventArgs(300, 500) ); //...
Eventy w Unity public class EventManager public static /* some public static /* some public static /* some public static /* some type type type type */ */ */ */ ScreenChanged; UserTapped; GamePaused; MainMenuShown; Do każdego eventu musimy zdefiniować typ delegacji, np. void DelegateType(UserTappedEventArgs args) Jeśli skorzystamy z C#-owego eventu, to nie będziemy mogli wywołać go spoza klasy EventManager Rozwiązanie: public class EventInvoker<T> where T : EventArgs public delegate void DelegateType(T args); public event DelegateType Listeners = null; public void Invoke(T args) if (Listeners == null) return; if (args == null) Debug.LogError("Event Arguments cannot " + "be null, ignoring an event"); return; Listeners.Invoke(args);
Eventy w Unity public class EventManager public static EventInvoker<UserTappedEventArgs> UserTapped = new EventInvoker<UserTappedEventArgs>(); class Foo public Foo() EventManager.UserTapped.Listeners += OnUserTapped; class UserInterface void HandleInput() //... EventManager.UserTapped.Invoke(new UserTappedEventArgs(300, 500)); //......a gdy dużo eventów: partial class / kilka EventManagerów
Eventy w C++
Eventy w C++ Podobnie jak w C#, tylko samemu trzeba napisać odpowiednik C#-owego delegate / event std::function FastDelegate (by Don Clugston) szybkie modyfikowanie listy podczas przechodzenia po niej Czas wysłania i odebrania 10 mln eventów 6 5 czas [s] 4 std::function FastDelegate 3 2 1 0 MSVC, release g++, release (-O2)
Eventy w C++ (inne podejście) Stworzenie klasy UserTappedEventArgs = stworzenie nowego typu eventu Wszystkie delegacje typu void(eventargs*) wewnątrz rzutujemy np. na UserTappedEventArgs* Wówczas możemy trzymać wszystkie eventy w jednej mapie: std::map(eventtype, MyList<MyDelegate(void, EventArgs*)>) Nie musimy tworzyć pola do każdego eventu EventType? Można skorzystać z typeid (np. hash_code()) Z szablonami można to wszystko zrobić bez alokacji pamięci
Eventy w Unreal Engine 4
Eventy w Unreal Engine 4 np. GameMode
Eventy w Unreal Engine 4 Blueprint wroga
Eventy w Unreal Engine 4 Blueprint gracza
Eventy w Unreal Engine 4 Implementacja w C++
Eventy w Unreal Engine 4 Implementacja w C++
Podsumowanie
Eventy... pomagają pozbyć się zapętlonych referencji pomagają osiągnąć modularny kod upraszczają dodawanie nowych funkcjonalności są proste w implementacji pozwalają pisać bezpieczny kod w niektórych przypadkach mogą być znacznie szybsze, niż sprawdzanie tego samego warunku na wielu obiektach
Jednak... czasami mogą być trudne w debugowaniu trzeba pamiętać o wyrejestrowaniu obiektu, który zostaje zniszczony nie można rejestrować się do eventu, który właśnie się wykonuje należy korzystać z nich ostrożnie i w granicach zdrowego rozsądku
Literatura uzupełniająca Mike McShaffry, David Graham Game Coding Complete, Fourth Edition (rozdział 11: Game Event Management )
Literatura uzupełniająca Robert Nystrom Game Programming Patterns (gameprogrammingpatterns.com/observer.html)
Literatura uzupełniająca Don Clugston Member Function Pointers and the Fastest Possible C++ Delegates (www.codeproject.com/articles/7150/member-function-pointers-and-the-fastest-possible)
Dziękujemy za uwagę