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/da/6-space-game/4-collision-detection
softchris a7d39944e9
🌐 Update translations via Co-op Translator
1 month ago
..
solution 🌐 Update translations via Co-op Translator 3 months ago
your-work 🌐 Update translations via Co-op Translator 3 months ago
README.md 🌐 Update translations via Co-op Translator 1 month ago
assignment.md 🌐 Update translations via Co-op Translator 1 month ago

README.md

Byg et rumspil del 4: Tilføj en laser og registrer kollisioner

Quiz før lektionen

Quiz før lektionen

Tænk på øjeblikket i Star Wars, hvor Lukes proton-torpedoer ramte Dødsstjernens udstødningsport. Den præcise kollision ændrede galaksens skæbne! I spil fungerer kollisionsdetektion på samme måde - det afgør, hvornår objekter interagerer, og hvad der sker derefter.

I denne lektion vil du tilføje laser-våben til dit rumspil og implementere kollisionsdetektion. Ligesom NASAs missionplanlæggere beregner rumfartøjers baner for at undgå affald, vil du lære at registrere, når spilobjekter krydser hinanden. Vi bryder det ned i håndterbare trin, der bygger på hinanden.

Når du er færdig, vil du have et fungerende kampsystem, hvor lasere ødelægger fjender, og kollisioner udløser spilbegivenheder. De samme kollisionsprincipper bruges i alt fra fysiksimuleringer til interaktive webgrænseflader.

Lav lidt research om det allerførste computerspil, der nogensinde er skrevet. Hvad var dets funktionalitet?

Kollisionsdetektion

Kollisionsdetektion fungerer som nærhedssensorerne på Apollo-månemodulet - det kontrollerer konstant afstande og udløser advarsler, når objekter kommer for tæt på. I spil afgør dette system, hvornår objekter interagerer, og hvad der skal ske derefter.

Den tilgang, vi vil bruge, behandler hvert spilobjekt som en rektangel, ligesom lufttrafikstyringssystemer bruger forenklede geometriske former til at spore fly. Denne rektangelmetode kan virke simpel, men den er beregningsmæssigt effektiv og fungerer godt for de fleste spilsituationer.

Rektangelrepræsentation

Hvert spilobjekt har brug for koordinatgrænser, ligesom Mars Pathfinder-roveren kortlagde sin placering på Mars' overflade. Sådan definerer vi disse grænsekoordinater:

rectFromGameObject() {
  return {
    top: this.y,
    left: this.x,
    bottom: this.y + this.height,
    right: this.x + this.width
  }
}

Lad os bryde det ned:

  • Øverste kant: Det er bare, hvor dit objekt starter lodret (dets y-position)
  • Venstre kant: Hvor det starter vandret (dets x-position)
  • Nederste kant: Tilføj højden til y-positionen - nu ved du, hvor det slutter!
  • Højre kant: Tilføj bredden til x-positionen - og du har den komplette grænse.

Intersektionsalgoritme

At registrere rektangelintersektioner bruger logik, der ligner den måde, Hubble-rumteleskopet afgør, om himmelobjekter overlapper i dets synsfelt. Algoritmen kontrollerer for adskillelse:

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

Adskillelsestesten fungerer som radarsystemer:

  • Er rektangel 2 helt til højre for rektangel 1?
  • Er rektangel 2 helt til venstre for rektangel 1?
  • Er rektangel 2 helt under rektangel 1?
  • Er rektangel 2 helt over rektangel 1?

Hvis ingen af disse betingelser er sande, må rektanglerne overlappe. Denne tilgang minder om, hvordan radaroperatører afgør, om to fly er på sikre afstande.

Håndtering af objektlivscyklusser

Når en laser rammer en fjende, skal begge objekter fjernes fra spillet. Men at slette objekter midt i en løkke kan forårsage nedbrud - en lektie, der blev lært på den hårde måde i tidlige computersystemer som Apollo Guidance Computer. I stedet bruger vi en "mark for deletion"-tilgang, der sikkert fjerner objekter mellem frames.

