Fairtris — a fair implementation of Classic Tetris®

13

Jakieś dwa tygodnie temu zacząłem tworzyć mały projekt narzędzia, który by pozwolił mi sprawdzić w praktyce wymyślony algorytm generowania ciągu tetrisowych klocków. Jest to narzędzie w kształcie zalążka prostej gry, będącej klonem Tetrisa na konsolę NES, a która w przyszłości będzie dawała pograć w klocki, zachowując mechanikę rozgrywki zgodną z oryginalną wersją na NES-a.

screenshot-20210611040640.png

Wersja dla tego postaFairtris 1.0.0.19 (beta)
Najświeższa wersjaFairtris 2.3.1.2 (więcej informacji w tym poście)

Póki co, zaimplementowane są fundamenty, czyli obsługa okna i renderowania w nim zawartości, wsparcie klawiatury i kontrolerów USB (oraz możliwość mapowania klawiszy/przycisków według własnego uznania), oprogramowane są także wszystkie menu gry, ze wsparciem efektów dźwiękowych.

Dodatkowo, narzędzie obsługuje dwie skórki — ”nowoczesną” i klasyczną — które umożliwiają renderowanie gry w odcieniach ciemnych i z dodatkowymi informacjami podczas rozgrywki, lub w sposób klasyczny, zgodny z nintendowskią wersją. Skórka klasyczna zaimplementowana jest po to, aby można było Fairtrisa wykorzystać na stronie zwanej Maxout Club (stworzonej na potrzeby CTWC 2020) i sprawdzić czy mój algorytm nada się do turniejów (i jak ogólnie sprawdzi się w meczach 1vs1). Zestawienie ekranów wszystkich scen w dwóch skórkach:

screenshot-20210611040612.png

Małym dodatkiem jest obsługa paska postępu na przycisku aplikacji na pasku zadań. Przycisk ten wykorzystałem do podglądu danych na temat framerate'u (liczba klatek wyświetlana w formie tekstu) oraz obciążenia procesora przez Fairtrisa. Pasek postępu w kolorze zielonym to obciążenie w normie, żółtym to średnie, a czerwonym to wysokie (co zwiastuje lagi).


Narzędzie jest stworzone jako zwykła, jednowątkowa aplikacja okienkowa, której okno pozbawione jest belki. Jeśli okno nie jest wyświetlone na pełen ekran, to można je przesuwać w dowolne miejsce pulpitu (i dowolny ekran) za pomocą lewego przycisku myszy. Rolką myszy zmienia się rozmiar okna (opcję tę można zablokować w menu ustawień gry), natomiast domyślna klawiszologia wygląda tak:

MENU:

— nawigowanie po pozycjach menu,
— zmiana opcji danej pozycji menu,

S — wybranie aktywnej opcji menu,
Z — powrót do poprzedniego menu.

ROZGRYWKA:

— przesuwanie klocka na boki,
— hard-drop (natychmiastowe przyklejenie klocka do stosu, z dowolnej wysokości),
— soft-drop (przyspieszone opuszczanie klocka),

Z — obrót klocka odwrotnie do ruchu wskazówek zegara,
X — obrót zgodnie z ruchem wskazówek zegara,

S — pauza lub wznowienie gry,
A — ukrycie lub pokazanie następnego klocka.



Dodatkowo, przycisk F2 pozwala zresetować mapowanie klawiszy i ustawić klawiaturę jako domyślne urządzenie sterujące (w dowolnej scenie), w razie gdyby ktoś tak zepsuł mapowanie, że nie potrafi nawigować po menu. Natomiast przycisk F1 minimalizuje narzędzie i otwiera główną stronę repozytorium — w przyszłości będzie tam readme.md, a w nim samouczek.

Dla testów różnych rzeczy, póki co, w scenie rozgrywki obsługiwany jest klawisz Select (na klawiaturze to klawisz A), który symuluje koniec gry i przejście do sceny z podsumowaniem rozgrywki. I również dla testów, strzałka w górę powoduje renderowanie jaśniejszego tła — to do sprawdzenia migania tła po zdobyciu tetrisa. Obie te funkcje zostaną później usunięte. Natomiast Start (na klawiaturze S) powoduje pokazanie menu pauzy (tu już nic się nie zmieni, jest gotowe).

W najbliższych dniach zabiorę się za implementację właściwej części, czyli samej rozgrywki i trochę to potrwa, bo chcę idealnie przenieść mechanikę z NES-a do mojego narzędzia. Tymczasem szukam chętnych do pobawienia się obecną wersją i sprawdzenia, czy wszystko działa prawidłowo. W razie czego proszę o feedback i pochwalenie się swoimi przemyśleniami. ;)

6

Jestem rozczarowany. Działasz w Lazarusie, czyli narzędziu multiplalatformowym. A ograniczasz się tylko do Windows. Co by nie było - do gry o tak wypasionej grafice nie potrzebujesz directX ani nawet WinApi, choć zgaduję, że z tego drugiego namiętnie korzystasz.

0
Ghost_ napisał(a):

Jestem rozczarowany. Działasz w Lazarusie, czyli narzędziu multiplalatformowym. A ograniczasz się tylko do Windows.

Cieszę się, że poruszasz ten temat — możemy go omówić, tak aby później skupić się na rzeczach ważniejszych. ;)

A więc wyjaśnienie — to, że Lazarus jest wieloplatformowy, nie oznacza, że wszystko co się w nim programuje, również musi działać na wielu platformach. Wsparcie wielu platform to możliwość, a nie obowiązek, dlatego nie ma żadnych przeciwskazań, aby tworzyć oprogramowanie dedykowane jednej platformie.

