Obecnie próbuję wdrożyć system encji oparty na komponentach, w którym encja jest w zasadzie tylko identyfikatorem, a niektóre metody pomocnicze wiążą kilka komponentów razem, tworząc obiekt gry. Niektóre cele tego obejmują:
- Komponenty zawierają tylko stan (np. Pozycja, zdrowie, ilość amunicji) => logika przechodzi do „systemów”, które przetwarzają te komponenty i ich stan (np. PhysicsSystem, RenderSystem itp.)
- Chcę implementować komponenty i systemy zarówno w czystym języku C #, jak i za pomocą skryptów (Lua). Zasadniczo chcę mieć możliwość definiowania całkowicie nowych komponentów i systemów bezpośrednio w Lua bez konieczności ponownej kompilacji mojego źródła C #.
Teraz szukam pomysłów, jak radzić sobie z tym wydajnie i konsekwentnie, więc nie muszę używać innej składni, aby na przykład uzyskać dostęp do komponentów C # lub Lua.
Moje obecne podejście polegałoby na implementacji komponentów C # przy użyciu zwykłych, publicznych właściwości, prawdopodobnie ozdobionych pewnymi atrybutami informującymi redaktora o domyślnych wartościach i innych rzeczach. Następnie miałbym klasę C # „ScriptComponent”, która po prostu wewnętrznie otacza tabelę Lua, tworząc tę tabelę za pomocą skryptu i utrzymując cały stan tego konkretnego typu komponentu. Tak naprawdę nie chcę uzyskać dostępu do tego stanu od strony C #, ponieważ nie wiedziałbym w czasie kompilacji, jakie ScriptComponents z jakich właściwości byłyby dla mnie dostępne. Jednak edytor będzie musiał uzyskać do niego dostęp, ale wystarczy prosty interfejs podobny do następującego:
public ScriptComponent : IComponent
{
public T GetProperty<T>(string propertyName) {..}
public void SetProperty<T>(string propertyName, T value) {..}
}
Spowodowałoby to po prostu dostęp do tabeli Lua oraz ustawienie i odzyskanie tych właściwości z Lua, i mogłoby to być łatwo zawarte w czystych komponentach C # (ale wtedy używaj zwykłych właściwości C #, poprzez odbicie lub coś innego). Byłoby to używane tylko w edytorze, a nie w zwykłym kodzie gry, więc wydajność nie jest tutaj tak ważna. Konieczne byłoby wygenerowanie lub ręczne napisanie niektórych opisów komponentów dokumentujących, jakie właściwości faktycznie oferuje dany typ komponentów, ale nie byłby to ogromny problem i mógłby być wystarczająco zautomatyzowany.
Jak jednak uzyskać dostęp do komponentów od strony Lua? Jeśli zadzwonię do czegoś takiego getComponentsFromEntity(ENTITY_ID)
, prawdopodobnie dostanę kilka rodzimych składników C #, w tym „ScriptComponent”, jako dane użytkownika. Dostęp do wartości z zawiniętej tabeli Lua spowodowałby, że wywołałem GetProperty<T>(..)
metodę zamiast bezpośredniego dostępu do właściwości, tak jak w przypadku innych składników C #.
Może napisz specjalną getComponentsFromEntity()
metodę, która ma być wywoływana z Lua, która zwraca wszystkie natywne komponenty C # jako dane użytkownika, z wyjątkiem „ScriptComponent”, gdzie zamiast tego zwróci zapakowaną tabelę. Ale będą inne metody związane z komponentami i tak naprawdę nie chcę powielać wszystkich tych metod zarówno w celu wywołania z kodu C # lub ze skryptu Lua.
Ostatecznym celem byłaby obsługa wszystkich typów komponentów w ten sam sposób, bez specjalnej składni przypadków rozróżniającej komponenty natywne i komponenty Lua - szczególnie od strony Lua. Np. Chciałbym móc napisać taki skrypt Lua:
entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")
nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3
Skrypt nie powinien dbać o to, jakiego rodzaju komponent faktycznie ma, i chciałbym użyć tych samych metod, których użyłem po stronie C # do pobierania, dodawania lub usuwania komponentów.
Może istnieją jakieś przykładowe implementacje integracji skryptów z takimi systemami?
Odpowiedzi:
Następstwem odpowiedź FXIII za to, że należy zaprojektować wszystko (że byłoby modyfikowalne) w języku skryptowym pierwszy (lub przynajmniej bardzo przyzwoity część z nich), aby upewnić się, że logika integracji faktycznie rezerwy na modding. Gdy masz pewność, że twoja integracja skryptów jest wystarczająco wszechstronna, przepisz wymagane bity w języku C #.
Osobiście nienawidzę tej
dynamic
funkcji w C # 4.0 (jestem ćpunem w języku statycznym) - ale bez wątpienia jest to scenariusz, w którym powinien być używany i tam, gdzie naprawdę świeci. Twoje komponenty C # byłyby po prostu silnymi / rozwijanymi obiektami behawioralnymi .Twoje komponenty Lua byłyby całkowicie dynamiczne (ten sam artykuł powyżej powinien dać ci wyobrażenie o tym, jak to zaimplementować). Na przykład:
Teraz, ponieważ używasz
dynamic
, możesz używać obiektów Lua tak, jakby były prawdziwymi klasami w języku C #.źródło
Wolę zrobić coś przeciwnego: napisz wszystko za pomocą dynamicznego języka, a następnie wyśledzić wąskie gardła (jeśli istnieją). Po znalezieniu możesz selektywnie zaimplementować funkcjonalność za pomocą C # lub (raczej) C / C ++.
Mówię ci to głównie dlatego, że staramy się ograniczyć elastyczność twojego systemu w części C #; w miarę jak system się komplikuje, Twój „silnik” C # zacznie wyglądać jak dynamiczny interpreter, prawdopodobnie nie najlepszy. Pytanie brzmi: czy chcesz zainwestować większość czasu w pisanie ogólnego silnika C # czy przedłużyć swoją grę?
Jeśli twoim celem jest drugi, jak sądzę, prawdopodobnie wykorzystasz swój czas, skupiając się na mechanice gry, pozwalając doświadczonemu tłumaczowi na uruchomienie dynamicznego kodu.
źródło