Sådan markerer vi noget for fjernelse:

// Mark object for removal
enemy.dead = true;

Hvorfor denne tilgang fungerer:

  • Vi markerer objektet som "dødt", men sletter det ikke med det samme.
  • Dette lader den aktuelle spilframe afslutte sikkert.
  • Ingen nedbrud fra forsøg på at bruge noget, der allerede er væk!

Derefter filtrerer vi markerede objekter ud før næste render-cyklus:

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

Hvad denne filtrering gør:

  • Skaber en ny liste med kun de "levende" objekter.
  • Smider alt ud, der er markeret som dødt.
  • Holder dit spil kørende glat.
  • Forhindrer hukommelsesopblæsning fra akkumulerede ødelagte objekter.

Implementering af lasermekanik

Laserprojektiler i spil fungerer på samme princip som fotontorpedoer i Star Trek - de er diskrete objekter, der bevæger sig i lige linjer, indtil de rammer noget. Hver gang du trykker på mellemrumstasten, skabes et nyt laserobjekt, der bevæger sig hen over skærmen.

For at få dette til at fungere skal vi koordinere nogle forskellige dele:

Nøglekomponenter at implementere:

  • Opret laserobjekter, der spawner fra heltenes position.
  • Håndter tastaturinput for at udløse laseroprettelse.
  • Administrer lasernes bevægelse og livscyklus.
  • Implementer visuel repræsentation for laserprojektilerne.

Implementering af skydehastighedskontrol

Ubegrænsede skydehastigheder ville overbelaste spilmotoren og gøre gameplayet for nemt. Rigtige våbensystemer står over for lignende begrænsninger - selv USS Enterprises phasere havde brug for tid til at genoplade mellem skud.

Vi implementerer et cooldown-system, der forhindrer hurtig affyring, mens det opretholder responsive kontroller:

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

class Weapon {
  constructor() {
    this.cooldown = null;
  }
  
  fire() {
    if (!this.cooldown || this.cooldown.cool) {
      // Create laser projectile
      this.cooldown = new Cooldown(500);
    } else {
      // Weapon is still cooling down
    }
  }
}

Hvordan cooldown fungerer:

  • Når det oprettes, starter våbnet "varmt" (kan ikke skyde endnu).
  • Efter timeout-perioden bliver det "køligt" (klar til at skyde).
  • Før affyring kontrollerer vi: "Er våbnet køligt?"
  • Dette forhindrer spamklik, mens det holder kontrollerne responsive.

Henvis til lektion 1 i rumspilserien for at minde dig selv om cooldowns.

Opbygning af kollisionssystemet

Du vil udvide din eksisterende rumspilkode for at skabe et kollisionsdetektionssystem. Ligesom den internationale rumstations automatiske kollisionsundgåelsessystem vil dit spil kontinuerligt overvåge objektpositioner og reagere på intersektioner.

Start med koden fra din tidligere lektion, og tilføj kollisionsdetektion med specifikke regler, der styrer objektinteraktioner.

💡 Pro Tip: Laser-sprite er allerede inkluderet i din assets-mappe og refereret i din kode, klar til implementering.

Kollisionsregler at implementere

Spilmekanik at tilføje:

  1. Laser rammer fjende: Fjendeobjektet ødelægges, når det rammes af et laserprojektil.
  2. Laser rammer skærmgrænse: Laser fjernes, når den når skærmens øverste kant.
  3. Fjende og helt kollision: Begge objekter ødelægges, når de krydser hinanden.
  4. Fjende når bunden: Spillet er tabt, når fjender når skærmens bund.

Opsætning af dit udviklingsmiljø

Godt nyt - vi har allerede sat det meste af grundlaget op for dig! Alle dine spilressourcer og grundlæggende struktur venter i undermappen your-work, klar til at du kan tilføje de seje kollisionsfunktioner.