Po drugie, aby napisać kod wieloplatformowy, należy mieć wiedzę na temat budowy i działania wszystkich platform, a takiej w przypadku Uniksów nie mam (czasu na to też nie). Ale jest takie coś jak wine, więc portowanie mogę sobie odpuścić — tym bardziej, że to narzędzie testowe, klepane do jednorazowych testów, a nie produkt mający żyć lata.

I po trzecie — kod jest publiczny, więc jeśli znajdzie się popyt na wersję natywną dla Linuksów czy macOS, to będzie możliwość rozwijania źródeł pod tym kątem. Rozwijania przez ludzi, którzy znają wystarczająco dobrze inne platformy, w odróżnieniu ode mnie.

Ale powtarzam — to narzędzie tylko do testów algorytmu (i pobawienia się kodem), nie zamierzam tego projektu rozwijać później, bo ani nie mam prawa robić klonów Tetrisa (dlatego copyleftuję tę pracę, coby TTC i Nintendo nie czepiały się), ani wersja na PC nie będzie chyba nigdy respektowana przez graczów i organizatorów, więc to i tak bez sensu.

Co by nie było - do gry o tak wypasionej grafice nie potrzebujesz directX ani nawet WinApi, choć zgaduję, że z tego drugiego namiętnie korzystasz.

Nie używam namiętnie WinAPI — sam funkcji z modułu Windows i MMSystem używam jawnie może w pięciu miejscach, reszta to czysty FCL i LCL. Nie mam po prostu czasu i potrzeby na zabawę API czy silnikami do gier i tworzenie poważnej produkcji, skoro wiem jak wszystko zaklepać pod okienka i tyle mi wystarczy. ;)

2

Jak spojrzałem na strukturę plików to moje pierwsze wrażenie było, że ich nazwy za bardzo niczego nie mówią. No i faktycznie w środku jest chaos.

Jest moduł Arrays zawierający... same tablice https://github.com/furious-programming/Fairtris/blob/master/source/Fairtris.Arrays.pp

A patrząc po nazwach tematy są przynajmniej trzy - rendering, obsługa klawiatury i dźwięk.

Jest też Logic, gdzie można znaleźć sobie wielki god class na 1312 linii https://github.com/furious-programming/Fairtris/blob/master/source/Fairtris.Logic.pp

5

@furious programming: wybacz offtop, ale sam zacząłes, więc chciałem się odnieść do paru spraw :P

to, że Lazarus jest wieloplatformowy, nie oznacza, że wszystko co się w nim programuje, również musi działać na wielu platformach

Oczywiście, że nie - ale to trochę bez sensu. Tak samo jak kupienie wypasionego ekspresu do kawy, który robi 30 różnych kaw, a potem korzystanie tylko z jednego programu, a do tego sypanie do niego najtańszej kawy z Tesco ;)

aby napisać kod wieloplatformowy, należy mieć wiedzę na temat budowy i działania wszystkich platform

Właśnie dzięki takim rzeczom jak LCL, które operują na bardzo wysokim poziomie abstrakcji, konieczną wiedze można ograniczyć do minimum. Masz obiekty w stylu TObrazek, TOkno czy TPlik i na nich działasz. A to, w jaki sposób TPlik wczyta zawartość pliku, przekaże ją do TObrazek, który się wyświetli na TOkno nie musi Cię interesować. To jest zadanie i problem twórców Lazarusa (czy innego wynalazku multiplatformowego) - zrobić tak, żeby to działało w taki sam sposób na różnych systemach. Wiadomo - dobrze jest wiedzieć (chociaż pobieżnie) jak to działa, powinno się przetestować na różnych konfiguracjach, ale nie jest to niezbędne. Tak samo jak dobry kierowca powinien mieć chociaż podstawową wiedzę o mechanice i sposobie działania silnika, ale wiele osób totalnie jest zielonych, a mimo to jeździ.

Ale jest takie coś jak wine, więc portowanie mogę sobie odpuścić

Używałeś tak realnie tego? Z tego co kojarzę, to jesteś windowsiarzem. Ja z kolei od dobrych kilku lat, jak mogę to głównie staram się działać na pingwinie. I od razu zaznaczam - windowsiarz to nie ma być żadna obelga, nie chcę tutaj zaczynać wojny o wyższości jednego nad drugim. Chodzi mi jedynie o to, że ja dobrze znam to narzędzie z własnego doświadczenia... i nie uważam, aby było ono (mimo ogromu pracy włożonego przez jego twórców) warte polecenia. To raczej jest eksperyment. OK, da się wiele rzeczy tam odpalić, ale efekty są bardzo dalekie od ideału - coś się źle wyświetli, czasami się wychrzani, jakieś artefakty na ekranie, kłopot z połączeniem z siecią/SQL, dziwny wygląd kontrolek, gorsza wydajność itp. OK, notatnik czy Painta odpali się bezstresowo, ale przy bardziej zaawansowanych projektach, moje doświadczenia z Wine pozostawiają wiele do życzenia.

Jest też Logic, gdzie można znaleźć sobie wielki god class

@szatkus - racja, superobiekt jest uznawany przez wieeele osób za antywzorzec, jednakże tutaj poprę @furious programming. O ile jest to robione sensownie, nie blokuje możliwości zmian, to taka jedna klasa, która ogarnia całość jest moim zdaniem całkowicie OK. Poza tym pamiętaj o pewnej rzeczy - inaczej się pisze kod w duzym korpo, kiedy developerzy się zmieniają, wiele osób pracuje nad tym samym, kod musi być łączony z innymi modułami itp, a inaczej jak sobie sam FP siedzi i pisze tetrisowe sprawy. To jak w przypadku gotowania - jak masz zakład produkujący parówki, to masz Sanepid, BHP, wytyczne dot. procesów technologicznych, atesty na dostawców i sprzęt itp. Ale nikt nie wymaga od babci piekącej sernik czy lepiącej w domu pierogi, żeby miała odpowiedni strój i cała się zdezynfekowała przed przystąpieniem do pracy ;)

