Czy obiekt w grze 2D powinien się renderować?

16

Tworzę grę 2D ulicznego wojownika, która nie jest oparta na kafelkach. Zwykle ludzie zalecają by byty były przekazywane do renderera, który je renderuje, a nie same, ale wydaje się, że odwrotność jest lepsza,

Dlaczego jedno jest lepsze od drugiego?

Dzięki

jmasterx
źródło
Jak myślisz, dlaczego odwrotność jest lepsza?
1
@Martin, ponieważ obiekt podpowie, która bitmapa i tak ma być użyta, więc dlaczego nie zrobić po prostu object-> render ();
jmasterx

Odpowiedzi:

11

Kilka uwag:

  • jak wspomniałeś, każdy duszek musiałby „podpowiedzieć”, której bitmapy użyć, ale jeśli byt musiał się wyrenderować. Jaka byłaby ta „wskazówka”? Jeśli jest to odniesienie do innej mapy bitowej, arkusza ikon itp. Dla każdej ikonki, możesz zużyć więcej pamięci niż to konieczne lub mieć problemy z zarządzaniem tą pamięcią. Zaletą osobnego mechanizmu renderującego jest to, że masz tylko jedną klasę odpowiedzialną za zarządzanie zasobami. To powiedziawszy, w grze walki podobnej do SF2 możesz mieć tylko dwa duszki;)

  • jak wspomniano w innym miejscu, ilekroć chcesz zmienić graficzny interfejs API, musisz zmienić kod dla wszystkich duszków.

  • renderowanie rzadko odbywa się bez odniesienia do jakiegoś kontekstu graficznego. Zatem istnieje zmienna globalna reprezentująca tę koncepcję lub każdy duszek ma interfejs z renderem (GraphicalContext ctx). Łączy to graficzny interfejs API i logikę gry (co niektórzy uznają za niestosowne) i może powodować problemy z kompilacją.

  • Osobiście uważam, że oddzielenie renderowania od poszczególnych podmiotów jest interesującym pierwszym krokiem w kierunku postrzegania twojej gry jako systemu, który niekoniecznie potrzebuje grafiki. Chodzi mi o to, że kiedy odsuwasz renderowanie na bok, zdajesz sobie sprawę, że wiele rozgrywki dzieje się w „nie graficznym świecie”, w którym ważne są współrzędne bytów, ich stany wewnętrzne itp. Otwiera to drzwi do zautomatyzowanych testów, bardziej oddzielonego systemu itp.

Podsumowując, wolę systemy, w których renderowanie jest wykonywane przez oddzielną klasę. To nie znaczy, że twoje duszki nie mogą mieć niektórych atrybutów, które są „graficznie powiązane” (nazwa animacji, ramka animacji, wysokość x szerokość, identyfikator duszka itp.), Jeśli to czyni renderer łatwiejszym do pisania lub bardziej wydajnym.

I nie wiem, czy miałoby to zastosowanie do 3D (gdzie pojęcie siatek i zmienna współrzędnych, której użyłbyś, być może byłyby powiązane z twoim 3D API; podczas gdy x, y, h, w jest prawie niezależne od jakiegokolwiek 2D API).

Mam nadzieję, że to pomoże.

phtrivier
źródło
11

Chcesz, aby system renderujący kontrolował to, co zostanie narysowane, kiedy. Jeśli zamiast tego duszki mają kontrolę nad renderowaniem, tracisz na dużej wydajności i elastyczności. Uważam, że kontrola nad systemem renderującym powoduje czystszy kod.

Niektóre zalety scentralizowanego renderowania:

  • zamawianie z:
    Jeśli same obiekty gry są odpowiedzialne za renderowanie, musisz upewnić się, że wywołujesz je we właściwej kolejności. W przeciwnym razie obiekty tła mogą być rysowane nad obiektami pierwszego planu.
    Mając kontrolę nad systemem renderowania, może sortować wszystkie obiekty renderowania, wykrywać nakładki w czasie renderowania i po prostu renderować je lub po prostu zrezygnować z zamawiania wszystkich razem. Chodzi o to, że decyzję można teraz łatwo podjąć.
  • wsadowe:
    Inną oczywistą zaletą umożliwienia kontroli systemu renderującego jest dozowanie. Tutaj ponownie system renderujący musi mieć opcję, aby sprite renderował udział jako teksturę. Może używać wycinania trójkątów, aby renderować wszystko za jednym razem. Może być w stanie buforować niektóre obliczenia renderowania. Lub może po prostu renderować każdego duszka po kolei bez tych wymyślnych rzeczy. (Uwaga: możliwa jest partia, gdy każdy obiekt się renderuje, ale problem jest mniej wydajny i bardziej złożony).

Sposób, w jaki implementuję to w moich grach, polega na tym, że obiekty gier rejestrują duszki, które chcą narysować w systemie renderowania. Gdy obiekt nie chce już, aby obiekt został narysowany, wyrejestrowuje duszka lub oznacza, że ​​jest nieaktywny.

