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/lt/6-space-game/4-collision-detection/README.md

11 KiB

Sukurkite kosminį žaidimą, 4 dalis: pridėkime lazerį ir aptikime susidūrimus

Prieš paskaitą: testas

Prieš paskaitą: testas

Šioje pamokoje išmoksite šaudyti lazeriais naudodami JavaScript! Į žaidimą pridėsime du dalykus:

  • Lazerį: šis lazeris bus iššautas iš herojaus laivo ir judės vertikaliai aukštyn
  • Susidūrimų aptikimą, kaip dalį šaudymo funkcionalumo, taip pat pridėsime keletą žaidimo taisyklių:
    • Lazeris pataiko į priešą: priešas miršta, jei jį pataiko lazeris
    • Lazeris pataiko į ekrano viršų: lazeris sunaikinamas, jei pataiko į ekrano viršutinę dalį
    • Priešo ir herojaus susidūrimas: priešas ir herojus sunaikinami, jei susiduria vienas su kitu
    • Priešas pasiekia ekrano apačią: priešas ir herojus sunaikinami, jei priešas pasiekia ekrano apačią

Trumpai tariant, jūs herojus turite pataikyti į visus priešus lazeriu, kol jie nepasiekia ekrano apačios.

Atlikite nedidelį tyrimą apie pirmąjį kada nors sukurtą kompiuterinį žaidimą. Kokios buvo jo funkcijos?

Būkime herojiški kartu!

Susidūrimų aptikimas

Kaip aptikti susidūrimus? Turime galvoti apie žaidimo objektus kaip apie stačiakampius, kurie juda. Kodėl? Nes vaizdas, naudojamas žaidimo objektui piešti, yra stačiakampis: jis turi x, y, plotį ir aukštį.

Jei du stačiakampiai, t. y. herojus ir priešas, persidengia, įvyksta susidūrimas. Kas turėtų nutikti, priklauso nuo žaidimo taisyklių. Norint įgyvendinti susidūrimų aptikimą, reikia:

  1. Būdo gauti stačiakampio reprezentaciją žaidimo objektui, kažką panašaus į tai:

    rectFromGameObject() {
      return {
        top: this.y,
        left: this.x,
        bottom: this.y + this.height,
        right: this.x + this.width
      }
    }
    
  2. Palyginimo funkcijos, kuri gali atrodyti taip:

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

Kaip sunaikinti objektus

Norint sunaikinti objektus žaidime, reikia pranešti žaidimui, kad jis nebeturėtų piešti šio objekto žaidimo cikle, kuris vyksta tam tikru intervalu. Vienas būdas tai padaryti pažymėti žaidimo objektą kaip mirusį, kai kažkas nutinka, pavyzdžiui:

// collision happened
enemy.dead = true

Tada galite pašalinti mirusius objektus prieš atnaujindami ekraną, pavyzdžiui:

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

Kaip iššauti lazerį

Lazerio iššovimas reiškia reakciją į klavišo paspaudimą ir objekto sukūrimą, kuris juda tam tikra kryptimi. Todėl reikia atlikti šiuos veiksmus:

  1. Sukurti lazerio objektą: iš herojaus laivo viršaus, kuris sukūrimo metu pradeda judėti aukštyn link ekrano viršaus.
  2. Priskirti kodą klavišo įvykiui: reikia pasirinkti klavišą klaviatūroje, kuris reprezentuos žaidėjo lazerio šūvį.
  3. Sukurti žaidimo objektą, kuris atrodo kaip lazeris, kai klavišas paspaudžiamas.

Lazerio šaudymo pertrauka

Lazeris turi būti iššaunamas kiekvieną kartą, kai paspaudžiamas klavišas, pavyzdžiui, space. Kad žaidimas nesukurtų per daug lazerių per trumpą laiką, reikia tai išspręsti. Sprendimas įgyvendinti vadinamąją pertrauką, laikmatį, kuris užtikrina, kad lazeris gali būti iššaunamas tik tam tikru dažniu. Tai galima įgyvendinti taip:

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.
    }
  }
}

Peržiūrėkite 1 pamoką kosminio žaidimo serijoje, kad prisimintumėte apie pertraukas.

Ką sukurti

Turėsite paimti esamą kodą (kurį turėjote išvalyti ir pertvarkyti) iš ankstesnės pamokos ir jį išplėsti. Galite pradėti nuo II dalies kodo arba naudoti kodą iš III dalies pradinio projekto.