1
cerrato napisał(a):

@furious programming: wybacz offtop, ale sam zacząłes, więc chciałem się odnieść do paru spraw :P

to, że Lazarus jest wieloplatformowy, nie oznacza, że wszystko co się w nim programuje, również musi działać na wielu platformach

Oczywiście, że nie - ale to trochę bez sensu. Tak samo jak kupienie wypasionego ekspresu do kawy, który robi 30 różnych kaw, a potem korzystanie tylko z jednego programu, a do tego sypanie do niego najtańszej kawy z Tesco ;)

Trochę słabe porównanie. Kompilowanie pod różne platformy to nie jest zmiana wartości selecta z "Windows" na "Linux" czy "MacOS" i wciśnięcie "Kompiluj". Trzeba o tym myśleć cały czas pisząc kod. A jak trafisz na bibliotekę, która ci ułatwi życie, ale pod spodem używa jakichś windowsowych API to co? Będziesz się męczył, żeby pokryć 99% rynku zamiast 90%? Zwłaszcza, że te pozostałe 10% spokojnie może użyć wirtualki albo emulatora. Do tego masz 3x więcej (albo i lepiej) testowania. Koszty wysokie, zysk marny.

Poza tym: chcę napisać grę na windowa, znam pascala i lazarusa, w lazarusie da się pisać w pascalu programy na windowsa. Robię to. A że lazarusa można używać do 10 innych rzeczy - co mnie to?

0
cerrato napisał(a):

@furious programming: wybacz offtop, ale sam zacząłes, więc chciałem się odnieść do paru spraw :P

Oczywiście, że nie - ale to trochę bez sensu. Tak samo jak kupienie wypasionego ekspresu do kawy, który robi 30 różnych kaw, a potem korzystanie tylko z jednego programu, a do tego sypanie do niego najtańszej kawy z Tesco ;)

No nie, różnica jest znacząca — ekspres możesz sobie kupić dowolny, a jedynym sensownym narzędziem do obiektowego Pascala, za którego nie muszę płacić, jest Lazarus (lub jego forki, ale ich nie chcę). Tak więc argument nietrafiony. Poza tym nie zależy mi na wsparciu innych platform, bo używam wyłącznie Windows, a projekt tworzę aby się pobawić, czyli zaimplementować wszystko co potrzeba, potestować swoje pomysły co do algorytmów generowania klocków, a później porzucić.

Kod źródłowy jest publiczny po to, aby ktoś chcący potestować źródła lokalnie, mógł sobie to zrobić, a także po to, aby mógł je forknąć, jeśli ktoś będzie chciał je rozwijać, jak już sam zakończę zabawę z tym projektem. A tutaj napisałem tylko po to, aby znaleźć chętnych do potestowania, wyłapania ewentualnych błędów i feedbacku — ze źródłami nie mam żadnego problemu, pomocy w ich pisaniu nie potrzebuję. ;)


iksde napisał(a):

Trochę słabe porównanie. Kompilowanie pod różne platformy to nie jest zmiana wartości selecta z "Windows" na "Linux" czy "MacOS" i wciśnięcie "Kompiluj". Trzeba o tym myśleć cały czas pisząc kod. A jak trafisz na bibliotekę, która ci ułatwi życie, ale pod spodem używa jakichś windowsowych API to co? Będziesz się męczył, żeby pokryć 99% rynku zamiast 90%? Zwłaszcza, że te pozostałe 10% spokojnie może użyć wirtualki albo emulatora. Do tego masz 3x więcej (albo i lepiej) testowania. Koszty wysokie, zysk marny.

W obecnej postaci, apka może być uruchomiona na dowolnym Windows (7, 8, 8.1, 10) — nie wymaga absolutnie żadnej biblioteki do uruchomienia (pokroju Direct3D czy OpenGL), pójdzie na wszystkim co ma klawiaturę i ekran. A na Linuksach i macOS można odpalić przez Wine i też pójdzie bez problemu. Tak więc nie tracę praktycznie nic, a czasu zyskuję bardzo dużo.


szatkus napisał(a):

Jak spojrzałem na strukturę plików to moje pierwsze wrażenie było, że ich nazwy za bardzo niczego nie mówią. No i faktycznie w środku jest chaos.

Nie widzę w tych źródłach chaosu. Każdy aspekt aplikacji jest wydzielony do osobnego modułu, a nazwy tych modułów mówią same o swojej zawartości (mniej lub bardziej, ale wolę, aby były jak najkrótsze i jednocześnie jak najbardziej opisowe).

Jest moduł Arrays zawierający... same tablice https://github.com/furious-programming/Fairtris/blob/master/source/Fairtris.Arrays.pp

A patrząc po nazwach tematy są przynajmniej trzy - rendering, obsługa klawiatury i dźwięk.

Ale w dalszym ciągu moduł ten zawiera (bo ma zawierać) same tablice. Służy tylko do deklaracji tablic, których wartości są stałe, pobrane z modułu Fairtris.Constants. Mogłem stałe liczbowe i tablice umieścić w jednym module, bo wszystkie mają stałe wartości, ale wolałem je wydzielić — w ten sposób mam mniej skakania po modułach w trakcie pisania.