Projektstruktur

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

Forstå filstrukturen:

  • Indeholder alle sprite-billeder, der er nødvendige for spilobjekterne.
  • Inkluderer hoved-HTML-dokumentet og JavaScript-applikationsfilen.
  • Tilbyder pakke-konfiguration til lokal udviklingsserver.

Start udviklingsserveren

Naviger til din projektmappe og start den lokale server:

cd your-work
npm start

Denne kommandosekvens:

  • Skifter mappe til din arbejdende projektmappe.
  • Starter en lokal HTTP-server på http://localhost:5000.
  • Serverer dine spilfiler til test og udvikling.
  • Muliggør live-udvikling med automatisk genindlæsning.

Åbn din browser og naviger til http://localhost:5000 for at se din aktuelle spiltilstand med helten og fjenderne gengivet på skærmen.

Trin-for-trin implementering

Ligesom den systematiske tilgang NASA brugte til at programmere Voyager-rumfartøjet, vil vi implementere kollisionsdetektion metodisk og bygge hver komponent trin for trin.

1. Tilføj rektangelkollisionsgrænser

Først skal vi lære vores spilobjekter at beskrive deres grænser. Tilføj denne metode til din GameObject-klasse:

rectFromGameObject() {
    return {
      top: this.y,
      left: this.x,
      bottom: this.y + this.height,
      right: this.x + this.width,
    };
  }

Denne metode opnår:

  • Opretter et rektangelobjekt med præcise grænsekoordinater.
  • Beregner nederste og højre kanter ved hjælp af position plus dimensioner.
  • Returnerer et objekt klar til kollisionsdetektionsalgoritmer.
  • Tilbyder en standardiseret grænseflade for alle spilobjekter.

2. Implementer intersektionsdetektion

Nu skal vi skabe vores kollisionsdetektiv - en funktion, der kan afgøre, hvornår to rektangler overlapper:

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

Denne algoritme fungerer ved:

  • Tester fire adskillelsesbetingelser mellem rektangler.
  • Returnerer false, hvis nogen adskillelsesbetingelse er sand.
  • Indikerer kollision, når ingen adskillelse eksisterer.
  • Bruger negationslogik for effektiv intersektionstest.

3. Implementer laserskyde-system

Nu bliver det spændende! Lad os opsætte laserskyde-systemet.

Meddelelseskonstanter

Først skal vi definere nogle meddelelsestyper, så forskellige dele af vores spil kan kommunikere med hinanden:

KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",

Disse konstanter tilbyder:

  • Standardiserer begivenhedsnavne i hele applikationen.
  • Muliggør konsistent kommunikation mellem spilsystemer.
  • Forhindrer tastefejl i begivenhedshåndtering.
Håndtering af tastaturinput

Tilføj mellemrumstast-detektion til din tastaturbegivenhedslytter:

} else if(evt.keyCode === 32) {
  eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}

Denne inputhåndtering:

  • Registrerer mellemrumstasttryk ved hjælp af keyCode 32.
  • Udsender en standardiseret begivenhedsmeddelelse.
  • Muliggør løsrevet skyde-logik.
Opsætning af begivenhedslytter

Registrer skydeadfærd i din initGame()-funktion:

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

Denne begivenhedslytter:

  • Reagerer på mellemrumstast-begivenheder.
  • Kontrollerer skydecooldown-status.
  • Udløser laseroprettelse, når det er tilladt.

Tilføj kollisionshåndtering for laser-fjende-interaktioner:

eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
  first.dead = true;
  second.dead = true;
});

Denne kollisionshåndtering:

  • Modtager kollisionsbegivenhedsdata med begge objekter.
  • Markerer begge objekter til fjernelse.
  • Sikrer korrekt oprydning efter kollision.

4. Opret Laser-klassen

