Czy wdrożenie własnego języka skryptowego jest wykonalne?

21

Piszę grę beat'em w C ++ i nadszedł czas na wdrożenie skryptów dla zdarzeń, wyzwalaczy, scenek przerywnikowych itp. Czytałem w Internecie i otrzymałem sporo informacji. Moim pierwszym rozwiązaniem byłoby wdrożenie własnego języka skryptowego, takiego jak ten w Cave Story . Widziałem to sugerowane, ale większość ludzi sugeruje lua, ale to nie wydaje się pasować do mojego rodzaju programowania.

Czy stworzyłeś własny język skryptowy? Dlaczego zdecydowałeś się rzucić własny zamiast istniejącego? Jakie zasoby skonsultowałeś podczas tworzenia?

skiperic
źródło
43
Moja osobista ogólna zasada brzmi: jeśli musisz zapytać innych ludzi, czy warto rzucić własne _____, to nie jest to dobry pomysł.
sam hocevar
2
Pamiętaj, że jeśli wdrożysz swój własny język, ludzie muszą się go nauczyć i mieć nadzieję, że w twoim tłumaczu nie będzie żadnych błędów, podczas gdy języki takie jak Lua są bardziej popularne i znacznie rzadziej zawierają błędy.
Nick Caplinger
2
@NickCaplinger uderza go w głowę. Wprowadzenie własnych oznacza, że ​​nie ma dokumentacji, społeczności, samouczków stron trzecich, StackExchange itp. Wszelkie drobne postępy w składni lub integracji przeważą brak wsparcia, chyba że jesteś naprawdę dobrze znanym programistą z miliony fanów.
Sean Middleditch,
4
Pamiętaj również o oprzyrządowaniu. To jest mój problem z D, Rust, Dart itp. Twoja składnia sama w sobie nie ma sensu. Potrzebujesz odpowiedniego edytora, podświetlania, „Intellisense” (uzupełnianie kodu), dobrej diagnostyki, obsługi refaktoryzacji, obsługi dokumentacji wbudowanej itp., Aby naprawdę być cokolwiek wart. Nie wspominając już o wszystkich rzeczywistych funkcjach językowych, integracji, wydajności itd.
Sean Middleditch,
1
Jeśli jesteś programistą C ++ i nie lubisz LUA, spróbuj zamiast tego JavaScript.
zeel

Odpowiedzi:

42

Nie. Przynajmniej nie.

Jest to bardzo częsty przypadek wymyślania na nowo kierownicy gier, błąd, który jest nadal dość popularny.

Jeśli zadajesz to pytanie, bardzo prawdopodobne, że wpłyną na ciebie działania innych, więc spójrz na to, co Epic Games właśnie zrobiło z Unreal Engine:

  • UE3 miał niestandardową, dziwną, niezoptymalizowaną, trudną do debugowania rzecz UnrealScript,
  • Jeśli pogłoska jest prawdziwa, jej wsparcie jest usuwane w UE4 , na rzecz bibliotek DLL C ++ z możliwością ponownego ładowania na gorąco.

Czy uważasz, że możesz zrobić lepiej niż Epic?

Tworzenie języków programowania należy do twórców języków programowania , a nie inżynierów gier.

Wiele lat zajmuje język, aby język był w pełni dojrzały, a towarzyszący mu zestaw narzędzi (kompilator, linker, interpreter, debugger ...) jest użyteczny. Obecnie masz pod ręką wiele dostępnych rozwiązań, więc nie ma absolutnie żadnego powodu, aby zacząć od nowa, przynajmniej jeśli celem jest po prostu stworzenie gry. Kropka.

Aby odpowiedzieć na twoje pytania dodatkowe, nie, z tych właśnie powodów nigdy nie wdrożyłem własnego języka skryptowego. Ale bardzo cierpiałem z niektórymi na wpół upieczonymi. Ponieważ zostały stworzone z myślą o bardzo wąskiej funkcji, zawsze mieli te małe szalone dziwactwa, które doprowadzają Cię do szaleństwa. Często spędzasz strasznie dużo czasu, próbując obejść ograniczenia języka zamiast tworzyć grę.