Jest też Logic, gdzie można znaleźć sobie wielki god class na 1312 linii https://github.com/furious-programming/Fairtris/blob/master/source/Fairtris.Logic.pp

Jeśli uznajesz coś za „god class”, tylko dlatego, że ma ponad tysiąc linijek, to wypaczasz znaczenie tego terminu. Bo god class (a raczej god object), to obiekt, który zbyt wiele wie lub zbyt wiele robi. A klasa TLogic zajmuje się jedynie wykonywaniem logiki gry i niczym innym — pozostałymi aspektami zajmują się inne klasy, w tym sterowaniem oknem, scenami, aktualizowaniem inputu, renderowaniem itd.

Mógłbym tę klasę podzielić np. na 11 mniejszych, dla każdej sceny osobna klasa, ale to nie sprawi w magiczny sposób, że TLogic będzie wiedzieć czy robić mniej. Za to dojdzie dodatkowe przepychanie danych w parametrach oraz wzrośnie liczba modułów. IMO bezcelowa masturbacja źródeł.


I oczywiście, że można tę gierkę zaimplementować lepiej — sam wiem co i jak powinno być zrobione lepiej.

Np. implementacja menu jest mocno uproszczona i słaba, nie powinny być hardkodowane pozycje dla każdego menu, tak samo jak ich renderowanie. Menu powinno być obiektem zawierającym dowolną liczbę submenu oraz itemów, gdzie każde submenu i każdy item powinnien być obiektem, posiadającym swój typ, pozycję, stan itd. — taki mini-LCL. Menu powinno być opisane w konfigu, tak aby można je było łatwo modyfikować, bez konieczności zmiany kodu źródłowego. A renderer powinien brać instancję dowolnego menu i renderować je według informacji dotyczacych jego layoutu — czyli powinien być uniwersalny.

Tyle że nie mam czasu na to wszystko, bo jedyne co chcę przetestować, to to, jak wypadnie w praktyce mój algorytm losujący klocki — reszta to małoznaczące dodatki. Kod piszę szybko, bez projektowania (typowe „programowanie ekstremalne”) i nie przejmuję się tym, że struktura kodu nieco odbiega od standardów. Dlatego trochę rzeczy hardkoduję, trochę kopiuję, korzystam ze zmiennych globalnych z referencjami głównych klas, zamiast implementować wzorce i jadę dalej.

Gdybym chciał wszystko zrobić porządnie, to bym musiał poświęcić na implementację z pół roku, a mi bardzo zależy na czasie — miesiąc i ma być gotowe. Idealnie by było, gdybym zdążył przed 26 czerwca, czyli przed zbliżającymi się Mistrzostwami Polski, ale szanse są marne. :D

1

Napiszę w osobnym poście, coby nie umknęło nikomu. ;)

Jeśli ktoś jest chętny pomóc, to nie skupiajcie się na jakości kodu, a pobierzcie apkę testową z załącznika lub źródła z repozytorium i dajcie znać, czy program działa prawidłowo — czy się uruchamia, czy modyfikowanie jego ustawień działa poprawnie, czy nie ma problemów z obsługą klawiatury i konfigurowaniem kontrolera itd.

Czyli w skrócie, bądźcie jak normalni beta-testerzy z łapanki — dostajecie wersję beta bez źródeł, odpalacie, sprawdzacie czy wszystko działa i dajecie znać, jeśli są problemy. Kod źródłowy w przypadku tego projektu jest absolutnie nieistotny i nawet ja tę kwestię olewam, bo nie jest to istotą jego powstawania. Docelowej grupy odbiorców i tak aspekty programistyczne nie interesują, a sam projekt nie ma żadnej przyszłości i za dwa miesiące pewnie nawet nie będę pamiętał, że go stworzyłem.

Dziękuję za każdy feedback dotyczący problemów z runtimem.

2

@furious programming: ale ja ci nie każę pisać na inne platformy. Chciałeś oceny to ją masz. Uważasz to za mało ważne, to pisz sobie tylko pod Windows.

Przeciwwskazań nie ma. Rozczarowany jestem. To moja (i nie tylko) ocena.

0

@Ghost_: nie chciałem oceny, a testerów do pomocy przy wyłapywaniu bugów związanych z działaniem programu. Myślę, że nie powinno to być trudne do zrozumienia, tym bardziej, że w pierwszym poście napisałem jakie mam oczekiwania i napisałem wszystko co jest potrzebne do przetestowania działania gry.

I tak — nie zależy mi na innych platformach, nie mam na to czasu, więc ich nie tykam. ;)

Kiedyś robiłem cos podobnego, też gierkę, ale platformówkę — Deep Platformer — i ta była wieloplatformowa, bo chciałem aby dało się nią skompilować pod Linuksami i macOS. A tutaj nie chcę, więc temat wieloplatformowości olewam.

1

Mam wątpliwości, czy z takim nastawieniem znajdziesz kogoś, kto ci pomoże. Ale życzę powodzenia.

3

Na kontrolerze od Switcha nie łapie większości przycisków. Poza na padzie od Xa nie działa sterowanie na strzałkach. I IMHO mapowanie przycisków jest dziwne, zwykle w grach najważniejsze funkcje mają przyciska A i B, a nie X i Y. No i spodziewałem się, że SELECT i START będą zmapowane do przycisków na środku kontrolera (- i + według nomenklatury Nintendo).

0
Ghost_ napisał(a):

Mam wątpliwości, czy z takim nastawieniem znajdziesz kogoś, kto ci pomoże. Ale życzę powodzenia.

Co ma piernik do wiatraka? Szukam kogoś, kto odpali apkę i się nią pobawi, a że większość użytkowników tego serwisu jednak z Windowsów korzysta, to nie widzę żadnych przeciwskazań. A jak ktoś z tutejszych ma np. Linuksa, to prawie na bank ma też Wine, więc to też żaden problem.

