You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/pl/6-space-game/4-collision-detection/README.md

11 KiB

Stwórz grę kosmiczną, część 4: Dodawanie lasera i wykrywanie kolizji

Quiz przed wykładem

Quiz przed wykładem

W tej lekcji nauczysz się, jak strzelać laserami za pomocą JavaScript! Dodamy dwie rzeczy do naszej gry:

  • Laser: laser wystrzeliwany z Twojego statku bohatera, poruszający się pionowo w górę
  • Wykrywanie kolizji, jako część implementacji możliwości strzelania, dodamy również kilka zasad gry:
    • Laser trafia wroga: Wróg ginie, jeśli zostanie trafiony laserem
    • Laser trafia górną część ekranu: Laser zostaje zniszczony, jeśli uderzy w górną część ekranu
    • Kolizja wroga i bohatera: Wróg i bohater zostają zniszczeni, jeśli się zderzą
    • Wróg trafia dolną część ekranu: Wróg i bohater zostają zniszczeni, jeśli wróg dotrze do dolnej części ekranu

Krótko mówiąc, Ty -- bohater -- musisz trafić wszystkich wrogów laserem, zanim uda im się dotrzeć do dolnej części ekranu.

Poszukaj informacji o pierwszej grze komputerowej, jaka kiedykolwiek powstała. Jakie miała funkcje?

Bądźmy bohaterami razem!

Wykrywanie kolizji

Jak wykrywać kolizje? Musimy traktować nasze obiekty w grze jako prostokąty poruszające się po ekranie. Dlaczego? Ponieważ obraz używany do rysowania obiektu w grze to prostokąt: ma x, y, szerokość i wysokość.

Jeśli dwa prostokąty, np. bohater i wróg, przecinają się, mamy kolizję. Co powinno się wtedy wydarzyć, zależy od zasad gry. Aby zaimplementować wykrywanie kolizji, potrzebujesz:

  1. Sposobu na uzyskanie reprezentacji prostokąta dla obiektu w grze, coś takiego:

    rectFromGameObject() {
      return {
        top: this.y,
        left: this.x,
        bottom: this.y + this.height,
        right: this.x + this.width
      }
    }
    
  2. Funkcji porównującej, która może wyglądać tak:

    function intersectRect(r1, r2) {
      return !(r2.left > r1.right ||
        r2.right < r1.left ||
        r2.top > r1.bottom ||
        r2.bottom < r1.top);
    }
    

Jak niszczyć obiekty

Aby niszczyć obiekty w grze, musisz poinformować grę, że nie powinna już rysować tego elementu w pętli gry, która uruchamia się w określonych odstępach czasu. Można to zrobić, oznaczając obiekt w grze jako martwy, gdy coś się wydarzy, na przykład:

// collision happened
enemy.dead = true

Następnie możesz usunąć martwe obiekty przed ponownym rysowaniem ekranu, na przykład:

gameObjects = gameObject.filter(go => !go.dead);

Jak wystrzelić laser

Wystrzelenie lasera oznacza reakcję na zdarzenie klawisza i utworzenie obiektu, który porusza się w określonym kierunku. Musimy zatem wykonać następujące kroki:

  1. Utwórz obiekt lasera: zaczynając od górnej części statku bohatera, który po utworzeniu zaczyna poruszać się w górę w kierunku górnej części ekranu.
  2. Podłącz kod do zdarzenia klawisza: musimy wybrać klawisz na klawiaturze, który będzie reprezentował strzał lasera przez gracza.
  3. Utwórz obiekt gry, który wygląda jak laser, gdy klawisz zostanie naciśnięty.

Czas odnowienia lasera

Laser musi być wystrzeliwany za każdym razem, gdy naciśniesz klawisz, na przykład spację. Aby zapobiec tworzeniu zbyt wielu laserów w krótkim czasie, musimy to naprawić. Rozwiązaniem jest zaimplementowanie tzw. czasu odnowienia (cooldown), czyli timera, który zapewnia, że laser może być wystrzeliwany tylko co jakiś czas. Możesz to zaimplementować w następujący sposób:

class Cooldown {
  constructor(time) {
    this.cool = false;
    setTimeout(() => {
      this.cool = true;
    }, time)
  }
}

class Weapon {
  constructor {
  }
  fire() {
    if (!this.cooldown || this.cooldown.cool) {
      // produce a laser
      this.cooldown = new Cooldown(500);
    } else {
      // do nothing - it hasn't cooled down yet.
    }
  }
}

Przypomnij sobie lekcję 1 z serii o grze kosmicznej, aby przypomnieć sobie, czym jest cooldown.

Co zbudować

Weź istniejący kod (który powinieneś już uporządkować i zrefaktoryzować) z poprzedniej lekcji i rozbuduj go. Możesz zacząć od kodu z części II lub użyć kodu z Części III - starter.

wskazówka: laser, z którym będziesz pracować, znajduje się już w Twoim folderze z zasobami i jest odwoływany w Twoim kodzie

  • Dodaj wykrywanie kolizji, gdy laser zderzy się z czymś, powinny obowiązywać następujące zasady:
    1. Laser trafia wroga: wróg ginie, jeśli zostanie trafiony laserem
    2. Laser trafia górną część ekranu: laser zostaje zniszczony, jeśli uderzy w górną część ekranu
    3. Kolizja wroga i bohatera: wróg i bohater zostają zniszczeni, jeśli się zderzą
    4. Wróg trafia dolną część ekranu: wróg i bohater zostają zniszczeni, jeśli wróg dotrze do dolnej części ekranu

Zalecane kroki