patarimas: lazeris, su kuriuo dirbsite, jau yra jūsų resursų aplanke ir yra nurodytas jūsų kode

  • Pridėkite susidūrimų aptikimą, kai lazeris susiduria su kažkuo, turėtų būti taikomos šios taisyklės:
    1. Lazeris pataiko į priešą: priešas miršta, jei jį pataiko lazeris
    2. Lazeris pataiko į ekrano viršų: lazeris sunaikinamas, jei pataiko į ekrano viršutinę dalį
    3. Priešo ir herojaus susidūrimas: priešas ir herojus sunaikinami, jei susiduria vienas su kitu
    4. Priešas pasiekia ekrano apačią: priešas ir herojus sunaikinami, jei priešas pasiekia ekrano apačią

Rekomenduojami žingsniai

Raskite failus, kurie buvo sukurti jums aplanke your-work. Jame turėtų būti:

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

Pradėkite savo projektą aplanke your_work įvesdami:

cd your-work
npm start

Tai paleis HTTP serverį adresu http://localhost:5000. Atidarykite naršyklę ir įveskite šį adresą, šiuo metu turėtų būti matomas herojus ir visi priešai, tačiau niekas dar nejuda :).

Pridėkite kodą

  1. Sukurkite stačiakampio reprezentaciją savo žaidimo objektui, kad galėtumėte aptikti susidūrimus Žemiau pateiktas kodas leidžia gauti stačiakampio reprezentaciją GameObject. Redaguokite savo GameObject klasę, kad ją išplėstumėte:

    rectFromGameObject() {
        return {
          top: this.y,
          left: this.x,
          bottom: this.y + this.height,
          right: this.x + this.width,
        };
      }
    
  2. Pridėkite kodą, kuris tikrina susidūrimus Tai bus nauja funkcija, kuri tikrina, ar du stačiakampiai persidengia:

    function intersectRect(r1, r2) {
      return !(
        r2.left > r1.right ||
        r2.right < r1.left ||
        r2.top > r1.bottom ||
        r2.bottom < r1.top
      );
    }
    
  3. Pridėkite lazerio šaudymo funkcionalumą

    1. Pridėkite klavišo įvykio pranešimą. Space klavišas turėtų sukurti lazerį tiesiai virš herojaus laivo. Pridėkite tris konstantas į Messages objektą:

       KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
       COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
       COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
      
    2. Apdorokite space klavišą. Redaguokite window.addEventListener funkciją, kad apdorotumėte space klavišą:

        } else if(evt.keyCode === 32) {
          eventEmitter.emit(Messages.KEY_EVENT_SPACE);
        }
      
    3. Pridėkite klausytojus. Redaguokite initGame() funkciją, kad užtikrintumėte, jog herojus gali šaudyti paspaudus space klavišą:

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

      ir pridėkite naują eventEmitter.on() funkciją, kad užtikrintumėte elgesį, kai priešas susiduria su lazeriu:

      eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
        first.dead = true;
        second.dead = true;
      })
      
    4. Judinkite objektą, Užtikrinkite, kad lazeris palaipsniui judėtų ekrano viršaus link. Sukurkite naują Laser klasę, kuri išplečia GameObject, kaip darėte anksčiau:

        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. Apdorokite susidūrimus, Įgyvendinkite lazerio susidūrimo taisykles. Pridėkite updateGameObjects() funkciją, kuri tikrina susidūrimus tarp objektų:

      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);
      }  
      

      Įsitikinkite, kad pridėjote updateGameObjects() į savo žaidimo ciklą window.onload.

    6. Įgyvendinkite pertrauką lazeriui, kad jis galėtų būti iššaunamas tik tam tikru dažniu.

      Galiausiai, redaguokite Hero klasę, kad ji galėtų turėti pertrauką:

      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;
       }
      }
      

Šiuo metu jūsų žaidimas turi tam tikrą funkcionalumą! Galite naviguoti naudodami rodyklių klavišus, šaudyti lazeriu paspaudę space klavišą, o priešai dingsta, kai juos pataikote. Puikiai padirbėta!


🚀 Iššūkis

Pridėkite sprogimą! Pažvelkite į žaidimo resursus Space Art repo ir pabandykite pridėti sprogimą, kai lazeris pataiko į ateivį.

Po paskaitos: testas

Po paskaitos: testas

Apžvalga ir savarankiškas mokymasis

Eksperimentuokite su intervalais savo žaidime. Kas nutinka, kai juos pakeičiate? Skaitykite daugiau apie JavaScript laiko įvykius.

Užduotis

Tyrinėkite susidūrimus


Atsakomybės apribojimas:
Šis dokumentas buvo išverstas naudojant AI vertimo paslaugą Co-op Translator. Nors stengiamės užtikrinti tikslumą, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Kritinei informacijai rekomenduojama naudoti profesionalų žmogaus vertimą. Mes neprisiimame atsakomybės už nesusipratimus ar klaidingus interpretavimus, atsiradusius dėl šio vertimo naudojimo.