Problemem nie jest to, że nikogo nie znajdę, bom głupi i wsparcia dla Uniksów nie implementuję, a to, że to forum programistyczne i nie skupiacie się na tym czego oczekuję (testów organoleptycznych), a próbujecie wykorzystać swoją programistyczną wiedzę i dotykacie tematu źródeł. Czyli ja o jednym, wy o drugim. :D


szatkus napisał(a):

Na kontrolerze od Switcha nie łapie większości przycisków. Poza na padzie od Xa nie działa sterowanie na strzałkach.

Widać joyGetPosEx stanu tych kontrolerów nie jest w stanie poprawnie odczytać.

I IMHO mapowanie przycisków jest dziwne, zwykle w grach najważniejsze funkcje mają przyciska A i B, a nie X i Y.

W kontrolerach NES-a też są przyciski B i A, domyślnie zmapowane do klawiszy Z i X. Czyli odpowiednik nintendowskich przycisków B i A to klawisze Z i X. Nintendowskich, czyli takich:

screenshot-20210611185558.png

No i spodziewałem się, że SELECT i START będą zmapowane do przycisków na środku kontrolera (- i + według nomenklatury Nintendo).

Spodziewałeś się, że napiszę obsługę kontrolerów w taki sposób, że program sam będzie rozpoznawał wszystkie gamepady świata i mapował im przyciski w odpowiedni sposób? Nie wymagasz ode mnie zbyt wiele? ;)

Przyciski kontrolera domyślnie zmapowane są dla kodów 1, 2, 3 i 4 — nieważne jaki kontroler podłączysz. Każdy ma inną liczbę przycisków i inny ich układ, dlatego nie da się napisać kodu tak, aby po podłączeniu kontrolera, samo się wszystko dobrze ustawiło. I po to właśnie jest menu konfiguracji kontrolera, abyś mógł sobie odpowiednio kontroler zmapować.

Jeśli możesz, wejdź do ustawień kontrolera, wybierz opcję change i ustaw samemu przyciski dla Select, Start, B i A. I jeśli możesz to sprawdź, czy możesz np. do przycisku Select przypisać dowolny przycisk kontrolera, inny niż wychylanie analogowych gałek/spustów (analogi nie są obsługiwane, bo nie mają zastosowania w Tetrisie). W ten sposób dowiemy się czy jest problem z odczytywaniem przycisków, czy problem wynika z braku zrozumienia funkcji mapowania kontrolera.

Daj znać, jeśli nie rozumiesz jak ten test przeprowadzić.

1

Nie ma za dużo chyba jeszcze do testowania? Skąd pochodzą screeny w pierwszym poście? Czy da się tam faktycznie postawić klocka?
Odpaliłem, działa, nie ma za dużo do niedziałania bo nie ma za dużo w ogóle.
Jedyne od strony UX czego można się przyczepić to że w menu strzałka w górę nie przenosi kursor na sam dół i że w sumie nie wiadomo po co opcja "Back" skoro jest klawisz robiący to samo.
No i odpala mi się zawsze na drugim ekranie z jakiegoś powodu, jedyna opcja przeniesienia to zmiana z fullscreen na zoom, przeniesienie myszką okna i zmiana na fullscreen. Z jakiegoś powodu też odpala się w 60 fps mimo że ekran ma 144Hz. Skąd te 60Hz? To nie jest ani PAL ani NTSC

0
obscurity napisał(a):

Nie ma za dużo chyba jeszcze do testowania? Skąd pochodzą screeny w pierwszym poście? Czy da się tam faktycznie postawić klocka?

Pisałem, że właściwa część rozgrywki nie jest jeszcze zrobiona. Najpierw robię fundamenty, czyli menu z możliwością konfiguracji inputu i wyborem ustawień rozgrywki, a dopiero jak będę miał czym grać i wiedział jak gra ma się zachowywać (region, RNG, poziom startowy), to wtedy zaimplementuję rozgrywkę (czyli teraz się tym zajmuję). Na odwrót to nie ma prawda działać.

Obrazek w pierwszym poście zawiera prototypy wszystkich scen z przykładowymi danymi, nie zrzuty z programu. Dzięki temu każdy czytelnik może zobaczyć jak będą wyglądały wszystkie sceny w grze już teraz, zamiast czekać kilka tygodni na zaimplementowanie wszystkich funkcji. A to co już jest oprogramowane, można zobaczyć testując opublikowaną wersję (w załączniku pierwszego posta jest).

Odpaliłem, działa, nie ma za dużo do niedziałania bo nie ma za dużo w ogóle.

Jest dużo, tylko dla Ciebie akurat wszystko jest nieistotne, skoro nie możesz „postawić klocka”. ;)

Jedyne od strony UX czego można się przyczepić to że w menu strzałka w górę nie przenosi kursor na sam dół […]

Zastanawiałem się nad tym i chyba dodam taką opcję.

[…] i że w sumie nie wiadomo po co opcja "Back" skoro jest klawisz robiący to samo.

Po to są pozycje BACK, aby nie trzeba było się głowić jak wyjść z danego menu. I nie sądzę, aby w normalnych grach nie było takich pozycji — grałem w dość sporo gier i każda miała albo pozycję w rodzaju moich BACK, albo przycisk obsługiwany z poziomu myszy (i dodatkowo obsługę klawisza Esc).

Tak więc nie sądzę, aby moja gierka odstawała w tym temacie od UI typowych gier.

No i odpala mi się zawsze na drugim ekranie z jakiegoś powodu […]

