Simply Tetris - Cordova

Odpowiedz Nowy wątek
2019-09-03 17:02
0

Napisany w celach edukacyjnych. Na co dzień nie piszę w JS, więc postanowiłam sobie poćwiczyć. Zachęcam do oceny kodu, chętnie przyjmę krytykę na klatę.
Do samej mechaniki gry wykorzystałam framework Phaser.io (https://phaser.io/).

https://bitbucket.org/or/tetris/

edytowany 1x, ostatnio: aurel, 2019-09-03 17:18

Pozostało 580 znaków

2019-09-03 17:32
0

Ogólnie OK, ale jakbyś mogła popracować, bo klawisze bardzo "mulisto" reagują na wciśnięcia, długo trzeba czekać na reakcję, a jak przytrzymuję chwilę to zamiast szybkiego przesunięcia, to sobie klocek tak skacze. Nie wyobrażam sobie grać w ten sposób przy wyższych prędkościach. Wszystkie tetrisy które kojarzę, reagowały bardzo szybko i responsywnie. @furious programming może potwierdzić ;)


That game of life is hard to play
I'm gonna lose it anyway
The losing card I'll someday lay
So this is all I have to say
Hmmm podpowiedz proszę, na jakim telefonie testowałeś? U mnie zmulenia nie zauważyłam. - aurel 2019-09-03 18:07
Lenovo K6 Power - cerrato 2019-09-03 21:04

Pozostało 580 znaków

2019-09-03 22:00
2

Tak na szybko:

  • nie uzywaj var - uzywaj const (lub let jesli potrzebujesz ponownie przypisać wartość),
  • masz wszędzie magic numbers
  • god objects (np. klasa Game)
  • mieszanie odpowiedzialności, przydługie metody, przydługie linie,
  • problemy z DRY, np. JSON.parse(JSON.stringify(this.blockShape[this.rotation])); ciągle się powtarza (już nie wnikam czemu tak kopiujesz obiekty),
  • for..in nie słuzy do iterowania po tablicach (a to zdaje się robisz) - służy do tego for..of
  • WTF:
    wait() {
    //waste some time
    this.r = 0;
    for(var i = 0; i < 10000000; i++)
    {
      var a = Math.sqrt(i);
      this.r += a * a;
    }
    }

Ogólnie cięzko mi się czyta ten kod, trochę takie spaghetti miejscami.

edytowany 1x, ostatnio: Maciej Cąderek, 2019-09-03 22:02
@Maciej Cąderek: nie znam za bardzo JS stad pytanie czemu var jest zle ? - WhiteLightning 2019-09-04 10:30
var mają zasięg funkcji, a let zasięg bloku - czyli tutaj np. "i" nie przestanie istnieć po zakończeniu pętli. - Crazy_Rockman 2019-09-04 10:40

Pozostało 580 znaków

2019-09-04 12:34
0

(już nie wnikam czemu tak kopiujesz obiekty)

Chciałam oderwać referencję, a inaczej mi nie wychodziło. Podpowiesz, jak powinno być?

WTF:

Noo tego to muszę się pozbyć :D Generalnie chodziło o to, że bez tego klocki latały jak naspidowane, przez co nie dało się grać. Jednak teraz wiem, że w efekcie np. u @cerrato skacze, zamiast działać płynnie... Więc muszę wymyślić jakiś lepszy sposób.

Pozostało 580 znaków

2019-09-04 16:35
2

Nie jestem specem ani od mobilnego Tetrisa, ani od programowania aplikacji mobilnych, więc niewiele jestem w stanie doradzić w kwestii związanej z kodem źródłowym. Ogólnie to nienawidzę urządzeń mobilnych (smartfonów i tabletów) z dotykowymi ekranami. ;)


Jednak jeśli chodzi o mechanikę gry i jej grywalność, to natychmiastowa reakcja na gesty gracza jest w przypadku Tetrisa obowiązkowa – w końcu to gra zręcznościowa, w której refleks jest kwestią kluczową. W sumie tak samo obowiązkowe są specjalne ruchy (tuck – wsunięcia od boku; spin – obroty w celu wypełnienia dziur), które nie wymagają dodatkowej logiki, a powinny wynikać z dobrze napisanego silnika gry.

Jeśli chciałabyś co nieco dowiedzieć się o algorytmach dotyczących obsługi klawiszy sterowania i wykonywania kolejnych ruchów klocków to polecam artykuł Applying Artificial Intelligence to Nintendo Tetris i rozdział The Mechanics of Nintendo Tetris. Znajdziesz w nim dokładny opis mechaniki gry oraz kod źródłowy w assemblerku dla MOS6502 – co prawda nie ma on większej wartości w tym przypadku, ale w komentarzach jest podany odpowiednik w pseudokodzie podobnym do C, więc łatwo zrozumieć co jest grane.

Chodzi o ogólne rozeznanie się z algorytmami, nie o gotowce. Polecam ten artykuł dlatego że jest bardzo szczegółowy, a poza tym opisuje wg mnie najlepszą wersję Tetrisa pod względem klasycznej mechaniki (dość prostej w implementacji).