Jeśli powodem, dla którego chcesz stworzyć język, jest to, że jest on przeznaczony do użytku przez osoby, które nie znają dobrze programowania, lub jeśli uważasz, że potrzebujesz go, ponieważ chcesz czegoś bardzo specyficznego dla domeny, pozwól mi powiedzieć, że są to również złe powody. Możesz napisać bardzo wysoki interfejs API z funkcjami, które do_what_they_say_and_say_what_they_do()i prosty kod, który ujawnia jego podstawowe użycie. Twoi niezbyt techniczni użytkownicy chętnie nauczą się trochę programowania i będziesz zadowolony, że nie będziesz ograniczany przez źle zaimplementowany język.

Tak więc, ponieważ zabrzmi to nieco gwałtownie lub nawet surowo, powiem, że jest jeden przypadek, w którym może to mieć sens: jeśli chcesz dowiedzieć się, jak powstaje język skryptowy. Ale proszę, jeśli to zrobisz: nie zmuszaj innych do korzystania z niego.

Edytować

Właśnie spojrzałem na listę poleceń Cave Story, którą połączyłeś. Ojej:

<ECJx:y      [EC?] Jump           @ Jump to event Y if any npc with ID X is present

Nie chcę okazywać braku szacunku twórcy Cave Story, ale jest to doskonały przykład prostej listy poleceń, która zmutowała się w niekontrolowanym niestandardowym języku skryptowym. Może to być nadal przydatne dla pojedynczego programisty lub bardzo małego zespołu, ale na tym etapie radzę, aby przejść na odpowiedni i kompletnie sprawdzony język Turinga (np. Lua), w którym możesz:

if (npc.id == x) then
    jump_to_event(y)
end

Ułatwi to znacznie, gdy na przykład będziesz potrzebować bardziej złożonego warunku:

if (npc.id == x) or (npc.type == "enemy") then
    jump_to_event(y)
end
Laurent Couvidou
źródło
21
+1 „Tworzenie języków programowania należy do twórców języków programowania, a nie inżynierów gier”. Ten cytat nie może być bardziej poprawny. Po wzięciu lekcji języka programowania prowadzonych przez faceta, który był świstem z języków programowania, ludzie ci należą do innej rasy. Jedna klasa uświadomiła mi ilość teorii i matematyki CS, które są potrzebne do stworzenia prawdziwego języka programowania. To sprawiło, że jeszcze bardziej doceniam tych ludzi i ich umiejętności. Pozwalają mi tworzyć gry, nie przeszkadzam im i pozwalam im tworzyć języki.
Dean Knight
+1, nie znam lua ani niestandardowego języka skryptowego, ale było oczywiste, co się dzieje w lua i tam ważna jest użyteczność
RhysW,
1
W tym czy w drugim nie ma nic magicznego, co uniemożliwia naukę obu. Unreal może usuwa UnrealScript, ale znacznie poprawia i wypełnia Kismet, aby był zdolnym i kompletnym wizualnym językiem skryptowym. Nie usunęli UnrealScript, ponieważ jest to błąd, usuwają go, ponieważ jest przestarzały ze względu na znacznie trudniejsze funkcje (ponowne ładowanie na gorąco C ++, zaktualizowany Kismit). Nie rozumiem tego, że nie zgadzam się z twoją tezą: pisanie własnego języka jest ogromną stratą czasu, źródłem błędów i przyczyną dużych krzywych uczenia się itp.
Sean Middleditch,
2
@ ott-- Nie obawiam się też, chodzi mi o to, że rozszerzenie tej notacji sprawi, że będzie ona coraz bardziej skomplikowana, podczas gdy używanie właściwego języka przede wszystkim pomoże na dłuższą metę. Koszty ogólne nie są istotne w porównaniu do łatwości użycia w tym przypadku, IMHO.
Laurent Couvidou,
1
Zauważ, że UnrealScript został zastąpiony przez Blueprints (ale założę się, że moi koledzy wrócą). Biblioteki DLL Hotloading to funkcja ortogonalna.
sam hocevar
9