Domyślnie uruchamia się na głównym ekranie, czyli tym o indeksie 0. Numer ekranu oraz współrzędne okna są zapisywane do konfiga przy zamykaniu programu. Jeśli okno było otwarte na pełen ekran, to współrzędne są w konfigu zerowane, bo podczas kolejnego rozruchu będą ignorowane i okno zostanie wyrzucone na pełen ekran.

[…] jedyna opcja przeniesienia to zmiana z fullscreen na zoom, przeniesienie myszką okna i zmiana na fullscreen.

W trybie fullscreen, przeciąganie okna jest zablokowane, po to, aby gracz przez przypadek go nie przesunął i nie musiał później co do piksela ustawiać go tak, aby przykrywało cały pulpit. A kursor jest ukrywany po to, aby nie denerwował. Tak więc jeśli ktoś chce przenieść okno na inny ekran, to rolką myszy wystarczy je skurczyć, przenieść, i znów rolką powiększyć na pełen ekran. Nie trzeba tego robić po każdym włączeniu, bo program zapamiętuje współrzędne okna i jego rozmiar z poprzedniej sesji.

Z jakiegoś powodu też odpala się w 60 fps mimo że ekran ma 144Hz. Skąd te 60Hz? To nie jest ani PAL ani NTSC

Kiedy to właśnie jest NTSC.

Tak to ma działać, aby zachować pełną zgodność z oryginalną grą na NES-a. Tylko w ten sposób testowanie różnych aspektów klasycznego Tetrisa ma jakąkolwiek wartość. Nawet lagi są poprawnie odwzorowane — zamiast pomijania klatek, przetwarza się je z opóźnieniem. Poza tym im mniej mocy obliczeniowej program zużywa, tym więcej zostaje go dla innych procesów — to nie czasy DOS-a.

Tryby z prefiksem NTSC mają działać w 60fps, a tryby z prefiksem PAL oraz EUR w 50fps. Częstotliwość pracy zegara sterującego obsługą gry zmieniana jest w locie, podczas konfigurowania rozgrywki w menu SET UP GAME.

0

Zaimplementowałem zawijanie zaznaczenia we wszystkich menu. Dodatkowo, wykorzystałem tę samą metodę kontroli zakresu indeksów do zapętlenia wyboru wartości we wszystkich wybieralnych pozycjach menusów, coby zachować spójność działania. No i fajnie działa — nawigacja jest ciut wygodniejsza.

2

Ale bucowatosc bije z tego tematu :-D smiesznie tutaj

0

@tmk3: wg mnie nie jest tak źle. Plusiki wpadły, więc projekt został ”zauważony”, kilku się pobawiło i zgłosiło uwagi — @szatkus dał znać, że jest kłopot z niektórymi kontrolerami, a @obscurity, że menu mogło by zapętlać zaznaczenie, żeby lepiej się nawigowało (i dorzuciłem taką opcję) i kilka innych uwag. Wiem że na razie nie ma za wiele do testowania, ale o ewentualnych problemach wolałbym wiedzieć już teraz — im wcześniej tym lepiej. Błędy zawsze się gdzieś czają i mogą się nigdy nie objawić na mojej konfiguracji, dlatego poprosiłem o testy.

Kilku użytkowników nie zrozumiało, że źródła w tym przypadku są nieistotne, no to trzeba było tę kwestię wyjaśnić. Nie udzielam się zbyt często w tym dziale i widać błędnie założyłem, że jeśli napiszę o testach działania, to nikt nie będzie się zajmował źródłami, a wykona tylko testy manualne, na których mi zależy.

Chyba że ”bucowatością” nazywasz to, że odpowiadam na feedback i rozwiewam wątpliwości co do specyfiki działania tej apki, to w takim razie masz błędne postrzeganie tego, co tutaj robię. Ta aplikacja jest specyficzna czy nietypowa i rozumiem, jeśli ktoś może mieć uwagi. Ale jeśli ktoś nazywa źródła „posranymi” i wciska mi banialuki, że kod kontrolujący przepływ sterowania nie może być nazywany logiką, to cóż… nie dam sobie wejść na głowę.


Tak więc jeśli ktoś ma jakieś uwagi co do tej gierki to może pisać śmiało, nawet jeśli nie do końca rozumie jak to wszystko ma działać. Najbardziej mi zależy na wyłapaniu jawnych błędów — np. aplikacja nie uruchamia się, wyłącza się niespodziewanie (kontrolki błędów nie ma w ogóle, więc jak pojawi się wyjątek to proces umrze), wyświetla się nie tam gdzie trzeba, zamula za bardzo, renderuje się nieprawidłowo, mapowanie inputu działa źle itd.

Obecnie analizuję mechanikę oryginału i powoli implementuję właściwą część rozgrywki. Chwilę to zajmie, bo idealne odwzorowanie mechaniki wersji NES-owej nie jest dla mnie jeszcze w 100% zrozumiałe. Ale to prędzej czy później sobie rozgryzę.

2

No i spodziewałem się, że SELECT i START będą zmapowane do przycisków na środku kontrolera (- i + według nomenklatury Nintendo).

Spodziewałeś się, że napiszę obsługę kontrolerów w taki sposób, że program sam będzie rozpoznawał wszystkie gamepady świata i mapował im przyciski w odpowiedni sposób? Nie wymagasz ode mnie zbyt wiele? ;)

Ty, no ale poprosiłeś o testy to przetestował i dał feedback, sam sobie to testuj z takim podejściem:D

0

