# Crie um Jogo Espacial Parte 1: Introdução ![video](../../../../6-space-game/images/pewpew.gif) ## Quiz Pré-Aula [Quiz pré-aula](https://ff-quizzes.netlify.app/web/quiz/29) ### Herança e Composição no desenvolvimento de jogos Em aulas anteriores, não havia muita necessidade de se preocupar com a arquitetura dos aplicativos que você criou, já que os projetos eram muito pequenos em escopo. No entanto, à medida que suas aplicações crescem em tamanho e complexidade, as decisões arquiteturais se tornam uma preocupação maior. Existem duas abordagens principais para criar aplicações maiores em JavaScript: *composição* ou *herança*. Ambas têm prós e contras, mas vamos explicá-las no contexto de um jogo. ✅ Um dos livros de programação mais famosos já escritos trata de [design patterns](https://en.wikipedia.org/wiki/Design_Patterns). Em um jogo, você tem `objetos do jogo`, que são objetos que existem na tela. Isso significa que eles têm uma localização em um sistema de coordenadas cartesianas, caracterizada por ter uma coordenada `x` e `y`. À medida que você desenvolve um jogo, perceberá que todos os seus objetos do jogo têm propriedades padrão, comuns a qualquer jogo que você criar, ou seja, elementos que são: - **baseados em localização**: A maioria, se não todos, os elementos do jogo são baseados em localização. Isso significa que eles têm uma localização, um `x` e um `y`. - **móveis**: São objetos que podem se mover para uma nova localização. Normalmente, isso inclui um herói, um monstro ou um NPC (personagem não jogável), mas não, por exemplo, um objeto estático como uma árvore. - **autodestrutíveis**: Esses objetos existem apenas por um período de tempo definido antes de serem marcados para exclusão. Geralmente, isso é representado por um booleano `dead` ou `destroyed` que sinaliza ao motor do jogo que esse objeto não deve mais ser renderizado. - **com tempo de espera**: 'Tempo de espera' é uma propriedade típica entre objetos de curta duração. Um exemplo típico é um pedaço de texto ou efeito gráfico, como uma explosão, que deve ser visto apenas por alguns milissegundos. ✅ Pense em um jogo como Pac-Man. Você consegue identificar os quatro tipos de objetos listados acima nesse jogo? ### Expressando comportamento Tudo o que descrevemos acima são comportamentos que os objetos do jogo podem ter. Então, como codificamos isso? Podemos expressar esse comportamento como métodos associados a classes ou objetos. **Classes** A ideia é usar `classes` em conjunto com `herança` para adicionar um determinado comportamento a uma classe. ✅ Herança é um conceito importante para entender. Saiba mais no [artigo da MDN sobre herança](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain). Expressado em código, um objeto do jogo pode ser algo assim: ```javascript //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(); ``` ✅ Reserve alguns minutos para imaginar como seria um herói do Pac-Man (Inky, Pinky ou Blinky, por exemplo) escrito em JavaScript. **Composição** Uma maneira diferente de lidar com herança de objetos é usando *Composição*. Nesse caso, os objetos expressam seu comportamento assim: ```javascript //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'); ``` **Qual padrão devo usar?** A escolha do padrão depende de você. O JavaScript suporta ambos os paradigmas. -- Outro padrão comum no desenvolvimento de jogos aborda o problema de gerenciar a experiência do usuário e o desempenho do jogo. ## Padrão Pub/Sub ✅ Pub/Sub significa 'publicar-assinar' Esse padrão aborda a ideia de que as partes distintas da sua aplicação não devem saber umas das outras. Por quê? Isso facilita muito a visualização geral do que está acontecendo se as várias partes estiverem separadas. Também facilita mudar o comportamento repentinamente, se necessário. Como fazemos isso? Estabelecendo alguns conceitos: - **mensagem**: Uma mensagem geralmente é uma string de texto acompanhada de uma carga opcional (um dado que esclarece sobre o que é a mensagem). Uma mensagem típica em um jogo pode ser `KEY_PRESSED_ENTER`. - **publicador**: Este elemento *publica* uma mensagem e a envia para todos os assinantes. - **assinante**: Este elemento *ouve* mensagens específicas e executa alguma tarefa como resultado de receber essa mensagem, como disparar um laser. A implementação é bem pequena, mas é um padrão muito poderoso. Veja como pode ser implementado: ```javascript //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)) } } } ``` Para usar o código acima, podemos criar uma implementação muito simples: ```javascript //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) } }); ``` No exemplo acima, conectamos um evento de teclado, `ArrowLeft`, e enviamos a mensagem `HERO_MOVE_LEFT`. Escutamos essa mensagem e movemos o `herói` como resultado. A força desse padrão é que o listener de eventos e o herói não sabem um do outro. Você pode remapear o `ArrowLeft` para a tecla `A`. Além disso, seria possível fazer algo completamente diferente no `ArrowLeft` fazendo algumas edições na função `on` do eventEmitter: ```javascript eventEmitter.on(Messages.HERO_MOVE_LEFT, () => { hero.move(5,0); }); ``` À medida que as coisas ficam mais complicadas com o crescimento do seu jogo, esse padrão mantém a mesma complexidade e seu código permanece limpo. É altamente recomendado adotar esse padrão. --- ## 🚀 Desafio Pense em como o padrão pub-sub pode melhorar um jogo. Quais partes deveriam emitir eventos e como o jogo deveria reagir a eles? Agora é sua chance de ser criativo, pensando em um novo jogo e como suas partes poderiam se comportar. ## Quiz Pós-Aula [Quiz pós-aula](https://ff-quizzes.netlify.app/web/quiz/30) ## Revisão e Autoestudo Saiba mais sobre Pub/Sub [lendo sobre o assunto](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon). ## Tarefa [Crie um protótipo de jogo](assignment.md) --- **Aviso Legal**: Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações equivocadas decorrentes do uso desta tradução.