Jestem bardzo nowy w tworzeniu gier, ale nie w programowaniu.
Gram (ponownie) w grę typu Pong, używając canvas
elementu JavaScript .
Utworzyłem Paddle
obiekt, który ma następujące właściwości ...
width
height
x
y
colour
Mam również Pong
obiekt, który ma właściwości takie jak ...
width
height
backgroundColour
draw()
.
draw()
Metoda obecnie jest zresetowanie canvas
i to jest pytanie, gdzie wpadł.
W przypadku, gdy Paddle
przedmiot posiada draw()
metoda odpowiedzialna za jego rysunku, lub jeżeli draw()
w Pong
obiekcie jest odpowiedzialny za przygotowanie jego uczestników (zakładam, że jest poprawne określenie, proszę mnie poprawić, jeśli jestem nieprawidłowe).
Uznałem, że korzystne Paddle
byłoby rysowanie samego siebie, ponieważ tworzę instancję dwóch obiektów Player
i Enemy
. Gdyby nie było w Pong
„s draw()
, to muszę napisać podobny kod dwukrotnie.
Jaka jest tutaj najlepsza praktyka?
Dzięki.
źródło
Odpowiedzi:
Nakłanianie aktorów do rysowania się nie jest dobrym projektem z dwóch głównych powodów:
1) narusza zasadę pojedynczej odpowiedzialności , ponieważ ci aktorzy prawdopodobnie mieli przed sobą inną pracę, zanim wrzuciliście do nich kod renderujący.
2) utrudnia przedłużenie; jeśli każdy typ aktora implementuje własny rysunek i konieczna jest ogólna zmiana sposobu rysowania , może być konieczne zmodyfikowanie dużej ilości kodu. Unikanie nadmiernego dziedziczenia może złagodzić to w pewnym stopniu, ale nie całkowicie.
Lepiej, żeby Twój renderer obsługiwał rysunek. W końcu to właśnie znaczy być rendererem. Metoda losowania renderera powinna przyjmować obiekt „renderuj opis”, który zawiera wszystko, czego potrzebujesz do renderowania rzeczy. Odniesienia do (prawdopodobnie udostępnionych) danych geometrii, transformacji specyficznych dla instancji lub właściwości materiału, takich jak kolor itp. Następnie rysuje to i nie przejmuje się tym, czym powinien być ten opis renderowania.
Twoi aktorzy mogą następnie trzymać się opisu renderowania, który sami tworzą. Ponieważ aktorzy są zwykle typami przetwarzania logicznego, mogą w razie potrzeby przesuwać zmiany stanu do opisu renderowania - na przykład, gdy aktor odniesie uszkodzenie, może ustawić kolor opisu renderowania na czerwony, aby to wskazać.
Następnie możesz po prostu iterować każdego widocznego aktora, sprawdzać jego opisy renderowania w rendererze i pozwolić mu działać (w zasadzie; możesz to uogólnić jeszcze bardziej).
źródło
actor.draw(renderer,x,y)
niżrenderer.draw(actor.getAttribs(),x,y)
.struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }
mniej więcej mniej więcej mój renderer. W ten sposób mechanizm renderujący nie dba o to, co reprezentują dane, po prostu je przetwarza. Na podstawie tych informacji mogę również zdecydować, czy posortować kolejność połączeń losujących, aby zmniejszyć ogólne połączenia GPU.Odwiedzający mogą tu być użyteczne.
To, co możesz zrobić, to mieć interfejs Renderera, który wie, jak narysować każdy obiekt oraz metodę „narysuj siebie” w każdym z aktorów, która określa, którą (konkretną) metodę renderera wywołać, np.
W ten sposób nadal łatwo jest przenieść do innej biblioteki graficznej lub frameworka (kiedyś przeniosłem grę ze Swing / Java2D do LWJGL i bardzo się ucieszyłem, że użyłem tego wzorca zamiast przekazywać
Graphics2D
.)Jest jeszcze jedna zaleta: renderer może być testowany niezależnie od dowolnego kodu aktora
źródło
W twoim przykładzie chciałbyś, aby obiekty Pong implementowały funkcję draw () i renderowały się.
Chociaż nie zauważysz żadnych znaczących korzyści w projekcie o tej wielkości, warto oddzielić logikę gry od ich wizualnej reprezentacji (renderowania).
Rozumiem przez to, że masz obiekty gry, które można aktualizować (), ale nie mają pojęcia o tym, jak są renderowane, zajmują się tylko symulacją.
Wtedy miałbyś klasę PongRenderer (), która ma odwołanie do obiektu Pong, a następnie zajmuje się renderowaniem klasy Pong (), może to obejmować renderowanie Paddles lub posiadanie klasy PaddleRenderer, która się tym zajmie.
Rozdzielenie problemów w tym przypadku jest dość naturalne, co oznacza, że twoje klasy mogą być mniej rozdęte, i łatwiej jest modyfikować sposób renderowania rzeczy, nie musi już podążać za hierarchią, którą wykonuje twoja symulacja.
źródło
Jeśli miałbyś to zrobić w funkcji
Pong
draw (), nie musiałbyś duplikować kodu - wystarczy wywołać funkcjęPaddle
draw () dla każdej z łyżek. Więc w swoimPong
draw () miałbyśźródło
Każdy obiekt powinien zawierać własną funkcję losowania, którą powinien wywołać kod aktualizacji gry lub kod losowania. Lubić
To nie jest kod, ale tylko pokazanie, jak należy rysować obiekty.
źródło