Wszystko, co powiedział. Jeśli łatwiej jest renderować obiekty gry za pomocą wszelkich środków, zrób to w ten sposób. O wiele ważniejsze jest robić postępy i wyciągać coś / cokolwiek narysować niż mieć idealną architekturę.

deft_code
źródło
O porządkowaniu Z, jeśli obiekty same się rysują, a system nie decyduje o kolejności wywołania metody rysowania? Mam na myśli, że scentralizowane vs niecentralne wydają się nie mieć znaczenia w przypadku uporządkowania w Z.
GorillaApe,
3

Uwaga: twoje pytanie nie zawiera zbyt wielu szczegółów, więc odpowiadam ogólną zasadą. Przepraszam, jeśli źle zrozumiałem twoje użycie lub „renderowanie”.

Zasadniczo używam zewnętrznego obiektu do renderowania różnych aktorów w scenie jako sposobu enkapsulacji właściwości i metod na poziomie sceny poza poszczególne „obiekty aktorskie”. Obiekty na scenie powinny zawierać wyłącznie wewnętrzne metody i właściwości; powinni wiedzieć tylko, kim są i czym się zajmują. Przypuszczalnie będą na nie wpływały inne obiekty w grze, a także wkład użytkownika. Wpłynie to na to, jak / czy są renderowane na ekranie. „Obiekt reżysera” może na przykład tłumaczyć naciśnięcie klawisza „w”, aby przejść, a następnie powiedzieć obiektowi aktora .jump (). Taka logika na poziomie reżysera może również nakazać aktorom całkowite wejście lub wyjście ze sceny.

Pozdrawiam, David


źródło
Ale w tym sensie reżyser nie mógł po prostu powiedzieć acton-> setVisible (false); ?
jmasterx
Nawet w przypadku setVisible (false) jest to jednostka zewnętrzna, która wykonuje rendering, sprawdzając widoczną zmienną aktora i renderując ją tylko wtedy, gdy jest prawdziwa.
Nav
Samo uczynienie aktora niewidzialnym nie usuwa go ze sceny. Musi także przestać uczestniczyć w kolizjach itp.
finnw
3

Co jeśli pewnego dnia chcesz przenieść swoją grę do innej rozdzielczości (np. IPhone i znajomi). Tak więc, globalna właściwość dotycząca renderowania zmian, jak łatwo zaktualizować kod?


źródło
3

Użyłem projektu opartego na obserwatorach. Kiedy utworzyłem instancję klasy, którą chciałem wyrenderować, wówczas wskaźnik do niej był przechowywany w centralnej klasie Renderer. Po wywołaniu RenderFrame()moduł renderujący ma już wszystkie istniejące obiekty do renderowania i uzyskał dostęp do ich właściwości, aby to zrobić. Same klasy nie miały pojęcia, że ​​zostaną w ogóle wyrenderowane. Ten interfejs API był ładny, czysty i łatwy w użyciu.

DeadMG
źródło
1
+1 ciekawe. Użyłem tego podejścia do dźwięku podczas korzystania z Wzorca gościa dla grafiki. Pomyślałem, że ma to większy sens dla dźwięku, ponieważ podczas gdy grafika i sztuczna inteligencja działają na tym samym zegarze, mikser audio działa na innym, więc model zdarzeń jest łatwiejszy. Również nie jest krytyczne, jeśli zdarzenie ruchu (które powoduje zmianę panoramy / pogłosu kanału audio) pojawia się kilka milisekund z opóźnieniem, ale jest krytyczne, jeśli duszek jest rysowany w złym stanie.
finnw
2

Ogólnie rzecz biorąc, zawsze chodzi o to, jak łatwo jest utrzymać i rozwinąć kod. Jutro odkryjesz, że nie podoba ci się graficzny interfejs API, którego używasz obecnie, i chcesz się przełączyć. Czy będziesz musiał przejść przez wszystkie klasy obiektów i wszystko zmienić, czy też po prostu musisz zmienić kod w jednym centralnym punkcie projektu?

To zależy od tego, co naprawdę robią twoje obiekty, gdy wywołujesz render (). Dopóki tylko owijają wywołania metod wokół twojego silnika graficznego, jest całkowicie w porządku, ponieważ logika <-> rozróżnienie grafiki będzie nadal podane.

Na przykład, jeśli metody render () są w zasadzie metodami wygody i wyglądają mniej więcej tak:

void MyClass::render(const Graphics &g)
{
    g.draw(this);
}

lub

void MyClass::render()
{
   mySprite->render();
}

lub

void MyClass::render()
{
    mySprite->UseShader(thatshader);
    mySprite->render();
}

lub blisko tego, nie sądzę, żeby to był jakiś problem.

TravisG
źródło