Czy stworzyłeś własny język skryptowy i dlaczego zdecydowałeś się stworzyć własny język zamiast używać istniejącego?

Mam, chociaż pożyczyłem składnię z innych języków. W sumie było to wspaniałe doświadczenie edukacyjne, aw moim przypadku nie takie trudne, ponieważ język był prosty.

Zrobiłem to głównie dlatego, że chciałem używać regularnej składni w stylu C do moich skryptów, ponieważ wszyscy w zespole byli z nią zaznajomieni.

Byłem również zainteresowany nauczeniem się trochę o implementacji języków programowania, a ponieważ potrzebowałem tylko bardzo prostego podzbioru funkcji (zmiennych, arytmetyki, warunków, pętli i wywoływania funkcji w grze lub coroutines), postanowiłem spróbować.

Jakie zasoby skonsultowałeś podczas tworzenia?

Moim głównym zasobem była ta książka:

wprowadź opis zdjęcia tutaj

Z tej książki udało mi się nauczyć wystarczająco dużo, aby wdrożyć:

  • Lexer: co było prawie trywialne przy użyciu wyrażeń regularnych, wystarczy użyć Regex.Match do identyfikacji i dzielenia tokenów.
  • Parser rekurencyjnego zejścia: w zasadzie jedna funkcja dla każdej reguły w gramatyce i niektóre metody pomocnicze do patrzenia w przód i używania żetonów. Inspirację sprawdziłem w specyfikacji gramatyki C #. Najtrudniejsza część dotyczyła priorytetów arytmetyki i operatora relacji bez wchodzenia w nieskończoną rekurencję.
  • Interpreter AST: korzystając z wzorca projektowego dla gości, przeszedłem drzewo wygenerowane z parsera i rekursywnie wykonałem każdy węzeł instrukcji.

Zatrzymałbym się tam, ponieważ prawie wszystko, czego potrzebowałem, już działało, z wyjątkiem jednej rzeczy - wzywania i ustępowania na temat korporacji Unity3D głęboko z interpretera rekurencyjnego. Aby rozwiązać ten problem, musiałem pozbyć się rekurencji i ponownie zwróciłem się do książki. Tym razem dodałem:

  • Kompilator: inny gość, ale zamiast wykonywać kod, generuje listę małych instrukcji atomowych z każdego węzła AST (tj. Prosty niestandardowy język asemblera oparty na stosie). Operacje takie jak pętla while lub warunek if są konwertowane na etykiety i instrukcje typu goto. W tym momencie piszę również skompilowany skrypt na dysk jako plik binarny.
  • Interpretator kodu bajtowego: po prostu iteruje płaską listę instrukcji. Bez rekurencji integracja z korporacjami Unity3D była teraz łatwa.

Cały proces zajął około 2 tygodni od zerowej wiedzy i był świetną zabawą :)

PS: Na koniec chciałem również dodać wyróżnianie składni i inteligencję dla mojego niestandardowego języka w naszych narzędziach. Scintilla była pod tym względem ratownikiem. Użyłem następującego opakowania:

http://scintillanet.codeplex.com/

David Gouveia
źródło
5

Widziałem to sugerowane, ale większość ludzi sugeruje lua, ale to nie wydaje się pasować do mojego rodzaju programowania.

OK, spróbujmy z innej perspektywy: co takiego w Lui nie lubisz? Czy jest to coś, co można łatwo naprawić, czy może jest to coś fundamentalnego?