Zlokalizuj pliki, które zostały dla Ciebie utworzone w podfolderze your-work. Powinny zawierać:

-| assets
  -| enemyShip.png
  -| player.png
  -| laserRed.png
-| index.html
-| app.js
-| package.json

Rozpocznij projekt w folderze your_work, wpisując:

cd your-work
npm start

Powyższe uruchomi serwer HTTP pod adresem http://localhost:5000. Otwórz przeglądarkę i wpisz ten adres, obecnie powinien wyświetlać bohatera i wszystkich wrogów, ale nic się jeszcze nie porusza :).

Dodaj kod

  1. Utwórz reprezentację prostokąta dla obiektu gry, aby obsłużyć kolizje. Poniższy kod pozwala uzyskać reprezentację prostokąta dla GameObject. Edytuj klasę GameObject, aby ją rozszerzyć:

    rectFromGameObject() {
        return {
          top: this.y,
          left: this.x,
          bottom: this.y + this.height,
          right: this.x + this.width,
        };
      }
    
  2. Dodaj kod sprawdzający kolizje. Będzie to nowa funkcja testująca, czy dwa prostokąty się przecinają:

    function intersectRect(r1, r2) {
      return !(
        r2.left > r1.right ||
        r2.right < r1.left ||
        r2.top > r1.bottom ||
        r2.bottom < r1.top
      );
    }
    
  3. Dodaj możliwość wystrzeliwania lasera

    1. Dodaj komunikat o zdarzeniu klawisza. Klawisz spacja powinien tworzyć laser tuż nad statkiem bohatera. Dodaj trzy stałe w obiekcie Messages:

       KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
       COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
       COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
      
    2. Obsłuż klawisz spacji. Edytuj funkcję window.addEventListener dla zdarzenia keyup, aby obsługiwała spację:

        } else if(evt.keyCode === 32) {
          eventEmitter.emit(Messages.KEY_EVENT_SPACE);
        }
      
    3. Dodaj nasłuchiwacze. Edytuj funkcję initGame(), aby upewnić się, że bohater może strzelać, gdy naciśnięty zostanie klawisz spacji:

      eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
       if (hero.canFire()) {
         hero.fire();
       }
      

      oraz dodaj nową funkcję eventEmitter.on(), aby zapewnić odpowiednie zachowanie, gdy wróg zderzy się z laserem:

      eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
        first.dead = true;
        second.dead = true;
      })
      
    4. Porusz obiektem, Upewnij się, że laser stopniowo przesuwa się w górę ekranu. Utworzysz nową klasę Laser, która rozszerza GameObject, jak wcześniej:

        class Laser extends GameObject {
        constructor(x, y) {
          super(x,y);
          (this.width = 9), (this.height = 33);
          this.type = 'Laser';
          this.img = laserImg;
          let id = setInterval(() => {
            if (this.y > 0) {
              this.y -= 15;
            } else {
              this.dead = true;
              clearInterval(id);
            }
          }, 100)
        }
      }
      
    5. Obsłuż kolizje, Zaimplementuj zasady kolizji dla lasera. Dodaj funkcję updateGameObjects(), która testuje kolidujące obiekty:

      function updateGameObjects() {
        const enemies = gameObjects.filter(go => go.type === 'Enemy');
        const lasers = gameObjects.filter((go) => go.type === "Laser");
      // laser hit something
        lasers.forEach((l) => {
          enemies.forEach((m) => {
            if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) {
            eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
              first: l,
              second: m,
            });
          }
         });
      });
      
        gameObjects = gameObjects.filter(go => !go.dead);
      }  
      

      Upewnij się, że dodałeś updateGameObjects() do swojej pętli gry w window.onload.

    6. Zaimplementuj cooldown dla lasera, aby mógł być wystrzeliwany tylko co jakiś czas.

      Na koniec edytuj klasę Hero, aby mogła obsługiwać cooldown:

      class Hero extends GameObject {
       constructor(x, y) {
         super(x, y);
         (this.width = 99), (this.height = 75);
         this.type = "Hero";
         this.speed = { x: 0, y: 0 };
         this.cooldown = 0;
       }
       fire() {
         gameObjects.push(new Laser(this.x + 45, this.y - 10));
         this.cooldown = 500;
      
         let id = setInterval(() => {
           if (this.cooldown > 0) {
             this.cooldown -= 100;
           } else {
             clearInterval(id);
           }
         }, 200);
       }
       canFire() {
         return this.cooldown === 0;
       }
      }
      

Na tym etapie Twoja gra ma już pewną funkcjonalność! Możesz poruszać się za pomocą klawiszy strzałek, strzelać laserem za pomocą spacji, a wrogowie znikają, gdy ich trafisz. Brawo!


🚀 Wyzwanie

Dodaj eksplozję! Sprawdź zasoby gry w repozytorium Space Art i spróbuj dodać eksplozję, gdy laser trafi w obcego.

Quiz po wykładzie

Quiz po wykładzie

Przegląd i samodzielna nauka

Eksperymentuj z interwałami w swojej grze. Co się stanie, gdy je zmienisz? Przeczytaj więcej o zdarzeniach czasowych w JavaScript.

Zadanie

Zbadaj kolizje


Zastrzeżenie:
Ten dokument został przetłumaczony za pomocą usługi tłumaczenia AI Co-op Translator. Chociaż dokładamy wszelkich starań, aby tłumaczenie było precyzyjne, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego rodzimym języku powinien być uznawany za wiarygodne źródło. W przypadku informacji o kluczowym znaczeniu zaleca się skorzystanie z profesjonalnego tłumaczenia przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.