Implementer et laserprojektil, der bevæger sig opad og administrerer sin egen livscyklus:

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

Denne klasseimplementering:

  • Udvider GameObject for at arve grundlæggende funktionalitet.
  • Indstiller passende dimensioner for laser-sprite.
  • Skaber automatisk opadgående bevægelse ved hjælp af setInterval().
  • Håndterer selvdestruktion, når den når skærmens top.
  • Administrerer sin egen animationstiming og oprydning.

5. Implementer kollisionsdetektionssystem

Opret en omfattende kollisionsdetektionsfunktion:

function updateGameObjects() {
  const enemies = gameObjects.filter(go => go.type === 'Enemy');
  const lasers = gameObjects.filter(go => go.type === "Laser");
  
  // Test laser-enemy collisions
  lasers.forEach((laser) => {
    enemies.forEach((enemy) => {
      if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
        eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
          first: laser,
          second: enemy,
        });
      }
    });
  });

  // Remove destroyed objects
  gameObjects = gameObjects.filter(go => !go.dead);
}

Dette kollisionssystem:

  • Filtrerer spilobjekter efter type for effektiv testning.
  • Tester hver laser mod hver fjende for intersektioner.
  • Udsender kollisionsbegivenheder, når intersektioner registreres.
  • Rydder ødelagte objekter op efter kollisionsbehandling.

⚠️ Vigtigt: Tilføj updateGameObjects() til din hovedspilsløkke i window.onload for at aktivere kollisionsdetektion.

6. Tilføj cooldown-system til Hero-klassen

Forbedr Hero-klassen med skyde-mekanik og hastighedsbegrænsning:

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

Forstå den forbedrede Hero-klasse:

  • Initialiserer cooldown-timeren til nul (klar til at skyde).
  • Opretter laserobjekter placeret over helteskibet.
  • Indstiller cooldown-periode for at forhindre hurtig affyring.
  • Reducerer cooldown-timeren ved hjælp af intervalbaserede opdateringer.
  • Tilbyder statuskontrol for affyring via canFire()-metoden.

Test din implementering

Dit rumspil har nu komplette kollisionsdetektions- og kampmekanikker. 🚀 Test disse nye funktioner:

  • Naviger med piletasterne for at verificere bevægelseskontroller.
  • Affyr lasere med mellemrumstasten - bemærk, hvordan cooldown forhindrer spamklik.
  • Observer kollisioner, når lasere rammer fjender, og udløser fjernelse.
  • Bekræft oprydning, da ødelagte objekter forsvinder fra spillet.

Du har med succes implementeret et kollisionsdetektionssystem ved hjælp af de samme matematiske principper, der guider rumfartsnavigation og robotteknologi.

GitHub Copilot Agent Challenge 🚀

Brug Agent-tilstand til at fuldføre følgende udfordring:

Beskrivelse: Forbedr kollisionsdetektionssystemet ved at implementere power-ups, der spawner tilfældigt og giver midlertidige evner, når de samles op af helteskibet.

Opgave: Opret en PowerUp-klasse, der udvider GameObject, og implementer kollisionsdetektion mellem helten og power-ups. Tilføj mindst to typer power-ups: en, der øger skydehastigheden (reducerer cooldown), og en anden, der skaber et midlertidigt skjold. Inkluder spawn-logik, der skaber power-ups med tilfældige intervaller og positioner.


🚀 Udfordring

Tilføj en eksplosion! Tag et kig på spilressourcerne i Space Art repo og prøv at tilføje en eksplosion, når laseren rammer en alien.

Quiz efter lektionen

Quiz efter lektionen

Gennemgang & Selvstudie

Eksperimenter med intervallerne i dit spil indtil videre. Hvad sker der, når du ændrer dem? Læs mere om JavaScript timing events.

Opgave

Udforsk kollisioner


Ansvarsfraskrivelse:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten Co-op Translator. Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.