Na przykład, użyj słów kluczowych, takich jak then/do/endoznaczenie bloku kodu, zamiast dobrych nawiasów klamrowych w stylu C / C ++. Jeśli to twój problem ... możesz to naprawić. Wszystko, co musisz zrobić, to zdefiniować własny mały dialekt Lua i napisać proste narzędzie do transformacji, które przekształci składnię nawiasów klamrowych w rzeczywistą Lua.

Czy chcesz + = w jakiejś formie? To również łatwo zrobić w systemie przetwarzania wstępnego. Po prostu zamień oświadczenia formularza expr1 += expr2na expr1 = expr1 + expr2.

To prawda, że ​​będziesz musiał znaleźć sposób na wykrycie, czy para nawiasów klamrowych reprezentuje stół, czy do/endparę. I trzeba by podpiąć swój system wstępnego przetwarzania w Lua nadrzędnymi dofile, loadstringi innych standardowych funkcji bibliotecznych Lua. Ale wszystko to jest ostatecznie wykonalne.

Jeśli martwisz się takimi drobnymi problemami, a jesteś zbyt przywiązany do jednego stylu programowania, aby po prostu zmienić sposób kodowania (uwaga: jest to na ogół okropna jakość jako programista), jest to o wiele bardziej realna alternatywa niż po prostu pisanie własnego języka. To zajęłoby może parę tygodniu najwyżej . Porównaj to z latami spędzonymi na odpowiednim języku, z bogatym wsparciem debugowania i tym podobne.

Jeśli Twoje problemy są większe niż powyższe (globały są domyślne, co wymaga użycia localwszędzie), niektórymi z nich można zarządzać (po prostu spraw, aby deklarowanie nowego globalnego było błędem, poprzez użycie zmienionych środowisk i metatabli). Jeśli nienawidzisz funkcji jako pierwszorzędnych obiektów, coroutines, śmieciarek lub innych podstawowych elementów Lua ... cóż, jesteś w piekle własnej produkcji;)

A jeśli naprawdę chcesz być naprawdę hardkorowy, możesz napisać własny język, który skompilujesz do Lua. Dzięki temu będziesz mógł przynajmniej wykorzystać bardzo dobrze przetestowane środowisko uruchomieniowe Lua, wyjątkowy interfejs Lua API i inne podstawowe funkcje Lua, wszystko z Twojego języka! Lua. To zajmie trochę czasu, ale wciąż nie będzie lat , które spędziłbyś na czymś innym.

Nicol Bolas
źródło
Wydaje mi się, że mógłbym po prostu zakodować funkcje, gdybym używał Lua. Wydaje mi się, że po prostu zmieniam kod.
skiperic
1
@skiperic: Nie wiem, co masz na myśli. Czy możesz podać przykład?
Nicol Bolas,
Zobacz odpowiedź Laurenta powyżej.
skiperic
2
@skiperic: To tak naprawdę nie wyjaśnia, czym się zajmujesz. Czy umiesz kodować w Lua? Tak. A problemem jest ...?
Nicol Bolas,
1
@BartekBanachewicz: Nawet akceptując, że jest to C API, wciąż przewyższa wiele skryptów API opartych na C ++, które widziałem. Jest to jedyny skryptowy interfejs API, który faktycznie rozważałbym użycie surowego , bez jakiegoś automatycznego spoiwa.
Nicol Bolas,
3

Aby uzupełnić inne odpowiedzi, nie jest to opcja ściśle binarna. W ramach istniejącego języka skryptowego można również utworzyć własny język specyficzny dla domeny . Zaletami tego podejścia są:

  • W rzeczywistości nie musisz mieszać się z formalnymi gramatykami, analizatorami składni, wdrażaniem niestandardowych edytorów itp.
  • Większość korzyści czerpiesz z implementacji specjalistycznego języka skryptowego, tj. Dodatkowej abstrakcji, specyficznej dla twojej gry.
  • vs. homebrew: użytkownicy znający wybrany przez ciebie język skryptowy będą mogli natychmiast korzystać z Twojej DSL.
  • vs. zwykły istniejący język: użytkownicy niezaznajomieni z językiem skryptowym będą potrzebowali jedynie wiedzy na temat DSL do modyfikacji gry.