@Kerubyte: no to weź mi wytłumacz, dlaczego przyciski miałyby być od razu zmapowane? Nie da się napisać kodu, który poprawnie zmapuje przyciski dla dowolnego podłączonego kontrolera, bo nie wiadomo ile przycisków ma kontroler, jaki mają fizyczny układ na obudowie oraz jakich skankodów użył producent dla każdego z nich.

Tutaj problemem nie jest implementacja obsługi kontrolera, a niepoprawne pojmowanie tego, jak to powinno działać. Dlatego i tę wątpliwość rozwiałem, odpowiadając, że kolega za wiele oczekuje i że to czego oczekuje jest awykonalne. Tak więc w czym problem? ;)

2
furious programming napisał(a):

@Kerubyte: no to weź mi wytłumacz, dlaczego przyciski miałyby być od razu zmapowane? Nie da się napisać kodu, który poprawnie zmapuje przyciski dla dowolnego podłączonego kontrolera, bo nie wiadomo ile przycisków ma kontroler, jaki mają fizyczny układ na obudowie oraz jakich skankodów użył producent dla każdego z nich.

Tak naprawdę nie jest tak źle - obecnie kontrolery najczęściej mają layout albo z xboxa albo z ps4. Wystarczy zamapować z 4-5 najpopularniejszych kontrolerów (xbox, ps, switch, steam i może nes bo choć nie jest popularny to chyba do niego nawiązuje "gra") żeby obsłużyć 99% z nich. Nie trzeba pewnie ich nawet posiadać bo na pewno kody znajdziesz w Internecie. Wiadomo że jak ktoś bedzie chciał grać na kierownicy w tetrisa to będzie musiał sobie samemu to zmapować.

0
obscurity napisał(a):

Tak naprawdę nie jest tak źle - obecnie kontrolery najczęściej mają layout albo z xboxa albo z ps4.

XBox odpada, bo do obsługi wymaga użycia XInput. Kontrolery od PS mają mapowanie 0189, ale nie mają odpowiedników start i select w podobnym układzie, a options i jakiś share, który nie wiem czy pod Windows nie triggeruje jakichś systemowych funkcji (ktoś ma o tym pojęcie?). Inne kontrolery i klony mają różne mapowanie i nie da się ustalić spójnego układu — bywa że 0123, bywa że zupełnie inne, wymieszane czy na odwrót. No nie przewidzi.

Wystarczy zamapować z 4-5 najpopularniejszych kontrolerów (xbox, ps, switch, steam i może nes bo choć nie jest popularny to chyba do niego nawiązuje "gra") żeby obsłużyć 99% z nich.

To tylko zgadywanki. Kontrolerów imitujących te oryginalne do NES-a jest masa, od typowo chińskich podróbek, poprzez retro-bity, Data Frogi, Hyperkiny, aż po droższe typu 8BitDo itp. Z takich korzystają tetrisowi gracze, bo są konstrukcją zbliżone do oryginalnych lub nawet identyczne (jak Data Frog). A jeśli ktoś ma możliwość, to podłącza oryginalny kontroler NES-owy do peceta poprzez specjalną przelotkę (7-pin do USB) — producentów też jest kilku i nie ma szans na znalezienie skan-kodów.

Nie trzeba pewnie ich nawet posiadać bo na pewno kody znajdziesz w Internecie. Wiadomo że jak ktoś bedzie chciał grać na kierownicy w tetrisa to będzie musiał sobie samemu to zmapować.

Problemem nie jest to czy będzie musiał mapować, a to, dlaczego miałby tego nie robić. Nieważne jaki kontroler miałem i nieważne w jaką grę grałem, zawsze po pierwszym jego podłączeniu do peceta, wchodziłem w ustawienia i mapowałem przyciski, tak aby dobrać wygodny dla siebie układ. Stąd nie mam bladego pojęcia dlaczego ktoś wymaga od oprogramowania, aby to zrobiło za niego. Wygląda mi na to, że przyzwyczajenie do od razu zmapowanych przycisków pochodzi z konsol, w których przyciski są zmapowane, bo kontrolery są tylko jednego typu/jednej rodziny. No ale na pecetach tak niestety nie ma.

Najpewniej zrobię tak, że kody klawiszy klawiatury będą zmapowane w sposób domyślny, bliski temu uniwersalnemu, stosowanemu przez graczy emulatorowych, a przyciski kontrolera nie będą w ogóle zmapowane. Po podłączeniu takiego do peceta i wybraniu urządzenia sterującego, trzeba będzie sobie przyciski ustawić. Podam taką informację w instrukcji na GitHub — pierwsze podłączenie kontrolera oraz FAQ dotyczące tego, dlaczego niektóre kontrolery nie są obsługiwane (i gałki analogowe). Choć z tych gotowych, najlepsze wydają się kody 0189, więc jest się nad czym zastanawiać.

W razie czego, jeśli znacie ”złoty środek”, to zawsze możecie się pochwalić kodami, które sami byście zastosowali jako domyślne. Ja niestety nie mam pod ręką innych kontrolerów niż Tracery i z pięć chińskich klonów różnych kontrolerów retro-konsol, więc mam ograniczone pole manewru. No ale stąd właśnie wiem, że każdy ma inaczej przypisane kody do przycisków.

4

Prace nad tym projektem cały czas trwają — obecnie zajmuję się implementacją głównej logiki gry (sterowaniem klockami).

Algorytmu sterowania nie wymyślam sam, a odwzorowuję ten oryginalny, z NES-owej wersji. Korzystam z różnych materiałów z sieci do tego celu. Niestety oryginalna logika jest dość zagmatwana, a tłumaczenie Assembly na Pascala to katorga. I o ile przesuwanie klocków (w tym automatyczne) oraz ich obracanie dało się dość łatwo napisać, tak ich opadanie jest tak skomplikowane, że przepisałem je 1:1, czyli użyłem etykiet i goto. ;)