edytowany 6x, ostatnio: furious programming, 2019-09-04 17:24
Tuck i spin powinny działać, ale ta responsywność u mnie była serio przesadzona - ledwo nacisnąłeś przycisk i klocek leciał do brzegu. - aurel 2019-09-04 17:26
Zapoznaj się z funkcjonalnością zwaną Delayed Auto Shift (w skrócie DAS) i tym w jaki sposób przesuwa się klocki podczas przyciskania i podczas przytrzymywania klawisza kierunku. Jednak aby to wykonać, potrzebne jest liczenie klatek gry oraz funkcja pokroju Sleep, która będzie odpowiadać za tworzenie przerw pomiędzy klatkami. - furious programming 2019-09-04 17:30
W razie czego filmik NES Tetris: Basics of DAS (Delayed Auto Shift) Piece Movement (i pozostałe części tej serii) powinien pomóc w zrozumieniu działania tej funkcjonalności. ;) - furious programming 2019-09-04 17:32
Super, dzięki za wskazówki :) - aurel 2019-09-04 17:40

Pozostało 580 znaków

2019-09-04 18:20
1

Chciałam oderwać referencję, a inaczej mi nie wychodziło. Podpowiesz, jak powinno być?

Jeśli obiekt który chcesz skopiować (this.blockShape[this.rotation]) to tablica wyglądająca mniej więcej tak (przynajmniej tak wywnioskowałem z kodu, choć same nazwy nie pomagają):

const arr = [{ x: 1, y: 1 }, { x: 1, y: 2 }] // itd.

To możesz ją skopiować np w ten sposób:

const copy = arr.map(item => ({ ...item }))

Ale ogólnie nie powinnaś dopuścić do sytuacji gdzie takie kopiowanie jest potrzebne. Deep copy jest koszowne obliczeniowo - szczególnie gdy piszesz grę gdzie masz na wszystko te ~16ms.

Noo tego to muszę się pozbyć :D Generalnie chodziło o to, że bez tego klocki latały jak naspidowane, przez co nie dało się grać. Jednak teraz wiem, że w efekcie np. u @cerrato skacze, zamiast działać płynnie... Więc muszę wymyślić jakiś lepszy sposób.

Wykonuj ruch klocków co któryś tick gry zamiast sztucznie spowalniać pętlę gry. Dzięki temu prędkość będzie przewidywalna, a pozostałe akcje, jak sterowanie, będą niezależne od prędkości klocków.

Pozostało 580 znaków

2019-09-04 20:38
1

Tu masz prymitywny przykład, z różnymi tickerami dla automatycznego progresu klocka i dla sterowania, z zachowaniem tych ~60 FPS:

  1. Robisz sobie obiekt z tickerami, dla każdego z nich:
    • określasz inrervał - ile klatek pomiędzy poszczególnymi ruchami
    • inicjalizujesz licznik / liczniki

Przykład:

const tickers = {
  controls: {
    interval: 10, // 6 times/s on 60FPS
    counters: {
      ArrowLeft: 0,
      ArrowRight: 0,
    },
  },
  game: {
    interval: 60, // 1 time/s on 60FPS
    counter: 0,
  }
}
  1. W pętli gry (u Ciebie to bodajże metoda update) odpowiednio inkrementujesz / resetujesz liczniki i wykonujesz akcje gdy licznik zgadza się z interwałem, np zakładając, że mamy pojedynczy kwadratowy klocek (square o rozmairze tileSize) :
if (keyboard.ArrowLeft) {
  if (tickers.controls.counters.ArrowLeft % tickers.controls.interval === 0) {
    square.x -= tileSize
  } 

  tickers.controls.counters.ArrowLeft++
} else {
  tickers.controls.counters.ArrowLeft = 0
}

if (keyboard.ArrowRight) {
  if (tickers.controls.counters.ArrowRight % tickers.controls.interval === 0) {
    square.x += tileSize
  } 

  tickers.controls.counters.ArrowRight++
} else {
  tickers.controls.counters.ArrowRight = 0
}

if (tickers.game.counter % tickers.game.interval === 0) {
  square.y += tileSize
  tickers.game.counter = 0
}

tickers.game.counter++

Demo: https://codepen.io/caderek/pen/gOYXvJL?editors=0010

Oczywiście można to zrobić bardziej elegancko, ale co do idei to chyba spełnia założenia:

  • nie wpływa na ogólną prędkość gry,
  • elementy mają różne czasy reakcji wg potrzeb,
  • reakcja na przycisk jest natychmiastowa (możliwy hyper tapping) przy zachowaniu interwałów gdy przycisk jest przytrzymany,
  • można łatwo przyspieszać spadanie klocków (zwiększać level) zmniejszając tickers.game.interval oraz umożliwić graczowi ustawienie prędkości samopowtarzania.
edytowany 8x, ostatnio: Maciej Cąderek, 2019-09-04 22:36

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0