Podstawową wadą jest to, że projektowałbyś DSL z ograniczeniami twojego „podstawowego” języka.

mikołak
źródło
2

Może to mieć sens w zależności od mechaniki gry. Niektóre gry z wystarczająco prostą mechaniką mogłyby używać interpretowanego języka, aby uchronić się przed nadmiernie skomplikowanym kodowaniem Lua / Python, ale oszczędności na złożoności mogą nie być zbyt wiele warte. Na przykład jedna z tych interaktywnych gier powieściowych mogłaby z łatwością użyć niestandardowego silnika skryptowego.

Jeśli Twoja gra obejmuje silnik fizyki, różne elementy gry oraz różną złożoność postaci i umiejętności, zdecydowanie powinieneś rozważyć spojrzenie na inne istniejące języki skryptowe, więc nie próbujesz dodawać niezbędnych funkcji do swojego niestandardowego języka ani naprawiać błędów za pomocą to. Chociaż Lua jest najprawdopodobniej najszybszy, istnieje wiele innych, które mogą ci się bardziej podobać ze względu na ich składnię, a wielu z nich chwali się, jak łatwo integrują się z C. Python, Ruby i Angelscript to tylko kilka. (Zapraszam do wspominania innych w komentarzach)

Jeśli upewnisz się, że takie języki są używane tylko do „sterowania logicznego” (tj. Do obsługi konkretnego przypadku kolizji dla niektórych typów obiektów, takich jak miotacz ognia dotykający bloku lodowego), wydajność nigdy nie będzie stanowić problemu. Oczywiście, z drugiej strony, jeśli użyjesz ich do bardziej rutynowego kodu (tworząc niestandardowy algorytm kontroli kolizji, który uruchamia każdą ramkę), bardziej prawdopodobne jest, że utkniesz w martwym punkcie.

Katana314
źródło
1
Lua jest również bardzo popularnym językiem skryptowym w tworzeniu gier.
Philipp
Tak, Lua jest używany wszędzie, ale OP zauważył, że tak naprawdę go nie lubił. Ważne mogą być preferencje dotyczące stylu kodowania.
Katana314,
1
Na przykład jedna z tych interaktywnych gier powieściowych mogłaby z łatwością użyć niestandardowego silnika skryptowego. ” Oczywiście nie widziałeś Inform . Naprawdę, nawet w przypadku tekstowych przygód, nie ma sensu wymyślać własnego języka.
Nicol Bolas,
1
@ Katana314: „ Preferencje stylu kodowania mogą być ważne. ” Szczerze mówiąc, światu byłoby lepiej, gdyby programiści zrzucili swoje wysokie konie na temat „stylu kodowania”. Wszyscy bylibyśmy lepszymi programistami, gdybyśmy przestali myśleć, że <wstaw tutaj preferowany język> jest zasadniczo lepszy niż wszyscy <wstaw tutaj preferowany język>. Używanie języków, które mogą nie podobać się składni znaków kompilacji i pomaga uniknąć tego rodzaju błędów.
Nicol Bolas,
1

Mówię, idź po to. Po wznowieniu byłby to dodatkowy pokaz umiejętności. Pamiętaj jednak, że najpierw potrzebujesz tej umiejętności. To nie będzie łatwe, będzie dość trudne. Istnieją książki na ten temat, a także samouczki online, ale ostatecznie sprowadzi się to do ciebie i twojego zrozumienia, jak działa kompilator oraz w jaki sposób kod jest analizowany i tłumaczony.

Upewnij się, że zaczynasz od prostych rzeczy, często testuj i trzymaj się swojego ideału. Ale zawsze pamiętaj, LUA jest dla ciebie.

Wolfgang Skyler
źródło
1
Wydaje mi się, że pytanie brzmi, czy celem jest zrobienie gry, czy stworzenie czegoś, co wygląda dobrze na CV.
Tridus,