Czy istnieje wspólna technika obsługi stanu (ogólnie) w funkcjonalnym języku programowania? W każdym (funkcjonalnym) języku programowania istnieją rozwiązania do obsługi stanu globalnego, ale chcę tego uniknąć w miarę możliwości.
Wszystkie stany w czysto funkcjonalny sposób są parametrami funkcji. Muszę więc ustawić cały stan gry (gigantyczna mapa świata, graczy, pozycje, wyniki, zasoby, wrogów, ...) jako parametr wszystkich funkcji, które chcą manipulować światem na danym wejściu lub wyzwalaczu . Sama funkcja pobiera odpowiednie informacje z obiektu blob gamestate, zrób coś z tym, manipuluj gamestate i zwróć gamestate. Ale wygląda to na kiepskie rozwiązanie problemu. Gdybym włożył cały gamestate do wszystkich funkcji, nie ma dla mnie korzyści w przeciwieństwie do zmiennych globalnych lub imperatywnego podejścia.
Mógłbym wstawić tylko odpowiednie informacje do funkcji i zwrócić działania, które zostaną podjęte dla danych wejściowych. Jedna funkcja stosuje wszystkie działania do gamestate. Ale większość funkcji wymaga wielu „istotnych” informacji. move()
potrzebuję pozycji obiektu, prędkości, mapy kolizji, pozycji wszystkich wrogów, obecnego stanu zdrowia ... Więc to podejście też nie działa.
Więc moje pytanie brzmi: jak poradzić sobie z ogromną ilością stanów w funkcjonalnym języku programowania - szczególnie w przypadku tworzenia gier?
EDYCJA: W Clojure istnieje kilka platform do budowania gier. Podejście polegające na częściowym rozwiązaniu tego problemu polega na tym, że wszystkie przedmioty w grze są tak zwane „byty” i wkładane do ogromnej torby. Gigant główną funkcją trzyma ekran i podmioty i wydarzenia uchwyt ( :on-key-down
, :on-init
...) dla tej jednostki i uruchomić główną pętlę wyświetlacza. Ale nie jest to czyste rozwiązanie, którego szukam.
źródło
move()
prawdopodobnie powinieneś przechodzić przez „bieżący” obiekt (lub jego identyfikator), plus świat, przez który się porusza, i po prostu wyliczyć aktualną pozycję i prędkość ... wyjście jest wtedy całym światem fizyki, a przynajmniej lista zmienionych obiektów.Odpowiedzi:
Skutki uboczne i stan w funkcjonalnych językach programowania są szerszym problemem w informatyce. Jeśli jeszcze ich nie spotkałeś, może rzuć okiem na monady . Ostrzegam jednak: są dość zaawansowaną koncepcją i większość ludzi, których znam (w tym mnie), stara się je zrozumieć. Istnieje wiele samouczków online, różniących się podejściem i wymaganiami wiedzy. Osobiście najbardziej lubiłem Erica Lipperta.
Eric Lippert o Monadach
Niektóre rzeczy do rozważenia:
Kilka ostatnich myśli:
Podsumowując, myślę, że nawet jeśli mogłoby to być interesujące z naukowego punktu widzenia, wątpię, aby takie podejście było praktyczne i warte wysiłku.
źródło
Napisałem kilka gier przy użyciu F # (multi-paradygmat, nieczysty, język funkcjonalny-pierwszy), z podejściami od OOP do FRP . To szerokie pytanie, ale dam z siebie wszystko.
Mój preferowany sposób to mieć niezmienny typ reprezentujący całą grę
State
. Następnie mam zmienne odniesienie do prądu,State
który jest aktualizowany za każdym razem. Nie jest to ściśle czysta, ale ogranicza zmienność do jednego miejsca.Nie prawda. Ponieważ ten
State
typ jest niezmienny, nie możesz mieć żadnego starego komponentu, który mutowałby świat w źle zdefiniowany sposób. To rozwiązuje największy problem zGameObject
podejściem (spopularyzowanym przez Unity): trudno jest kontrolować kolejnośćUpdate
połączeń.I w przeciwieństwie do globałów, jest łatwo testowany jednostkowo i równoległy,
Powinieneś także napisać funkcje pomocnicze, które otrzymują pod-właściwości stanu, aby rozwiązać problem.
Na przykład:
Tutaj
update
działa na cały stan, aleupdateSpaceShip
działa tylko na jednostkęSpaceShip
w izolacji.Moją sugestią byłoby stworzenie
Input
typu, który przechowuje stany klawiatury, myszy, pada itp. Następnie możesz napisać funkcję, która pobieraState
iInput
zwraca następnąState
:Aby dać Ci wyobrażenie o tym, jak to pasuje, ogólna gra może wyglądać mniej więcej tak:
W przypadku prostych gier możesz zastosować powyższe podejście (funkcje pomocnicze). Aby uzyskać coś bardziej skomplikowanego, możesz wypróbować „ soczewki ”.
W przeciwieństwie do niektórych tutaj komentarzy, zdecydowanie zalecałbym pisanie gier w (nieczystym) funkcjonalnym języku programowania, a przynajmniej spróbowanie! Przekonałem się, że dzięki temu iteracja jest szybsza, bazy kodu są mniejsze, a błędy mniej powszechne.
Nie sądzę też , że musisz uczyć się monad, aby pisać gry w (nieczystym) języku FP. Wynika to z faktu, że Twój kod najprawdopodobniej będzie blokował i jednowątkowy.
Jest to szczególnie prawdziwe, jeśli piszesz grę wieloosobową. Dzięki temu podejściu wszystko stanie się znacznie łatwiejsze, ponieważ pozwala na trywialną serializację stanu gry i wysyłanie go przez sieć.
Co do tego, dlaczego więcej gier nie jest napisanych w ten sposób ... Nie mogę powiedzieć. Jest to jednak prawdą we wszystkich domenach programistycznych (z wyjątkiem być może finansów), dlatego nie użyłbym tego jako argumentu, że języki funkcjonalne są nieodpowiednie dla programowania gier.
Warto również przeczytać Czysto funkcjonalne Retrogames .
źródło
To, czego szukasz, to tworzenie gier FRP.
Niektóre prezentacje wideo:
„Kontrolowanie czasu i przestrzeni: zrozumienie wielu sformułowań FRP” Evana Czaplickiego
Bodil Stokke: Reaktywne tworzenie gier dla wymagających Hipster [JSConf2014]
Uwielbiam tę rozmowę z Carmackiem, opisuje swoje doświadczenia z czysto funkcjonalnym programowaniem w rozwoju gry . Keynote Johna Carmacka na Quakecon 2013 część 4
Jest w 100% możliwe i lepiej, aby logika gry podstawowej była czysto funkcjonalna, przemysł jako całość jest po prostu opóźniony, utknął w jednym paradygmacie myślenia.
Można to zrobić również w Jedności.
Aby odpowiedzieć na pytanie, nowy stan gry będzie aktualizowany / tworzony za każdym razem, gdy coś się poruszy, jak mówi Carmel w swoim wystąpieniu, nie jest to problemem. Drastyczne zmniejszenie ogólnych kosztów poznawczych, które wynika z czysto funkcjonalnej, łatwej w utrzymaniu, elastycznej architektury, która znacznie przewyższa wydajność, o ile w ogóle istnieje.
źródło