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/nl/6-space-game/1-introduction/README.md

9.5 KiB

Bouw een Ruimtespel Deel 1: Introductie

video

Pre-Lecture Quiz

Pre-lecture quiz

Overerving en Compositie in game-ontwikkeling

In eerdere lessen hoefde je je niet veel zorgen te maken over de ontwerpstructuur van de apps die je bouwde, omdat de projecten erg klein van omvang waren. Maar naarmate je applicaties groter worden, worden architecturale beslissingen belangrijker. Er zijn twee belangrijke benaderingen om grotere applicaties in JavaScript te maken: compositie of overerving. Beide hebben voor- en nadelen, maar laten we ze uitleggen in de context van een spel.

Een van de beroemdste programmeerboeken ooit geschreven gaat over ontwerppatronen.

In een spel heb je game objects, objecten die op een scherm bestaan. Dit betekent dat ze een locatie hebben in een cartesiaans coördinatensysteem, gekenmerkt door een x- en y-coördinaat. Tijdens het ontwikkelen van een spel zul je merken dat al je game-objecten een standaard eigenschap hebben, gemeenschappelijk voor elk spel dat je maakt, namelijk elementen die:

  • locatie-gebaseerd zijn. De meeste, zo niet alle, game-elementen zijn locatie-gebaseerd. Dit betekent dat ze een locatie hebben, een x en y.
  • beweegbaar zijn. Dit zijn objecten die naar een nieuwe locatie kunnen bewegen. Dit is meestal een held, een monster of een NPC (een niet-speler personage), maar bijvoorbeeld geen statisch object zoals een boom.
  • zelfvernietigend zijn. Deze objecten bestaan slechts voor een bepaalde tijd voordat ze zichzelf markeren voor verwijdering. Meestal wordt dit weergegeven door een dead of destroyed boolean die aangeeft aan de game-engine dat dit object niet langer gerenderd moet worden.
  • cool-down hebben. 'Cool-down' is een typische eigenschap van kortlevende objecten. Een typisch voorbeeld is een stuk tekst of een grafisch effect zoals een explosie die slechts enkele milliseconden zichtbaar moet zijn.

Denk aan een spel zoals Pac-Man. Kun je de vier hierboven genoemde objecttypen in dit spel identificeren?

Gedrag uitdrukken

Alles wat we hierboven beschreven zijn gedragingen die game-objecten kunnen hebben. Maar hoe coderen we die? We kunnen dit gedrag uitdrukken als methoden die geassocieerd zijn met klassen of objecten.

Klassen

Het idee is om klassen in combinatie met overerving te gebruiken om een bepaald gedrag aan een klasse toe te voegen.

Overerving is een belangrijk concept om te begrijpen. Lees meer in MDN's artikel over overerving.

Uitgedrukt in code kan een game-object er typisch zo uitzien:


//set up the class GameObject
class GameObject {
  constructor(x, y, type) {
    this.x = x;
    this.y = y;
    this.type = type;
  }
}

//this class will extend the GameObject's inherent class properties
class Movable extends GameObject {
  constructor(x,y, type) {
    super(x,y, type)
  }

//this movable object can be moved on the screen
  moveTo(x, y) {
    this.x = x;
    this.y = y;
  }
}

//this is a specific class that extends the Movable class, so it can take advantage of all the properties that it inherits
class Hero extends Movable {
  constructor(x,y) {
    super(x,y, 'Hero')
  }
}

//this class, on the other hand, only inherits the GameObject properties
class Tree extends GameObject {
  constructor(x,y) {
    super(x,y, 'Tree')
  }
}

//a hero can move...
const hero = new Hero();
hero.moveTo(5,5);

//but a tree cannot
const tree = new Tree();

Neem een paar minuten om een Pac-Man held (bijvoorbeeld Inky, Pinky of Blinky) opnieuw te bedenken en hoe deze in JavaScript geschreven zou worden.

Compositie

Een andere manier om object-overerving te behandelen is door gebruik te maken van Compositie. Objecten drukken hun gedrag dan als volgt uit:

//create a constant gameObject
const gameObject = {
  x: 0,
  y: 0,
  type: ''
};

//...and a constant movable
const movable = {
  moveTo(x, y) {
    this.x = x;
    this.y = y;
  }
}
//then the constant movableObject is composed of the gameObject and movable constants
const movableObject = {...gameObject, ...movable};