Jak ktoś chce się pośmiać, to tutaj jest kod obsługi grawitacji — na razie nie próbuję tego skrócić, bo nie mam nawet pomysłu na to jak to zrobić. Zresztą olać — goto robi robotę, więc po co zmieniać. Mi tam takie skoki w niczym nie przeszkadzają. :D

Główną logikę wydzieliłem do osobnej klasy, ale nadal cała logika gry jest wykonywana liniowo, więc zgodnie z NES-em. Zostało mi jeszcze napisać kilka rzeczy związanych z graniem, czyli wykrywanie pełnych linii i ich animowane czyszczenie, a także aktualizowanie wszystkich liczników. Jak dobrze pójdzie to jutro skończę całą główną logikę i będę dokładnie testował to czy działa tak jak oryginał.

4

No, to czas się pobawić, wersja testowa w załączniku (albo kliknij tutaj aby pobrać). ;)

Główna mechanika gry została zaimplementowana — można startować z dowolnego poziomu i wybrać dowolną wersję regionalną, da się przesuwać klocki, obracać i upuszczać; stos działa poprawnie, więc można kasować linie jak się chce i zbierać punkty. Grać można klawiaturą lub kontrolerem (o ile działa w ramach bazowego sterownika HID).

Polecam nie zaczynać gier z poziomu wyższego niż 9, bo raczej nie ugracie zbyt długo. No i do zabawy raczej wziąć region NTSC/NTSC EXTENDED, bo działają w 60fps i są łatwiejsze. Główna mechanika działa praktycznie tak samo jak z oryginalnego Tetrisa na konsolę NES, ale niektóre rzeczy działają w przybliżony sposób, ze względu na brak w sieci szczegółowych informacji na ich temat.

Kilka rzeczy jeszcze czeka na zrobienie — nie ma jeszcze hard-dropu (strzałką w górę będzie można postawić klocek z dowolnej wysokości od razu na stosie), wyniki nie są rejestrowane i jeszcze kilka innych pierdół. No i na razie zaimplementowany jest jeden generator, więc wybór RNG niczego nie zmienia. A że jest to najgorszy generator (bo najmniej kodu wymagał do napisania), to generowane sekwencje często bywają ”nie fair”. :D

Jak skończę programować wszystkie aspekty tego projektu i już wszystko będzie działało prawidłowo, to wtedy użyję SDL-a, aby przyspieszyć renderowanie (mieć do dyspozycji akcelerację sprzętową oraz synchronizację pionową) i lepiej obsłużyć dźwięki (muzyki nie będzie, bo i tak każdy słucha swojej). A na sam koniec stworzę duże readme, w którym wszystkie aspekty będą porządnie opisane.


Sterowanie w menu:

, — poruszanie się po opcjach menu
, — zmiana wartości danej pozycji menu

Z — powrót do poprzedniego menu
X — wybranie aktywnej opcji

Sterowanie w grze:

, — przesuwanie klocka na boki (przytrzymaj, aby przesuwać automatycznie)
— przyspieszanie opadania klocka (soft-drop)

Z — obrót klocka odwrotnie do ruchu wskazówek zegara
X — obrót klocka zgodnie z ruchem wskazówek zegara

A — ukrycie lub pokazanie następnego klocka
S — pauza

1

@furious programming: Załączam zrzut ekranu - okienko gry jest bardzo małe. Możesz coś z tym zrobić?
Zrzut_01.PNGZrzut_02.PNG

@pstmax: Nie żartuj. Standard.
Zrzut_03.PNG

1

@pstmax: domyślnie okno jest rozciągnięte na cały ekran (na cały główny ekran, czyli pascalowy obszar Screen.PrimaryMonitor.BoundsRect). Rozmiar okna możesz zmienić w menu gry lub rolką myszy — ustaw kursor nad oknem i kręć kółkiem.

Sprawdź czy w opcjach masz ustawione FULLSCREEN i jeśli nie to go ustaw. ;)

3

@furious programming: OK. Nie lubię grać na fullscreen - nie po to mamy okienka (Windows) :-P
Poza tym po powiększeniu jakość grafiki mogłaby być lepsza.
Zrzut_04.PNG

2

W trybie niepełnoekranowym, okno można przesuwać w dowolne miejsce i na dowolny monitor — jakby co. Rozmiar okna i jego pozycja są zapisywane do pliku konfiguracyjnego, więc po ponownym uruchomieniu okno pojawi się na starym miejscu.

pstmax napisał(a):

Poza tym po powiększeniu jakość grafiki mogłaby być lepsza.

Będzie lepsza, jak użyję SDL i interpolacji. Na razie w trybie pełnoekranowym klatki renderowane są za pomocą Canvas.StretchDraw, a to oznacza skalowanie bez interpolacji, stąd piękny pixelart. ;)

PS: @pstmax — jak będziesz miał ukryty następny klocek to długo nie pograsz. W Tetrisie wybiera się miejsce dla bieżącego klocka przede wszystkim na podstawie tego, jaki klocek jest następny. Bez podglądu następnego, nikt nie umie grać, nawet profesjonaliści. :D

0

@furious programming: Pełna mobilizacja, co do grafiki i będzie naprawdę super gierka - idealna na sprzęt w biurach administracji państwowej. Pomyśl tylko jeszcze na tym aby dodać jako dodatek typu kalendarzyk, sticky notes i będziesz miał to w każdym biurze. :-P
Pytanie: Co to RNG Type ?

1

Piękne jest zakończenie ze zbitym ekranem :D

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.