//then create a function to create a new Hero who inherits the movableObject properties
function createHero(x, y) {
  return {
    ...movableObject,
    x,
    y,
    type: 'Hero'
  }
}
//...and a static object that inherits only the gameObject properties
function createStatic(x, y, type) {
  return {
    ...gameObject
    x,
    y,
    type
  }
}
//create the hero and move it
const hero = createHero(10,10);
hero.moveTo(5,5);
//and create a static tree which only stands around
const tree = createStatic(0,0, 'Tree'); 

Welk patroon moet ik gebruiken?

Het is aan jou welk patroon je kiest. JavaScript ondersteunt beide paradigma's.

--

Een ander patroon dat vaak voorkomt in game-ontwikkeling richt zich op het omgaan met de gebruikerservaring en prestaties van het spel.

Pub/sub-patroon

Pub/Sub staat voor 'publish-subscribe'

Dit patroon richt zich op het idee dat de verschillende delen van je applicatie niets van elkaar hoeven te weten. Waarom is dat? Het maakt het veel gemakkelijker om in het algemeen te zien wat er gebeurt als verschillende delen gescheiden zijn. Het maakt het ook eenvoudiger om gedrag plotseling te veranderen als dat nodig is. Hoe bereiken we dit? Door enkele concepten vast te stellen:

  • bericht: Een bericht is meestal een tekststring vergezeld van een optionele payload (een stukje data dat verduidelijkt waar het bericht over gaat). Een typisch bericht in een spel kan zijn KEY_PRESSED_ENTER.
  • publisher: Dit element publiceert een bericht en stuurt het naar alle abonnees.
  • subscriber: Dit element luistert naar specifieke berichten en voert een taak uit als gevolg van het ontvangen van dit bericht, zoals het afvuren van een laser.

De implementatie is vrij klein van omvang, maar het is een zeer krachtig patroon. Hier is hoe het kan worden geïmplementeerd:

//set up an EventEmitter class that contains listeners
class EventEmitter {
  constructor() {
    this.listeners = {};
  }
//when a message is received, let the listener to handle its payload
  on(message, listener) {
    if (!this.listeners[message]) {
      this.listeners[message] = [];
    }
    this.listeners[message].push(listener);
  }
//when a message is sent, send it to a listener with some payload
  emit(message, payload = null) {
    if (this.listeners[message]) {
      this.listeners[message].forEach(l => l(message, payload))
    }
  }
}

Om de bovenstaande code te gebruiken, kunnen we een zeer kleine implementatie maken:

//set up a message structure
const Messages = {
  HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//invoke the eventEmitter you set up above
const eventEmitter = new EventEmitter();
//set up a hero
const hero = createHero(0,0);
//let the eventEmitter know to watch for messages pertaining to the hero moving left, and act on it
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
  hero.move(5,0);
});

//set up the window to listen for the keyup event, specifically if the left arrow is hit, emit a message to move the hero left
window.addEventListener('keyup', (evt) => {
  if (evt.key === 'ArrowLeft') {
    eventEmitter.emit(Messages.HERO_MOVE_LEFT)
  }
});

Hierboven verbinden we een toetsenbordgebeurtenis, ArrowLeft, en sturen het bericht HERO_MOVE_LEFT. We luisteren naar dat bericht en verplaatsen de hero als gevolg. De kracht van dit patroon is dat de event listener en de hero niets van elkaar weten. Je kunt de ArrowLeft opnieuw toewijzen aan de A-toets. Daarnaast zou het mogelijk zijn om iets totaal anders te doen op ArrowLeft door een paar aanpassingen te maken aan de on-functie van de eventEmitter:

eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
  hero.move(5,0);
});

Naarmate je spel groeit en complexer wordt, blijft dit patroon even eenvoudig en blijft je code overzichtelijk. Het wordt sterk aanbevolen om dit patroon te gebruiken.


🚀 Uitdaging

Denk na over hoe het pub-sub-patroon een spel kan verbeteren. Welke onderdelen zouden gebeurtenissen moeten uitzenden, en hoe zou het spel daarop moeten reageren? Dit is je kans om creatief te zijn en na te denken over een nieuw spel en hoe de onderdelen ervan zich zouden kunnen gedragen.

Post-Lecture Quiz

Post-lecture quiz

Review & Zelfstudie

Leer meer over Pub/Sub door er meer over te lezen.

Opdracht

Maak een mock-up van een spel


Disclaimer:
Dit document is vertaald met behulp van de AI-vertalingsservice Co-op Translator. Hoewel we streven naar nauwkeurigheid, willen we u erop wijzen dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor kritieke informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.