Update Translations

This commit is contained in:
Lee Stott
2025-08-23 20:47:28 +01:00
committed by GitHub
parent eff26d962b
commit 4c689557b5
497 changed files with 0 additions and 50930 deletions

View File

@@ -1,216 +0,0 @@
# Construye un juego espacial Parte I: Introducción
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29)
### Herencia y composición en el desarrollo de juegos
En lecciones anteriores, no había mucha necesidad de preocuparse por la arquitectura de diseño de las aplicaciones que creó, ya que los proyectos tenían un alcance muy pequeño. Sin embargo, cuando sus aplicaciones crecen en tamaño y alcance, las decisiones de arquitectura se vuelven una preocupación mayor. Hay dos enfoques principales para crear aplicaciones más grandes en JavaScript: *composición* o *herencia*. Ambos tienen pros y contras, pero vamos a explicarlos desde el contexto de un juego.
✅ Uno de los libros de programación más famosos jamás escrito tiene que ver con [patrones de diseño](https://en.wikipedia.org/wiki/Design_Patterns).
En un juego tienes `game objects` que son objetos que existen en una pantalla. Esto significa que tienen una ubicación en un sistema de coordenadas cartesiano, caracterizado por tener una coordenada `x` e `y`. A medida que desarrolle un juego, notará que todos los objetos de su juego tienen una propiedad estándar, común para todos los juegos que crea, es decir, elementos que son:
- **location-based** (basado en la ubicación): La mayoría, si no todos, los elementos del juego se basan en la ubicación. Esto significa que tienen una ubicación, una `x` y una` y`.
- **movable** (movible): Estos son objetos que pueden moverse a una nueva ubicación. Suele ser un héroe, un monstruo o un NPC (un personaje no jugador), pero no, por ejemplo, un objeto estático como un árbol.
- **self-destructing** (autodestructivo): Estos objetos solo existen durante un período de tiempo determinado antes de que se configuren para su eliminación. Por lo general, esto está representado por un booleano `dead` o `destroyed` que indica al motor del juego que este objeto ya no debe procesarse.
- **cool-down** (enfriamiento): 'Cool-down' es una propiedad típica entre los objetos de corta duración. Un ejemplo típico es un fragmento de texto o efecto gráfico como una explosión que solo debería verse durante unos pocos milisegundos.
✅ Piense en un juego como Pac-Man. ¿Puedes identificar los cuatro tipos de objetos enumerados anteriormente en este juego?
### Expresando comportamiento
Todo lo que describimos anteriormente son comportamientos que pueden tener los objetos del juego. Entonces, ¿cómo los codificamos? Podemos expresar este comportamiento como métodos asociados a clases u objetos.
**Clases**
La idea es usar `classes` junto con `inheritance` para lograr agregar un cierto comportamiento a una clase.
✅ La herencia es un concepto importante de comprender. Obtenga más información en el [artículo de MDN sobre herencia](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Expresado a través de código, un objeto de juego normalmente puede verse así:
```javascript
//configurar la clase GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//esta clase extenderá las propiedades de clase inherentes del GameObject
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//este objeto móvil se puede mover en la pantalla
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//esta es una clase específica que extiende la clase Movable, por lo que puede aprovechar todas las propiedades que hereda
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//esta clase, por otro lado, solo hereda las propiedades GameObject
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
// un héroe puede moverse...
const hero = new Hero();
hero.moveTo(5,5);
//pero un árbol no puede
const tree = new Tree();
```
✅ Tómate unos minutos para volver a imaginar un héroe de Pac-Man (Inky, Pinky o Blinky, por ejemplo) y cómo se escribiría en JavaScript.
**Composición**
Una forma diferente de manejar la herencia de objetos es usando *Composición*. Entonces, los objetos expresan su comportamiento así:
```javascript
//crear un gameObject constante
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...y un movible constante
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//entonces la constante movableObject está compuesta por gameObject y constantes movibles
const movableObject = {...gameObject, ...movable};
//luego crea una función para crear un nuevo Hero que hereda las propiedades de movableObject
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...y un objeto estático que hereda solo las propiedades de gameObject
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//crea el héroe y muévelo
const hero = createHero(10,10);
hero.moveTo(5,5);
//y crea un árbol estático que solo se para alrededor
const tree = createStatic(0,0, 'Tree');
```
**¿Qué patrón debo usar?**
Depende de usted qué patrón elija. JavaScript es compatible con ambos paradigmas.
--
Otro patrón común en el desarrollo de juegos aborda el problema de manejar la experiencia y el rendimiento del usuario del juego.
## Patrón de pub/sub
✅ Pub/Sub significa publish-subscribe (publicar-suscribirse).
Este patrón aborda la idea de que las distintas partes de su aplicación no deben conocerse entre sí. ¿Porqué es eso? Hace que sea mucho más fácil ver lo que sucede en general si se separan varias partes. También facilita el cambio repentino de comportamiento si es necesario. ¿Cómo logramos esto? Hacemos esto estableciendo algunos conceptos:
- **message** (mensaje: un mensaje suele ser una cadena de texto acompañada de una carga útil opcional (un dato que aclara de qué se trata el mensaje). Un mensaje típico en un juego puede ser `KEY_PRESSED_ENTER`.
- **publisher** (editor): este elemento *publica* un mensaje y lo envía a todos los suscriptores.
- **subscriber** (suscriptor): Este elemento *escucha* mensajes específicos y realiza alguna tarea como resultado de recibir este mensaje, como disparar un láser.
La implementación es bastante pequeña pero es un patrón muy poderoso. Así es como se puede implementar:
```javascript
//configurar una clase EventEmitter que contenga oyentes
class EventEmitter {
constructor() {
this.listeners = {};
}
//cuando se recibe un mensaje, deje que el oyente maneje su carga útil
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//cuando se envía un mensaje, envíelo a un oyente con alguna carga útil
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
Para usar el código anterior, podemos crear una implementación muy pequeña:
```javascript
//configurar una estructura de mensaje
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//invocar el eventEmitter que configuró anteriormente
const eventEmitter = new EventEmitter();
//configurar un héroe
const hero = createHero(0,0);
//Informe al emisor de eventos que esté atento a los mensajes relacionados con el héroe que se mueve hacia la izquierda y actúe en consecuencia
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//configurar la ventana para escuchar el evento keyup, específicamente si se golpea la flecha izquierda, emite un mensaje para mover al héroe a la izquierda
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
Arriba conectamos un evento de teclado, `ArrowLeft` y enviamos el mensaje `HERO_MOVE_LEFT`. Escuchamos ese mensaje y, como resultado, movemos al `hero`. El punto fuerte de este patrón es que el oyente del evento y el héroe no se conocen. Puede reasignar la ʻArrowLeft` a la tecla ʻA`. Además, sería posible hacer algo completamente diferente en `ArrowLeft` haciendo algunas ediciones en la función `on` del eventEmitter:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
A medida que las cosas se complican cuando tu juego crece, este patrón permanece igual en complejidad y tu código se mantiene limpio. Realmente se recomienda adoptar este patrón.
🚀Desafío: Piense en cómo el patrón pub-sub puede mejorar un juego. ¿Qué partes deberían emitir eventos y cómo debería reaccionar el juego ante ellos? Ahora tienes la oportunidad de ser creativo, pensar en un nuevo juego y en cómo podrían comportarse sus partes.
## [Post-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30)
## Revisión y autoestudio
Obtenga más información sobre Pub / Sub al [leer sobre él](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon).
**Tarea**: [Mock up a game](assignment.es.md)

View File

@@ -1,224 +0,0 @@
# Développez un jeu spatial, partie 1: Introduction
![video](../../images/pewpew.gif)
## Quiz préalable
[Quiz préalable](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=fr)
### Héritage et composition dans le développement de jeux
ans les leçons précédentes, il n'y avait pas vraiment besoin de s'inquiéter de l'architecture de conception des applications que vous avez créées, car les projets étaient de très petite portée. Cependant, lorsque vos applications augmentent en taille et en étendue, les décisions architecturales deviennent une préoccupation plus importante. Il existe deux approches principales pour créer des applications plus volumineuses en JavaScript: la *composition* ou l'*héritage*. Il y a des avantages et des inconvénients dans les deux cas, mais expliquons-les dans le contexte d'un jeu.
✅ L'un des livres de programmation les plus célèbres jamais écrits concerne les [modèles de conception](https://en.wikipedia.org/wiki/Design_Patterns).
Dans un jeu, vous avez des `game objects` qui sont des objets qui existent sur un écran. Cela signifie qu'ils ont un emplacement sur un système de coordonnées cartésien, caractérisé par une coordonnée `x` et `y`. Au fur et à mesure que vous développez un jeu, vous remarquerez que tous vos objets de jeu ont une propriété standard, commune à chaque jeu que vous créez, à savoir des éléments qui sont:
- **location-based** La plupart des éléments de jeu, sinon tous, sont basés sur l'emplacement. Cela signifie qu'ils ont un emplacement, un `x` et un `y`.
- **movable** Ce sont des objets qui peuvent se déplacer vers un nouvel emplacement. Il s'agit généralement d'un héros, d'un monstre ou d'un PNJ (un personnage non joueur), mais pas par exemple, d'un objet statique comme un arbre.
- **self-destructing** Ces objets n'existent que pendant une période définie avant d'être configurés pour être supprimés. Habituellement, cela est représenté par un booléen `dead` ou `destroyed` qui signale au moteur de jeu que cet objet ne doit plus être rendu.
- **cool-down** 'Cool-down' est une propriété typique parmi les objets à courte durée de vie. Un exemple typique est un morceau de texte ou un effet graphique comme une explosion qui ne devrait être vu que pendant quelques millisecondes.
✅ Pensez à un jeu comme Pac-Man. Pouvez-vous identifier les quatre types d'objets énumérés ci-dessus dans ce jeu?
### Exprimer le comportement
Tout ce que nous avons décrit ci-dessus est le comportement que les objets du jeu peuvent avoir. Alors comment les encoder? Nous pouvons exprimer ce comportement sous forme de méthodes associées à des classes ou à des objets.
**Classes**
L'idée est d'utiliser des `classes` en conjonction avec l'`héritage` pour accomplir l'ajout d'un certain comportement à une classe.
✅ L'héritage est un concept important à comprendre. En savoir plus sur l'[article de MDN sur l'héritage](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Exprimé via du code, un objet de jeu peut généralement ressembler à ceci:
```javascript
//configurer la classe GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//cette classe étendra les propriétés de classe inhérentes à GameObject
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//cet objet mobile peut être déplacé sur l'écran
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//il s'agit d'une classe spécifique qui étend la classe Movable, afin qu'elle puisse profiter de toutes les propriétés dont elle hérite
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//cette classe, en revanche, n'hérite que des propriétés de GameObject
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//un héros peut bouger...
const hero = new Hero();
hero.moveTo(5,5);
//mais un arbre ne le peut pas
const tree = new Tree();
```
✅ Prenez quelques minutes pour réimaginer un héros de Pac-Man (Inky, Pinky ou Blinky, par exemple) et comment il serait écrit en JavaScript.
**Composition**
Une autre manière de gérer l'héritage d'objet consiste à utiliser la *Composition*. Ensuite, les objets expriment leur comportement comme ceci:
```javascript
//créer un constant gameObject
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...et un constant movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//alors la constante mobileObject est composée du gameObject et des constantes mobiles
const movableObject = {...gameObject, ...movable};
//puis créez une fonction pour créer un nouveau héros qui hérite des propriétés mobileObject
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...et un objet statique qui hérite uniquement des propriétés de gameObject
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//créer le héros et le déplacer
const hero = createHero(10,10);
hero.moveTo(5,5);
//et créer un arbre statique qui ne se trouve que dans les environs
const tree = createStatic(0,0, 'Tree');
```
**Quel modèle dois-je utiliser?**
C'est à vous de décider quel modèle vous choisissez. JavaScript prend en charge ces deux paradigmes.
--
Un autre modèle courant dans le développement de jeux aborde le problème de la gestion de l'expérience utilisateur et des performances du jeu.
## Modèle Pub/sub
✅ Pub/Sub signifie 'publish-subscribe' (publier-s'abonner en français)
Ce modèle répond à l'idée que les parties disparates de votre application ne devraient pas se connaître. Pourquoi donc? Il est beaucoup plus facile de voir ce qui se passe en général si différentes parties sont séparées. Cela facilite également le changement soudain de comportement si vous en avez besoin. Comment accomplir cela? Pour ce faire, nous établissons quelques concepts:
- **message**: Un message est généralement une chaîne de texte accompagnée d'une charge utile facultative (une donnée qui clarifie le sujet du message). Un message typique dans un jeu peut être `KEY_PRESSED_ENTER`.
- **publisher**: Cet élément *publie* un message et l'envoie à tous les abonnés.
- **subscriber**: Cet élément *écoute* des messages spécifiques et exécute une tâche à la suite de la réception de ce message, comme tirer un laser.
L'implémentation est assez petite mais c'est un modèle très puissant. Voici comment cela peut être mis en œuvre:
```javascript
//configurer une classe EventEmitter qui contient des écouteurs
class EventEmitter {
constructor() {
this.listeners = {};
}
//lorsqu'un message est reçu, laissez l'auditeur gérer sa charge utile
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//lorsqu'un message est envoyé, envoyez-le à un auditeur avec une charge utile
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
Pour utiliser le code ci-dessus, nous pouvons créer une très petite implémentation:
```javascript
//mettre en place une structure de message
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//invoquez l'eventEmitter que vous avez configuré ci-dessus
const eventEmitter = new EventEmitter();
//définissez un héros
const hero = createHero(0,0);
//faites savoir à l'EventEmitter de surveiller les messages concernant le héros qui se déplace vers la gauche et agissez en conséquence
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//configurer la fenêtre pour écouter l'événement keyup, en particulier si la flèche gauche est touchée, émettre un message pour déplacer le héros vers la gauche
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
Ci-dessus, nous connectons un événement de clavier, `ArrowLeft` et envoyons le message `HERO_MOVE_LEFT`. Nous écoutons ce message et faisons bouger le `hero` en conséquence. La force de ce modèle est que l'auditeur de l'événement et le héros ne se connaissent pas. Vous pouvez remapper la touche `ArrowLeft` sur la touche `A`. De plus, il serait possible de faire quelque chose de complètement différent sur `ArrowLeft` en apportant quelques modifications à la fonction `on` de l'eventEmitter:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
Au fur et à mesure que les choses se compliquent lorsque votre jeu s'étend, ce modèle reste le même en complexité et votre code reste propre. Il est vraiment recommandé d'adopter ce modèle.
---
## 🚀 Challenge
Pensez à la façon dont le modèle pub-sub peut améliorer un jeu. Quelles parties doivent émettre des événements, et comment le jeu doit-il y réagir? C'est maintenant votre chance de faire preuve de créativité, en pensant à un nouveau jeu et à la façon dont ses parties pourraient se comporter.
## Quiz de validation des connaissances
[Quiz de validation des connaissancesz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=fr)
## Révision et étude personnelle
En savoir plus sur Pub/Sub en [lisant sur ce sujet](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon).
## Affectation
[Conception d'un jeu](assignment.fr.md)

View File

@@ -1,222 +0,0 @@
# एक अंतरिक्ष खेल बनाएँ भाग 1: परिचय
![वीडियो](../../images/pewpew.gif)
## लेक्चर से पहलेकी क्विज
[लेक्चर से पहलेकी क्विज ](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=hi)
### खेल के विकास में इन्हेरिटेंस और कम्पोजीशन
पहले के पाठों में, आपके द्वारा बनाए गए ऐप्स के डिज़ाइन आर्किटेक्चर के बारे में ज्यादा चिंता करने की ज़रूरत नहीं थी, क्योंकि प्रोजेक्ट बहुत कम दायरे में थे. हालाँकि, जब आपके अनुप्रयोग आकार और दायरे में बढ़ते हैं, तो वास्तु निर्णय एक बड़ी चिंता बन जाते हैं. जावास्क्रिप्ट में बड़ा अनुप्रयोग बनाने के लिए दो प्रमुख दृष्टिकोण हैं: _composition_ या _inheritance_. दोनों के पक्ष और विपक्ष हैं लेकिन किसी खेल के संदर्भ में उन्हें समझते हैं .
✅ अब तक लिखी गई सबसे प्रसिद्ध प्रोग्रामिंग किताबों में से एक है [डिजाइन पैटर्न](https://en.wikipedia.org/wiki/Design_Patterns)
एक गेम में आपके पास `game objects` हैं जो एक स्क्रीन पर मौजूद ऑब्जेक्ट हैं. इसका मतलब है कि उनके पास एक कार्टेशियन समन्वय प्रणाली पर एक स्थान है, जिसमें एक `x` और` y` समन्वय है. जैसा कि आप एक गेम विकसित करते हैं, आप देखेंगे कि आपके सभी गेम ऑब्जेक्ट में एक मानक गुण है, जो आपके द्वारा बनाए जाने वाले प्रत्येक गेम के लिए सामान्य है, अर्थात् तत्व हैं:
- **location-based** अधिकांश, यदि सभी नहीं हैं, तो खेल तत्व स्थान आधारित हैं. इसका मतलब है कि उनके पास एक स्थान है, एक `x` और` y`.
- **movable** ये ऐसी वस्तुएं हैं जो एक नए स्थान पर जा सकती हैं. यह आम तौर पर एक नायक, एक राक्षस या एक एनपीसी (एक गैर खिलाड़ी चरित्र) है, लेकिन उदाहरण के लिए, एक पेड़ जैसी स्थिर वस्तु नहीं.
- **self-destructing** ये ऑब्जेक्ट केवल समय की एक निर्धारित अवधि के लिए मौजूद होते हैं, इससे पहले कि वे स्वयं को हटाने के लिए सेट करें. आमतौर पर यह एक `मृत` या` नष्ट` बूलियन द्वारा दर्शाया जाता है जो गेम इंजन को संकेत देता है कि इस ऑब्जेक्ट को अब प्रस्तुत नहीं किया जाना चाहिए.
- **cool-down** 'कूल-डाउन' एक अल्पकालिक वस्तुओं में से एक विशिष्ट गुण है. एक विशिष्ट उदाहरण एक विस्फोट की तरह पाठ या चित्रमय प्रभाव का एक टुकड़ा है जिसे केवल कुछ मिलीसेकंड के लिए देखा जाना चाहिए.
✅ पैक मैन जैसे खेल के बारे में सोचें. क्या आप इस खेल में ऊपर सूचीबद्ध चार ऑब्जेक्ट प्रकारों की पहचान कर सकते हैं?
### व्यवहार व्यक्त करना
हम ऊपर वर्णित सभी व्यवहार हैं जो गेम ऑब्जेक्ट्स हो सकते हैं . तो हम उन सबको को कैसे एनकोड करेंगे? हम इस व्यवहार को या तो क्लासेज या objects से संबंधित विधियों के रूप में व्यक्त कर सकते हैं.
**क्लासेज**
विचार एक क्लास के लिए एक निश्चित व्यवहार को जोड़ने के लिए `inheritence` के साथ संयोजन में `inheritence` का उपयोग करने के लिए है.
✅ समझने के लिए इन्हेरिटेंस एक महत्वपूर्ण अवधारणा है। [एमडीएन के इन्हेरिटेंस के बारे में लेख](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototyp_chain) पर और जानें.
कोड के माध्यम से व्यक्त, एक गेम ऑब्जेक्ट आमतौर पर इस तरह दिख सकता है:
```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();
```
✅ एक पैक-मैन हीरो (इंकी, पिंकी या ब्लंकी, उदाहरण के लिए) को फिर से लागू करने के लिए कुछ मिनट लें और यह जावास्क्रिप्ट में कैसे लिखा जाएगा.
**कम्पोजीशन**
ऑब्जेक्ट इन्हेरिटेंस को संभालने का एक अलग तरीका _Composition_ का उपयोग करते है. फिर, ऑब्जेक्ट अपने व्यवहार को इस तरह व्यक्त करते हैं:
```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');
```
**मुझे किस पैटर्न का उपयोग करना चाहिए?**
यह आपके ऊपर है कि आप किस पैटर्न को चुनते हैं । जावास्क्रिप्ट इन दोनों प्रतिमानों का समर्थन करता है.
--
खेल के विकास में एक और पैटर्न आम है जो गेम के उपयोगकर्ता अनुभव और प्रदर्शन को संभालने की समस्या को संबोधित करता है.
## Pub/sub पैटर्न
✅ Pub/Sub अर्थ 'publish-subscribe' है
यह पैटर्न इस विचार को संबोधित करता है कि आपके आवेदन के असमान भागों को एक दूसरे के बारे में पता नहीं होना चाहिए. यह देखने के लिए बहुत आसान बनाता है कि सामान्य रूप से क्या हो रहा है अगर विभिन्न भागों को अलग किया जाता है. यह भी अगर आप की जरूरत है अचानक व्यवहार बदलने के लिए आसान बनाता है. हम इसे कैसे पूरा करेंगे? हम कुछ अवधारणाओं को स्थापित करके ऐसा करते हैं:
- **message**: एक संदेश आमतौर पर एक टेक्स्ट स्ट्रिंग होता है जिसमें वैकल्पिक पेलोड (डेटा का एक टुकड़ा जो स्पष्ट करता है कि संदेश क्या है). एक खेल में एक विशिष्ट संदेश `KEY_PRESSED_ENTER` हो सकता है।
- **publisher**: यह तत्व एक संदेश \_प्रकाशित\_ करता है और इसे सभी ग्राहकों को भेजता है.
- **subscriber**: यह तत्व विशिष्ट संदेशों को \_सूचीबद्ध\_ करता है और इस संदेश को प्राप्त करने के परिणामस्वरूप कुछ कार्य करता है, जैसे कि लेजर फायरिंग.
कार्यान्वयन आकार में काफी छोटा है लेकिन यह एक बहुत शक्तिशाली पैटर्न है। यहां बताया गया है कि इसे कैसे लागू किया जा सकता है:
```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));
}
}
}
```
उपरोक्त कोड का उपयोग करने के लिए हम एक बहुत छोटा कार्यान्वयन बना सकते हैं:
```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);
}
});
```
ऊपर हम एक कीबोर्ड ईवेंट कनेक्ट करते हैं, `ArrowLeft` और `HERO_MOVE_LEFT` संदेश भेजें. हम उस संदेश को सुनते हैं और परिणामस्वरूप `hero` को स्थानांतरित करते हैं. इस पैटर्न के साथ ताकत यह है कि इवेंट लिस्टनेर और हीरो एक दूसरे के बारे में नहीं जानते हैं. आप `ArrowLeft` को `A` की के लिए रीमैप कर सकते हैं. इसके अतिरिक्त यह `ArrowLeft` पर पूरी तरह से अलग कुछ करने के लिए संभव हो जाएगा करने के लिए EventEmitter की `on` समारोह में कुछ संपादन:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5, 0);
});
```
जब आपका गेम बढ़ता है तो चीजें अधिक जटिल हो जाती हैं, यह पैटर्न जटिलता में समान रहता है और आपका कोड साफ रहता है. यह वास्तव में इस पैटर्न को अपनाने की सिफारिश की गई है.
---
## 🚀 चुनौती
इस बारे में सोचें कि pub-sub पैटर्न एक खेल को कैसे बढ़ा सकते हैं. किन हिस्सों को घटनाओं का उत्सर्जन करना चाहिए, और खेल को उन्हें कैसे प्रतिक्रिया देनी चाहिए? अब आपका मौका रचनात्मक बनने का है, एक नए खेल के बारे में सोचकर और उसके भागों का व्यवहार कैसे हो सकता है.
## लेक्चर बाद की क्विज
[लेक्चर बाद की क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=hi)
## समीक्षा और स्व अध्ययन
pub/sub[के बारे में पढ़े](https://docs.microsoft.com/azure/altecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon) और अधिक जानें .
## कार्यभार
[एक खेल का नका](assignment.hi.md)

View File

@@ -1,224 +0,0 @@
# Costruire un Gioco Spaziale Parte 1: Introduzione
![video](../../images/pewpew.gif)
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=it)
### Ereditarietà e Composizione nello sviluppo del gioco
Nelle lezioni precedenti, non c'era molto bisogno di preoccuparsi dell'architettura di progettazione delle app che sono state create, poiché i progetti erano di portata molto ridotta. Tuttavia, quando le dimensioni e l'ambito delle applicazioni aumentano, le decisioni sull'architettura diventano una preoccupazione maggiore. Esistono due approcci principali per creare applicazioni più grandi in JavaScript: *composizione* o *ereditarietà*. Ci sono pro e contro in entrambi, che verranno spiegiati dall'interno del contesto di un gioco.
✅ Uno dei libri di programmazione più famosi mai scritti ha a che fare con [i modelli di progettazione](https://it.wikipedia.org/wiki/Design_Patterns).
In un gioco ci sono `oggetti di gioco` che esistono su uno schermo. Ciò significa che hanno una posizione su un sistema di coordinate cartesiane, caratterizzati dall'avere una coordinata `x` e una `y` . Man mano che si sviluppa un gioco si noterà che tutti gli oggetti di gioco hanno una proprietà standard, comune a ogni gioco che si crea, ovvero elementi che sono:
- **basati sulla posizione** La maggior parte, se non tutti, gli elementi del gioco sono basati sulla posizione. Ciò significa che hanno una posizione, una `x` e una `y`.
- **mobili** Questi sono oggetti che possono spostarsi in una nuova posizione. Tipicamente un eroe, un mostro o un personaggio non giocante (NPC - Non Player Character), ma non, ad esempio, un oggetto statico come un albero.
- **autodistruggenti** Questi oggetti esistono solo per un determinato periodo di tempo prima che vengano contrassegnati per l'eliminazione. Di solito questo è rappresentato da un booleano `morto` o `distrutto` che segnala al motore di gioco che questo oggetto non dovrebbe più essere renderizzato.
- **raffreddamento** Il "raffreddamento" è una proprietà tipica tra gli oggetti di breve durata. Un classico esempio è un pezzo di testo o un effetto grafico come un'esplosione che dovrebbe essere visto solo per pochi millisecondi.
✅ Si pensi a un gioco come Pac-Man. Si riescono a identificare i quattro tipi di oggetti sopra elencati in questo gioco?
### Esprimere il comportamento
Quanto descritto sopra è il comportamento che possono avere gli oggetti di gioco. Allora come vanno codificati? Si può esprimere questo comportamento tramite metodi associati a classi o oggetti.
**Classi**
L'idea è di usare `classi` insieme all'`ereditarietà` per ottenere l'aggiunta di un determinato comportamento a una classe.
✅ L'ereditarietà è un concetto importante da comprendere. Ulteriori informazioni sull'[articolo di MDN sull'ereditarietà](https://developer.mozilla.org/it/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Espresso tramite codice, un oggetto di gioco può tipicamente avere questo aspetto:
```javascript
//Imposta la classe GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//Questa classe estenderà le proprietà di classe di GameObject
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//questo oggetto movibile può essere spostato nello schermo
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//classe specifica che estende la classe Movable, per poter approfittare di tutte le proprietà che eredita
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//questa classe, d'altro canto, eredita solo le proprietà di GameObject
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//un eroe può spostarsi...
const hero = new Hero();
hero.moveTo(5,5);
//ma un albero no
const tree = new Tree();
```
✅ Ci si prenda qualche minuto per rivedere un eroe di Pac-Man (Inky, Pinky o Blinky, per esempio) e come sarebbe scritto in JavaScript.
**Composizione**
Un modo diverso di gestire l'ereditarietà degli oggetti consiste nell'usare la *composizione*. Con questo sistema gli oggetti esprimono il loro comportamento in questo modo:
```javascript
//crea una costante gameObject
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...e una costante movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//poi la costante movableObject viene composta dalle costanti gameObject e movable
const movableObject = {...gameObject, ...movable};
//quindi si scrive una funzione per crear un nuovo eroe che eredita le proprietà di movableObject
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...e un oggetto statico che eredita solo le proprietà di gameObject
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//crea l'eroe e lo muove
const hero = createHero(10,10);
hero.moveTo(5,5);
//e crea un albero statico immobile
const tree = createStatic(0,0, 'Tree');
```
**Quale modello si dovrebbe usare?**
Dipende dallo sviluppatore quale modello scegliere. JavaScript supporta entrambi questi paradigmi.
--
Un altro modello comune nello sviluppo di giochi affronta il problema della gestione dell'esperienza utente e delle prestazioni del gioco.
## Modello pub/sub
✅ Pub/Sub sta per pubblica/sottoscrivi ('publish-subscribe')
Questo modello indirizza l'idea che parti disparate della propria applicazione non dovrebbero sapere l'una dell'altra. Perché? Rende molto più facile vedere cosa sta succedendo in generale se le varie parti sono separate. Inoltre, è più facile cambiare improvvisamente un comportamento se necessario. Come si realizza? Si fa stabilendo alcuni concetti:
- **messaggio**: un messaggio è solitamente una stringa di testo accompagnata da un payload opzionale (un dato che chiarisce di cosa tratta il messaggio). Un messaggio tipico in un gioco può essere `KEY_PRESSED_ENTER`.
- **publisher**: questo elemento *pubblica* un messaggio e lo invia a tutti i sottoscrittori.
- **subscriber**: questo elemento *ascolta* messaggi specifici e svolge alcune attività come risultato della ricezione di questo messaggio, come sparare con un laser.
L'implementazione è di dimensioni piuttosto ridotte ma è un modello molto potente. Ecco come può essere implementato:
```javascript
//imposta la classe EventEmitter che contiene i listener
class EventEmitter {
constructor() {
this.listeners = {};
}
//quando un messaggio viene ricevuto, si fa gestire il suo payload al listener
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//quando un messaggio viene ricevuto, si invia a un listener con un payload
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
Per utilizzare il codice qui sopra si può creare un'implementazione molto piccola:
```javascript
//impostazione di una struttura di messaggio
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//invoca l'eventEmitter impostato sopra
const eventEmitter = new EventEmitter();
//imposta un eroe
const hero = createHero(0,0);
//fa in modo che eventEmitter sappia come monitorare i messages di pertinenza dell'eroe per spostarsi a sinistra, e agisce di conseguenza
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//imposta window per ascoltare un evento keyup, nello specifico se viene premuto freccia sinistra emette un messaggio che fa spostare l'eroe a sinistra
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
Sopra si collega un evento della tastiera, `ArrowLeft` e si invia il messaggio `HERO_MOVE_LEFT` (spostamento a sinistra - n.d.t.). Questo messaggio viene recepito e come risultato si sposta l'eroe (`hero`). Il punto di forza di questo modello è che l'event listener e l'eroe non sanno nulla l'uno dell'altro. Si può rimappare `ArrowLeft` sul tasto `A`. Inoltre sarebbe possibile fare qualcosa di completamente diverso su `ArrowLeft` apportando alcune modifiche alla funzione `on` di eventEmitter:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
Man mano che le cose diventano più complicate quando il gioco cresce, questo modello rimane lo stesso in termini di complessità e il proprio codice rimane pulito. Si consiglia vivamente di adottare questo modello.
---
## 🚀 Sfida
Pensare a come il modello pub/sub può migliorare un gioco. Quali parti dovrebbero emettere eventi e come dovrebbe reagire a questi il gioco? Ora si ha la possibilità di essere creativi, pensando a un nuovo gioco e a come potrebbero comportarsi le sue parti.
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=it)
## Revisione e Auto Apprendimento
Saperne di più su Pub/Sub [leggendo qui](https://docs.microsoft.com/it-it/azure/architecture/patterns/publisher-subscriber?WT.mc_id=academic-4621-cxa).
## Compito
[Produrre uno schizzo di un gioco](assignment.it.md)

View File

@@ -1,224 +0,0 @@
# スペースゲーム構築プロジェクト その 1: イントロダクション
![video](../../images/pewpew.gif)
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=ja)
### ゲーム開発における継承とコンポジション
以前のレッスンでは、プロジェクトの規模が非常に小さかったため、構築したアプリの設計アーキテクチャを気にする必要はあまりありませんでした。しかし、アプリケーションの規模や範囲が大きくなると、アーキテクチャの決定がより大きな関心事になります。JavaScript で大規模なアプリケーションを作成するには、大きく分けて2つのアプローチがあります。*コンポジション* と *継承* です。どちらにも長所と短所がありますが、ゲームの文脈から説明してみましょう。
✅ これまでに書かれたプログラミング本の中で最も有名なものの一つは、[デザインパターン](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2))と関係があります。
ゲームには `ゲームオブジェクト` があり、これは画面上に存在するオブジェクトです。つまり、それらは直交座標系上に位置しており、`x``y` の座標を持つことが特徴です。ゲームを開発していくうちに、すべてのゲームオブジェクトには、すべてのゲームに共通する標準的な特性があることに気づくでしょう。
- **location-based** すべてではないにしても、ほとんどのゲーム要素は位置情報に基づいています。これは、場所、`x``y` を持つことを意味します
- **movable** これらは、新しい場所に移動することができるオブジェクトです。これは典型的にはヒーローやモンスター、NPC (プレイヤーではないキャラクター) などですが、例えば木のような静的なオブジェクトはそうではありません
- **self-destructing** これらのオブジェクトは、削除のために自分自身を設定する前に、一定期間だけ存在します。通常、これは `dead` または `destroyed` ブール値で表され、このオブジェクトがレンダリングされなくなることをゲームエンジンに通知します
- **cool-down** 「クールダウン」は、短命なオブジェクトの典型的なプロパティです。典型的な例としては、数ミリ秒しか見られない爆発のようなテキストやグラフィック効果があります。
✅ パックマンのようなゲームについて考えてみましょう。このゲームでは、上に挙げた4つのオブジェクトの種類を特定できますか?
### 行動の表現
上で説明したのは、すべてゲームオブジェクトが持つことのできる動作です。では、それらをどのようにエンコードするのでしょうか? この動作をクラスやオブジェクトに関連付けられたメソッドとして表現することができます。
**クラス**
クラスに特定の振る舞いを追加するために `クラス``継承` と組み合わせて使うという考え方です。
✅ 継承は理解しておくべき重要な概念です。[継承に関する MDN の記事](https://developer.mozilla.org/ja/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)で詳しく解説しています。
コードで表現すると、ゲームオブジェクトは通常このようになります。
```javascript
//GameObject クラスを設定します。
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//このクラスは GameObject の固有のクラスプロパティを拡張します。
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//この移動可能なオブジェクトは、画面上で移動することができます。
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//これは Movable クラスを拡張した特定のクラスで、継承しているすべてのプロパティを利用することができます。
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//一方、このクラスは GameObject のプロパティのみを継承しています。
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//hero は動くことができます
const hero = new Hero();
hero.moveTo(5,5);
//しかし木にはできません
const tree = new Tree();
```
✅ パックマンのヒーロー (例えば、Inky、Pinky、Blinky など) と、それが JavaScript でどのように書かれているかを、数分かけて再定義してみましょう。
**コンポジション**
オブジェクトの継承を処理する別の方法として、*コンポジション* を使用する方法があります。すると、オブジェクトは次のように動作を表現します。
```javascript
//定数の gameObject を作成する
const gameObject = {
x: 0,
y: 0,
type: ''
};
//そして定数の movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//そして、定数 movableObject は定数 gameObject と movable で構成されます。
const movableObject = {...gameObject, ...movable};
//次に、movableObject のプロパティを継承する新しい Hero を作成する関数を作成します。
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...そして gameObject プロパティのみを継承する静的オブジェクト。
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//hero を作って動かします
const hero = createHero(10,10);
hero.moveTo(5,5);
//そして、周りに立つだけの木を作ります
const tree = createStatic(0,0, 'Tree');
```
**どのパターンを使えばいいのか?**
どちらのパターンを選ぶかはあなた次第です。JavaScript はこれらのパラダイムの両方をサポートしています。
--
ゲーム開発に共通するもう一つのパターンは、ゲームのユーザーエクスペリエンスとパフォーマンスを処理する問題を扱っています。
## Pub/Sub パターン
✅ Pub/Sub は 'publish-subscribe' の略です。
このパターンは、あなたのアプリケーションのバラバラな部分が別のものを知ってはいけないという考えを示しています。それはなぜでしょうか? 様々な部分が分離されていると、一般的に何が起こっているのかを見るのがとても簡単になります。また、必要に応じて突然動作を変更することも容易になります。どのようにしてこれを達成するのでしょうか? いくつかの概念を確立することで実現します。
- **message**: メッセージは通常、オプションのペイロード (メッセージの内容を明確にするデータ) を伴ったテキスト文字列です。ゲームにおける典型的なメッセージは `KEY_PRESSED_ENTER` です
- **publisher**: この要素はメッセージを**発行し**、すべての購読者に送信します
- **subscriber**: この要素は、特定のメッセージを *待ち受け*、このメッセージを受信した結果として、レーザーを発射するなどのタスクを実行します
実装されているのはかなり小さいですが、かなり強力なパターンです。以下に実装方法を紹介します。
```javascript
//リスナーを含む EventEmitter クラスを設定します。
class EventEmitter {
constructor() {
this.listeners = {};
}
//メッセージを受信したときに、リスナーにそのペイロードを処理させます。
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//メッセージを受信したときに、リスナーにそのペイロードを処理させます。
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
上記のコードを使用するために、非常に小さな実装を作成することができます。
```javascript
//メッセージ構造を設定します
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//上記で設定した EventEmitter を呼び出します。
const eventEmitter = new EventEmitter();
//hero を設定します。
const hero = createHero(0,0);
//イベント送信者に hero が左に移動するメッセージを監視することを知らせ、それに基づいて行動するようにします。
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//keyup イベントをリッスンするためにウィンドウを設定し、具体的には左矢印がヒットした場合、hero を左に移動するためのメッセージを発します。
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
上ではキーボードイベント `ArrowLeft` を接続して `HERO_MOVE_LEFT` メッセージを送信している。私たちはそのメッセージを聞き、その結果として `hero` を移動させます。このパターンの強みは、イベントリスナーとヒーローがお互いのことを知らないことです。`ArrowLeft``A` キーにリマップすることができます。さらに、eventEmitter の `on` 関数にいくつかの編集を加えることで、`ArrowLeft` で全く異なることが可能になります。
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
ゲームが成長するときに物事がより複雑になったとしても、このパターンは複雑さの中で同じままで、あなたのコードはきれいなままになります。このパターンを採用することをお勧めします。
---
## 🚀 チャレンジ
pub-sub パターンがどのようにゲームを盛り上げるかを考えてみましょう。どのパーツがイベントを発生させ、それに対してゲームはどのように反応しますか? 新しいゲームとそのパーツがどのように振る舞うかを考えて、創造性を発揮するチャンスです。
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=ja)
## 復習と自己学習
Pub/Sub については、[こちらを読んでみてください](https://docs.microsoft.com/ja-jp/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon)。
## 課題
[ゲームのモックアップ](assignment.ja.md)

View File

@@ -1,224 +0,0 @@
# Space 게임 제작하기 파트 1: 소개
![video](../../images/pewpew.gif)
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=ko)
### 게임 개발의 상속과 구성
이전 강의에서는, 프로젝트의 범위가 매우 작았으므로, 만든 앱의 디자인 아키텍처에 대해 걱정할 필요가 없었습니다. 그러나, 애플리케이션의 크기와 범위가 커지면, 아키텍처 결정이 더 힘듭니다. JavaScript에서 더 큰 응용 프로그램을 만드는 데는 두 가지 주요 방식이 있습니다: *composition* 또는 *inheritance*. 둘 다 장점과 단점이 존재하지만 게임의 맥락에서 설명할 것 입니다.
✅ 가장 유명한 프로그래밍 책 중에는 [design patterns](https://en.wikipedia.org/wiki/Design_Patterns)과 관련이 있습니다.
게임에서는 화면에 존재하는 객체인 `game objects`가 있습니다. `x``y` 좌표를 갖는 것이 특징인, 데카르트 좌표계에 있음을 의미합니다. 게임을 개발할 때 만드는, 모든 게임 객체를 가지고 있는 게임에는 공통적인 표준 속성이 있습니다:
- **location-based** 전부는 아니지만, 대부분의 게임 요소는 위치 기반입니다. `x``y`, 위치를 가지고 있음을 의미합니다.
- **movable** 새 위치로 이동할 수 있는 객체입니다. 이는 일반적으로 영웅, 몬스터 혹은 NPC(non player character)이지만, 예를 들어, 나무와 같은 정적 객체는 아닙니다.
- **self-destructing** 이런 객체는 삭제 작업을 위해 설정되기 전 일정한 시간에만 존재합니다. 일반적으로 이 객체가 더 이상 렌더링하지 않도록 게임 엔진에 알리기 위해서는 `dead` 또는 `destroyed` 논리 자료형으로 표시됩니다.
- **cool-down** 'Cool-down'은 짧은-수명 객체의 일반적인 속성입니다. 일반적인 예시는 몇 milli 초 동안만 보이는 폭발과 같은 텍스트 또는 그래픽 이펙트입니다.
✅ Pac-Man과 같은 게임을 생각해보세요. 이 게임에서 위 나열된 4가지 객체 타입을 구별할 수 있나요?
### 행동 표현
설명한 모든 것은 게임 객체가 가질 수 있는 동작입니다. 그럼 어떻게 인코딩 될까요? 이 동작을 클래스 혹은 객체와 관련된 메소드로 나타낼 수 있습니다.
**Classes**
아이디어로는 클래스에 특정 동작을 추가하기 위해 `inheritance`과 함께 `classes`를 사용하는 것입니다.
✅ 상속은 이해해야 할 중요한 컨셉입니다. [MDN's article about inheritance](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)로 더 배워보세요.
코드를 통해 표현되는, 게임 객체는 일반적으로 다음과 같습니다:
```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();
```
✅ 잠시 시간을 내어 Pac-Man 영웅 (예를 들어, Inky, Pinky 혹은 Blinky)과 JavaScript로 작성되는 방법을 다시 구상하십시오.
**Composition**
객체 상속을 처리하는 다른 방법으로는 *Composition* 을 사용하는 것입니다. 그러면, 객체는 다음과 같이 동작을 표현합니다:
```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');
```
**어떤 패턴을 사용해야 하나요?**
어떤 패턴을 선택하는 지는 여러분에게 달려 있습니다. JavaScript는 이러한 패러다임을 모두 지원합니다.
--
게임 개발에서 흔히 볼 수 있는 또 다른 패턴으로는 게임의 사용자 경험과 성능 문제를 다룹니다.
## Pub/sub 패턴
✅ Pub/Sub은 'publish-subscribe'를 의미합니다
이 패턴은 애플리케이션에서 각자 다른 부분이 서로 알고 있으면 안된다는 아이디어를 다룹니다. 왜 그럴까요? 여러 부분이 분리 되어있다면 보통 무슨 일이 일어나는지 쉽게 볼 수 있습니다. 필요한 경우에는 바로 동작을 변경하기도 더 쉽습니다. 어떻게 해야 할까요? 몇 가지 컨셉을 정하고 지킵시다.
- **message**: 메시지는 일반적으로 선택적 payload (명확한 메시지 내용 데이터 조각)와 함께 제공되는 텍스트 문자열입니다. 게임의 일반적인 메시지는 `KEY_PRESSED_ENTER` 일 수 있습니다.
- **publisher**: 이 요소는 메시지를 *publishes*하고 모든 구독자에게 보냅니다.
- **subscriber**: 이 요소는 특정 메시지를 *listens* 하고 메시지를 수신한 결과로 레이저 발사와 같은 일부 작업을 수행합니다.
implementation은 크기가 매우 작지만 매우 강한 패턴입니다. 구현 방법은 다음과 같습니다:
```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))
}
}
}
```
위의 코드를 사용하기 위해서 매우 작은 implementation을 만들 수 있습니다:
```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)
}
});
```
위에서 우리는 키보드 이벤트 `ArrowLeft`를 연결하고, `HERO_MOVE_LEFT` 메시지를 보냅니다. 우리는 그 메시지를 듣고 최종적으로 `hero`을 움직입니다. 이 패턴의 강점은 이벤트 리스너와 영웅이 서로 알지 못한다는 점입니다. `ArrowLeft``A`키로 다시 매핑할 수도 있습니다. 추가적으로 eventEmitter의 `on` 함수를 조금 수정하여 `ArrowLeft`로 완전히 다른 작업을 할 수 있습니다:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
게임의 몸집이 커질 때 더 복잡해진다면, 이 패턴은 복잡성을 동일하면서 코드도 깨끗하게 유지합니다. 이 패턴을 채택하는 것은 정말 추천드립니다.
---
## 🚀 도전
pub-sub 패턴이 어떻게 게임을 발전시킬 수 있는지 생각해보세요. 어떤 부분이 이벤트를 어떻게 발생하고, 반응해야 하나요? 이제는 창의력을 발휘하고, 새로운 게임과 그 부분에 대해 어떻게 작동하는지 생각해볼 수 있는 기회입니다.
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=ko)
## 리뷰 & 자기주도 학습
[reading about it](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon)으로 Pub/Sub에 대해 조금 더 배워봅시다.
## 과제
[Mock up a game](../assignment.md)

View File

@@ -1,224 +0,0 @@
# Membina Permainan Angkasa Bahagian 1: Pengenalan
![video](../../images/pewpew.gif)
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29)
### Warisan dan Komposisi dalam pembangunan permainan
Dalam pelajaran sebelumnya, tidak banyak yang perlu dikhawatirkan mengenai reka bentuk reka bentuk aplikasi yang anda buat, kerana skop projeknya sangat kecil. Namun, apabila aplikasi anda bertambah besar dan luas, keputusan seni bina menjadi perhatian yang lebih besar. Terdapat dua pendekatan utama untuk membuat aplikasi yang lebih besar dalam JavaScript: *komposisi* atau *pewarisan*. Terdapat kebaikan dan keburukan untuk kedua-duanya tetapi mari kita jelaskan dari dalam konteks permainan.
✅ Salah satu buku pengaturcaraan paling terkenal yang pernah ditulis ada kaitannya dengan [corak reka bentuk](https://en.wikipedia.org/wiki/Design_Patterns).
Dalam permainan, anda mempunyai `objek permainan` yang merupakan objek yang ada di layar. Ini bermakna mereka mempunyai lokasi pada sistem koordinat kartesian, yang dicirikan oleh mempunyai koordinat `x` dan `y`. Semasa anda mengembangkan permainan, anda akan melihat bahawa semua objek permainan anda mempunyai harta benda yang biasa, biasa untuk setiap permainan yang anda buat, iaitu elemen yang:
- **berdasarkan lokasi** Sebilangan besar, jika tidak semua, elemen permainan berdasarkan lokasi. Ini bermaksud bahawa mereka mempunyai lokasi, `x` dan` y`.
- **bergerak** Ini adalah objek yang boleh berpindah ke lokasi baru. Ini biasanya pahlawan, raksasa atau NPC (watak bukan pemain), tetapi tidak misalnya, objek statik seperti pokok.
- **merosakkan diri** Objek-objek ini hanya wujud untuk jangka masa yang ditetapkan sebelum mereka disiapkan untuk dihapus. Biasanya ini diwakili oleh boolean `mati` atau `dimusnahkan` yang memberi isyarat kepada mesin permainan bahawa objek ini tidak lagi boleh diberikan.
- **cool-down** 'Cool-down' adalah sifat khas di antara objek yang berumur pendek. Contoh biasa ialah sekeping teks atau kesan grafik seperti letupan yang hanya dapat dilihat selama beberapa milisaat.
✅ Fikirkan permainan seperti Pac-Man. Bolehkah anda mengenal pasti empat jenis objek yang disenaraikan di atas dalam permainan ini?
### Menyatakan tingkah laku
Yang kami terangkan di atas adalah tingkah laku yang boleh dimiliki oleh objek permainan. Jadi bagaimana kita mengekodkannya? Kita boleh menyatakan tingkah laku ini sebagai kaedah yang berkaitan dengan kelas atau objek.
**Kelas**
Ideanya adalah untuk menggunakan `kelas` bersama dengan `pewarisan` untuk menyelesaikan penambahan tingkah laku tertentu ke kelas.
✅ Warisan adalah konsep penting untuk difahami. Ketahui lebih lanjut mengenai [artikel MDN mengenai pewarisan](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Diekspresikan melalui kod, objek permainan biasanya dapat terlihat seperti ini:
```javascript
//menubuhkan kelas GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//kelas ini akan memperluaskan sifat kelas yang ada pada GameObject
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//objek bergerak ini dapat dipindahkan di skrin
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//ini adalah kelas khusus yang meluaskan kelas Bergerak, sehingga dapat memanfaatkan semua sifat yang diwarisi
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//kelas ini, sebaliknya, hanya mewarisi sifat GameObject
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//hero dapat bergerak...
const hero = new Hero();
hero.moveTo(5,5);
//tetapi pokok tidak boleh
const tree = new Tree();
```
✅ Luangkan masa beberapa minit untuk membayangkan semula pahlawan Pac-Man (misalnya Inky, Pinky atau Blinky) dan bagaimana ia ditulis dalam JavaScript.
**Komposisi**
Cara yang berbeza untuk menangani pewarisan objek adalah dengan menggunakan *Komposisi*. Kemudian, objek menyatakan tingkah laku mereka seperti ini:
```javascript
//buat pemalar untuk gameObject
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...dan satu constant untuk bergerak
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//buat pemalar movableObject terdiri daripada pemalar gameObject dan bergerak
const movableObject = {...gameObject, ...movable};
//kemudian buat fungsi untuk membuat Hero baru yang mewarisi sifat movableObject
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...dan objek statik yang hanya mewarisi sifat gameObject
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//buat hero dan gerakkannya
const hero = createHero(10,10);
hero.moveTo(5,5);
//dan buat pokok statik yang hanya berdiri
const tree = createStatic(0,0, 'Tree');
```
**Corak mana yang harus saya gunakan?**
Terpulang kepada anda corak yang anda pilih. JavaScript menyokong kedua-dua paradigma ini.
--
Pola lain yang biasa dalam pengembangan permainan menangani masalah menangani pengalaman dan prestasi pengguna permainan.
## Corak pub / sub
✅ Pub / Sub bermaksud 'publish-subscribe'
Corak ini mengemukakan idea bahawa bahagian aplikasi anda yang berbeza tidak boleh saling mengenali. Kenapa begitu? Ini menjadikannya lebih mudah untuk melihat apa yang berlaku secara umum jika pelbagai bahagian dipisahkan. Ini juga menjadikannya lebih mudah untuk mengubah tingkah laku secara tiba-tiba jika anda perlu Bagaimana kita mencapainya? Kami melakukan ini dengan mewujudkan beberapa konsep:
- **mesej**: Mesej biasanya merupakan rentetan teks yang disertai dengan muatan pilihan (sekeping data yang menjelaskan tentang mesej itu) Mesej biasa dalam permainan boleh menjadi `KEY_PRESSED_ENTER`.
- **publish**: Elemen ini *menerbitkan* mesej dan mengirimkannya kepada semua pelanggan.
- **subscriber**: Elemen ini *mendengar* mesej tertentu dan menjalankan beberapa tugas sebagai hasil daripada menerima mesej ini, seperti menembakkan laser.
Pelaksanaannya cukup kecil tetapi coraknya sangat kuat. Inilah cara ia dapat dilaksanakan:
```javascript
//sediakan kelas EventEmitter yang mengandungi pendengar
class EventEmitter {
constructor() {
this.listeners = {};
}
//apabila mesej diterima, biarkan pendengar menangani muatannya
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//semasa mesej dihantar, hantarkannya kepada pendengar dengan muatan
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
Untuk menggunakan kod di atas, kita dapat membuat implementasi yang sangat kecil:
```javascript
//sediakan struktur mesej
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//memanggil eventEmitter yang anda tetapkan di atas
const eventEmitter = new EventEmitter();
//sediakan hero
const hero = createHero(0,0);
//biarkan eventEmitter tahu untuk menonton mesej yang berkaitan dengan pahlawan yang bergerak ke kiri, dan bertindak di atasnya
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//sediakan tetingkap untuk mendengarkan acara keyup, khususnya jika anak panah kiri dipukul, mengirimkan pesan untuk menggerakkan pahlawan ke kiri
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
Di atas kami menyambungkan acara papan kekunci, `ArrowLeft` dan menghantar mesej `HERO_MOVE_LEFT`. Kami mendengar mesej itu dan memindahkan `hero` sebagai hasilnya. Kekuatan dengan corak ini ialah pendengar acara dan wira tidak saling mengenali antara satu sama lain. Anda boleh memetakan semula `ArrowLeft` ke kekunci `A`. Selain itu mungkin untuk melakukan sesuatu yang sama sekali berbeza pada `ArrowLeft` dengan membuat beberapa pengeditan pada fungsi `on` eventEmitter:
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
Oleh kerana keadaan menjadi lebih rumit semasa permainan anda berkembang, corak ini tetap sama dalam kerumitan dan kod anda tetap bersih. Sangat digalakkan untuk menggunakan corak ini.
---
## 🚀 Cabaran
Fikirkan bagaimana corak pub-sub dapat meningkatkan permainan. Bahagian mana yang harus memancarkan acara, dan bagaimana reaksi permainan terhadapnya? Inilah peluang anda untuk menjadi kreatif, memikirkan permainan baru dan bagaimana bahagiannya berkelakuan.
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30)
## Mengkaji & Belajar Sendiri
Ketahui lebih lanjut mengenai Pub/Sub dengan [membaca mengenainya](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber).
## Tugasan
[Mockup permainan](assignment.ms.md)

View File

@@ -1,224 +0,0 @@
# 建立太空遊戲 Part 1簡介
![影片](../../images/pewpew.gif)
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/29?loc=zh_tw)
### 遊戲開發中的繼承(Inheritance)與組合(Composition)
在之前的課程中,因為專案較小的規模,我們不需要去擔憂應用程式的設計結構。然而,當你的應用程式規模越來越大時,結構的選擇就是一大課題。在 JavaScript 中,有兩種大方向來建立龐大的應用程式:*組合(Composition)*與*繼承(Inheritance)*。它們有各自的優缺點,我們會藉由遊戲內容來進行說明。
✅ 其中一本有名的程式設計用書是有關於[設計模式](https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B6%E7%9A%84%E5%9F%BA%E7%A1%80)。
在遊戲中你會有`遊戲物件`,顯示在畫面中。這代表它們在笛卡爾座標系中有各自的位置,以 `x``y` 座標點定義。當你在開發遊戲時,你會注意到所有的遊戲物件都有一套標準的規範,和大多數的遊戲相似,通常會有這些元素:
- **適地性** 大多數遊戲元素都是建立在位置上的。這代表他們有各自的所在處,一組 `x``y`
- **可移動的** 這些物件可以移動到新的位置。典型來說有英雄、怪物或是 NPC(Non Player Character),但有些例外,好比是樹這種常駐物件。
- **可自毀的** 這些物件只能存在於一小段時間,接著它們就會自我刪除。通常這是`死亡`或是`被摧毀`的布林訊號傳遞給遊戲引擎,告知物件不再需要被描繪出來。
- **冷卻時間** 「冷卻時間」是存活週期短的典型物件屬性。好比是一段文字、爆炸的視覺特效,只能呈現數毫秒的時間。
✅ 想想看遊戲小精靈(Pac-Man)。你能辨別出符合上述清單的其中四種物件嗎?
### 行為表達
以上的敘述皆在表達遊戲物件所進行的行為。那我們該如何去編寫它們呢?我們可以使用方法(methods)連接 classes 或是物件(objects)來表達這些行為。
**Classes**
這個想法是結合 `classes``繼承`的方式來在 class 中添加特定行為。
✅ 繼承是一個重要概念。在[有關繼承的 MDN 文章中](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)學習更多內容。
以程式碼來表達的話,一個遊戲物件通常會呈現這種形式:
```javascript
//設定 class GameObject
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
//這個 class 會繼承 GameObject 中 class 內容
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
}
//這個可移動物件可以在畫面上移動
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//這是特定的 class 繼承 Movable class它能使用所有繼承到的屬性內容
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
}
}
//另一方面,這個 class 只繼承到 GameObject 的內容
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
}
}
//英雄可以移動......
const hero = new Hero();
hero.moveTo(5,5);
//但樹木卻不能
const tree = new Tree();
```
✅ 花點時間重新構思小精靈(Pac-Man)的主角,或是 Inky、Pinky 與 Blinky 這幾隻鬼魂。它們該如何以 JavaScript 表現?
**組合**
另一種處理物件繼承的方式為*組合(Composition)*。物件以這種方式呈現它們的行為:
```javascript
//建立常數 gameObject
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...與常數 movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//常數 movableObject 是 gameObject 與 movable 的組合
const movableObject = {...gameObject, ...movable};
//利用函式建立新的英雄,繼承 movableObject 的內容
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
}
//...與常駐物件只繼承 gameObject 的屬性
function createStatic(x, y, type) {
return {
...gameObject
x,
y,
type
}
}
//建立可以移動的英雄
const hero = createHero(10,10);
hero.moveTo(5,5);
//和建立只能佇立於此的樹木
const tree = createStatic(0,0, 'Tree');
```
**我該使用哪一種設計模式?**
這都取決於你選擇何種設計模式。JavaScript 支援這兩種範例。
--
另一種在遊戲開發中常見的設計模式負責處理玩家的遊戲表現與遊戲體驗。
## 發布訂閱設計模式
✅ Pub/Sub 全名為 'publish-subscribe'
這個設計模式將應用程式內不同的模組分開處理,讓彼此不知道彼此的行為。為何要這樣做?這讓我們總觀上更輕易地了解各個模組的行為。也可以在你想要時輕易地改變模組的行為模式。我們該如何實踐它呢?我們先建立這幾個概念:
- **訊息** 一個訊息通常會以文字字串與額外的負載(payload) ── 一組定義訊息內容的資料 ── 呈現。遊戲中典型的訊息可以是 `KEY_PRESSED_ENTER`
- **發布者** 這個元素*發布*訊息給所有的訂閱者。
- **訂閱者** 這個元素*監聽*特定的訊息,並藉由執行某些任務以作為訊息的回應,例如發射雷射光。
實踐方法雖小,但這是功能強大的設計方式。這是它的建立方式:
```javascript
//設定 EventEmitter class 容納監聽者
class EventEmitter {
constructor() {
this.listeners = {};
}
//當訊息接收時,讓監聽者處理它的負載
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//當訊息發出時,附上負載發給監聽者
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
}
}
}
```
利用上述程式我們建立一套小型實作內容:
```javascript
//設定訊息種類
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
};
//調用你設定的 eventEmitter
const eventEmitter = new EventEmitter();
//設定英雄
const hero = createHero(0,0);
//讓 eventEmitter 監聽有關英雄往左移的訊息,並執行動作
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
//設定遊戲視窗來監聽鍵盤事件,當左方向鍵按壓時,發出英雄往左移的訊息
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
});
```
我們連接了鍵盤事件 `ArrowLeft` 並傳遞 `HERO_MOVE_LEFT` 訊息。我們監聽該訊息並移動 `hero` 作為結果。這種開發方式讓事件監聽者與英雄區隔開來。你也可以將 `ArrowLeft` 換成 `A` 鍵。此外,我們能修改 eventEmitter 的 on 函式,讓 `ArrowLeft` 事件產生截然不同的行為。
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
});
```
當遊戲越來越豐富、物件越來越複雜時,這套設計方式能維持程式碼的整潔。由衷建議善用這套設計模式。
---
## 🚀 挑戰
想想看發布訂閱模式可以如何增進一款遊戲。哪一個部份該發送事件,而遊戲又該如何回應事件?現在你有機會發揮你的創意,思考一款新遊戲和它運作的模組。
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/30?loc=zh_tw)
## 複習與自學
藉由[閱讀此連結](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon)來認識更多關於發布與訂閱的設計模式。
## 作業
[建立遊戲雛形](assignment.zh-tw.md)

View File

@@ -1,11 +0,0 @@
# Simula un juego
## Instrucciones
Utilizando los ejemplos de código de la lección, escriba una representación de un juego que disfrute. Tendrá que ser un juego simple, pero el objetivo es usar la clase o el patrón de composición y el patrón pub / sub para mostrar cómo se puede lanzar un juego. ¡Se creativo!
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | ----- | --- | - |
| | Se colocan tres elementos en la pantalla y se manipulan | Se colocan dos elementos en la pantalla y se manipulan | Un elemento se coloca en la pantalla y se manipula |

View File

@@ -1,11 +0,0 @@
# Conception d'un jeu
## Instructions
À l'aide des exemples de code de la leçon, concevez une représentation d'un jeu que vous aimez. Celà devra être un jeu simple, mais le but est d'utiliser soit le modèle de classe, soit le modèle de composition et le modèle pub/sub pour montrer comment un jeu pourrait se lancer. Faites preuve de créativité!
## Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | Trois éléments sont placés sur l'écran et manipulés | Deux éléments sont placés sur l'écran et manipulés | Un élément est placé sur l'écran et manipulé |

View File

@@ -1,11 +0,0 @@
# खेल का दिखावटी माडल
## अनुदेश
पाठ में कोड नमूनों का उपयोग करते हुए, आप जिस खेल का आनंद लेते हैं उसका प्रतिनिधित्व लिखें.यह एक साधारण खेल होना होगा, लेकिन लक्ष्य यह है कि क्लासेज या कम्पोजीशन पैटर्न और pub/sub पैटर्न का उपयोग करें यह दिखाने के लिए कि कोई गेम कैसे लॉन्च हो सकता है. रचनात्मक हो!
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | ----------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------- |
| | तीन तत्वों को स्क्रीन पर रखा जाता है और हेरफेर किया जाता है | दो तत्वों को स्क्रीन पर रखा जाता है और हेरफेर किया जाता है | एक तत्व को स्क्रीन पर रखा जाता है और हेरफेर किया जाता है |

View File

@@ -1,11 +0,0 @@
# Produrre uno schizzo di un gioco
## Istruzioni
Utilizzando gli esempi di codice nella lezione, scrivere una rappresentazione di un gioco che piace. Dovrà essere un gioco semplice, ma l'obiettivo è usare la classe o il modello di composizione e il modello pub/sub per mostrare come potrebbe essere avviato un gioco. Si dia sfogo alla propria creatività!
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | Tre elementi vengono posizionati sullo schermo e manipolati | Due elementi vengono posizionati sullo schermo e manipolati | Un elemento viene posizionato sullo schermo e manipolato |

View File

@@ -1,11 +0,0 @@
# ゲームのモックアップ
## 説明書
レッスンで使用したコードサンプルを使用して、好きなゲームの表現を書いてください。単純なゲームである必要がありますが、目標は、クラスまたは構成パターンと pub/sub パターンのいずれかを使用して、ゲームがどのように起動するかを示すことです。創造力を発揮してください。
## ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | 3つの要素を画面上に配置して操作する | 2つの要素を画面上に配置して操作する | 1つの要素を画面上に配置して操作する |

View File

@@ -1,11 +0,0 @@
# 게임 제작하기
## 설명
수업에 있는 예제 코드를 사용해서 게임을 만들어 봅시다. 간단한 게임이어야 하며, 목표는 class 또는 composition 패턴과 pub/sub 패턴 중 하나를 사용하여 게임을 만드는 것입니다. 창의력을 보여주세요!
## 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| 화면에 세개의 요소가 있고 조작할 수 있는 경우 | 화면에 두개의 요소가 있고 조작할 수 있는 경우 | 화면에 한개의 요소가 있고 조작할 수 있는 경우

View File

@@ -1,12 +0,0 @@
# Mock up permainan
## Arahan
Dengan menggunakan contoh kod dalam pelajaran, tulis representasi permainan yang anda nikmati. Ini mestilah permainan yang sederhana, tetapi tujuannya adalah menggunakan kelas atau corak komposisi dan corak pub / sub untuk menunjukkan bagaimana permainan dapat dilancarkan. Dapatkan kreatif!
## Rubrik
| Kriteria |
Contoh | Mencukupi | Usaha Lagi |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | Tiga elemen diletakkan di skrin dan dimanipulasi | Dua elemen diletakkan di skrin dan dimanipulasi | Satu elemen diletakkan di skrin dan dimanipulasi |

View File

@@ -1,11 +0,0 @@
# Maak een spel na
## Instructies
Gebruik de codevoorbeelden in de les om een weergave te maken van een spel dat u leuk vindt. Het zal een eenvoudig spel moeten zijn, maar het doel is om ofwel de class of het compositiepatroon en het pub/subpatroon te gebruiken om te laten zien hoe een spel zou kunnen starten. Wees creatief!
## Rubriek
| Criteria | Voorbeeldig | Voldoende | Moet worden verbeterd |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | Drie elementen worden op het scherm geplaatst en gemanipuleerd | Twee elementen worden op het scherm geplaatst en gemanipuleerd | Een element wordt op het scherm geplaatst en gemanipuleerd |

View File

@@ -1,11 +0,0 @@
# 建立遊戲雛形
## 簡介
使用本課程中的程式案例,編寫一款你喜歡的遊戲呈現方式。這是一款簡單小規模的遊戲,目的是要能以 class、組合模式與發布訂閱模式呈現遊戲的運作方式。發揮你的創意
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | -------------------------- | -------------------------- | ---------------------------- |
| | 畫面上有三個元素且能被控制 | 畫面上有兩個元素且能被控制 | 畫面上只有一個元素且能被控制 |

View File

@@ -1,212 +0,0 @@
# Construye un juego espacial Parte II: Dibuja héroes y monstruos en el lienzo
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31)
## El lienzo
El lienzo es un elemento HTML que por defecto no tiene contenido; es una pizarra en blanco. Necesita agregarle un dibujo sobre él.
✅ Lea [más sobre la API Canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API) en MDN.
Así es como se declara normalmente, como parte del cuerpo de la página:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
Arriba estamos configurando el `id`, `width` y `alto`.
- `id`: establezca esto para que pueda obtener una referencia cuando necesite interactuar con él.
- `width`: este es el ancho del elemento.
- `height`: esta es la altura del elemento.
## Dibujar geometría simple
Canvas utiliza un sistema de coordenadas cartesiano para dibujar cosas. Por lo tanto, usa un eje x y un eje y para expresar dónde se encuentra algo. La ubicación `0,0` es la posición superior izquierda y la inferior derecha es lo que se mencionó que era el ANCHO y ALTO del lienzo.
! [cuadrícula del lienzo](canvas_grid.png)
> Imagen de [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
Para dibujar en el elemento de lienzo, deberá seguir los siguientes pasos:
1. **Obtenga una referencia** al elemento Canvas.
1. **Obtenga una referencia** en el elemento Context que se encuentra en el elemento canvas.
1. **Realice una operación de dibujo** utilizando el elemento de contexto.
El código para los pasos anteriores generalmente se ve así:
```javascript
// dibuja un rectángulo rojo
// 1. obtener la referencia del lienzo
canvas = document.getElementById("myCanvas");
// 2. establecer el contexto en 2D para dibujar formas básicas
ctx = canvas.getContext("2d");
// 3. rellénalo con el color rojo
ctx.fillStyle = 'red';
// 4. y dibuja un rectángulo con estos parámetros, configurando la ubicación y el tamaño
ctx.fillRect(0,0, 200, 200) // x, y, ancho, alto
```
✅ La API de Canvas se enfoca principalmente en formas 2D, pero también puede dibujar elementos 3D en un sitio web; para esto, puede usar la [API WebGL](https://developer.mozilla.org/docs/Web/API/WebGL_API).
Puede dibujar todo tipo de cosas con la API de Canvas como:
- **Formas geométricas**, ya mostramos cómo dibujar un rectángulo, pero hay mucho más que puedes dibujar.
- **Texto**, puede dibujar un texto con cualquier fuente y color que desee.
- **Imágenes**, puede dibujar una imagen basada en un activo de imagen como .jpg o .png, por ejemplo.
✅ ¡Pruébalo! Sabes cómo dibujar un rectángulo, ¿puedes dibujar un círculo en una página? Eche un vistazo a algunos dibujos de Canvas interesantes en CodePen. Aquí hay un [ejemplo particularmente impresionante](https://codepen.io/dissimulate/pen/KrAwx).
## Cargar y dibujar un recurso de imagen
Usted carga un activo de imagen creando un objeto `Image` y estableciendo su propiedad `src`. Luego escuchas el evento `load` para saber cuándo está listo para usarse. El código se ve así:
### Cargar activo
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// image loaded and ready to be used
}
```
### Cargar patrón de activos
Se recomienda envolver lo anterior en una construcción así, para que sea más fácil de usar y solo intente manipularlo cuando esté completamente cargado:
```javascript
async function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// imagen cargada y lista para ser utilizada
}
resolve(img);
})
}
// usar así
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
Para dibujar activos del juego en una pantalla, su código se vería así:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
### Qué construir
Construirá una página web con un elemento Canvas. Debería representar una pantalla negra `1024*768`. Le proporcionamos dos imágenes:
- Barco héroe
![Barco héroe](../solution/assets/player.png)
- Monstruo 5*5
![Monster ship](../solution/assets/enemyShip.png)
### Pasos recomendados para iniciar el desarrollo
Busque los archivos que se han creado para usted en la subcarpeta `your-work`. Debe contener lo siguiente:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Abra la copia de esta carpeta en Visual Studio Code. Debe tener una configuración de entorno de desarrollo local, preferiblemente con Visual Studio Code con NPM y Node instalados. Si no tiene `npm` configurado en su computadora, [aquí le explicamos cómo hacerlo](https://www.npmjs.com/get-npm).
Inicie su proyecto navegando a la carpeta `your_work`:
```bash
cd your-work
npm start
```
Lo anterior iniciará un servidor HTTP en la dirección `http: // localhost: 5000`. Abra un navegador e ingrese esa dirección. Es una página en blanco ahora mismo, pero eso cambiará
> Nota: para ver los cambios en su pantalla, actualice su navegador.
### Agregar código
Agregue el código necesario a `your-work/app.js` para resolver lo siguiente
1. **Dibuja** un lienzo con fondo negro
> consejo: agregue dos líneas debajo del TODO apropiado en `/app.js`, estableciendo el elemento `ctx` en negro y las coordenadas superior / izquierda en 0,0 y la altura y el ancho para que sean iguales a los del lienzo.
2. **Cargar** texturas
> consejo: agregue las imágenes del jugador y del enemigo usando `await loadTexture` y pasando la ruta de la imagen. ¡Todavía no los verá en la pantalla!
3. **Dibuja** héroe en el centro de la pantalla en la mitad inferior
> consejo: use la API `drawImage` para dibujar heroImg en la pantalla, configurando `canvas.width / 2 - 45` y `canvas.height - canvas.height / 4)`;
4. **Dibujar** 5 * 5 monstruos
> consejo: ahora puedes descomentar el código para dibujar enemigos en la pantalla. A continuación, vaya a la función `createEnemies` y constrúyala.
Primero, configure algunas constantes:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
luego, crea un bucle para dibujar la matriz de monstruos en la pantalla:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## Resultado
El resultado final debería verse así:
![Pantalla negra con un héroe y monstruos 5*5](../partI-solution.png)
## Solución
Intente resolverlo usted mismo primero, pero si se atasca, eche un vistazo a una [solución](solution/app.js)
🚀 Desafío: ha aprendido a dibujar con la API Canvas enfocada en 2D; eche un vistazo a la [API WebGL](https://developer.mozilla.org/docs/Web/API/WebGL_API) e intente dibujar un objeto 3D.
## [Post-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32)
## Revisión y autoestudio
Obtenga más información sobre la API de Canvas [leyendo sobre ella](https://developer.mozilla.org/docs/Web/API/Canvas_API).
**Tarea**: [Jugar con la API de Canvas](assignment.es.md)

View File

@@ -1,216 +0,0 @@
# Construisez un jeu spatial, partie 2 : dessinez des héros et des monstres sur le canevas
## Quiz préalable
[Quiz préalable](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=fr)
## Le canevas
Le canevas est un élément HTML qui par défaut n'a pas de contenu ; c'est une ardoise vierge. Vous devez dessiner dessus pour y ajouter quelque chose.
✅ Lisez en [plus sur l'API Canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API) sur MDN.
Voici comment il est généralement déclaré, dans le corps de la page:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
Ci-dessus, nous définissons `id`, `width` et `height`.
- `id`: définissez-le pour obtenir une référence lorsque vous avez besoin d'interagir avec lui.
- `width`: c'est la largeur de l'élément.
- `height`: c'est la hauteur de l'élément.
## Dessiner une géométrie simple
Le Canvas utilise un système de coordonnées cartésiennes pour dessiner des choses. Ainsi, il utilise un axe x et un axe y pour exprimer où se trouve quelque chose. L'emplacement `0,0` est la position en haut à gauche et en bas à droite ce que vous appelez également la LARGEUR et la HAUTEUR du canevas.
![la grille du canevas](../canvas_grid.png)
> Image de [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
Pour dessiner sur l'élément de canevas, vous devrez suivre les étapes suivantes:
1. **Obtenir une référence** à l'élément Canvas.
1. **Obtenez une référence** sur l'élément Context qui se trouve sur l'élément canvas.
1. **Effectuez une opération de dessin** à l'aide de l'élément de contexte.
Le code pour les étapes ci-dessus ressemble communément à ceci:
```javascript
// dessine un rectangle rouge
//1. obtenir la référence du canevas
canvas = document.getElementById("myCanvas");
//2. définir le contexte en 2D pour dessiner des formes de base
ctx = canvas.getContext("2d");
//3. le remplir avec la couleur rouge
ctx.fillStyle = 'red';
//4. et dessiner un rectangle avec ces paramètres, en définissant l'emplacement et la taille
ctx.fillRect(0,0, 200, 200) // x,y, largeur, hauteur
```
✅ L'API Canvas se concentre principalement sur les formes 2D, mais vous pouvez également dessiner des éléments 3D sur un site Web ; pour cela, vous pouvez utiliser l'[API WebGL](https://developer.mozilla.org/docs/Web/API/WebGL_API).
Vous pouvez dessiner toutes sortes de choses avec l'API Canvas comme:
- **Des formes géométriques**, nous avons déjà montré comment dessiner un rectangle, mais il y a beaucoup plus que vous pouvez dessiner.
- **Du texte**, vous pouvez dessiner un texte avec la police et la couleur de votre choix.
- **Des images**, vous pouvez dessiner une image basée sur un élément d'image comme un .jpg ou .png par exemple.
✅ Essayez-le ! Vous savez dessiner un rectangle, pouvez-vous dessiner un cercle sur une page ? Jetez un œil à quelques dessins Canvas intéressants sur CodePen. Voici un [exemple particulièrement impressionnant](https://codepen.io/dissimulate/pen/KrAwx).
## Charger et dessiner un élément d'image
Vous chargez une ressource image en créant un objet `Image` et définissez sa propriété `src`. Ensuite, vous écoutez l'événement `load` pour savoir quand il est prêt à être utilisé. Le code ressemble à ceci:
### Charger l'élément
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// image chargée et prête à être utilisée
}
```
### Charger le modèle d'élément
Il est recommandé d'envelopper ce qui précède dans une construction comme celle-ci, il est donc plus facile à utiliser et vous n'essayez de le manipuler que lorsqu'il est complètement chargé:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// image chargée et prête à être utilisée
resolve(img);
}
})
}
// utiliser comme celà
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
Pour dessiner les ressources du jeu sur un écran, votre code ressemblerait à ceci:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## Il est maintenant temps de commencer à créer votre jeu
### Quoi construire
Vous allez créer une page Web avec un élément Canvas. Il devrait rendre un écran noir `1024*768`. Nous vous avons fourni deux images:
- Navire héros
![Hero ship](../solution/assets/player.png)
- Monstre 5*5
![Monster ship](../solution/assets/enemyShip.png)
### Étapes recommandées pour commencer le développement
Recherchez les fichiers qui ont été créés pour vous dans le sous-dossier `your-work`. Il doit contenir les éléments suivants:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Ouvrez la copie de ce dossier dans Visual Studio Code. Vous devez disposer d'une configuration d'environnement de développement local, de préférence avec Visual Studio Code ainsi que NPM et Node installés. Si `npm` n'est pas configuré sur votre ordinateur, [voici comment procéder](https://www.npmjs.com/get-npm).
Démarrez votre projet en accédant au dossier `your_work`:
```bash
cd your-work
npm start
```
Ce qui précède démarrera un serveur HTTP à l'adresse `http://localhost:5000`. Ouvrez un navigateur et saisissez cette adresse. C'est une page blanche actuellement, mais cela va changer
> Remarque : pour voir les changements sur votre écran, actualisez votre navigateur.
### Ajouter du code
Ajoutez le code nécessaire à `your-work/app.js` pour résoudre le problème ci-dessous
1. **Dessinez** un canevas avec un fond noir
> astuce : ajoutez deux lignes sous le TODO approprié dans `/app.js`, en définissant l'élément `ctx` sur noir et les coordonnées haut/gauche à 0,0 et la hauteur et la largeur à égales à celles du canevas.
2. **Charger** les textures
> astuce : ajoutez les images du joueur et de l'ennemi en utilisant `await loadTexture` et en passant le chemin de l'image. Vous ne les verrez pas encore à l'écran!
3. **Dessinez** le héros au centre de l'écran dans la moitié inférieure
> astuce : utilisez l'API `drawImage` pour dessiner heroImg à l'écran, en définissant `canvas.width / 2 - 45` et `canvas.height - canvas.height / 4)`;
4. **Dessinez** les monstres 5*5
> astuce : vous pouvez maintenant décommenter le code pour dessiner les ennemis à l'écran. Ensuite, allez à la fonction `createEnemies` et compilez la.
Tout d'abord, configurez quelques constantes:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
Ensuite, créez une boucle pour dessiner le tableau de monstres sur l'écran:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## Résultat
Le résultat final devrait ressembler à ça:
![Un écran noir avec un héros et 5*5 monstres](../partI-solution.png)
## Solution
Veuillez d'abord essayer de le résoudre vous-même, mais si vous êtes bloqué, jetez un œil à une [solution](../solution/app.js)
---
## 🚀 Challenge
Vous avez appris à dessiner avec l'API Canvas axée sur la 2D ; jetez un œil à l'[API WebGL](https://developer.mozilla.org/docs/Web/API/WebGL_API), et essayez de dessiner un objet 3D.
## Quiz de validation des connaissances
[Quiz de validation des connaissances](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=fr)
## Révision et étude personnelle
En savoir plus sur l'API Canvas en [lisant à ce sujet](https://developer.mozilla.org/docs/Web/API/Canvas_API).
## Affectation
[Jouer avec l'API Canvas](assignment.fr.md)

View File

@@ -1,218 +0,0 @@
# एक अंतरिक्ष खेल भाग 2 बनाएँ: कैनवस के लिए नायक और राक्षस बनाएँ
## लेक्चर पाहिले की क्विज
[लेक्चर पाहिले की क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=hi)
## कैनवास
कैनवास एक HTML तत्व है जो डिफ़ॉल्ट रूप से कोई सामग्री नहीं है; यह एक खाली स्लेट है. आपको उस पर ड्राइंग करके इसे जोड़ना होगा.
✅ MDN पर [कैनवस एपीआई के बारे में और पढ़ें](https://developer.mozilla.org/docs/Web/API/Canvas_API).
यहां बताया गया है कि पेज के मुख्य भाग के रूप में यह आमतौर पर घोषित किया जाता है:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
ऊपर हम `id`,`width` और `height` सेट कर रहे हैं
- `id`: इसे सेट करें ताकि आप एक संदर्भ प्राप्त कर सकें जब आपको इसके साथ परस्पर करने की आवश्यकता हो.
- `width`: यह तत्व की चौड़ाई है.
- `height`: यह तत्व की ऊंचाई है.
## सरल ज्यामिति खींचना
कैनवस चीजों को आकर्षित करने के लिए एक कार्टेशियन समन्वय प्रणाली का उपयोग कर रहा है.
इस प्रकार यह x- अक्ष और y- अक्ष का उपयोग करता है जहां कुछ स्थित है व्यक्त करने के लिए.स्थान `0,0` शीर्ष बाईं स्थिति है और नीचे दायाँ हिस्सा है जिसे आपने कैनवास का WIDTH और HEIGHT कहा है.
![कैनवास का ग्रिड](../canvas_grid.png)
> [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes) से छवि
कैनवास तत्व पर आकर्षित करने के लिए आपको निम्नलिखित चरणों से गुजरना होगा:
1. **एक संदर्भ प्राप्त करें** कैनवास तत्व के लिए.
2. **एक संदर्भ प्राप्त करें** संदर्भ तत्व पर जो कैनवास तत्व पर बैठता है.
3. **एक ड्राइंग ऑपरेशन** संदर्भ तत्व का उपयोग करके.
उपरोक्त चरणों के लिए कोड आमतौर पर ऐसा दिखता है:
```javascript
// draws a red rectangle
//1. get the canvas reference
canvas = document.getElementById("myCanvas");
//2. set the context to 2D to draw basic shapes
ctx = canvas.getContext("2d");
//3. fill it with the color red
ctx.fillStyle = "red";
//4. and draw a rectangle with these parameters, setting location and size
ctx.fillRect(0, 0, 200, 200); // x,y,width, height
```
✅ कैनवस एपीआई ज्यादातर 2डी आकृतियों पर केंद्रित है, लेकिन आप एक वेब साइट पर 3डी तत्वों को भी आकर्षित कर सकते हैं; इसके लिए, आप [वेबगियल एपीआई](https://developer.mozilla.org/docs/Web/API/WebGL_API) का उपयोग कर सकते हैं.
आप कैनवस एपीआई के साथ सभी प्रकार की चीजें आकर्षित कर सकते हैं जैसे की :
- **Geometrical shapes**, हमने पहले ही दिखाया है कि एक आयत कैसे खींचना है, लेकिन बहुत कुछ है जो आप खींच सकते हैं.
- **Text**, आप अपनी इच्छानुसार किसी भी फ़ॉन्ट और रंग के साथ एक पाठ खींच सकते हैं.
- **Images**, आप उदाहरण के लिए .jpg या .png जैसी छवि परिसंपत्ति के आधार पर चित्र बना सकते हैं.
✅ कोशिश करो! आप जानते हैं कि एक आयत कैसे बनाई जाती है, क्या आप किसी पृष्ठ पर एक वृत्त खींच सकते हैं? कोडपेन पर कुछ दिलचस्प कैनवस ड्रॉइंग पर एक नज़र डालें. यहाँ एक [विशेष रूप से प्रभावशाली उदाहरण है](https://codepen.io/dissimulate/pen/KrAwx).
## छवि एसेट लोड और ड्रा करे
आप एक 'Image' ऑब्जेक्ट बनाकर एक छवि एसेट लोड करते हैं और इसकी `src` गुण सेट करते हैं.तब आप यह जानने के लिए `load` घटना सुनते हैं कि यह कब उपयोग होने के लिए तैयार है. कोड इस तरह दिखता है:
### लोड एसेट
```javascript
const img = new Image();
img.src = "path/to/my/image.png";
img.onload = () => {
// image loaded and ready to be used
};
```
### लोड एसेट पैटर्न
इसे एक निर्माण में ऊपर से लपेटने की सिफारिश की गई है, इसलिए इसका उपयोग करना आसान है और आप इसे पूरी तरह से लोड होने पर केवल हेरफेर करने का प्रयास करते हैं:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// image loaded and ready to be used
resolve(img);
};
});
}
// use like so
async function run() {
const heroImg = await loadAsset("hero.png");
const monsterImg = await loadAsset("monster.png");
}
```
गेम एसेट्स को स्क्रीन पर खींचने के लिए, आपका कोड इस तरह दिखेगा:
```javascript
async function run() {
const heroImg = await loadAsset("hero.png");
const monsterImg = await loadAsset("monster.png");
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width / 2, canvas.height / 2);
ctx.drawImage(monsterImg, 0, 0);
}
```
## अब अपने खेल का निर्माण शुरू करने का समय आ गया है
### क्या बनना है
आप कैनवास तत्व के साथ एक वेब पेज बनाएंगे। यह एक काली स्क्रीन `1024 * 768` को प्रस्तुत करना चाहिए। हमने आपको दो चित्र प्रदान किए हैं:
- हीरो शिप
![हीरो शिप ](../solution/assets/player.png)
- 5\*5 मॉन्स्टर
![मॉन्स्टर शिप](../solution/assets/enemyShip.png)
### विकास शुरू करने के लिए अनुशंसित कदम
उन फ़ाइलों का पता लगाएँ जो आपके लिए `your-work` सब फ़ोल्डर में बनाई गई हैं. इसमें निम्नलिखित शामिल होना चाहिए:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
विजुअल स्टूडियो कोड में इस फ़ोल्डर की कॉपी खोलें। आपके पास स्थानीय विकास पर्यावरण सेटअप होना चाहिए, अधिमानतः एनपीएम और नोड के साथ विजुअल स्टूडियो कोड स्थापित किया जाना चाहिए. यदि आपके पास अपने कंप्यूटर पर `npm` सेट नहीं है, तो [यहाँ है कि कैसे करें](https://www.npmjs.com/get-npm).
`Your_work` फ़ोल्डर में नेविगेट करके अपनी परियोजना शुरू करें:
```bash
cd your-work
npm start
```
उपरोक्त पते पर एक HTTP सर्वर शुरू होगा `http: // localhost: 5000`। एक ब्राउज़र और उस पते पर इनपुट खोलें। यह अभी एक रिक्त पृष्ठ है, लेकिन यह बदल जाएगा
> नोट: अपनी स्क्रीन पर परिवर्तन देखने के लिए, अपने ब्राउज़र को ताज़ा करें.
### कोड जोड़ें
नीचे हल करने के लिए `your-work/app.js` के लिए आवश्यक कोड जोड़ें
1. काली पृष्ठभूमि के साथ एक कैनवास **ड्रा** करे
> टिप: `/app.js` में उपयुक्त TODO के तहत दो लाइनें जोड़ें,`ctx` तत्व को काला बनाने के लिए और शीर्ष/बाएँ निर्देशांक 0,0 पर हो और ऊँचाई और चौड़ाई कैनवास के बराबर हो।
2. **भार** बनावट
> टिप: `await loadTexture` का उपयोग करके खिलाड़ी और दुश्मन की छवियों को जोड़ें और छवि पथ में पास करें। आप उन्हें अभी तक स्क्रीन पर नहीं देखेंगे!
3. नीचे आधे में स्क्रीन के केंद्र में नायक **ड्रा** करे
> टिप: स्क्रीन पर HeroImg ड्रा करने के लिए `drawImage` API का उपयोग करें, `canvas.width / 2 - 45` और `canvas.height - canvas.height / 4)` की सेटिंग;
4. 5\*5 मॉन्स्टर **ड्रा** करे
> टिप: अब आप स्क्रीन पर दुश्मनों को आकर्षित करने के लिए कोड की टिप्पणी हटा सकते हैं। अगला, `createEnemy` फ़ंक्शन पर जाएं और इसे बनाएं.
सबसे पहले, कुछ स्थिरांक स्थापित करें:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
फिर, स्क्रीन पर राक्षसों की सरणी खींचने के लिए एक लूप बनाएं:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## परिणाम
समाप्त परिणाम ऐसा दिखना चाहिए:
![एक नायक और 5 * 5 राक्षसों के साथ काली स्क्रीन](../partI-solution.png)
## घोल
कृपया पहले इसे स्वयं हल करने का प्रयास करें, लेकिन यदि आप अटक जाते हैं, तो एक [समाधान] (../solution/app.js) पर एक नज़र डालें
---
## 🚀 चुनौती
आपने 2डी-केंद्रित कैनवस एपीआई के साथ ड्राइंग के बारे में सीखा है; [वेबगियल एपीआई](https://developer.mozilla.org/docs/Web/API/WebGL_API) पर एक नज़र डालें, और एक 3D ऑब्जेक्ट खींचने का प्रयास करें.
## पोस्ट-व्याख्यान प्रश्नोत्तरी
[पोस्ट-व्याख्यान प्रश्नोत्तरी](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=hi)
## समीक्षा और स्व अध्ययन
कैनवस एपीआई के बारे में अधिक जानकारी के लिए [इसके बारे में पढ़े](https://developer.mozilla.org/docs/Web/API/Canvas_API).
## असाइनमेंट
[कैनवस एपीआई के साथ खेले](assignment.hi.md)

View File

@@ -1,216 +0,0 @@
# Costruire un Gioco Spaziale Parte 2: Disegnare Eroi e Mostri sull'elemento Canvas
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=it)
## Canvas
Canvas è un elemento HTML che per impostazione predefinita non ha contenuto; è una lavagna vuota. Si può riempirla disegnandoci sopra.
✅ [Ulteriori informazioni sull'API Canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API) su MDN.
Ecco come viene tipicamente dichiarato, come parte dell'elemento body della pagina:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
Sopra si è impostato l'`id`entificativo, la larghezza `(width)` e l'altezza (`height`).
- `id`: va impostato in modo da poter ottenere un riferimento quando si deve interagire con l'elemento.
- `width`: questa è la larghezza dell'elemento.
- `height`: questa è l'altezza dell'elemento.
## Disegnare una geometria semplice
Canvas utilizza un sistema di coordinate cartesiane per disegnare le cose. Quindi utilizza un asse x e un asse y per esprimere dove si trova qualcosa. La posizione `0,0` è la posizione in alto a sinistra e quella in basso a destra è ciò che si è determinato come larghezza (WIDTH) e altezza (HEIGHT) del canvas
![la griglia del canvas](../canvas_grid.png)
> Immagine da [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
Per disegnare sull'elemento canvas si dovranno eseguire i seguenti passaggi:
1. **Ottenere un riferimento** all'elemento Canvas.
1. **Ottenere un riferimento** all'elemento Context che si trova sull'elemento canvas.
1. **Eseguire un'operazione di disegno** utilizzando l'elemento context.
Il codice per i passaggi precedenti di solito ha questo aspetto:
```javascript
// disegna un rettangolo rosso
//1. ottiene il riferimento per il canvas
canvas = document.getElementById("myCanvas");
//2. ottiene l'oggetto context per disegnare forme basiche in 2D
ctx = canvas.getContext("2d");
//3. lo riempie con il colore rosso
ctx.fillStyle = 'red';
//4. e disegna un rettangolo con questi parametri, impostando posizione e dimensione
ctx.fillRect(0,0, 200, 200) // x,y,larghezza, altezza
```
✅ L'API Canvas si concentra principalmente su forme 2D, ma si possono anche disegnare elementi 3D su un sito web; per questo, si potrebbe utilizzare l' [API WebGL](https://developer.mozilla.org/docs/Web/API/WebGL_API).
Si può disegnare ogni sorta di cose con l'API Canvas come:
- **Forme geometriche**, è già stato mostrato come disegnare un rettangolo, ma c'è molto di più che si può disegnare.
- **Testo**, si può disegnare un testo con qualsiasi carattere e colore si desideri.
- **Immagini**, si puòdisegnare un'immagine basandosi su una risorsa immagine come .jpg o .png, ad esempio.
Si faccia una prova! Si sa come disegnare un rettangolo, si può disegnare un cerchio su una pagina? Si dia un'occhiata ad alcuni interessanti disegni su canvas su CodePen. Ecco un [esempio particolarmente degno di nota](https://codepen.io/dissimulate/pen/KrAwx).
## Caricare e disegnare una risorsa immagine
Si carica una risorsa immagine creando un oggetto `Image` e impostando la sua proprietà `src` . Quindi ci si mette in ascolto per l'evento di caricamento (`load`) per sapere quando è pronto per essere utilizzato. Il codice si presenta cosí:
### Caricamento risorsa
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// immagine caricata e pronta all'uso
}
```
### Modello di Caricamento Risorsa
Si consiglia di racchiudere quanto sopra in un costrutto come questo, così è più facile da usare e si tenta di manipolarlo solo quando è completamente caricato:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// immagine caricata e pronta all'uso
resolve(img);
}
})
}
// usarlo in questo modo
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
Per disegnare risorse di gioco su uno schermo, il codice sarà simile a questo:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## Ora è il momento di iniziare a costruire il gioco
### Cosa costruire
Si costruirà una pagina web con un elemento Canvas. Si dovrebbe rendere uno schermo nero `1024 * 768`. Sono state fornite due immagini:
- Astronave dell'eroe
![Nave dell'eroe](../solution/assets/player.png)
- Mostro 5*5
![Nave del mostro](../solution/assets/enemyShip.png)
### Passaggi consigliati per iniziare lo sviluppo
Individuare i file che già sono stati creati nella sottocartella `your-work` della cartella di lavoro Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Aprire una copia di questa cartella in Visual Studio Code. È necessario disporre di una configurazione di ambiente di sviluppo locale, preferibilmente con Visual Studio Code con NPM e Node installati. Se non si è impostato `npm` sul proprio computer, [ecco come farlo](https://www.npmjs.com/get-npm).
Inizializzare il proprio progetto accedendo alla cartella `your_work` :
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP sull'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo. Al momento è una pagina vuota, ma cambierà
> Nota: per vedere le modifiche sullo schermo, aggiornare il contenuto del browser.
### Aggiungere codice
Aggiungi il codice necessario al file `your-work/app.js` per risolvere quanto segue
1. **Disegnare** un oggetto canvas con sfondo nero
> suggerimento: aggiungere due righe sotto il TODO appropriato in `/app.js`, impostando l'elemento `ctx` in modo che sia nero e le coordinate alto/sinistra a 0,0 e l'altezza e la larghezza uguali a quelle del canvas.
2. **Caricare** le strutture di gioco
> suggerimento: aggiungere le immagini del giocatore e del nemico usando `await loadTexture`, passando il percorso dell'immagine. Non saranno ancora visibili sullo schermo!
3. **Disegnare** l'eroe al centro dello schermo nella metà inferiore
> suggerimento: usare l'API `drawImage` per disegnare `heroImg` sullo schermo, impostando `canvas.width / 2 - 45` e `canvas.height - canvas.height / 4` come valori di coordinate x, y
4. **Disegnare** mostri 5*5
> suggerimento: ora si può rimuovere il commento dal codice per disegnare i nemici sullo schermo. Successivamente, passare alla funzione `createEnemies` e crearla.
Per prima cosa, impostare alcune costanti:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
quindi, creare un ciclo per disegnare l'array di mostri sullo schermo:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## Risultato
Il risultato finale dovrebbe essere così:
![Schermo nero con un eroe e mostri 5*5](../partI-solution.png)
## Soluzione
Per favore provare a risolverlo da soli, ma se si rimane bloccati, dare un'occhiata alla [soluzione](../solution/app.js)
---
## 🚀 Sfida
Si è imparato a disegnare con l'API Canvas incentrata sul 2D; dare un'occhiata all['API WebGL API](https://developer.mozilla.org/it/docs/Web/API/WebGL_API) e provare a disegnare un oggetto 3D.
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=it)
## Revisione e Auto Apprendimento
Scoprire di più sull'API Canvas raccogliendo [informazioni su di essa](https://developer.mozilla.org/docs/Web/API/Canvas_API).
## Compito
[Giocare con l'API Canvas](assignment.it.md)

View File

@@ -1,216 +0,0 @@
# スペースゲーム構築プロジェクト その 2: ヒーローとモンスターをキャンバスに描く
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=ja)
## Canvas
Canvas は HTML 要素で、デフォルトでは何のコンテンツもありません。何もない白紙の状態です。その上に描画することで、Canvas に追加する必要があります。
✅ MDN の [Canvas API についての詳細](https://developer.mozilla.org/ja/docs/Web/API/Canvas_API)はこちらをご覧ください。
ページの本文の一部として、一般的にどのように宣言されているかをご紹介します。
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
上では `id`, `width`, `height` を設定しています。
- `id`: 設定することで、これと対話する必要があるときに参照を取得できるようになります
- `width`: これは要素の幅です
- `height`: これは要素の高さです
## 簡単な幾何学図形の描画
キャンバスは、直交座標系を使って物を描いています。したがって、何かがどこにあるかを表現するために x 軸と y 軸を使用しています。`0,0` の位置が左上の位置で、右下の位置がキャンバスの幅と高さと言ったところです。
![the canvas's grid](../canvas_grid.png)
> 画像は [MDN](https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes) より
キャンバス要素に描画するには、以下の手順を踏む必要があります。
1. Canvas 要素への**参照を取得します**
1. canvas 要素の上にある Context 要素の**参照を取得します**
1. context 要素を使用して**描画操作を行います**
上記の手順のコードは、通常次のようになります。
```javascript
// 赤い四角を描きます。
//1. キャンバスの参照を取得します
canvas = document.getElementById("myCanvas");
//2. コンテキストを 2D に設定して基本的な図形を描画します。
ctx = canvas.getContext("2d");
//3. 赤で塗りつぶします。
ctx.fillStyle = 'red';
//4. そして、これらのパラメータで矩形を描画し、位置とサイズを設定します。
ctx.fillRect(0,0, 200, 200) // x, y, width, height
```
✅ Canvas API は主に 2D の図形に焦点を当てていますが、Web サイトに 3D の要素を描画することもできます。そのためには、[WebGL API](https://developer.mozilla.org/ja/docs/Web/API/WebGL_API) を使用するとよいでしょう。
Canvas API を使っていろいろなものを描くことができます。
- **幾何学的な形状**、我々はすでに長方形を描画する方法を示しましたが、あなたが描くことができるはるかに多くのものがあります
- **テキスト**は、任意のフォントと色でテキストを描くことができます
- **画像**では、例えば .jpg や .png のような画像アセットに基づいて画像を描くことができます
✅ やってみましょう! 長方形の描き方は知っていると思いますが、ページに円を描くことはできますか? CodePen に掲載されている面白い Canvas のドローイングを見てみましょう。ここに[特に印象的な例](https://codepen.io/dissimulate/pen/KrAwx)があります。
## 画像アセットの読み込みと描画
画像アセットをロードするには、`Image` オブジェクトを作成して `src` プロパティを設定します。そして、`load` イベントを待ち受け、それがいつ使用可能になるかを知ることができます。コードは次のようになります。
### アセットの読み込み
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// イメージが読み込まれて使用可能な状態になっています
}
```
### アセットの読み込みパターン
上記のようなコンストラクトで包んだ方が使いやすいですし、フルロードした時だけ操作しようとするのでおすすめです。
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// イメージが読み込まれて使用可能な状態になっています
resolve(img);
}
})
}
// 使いまわします
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
ゲームアセットを画面に描画するには、以下のようなコードになります。
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2, canvas.height/2);
ctx.drawImage(monsterImg, 0, 0);
}
```
## さあ、あなたのゲームを作り始める時が来ました。
### 何を構築するか
Canvas 要素を使って Web ページを作成します。これは黒い画面 `1024 * 768` をレンダリングする必要があります。画像は2枚用意しました。
- Hero の宇宙船
![Hero の宇宙船](../solution/assets/player.png)
- 5*5 monster
![Monster の宇宙船](../solution/assets/enemyShip.png)
### 開発を始めるための推奨ステップ
あなたのために作成されたファイルを `your-work` サブフォルダ内で探します。以下のファイルが含まれているはずです。
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
このフォルダのコピーを Visual Studio Code で開きます。ローカルの開発環境が設定されている必要があり、できれば Visual Studio Code に NPM と Node がインストールされている必要があります。もしあなたのコンピュータに `npm` がセットアップされていない場合は、[ここにその方法があります](https://www.npmjs.com/get-npm)。
`your_work` フォルダに移動してプロジェクトを開始します。
```bash
cd your-work
npm start
```
以上で、`http://localhost:5000` というアドレスに HTTP サーバが起動します。ブラウザを開いて、そのアドレスを入力してください。今は何も表示されていませんが、そのうち変わるでしょう。
> 注: 画面上の変更を確認するには、ブラウザを更新してください。
### コードの追加
以下の問題を解決するために必要なコードを `your-work/app.js` に追加します。
1. 黒い背景のキャンバスを**描画します**
> tip: `/app.js` の適切な TODO の下に 2 行を追加し、`ctx` 要素を黒にして上下の座標を 0,0 にし、高さと幅をキャンバスと同じにします。
2. テクスチャを**読み込みます**
> tip: プレイヤーと敵の画像を追加するには `await loadTexture` を使用し、画像パスを渡してください。まだ画面には表示されていません!
3. 下半分の画面中央にヒーローを**描画します**
> tip: heroImg を画面に描画するには `drawImage` API を使用し、`canvas.width / 2 - 45` と `canvas.height - canvas.height / 4)` を設定します。
4. 5*5 のモンスターを**描画します**
> tip: これで、画面上に敵を描画するコードのコメントを解除することができるようになりました。次に、`createEnemies` 関数に移動して、それを構築する。
まず、いくつかの定数を設定します。
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
そして、モンスターの配列を画面上に描画するループを作成します。
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## 結果
仕上がりはこんな感じになるはずです。
![hero と 5 * 5 モンスターで黒画面](../partI-solution.png)
## Solution
まずはご自身で解決してみていただきたいですが、行き詰った場合は [solution](../solution/app.js) を参考にしてみてください。
---
## 🚀 チャレンジ
2D を中心とした Canvas API での描画について学んできましたが、[WebGL API](https://developer.mozilla.org/ja/docs/Web/API/WebGL_API) を参考に、3D オブジェクトを描画してみましょう。
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=ja)
## 復習と自己学習
Canvas API については、[こちらを読んでいただく](https://developer.mozilla.org/ja/docs/Web/API/Canvas_API)ことで詳しく知ることができます。
## 課題
[Canvas API で遊ぶ](assignment.ja.md)

View File

@@ -1,216 +0,0 @@
# Space 게임 제작하기 파트 2: Canvas에 영웅과 몬스터 그리기
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=ko)
## Canvas
canvas는 내용이 없는 게 기본인 HTML 요소입니다; 빈 상태입니다. 그리는 작업으로 추가해야 합니다.
✅ MDN애서 [Canvas API에 대하여 더](https://developer.mozilla.org/docs/Web/API/Canvas_API) 읽어보세요.
보통 페이지 본문의 일부로 선언되는 방법은 다음과 같습니다:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
위에서 우리는 `id`, `width``height`를 설정합니다.
- `id`: 상호 작용을 해야 할 순간에 참조할 수 있도록 지정하세요.
- `width`: 요소의 너비입니다.
- `height`: 요소의 높이입니다.
## 간단한 geometry 그리기
캔버스는 데카르트 좌표계로 사물을 그립니다. 따라서 x-축과 y-축을 이용하여 무언가의 위치를 나타냅니다. 위치 `0,0`은 죄측 상단이며 우측 하단은 캔버스의 너비와 높이라고 말한 위치입니다.
![the canvas's grid](../canvas_grid.png)
> Image from [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
캔버스 요소에 그리려면 다음 단계를 거쳐야 합니다:
1. 캔버스 요소에 **참조를 가져옵니다**.
1. 캔버스 요소에 있는 Context 요소로 **참조를 가져옵니다**.
1. 컨텍스트 요소를 사용하여 **그리는 작업을 수행**합니다.
위 단계의 코드는 일반적으로 다음과 같습니다:
```javascript
// draws a red rectangle
//1. get the canvas reference
canvas = document.getElementById("myCanvas");
//2. set the context to 2D to draw basic shapes
ctx = canvas.getContext("2d");
//3. fill it with the color red
ctx.fillStyle = 'red';
//4. and draw a rectangle with these parameters, setting location and size
ctx.fillRect(0,0, 200, 200) // x,y,width, height
```
✅ Canvas API는 2D 모양에 가장 초점이 맞추어져 있습니다, 그러나 웹사이트에서 3D 요소를 그려야 된다면, [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API)를 사용해야 할 수도 있습니다.
Canvas API를 사용하면 다음과 같은 모든 종류를 그릴 수 있습니다:
- **기하학 모양**, 직사각형을 그리는 방법은 이미 보여 주었지만, 더 많이 그릴 수 있습니다.
- **텍스트**, 원하는 폰트와 색상으로 텍스트를 그릴 수 있습니다.
- **이미지**, 예를 들면 .jpg 혹은 .png와 같은 이미지 어셋을 바탕으로 이미지를 그릴 수 있습니다.
✅ 시도 해보세요! 직사각형을 어떻게 그리는지 알고 있으면, 패이지에 원을 그릴 수 있나요? CodePen에서 흥미로운 캔버스 그림을 보세요. 여기 [particularly impressive example](https://codepen.io/dissimulate/pen/KrAwx)이 있습니다.
## 이미지 어셋 불러오고 그리기
`Image` 객체를 만들고 `src` 속성을 설정하여 이미지 어셋을 불러옵니다. 그런 다음 `load` 이벤트를 수신하여 사용할 준비가 되었는지 알 수 있습니다. 코드는 다음과 같습니다:
### 어셋 불러오기
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// image loaded and ready to be used
}
```
### Load asset 패턴
위의 내용을 이 구조로 래핑하는 것이 좋습니다, 그래서 쓰기 더 쉽고 완전히 불러올 수 있을 때만 조작하려고 시도합니다:
```javascript
async function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// image loaded and ready to be used
}
resolve(img);
})
}
// use like so
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
게임 어셋을 화면에 그리려면, 코드는 다음과 같습니다:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## 이제 게임 제작을 시작할 시간입니다
### 무엇을 만드나요
Canvas 요소가 있는 웹 페이지를 만듭니다. 검은 화면 `1024 * 768`을 렌더링해야 합니다. 두 가지 이미지를 제공받았습니다:
- Hero ship
![Hero ship](../solution/assets/player.png)
- 5*5 monster
![Monster ship](../solution/assets/enemyShip.png)
### 개발 시작하기 위한 권장 단계
`your-work` 하위 폴더에서 생성된 파일을 찾습니다. 다음을 포함해야 합니다:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Visual Studio Code에서 폴더의 복사본을 엽니다. 로컬 개발 환경을 설정해야 합니다, NPM과 Node가 설치되어있는 Visual Studio Code를 사용하는 것이 좋습니다. 컴퓨터에 `npm`이 설정되어 있지 않은 경우에, [방법은 다음과 같습니다](https://www.npmjs.com/get-npm).
`your_work` 폴더로 이동하여 프로젝트를 시작합니다:
```bash
cd your-work
npm start
```
위 코드는 `http://localhost:5000` 주소에서 HTTP 서버를 시작합니다. 브라우저를 열고 해당 주소를 입력하세요. 지금은 비어있는 페이지지만, 곧 바뀔 것 입니다
> Note: 화면에서 변경점을 보고싶다면, 브라우저를 새로 고치세요.
### 코드 추가하기
필요한 코드를 `your-work/app.js`에 추가하여 아래 문제를 해결합니다
1. 검은 바탕으로 canvas **그리기**
> tip: `/app.js`의 TODO 아래에 적절히 두 줄을 추가하며, `ctx` 요소를 검은 색으로 설정하고 상단/좌측 좌표를 0,0으로 설정하고 높이와 너비를 캔버스와 동일하게 설정합니다.
2. 텍스쳐 **불러오기**
> tip: `await loadTexture`를 사용하여 플레이어와 적 이미지를 추가하고 이미지 경로를 전달합니다. 아직 화면에서 볼 수 없습니다!
3. 화면 중앙 하단의 중간에 영웅 **그리기**
> tip: `drawImage` API 를 사용하여 화면에 heroImg를 그립니다, `canvas.width / 2 - 45`와 `canvas.height - canvas.height / 4)`로 설정합니다.
4. 5*5 몬스터를 **그리기**
> tip: 이제 주석을 풀고 화면에 적을 그립니다. 그런 다음, `createEnemies` 함수로 가서 작성합니다.
먼저, 약간의 constants를 설정합니다:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
그런 다음, 화면에 몬스터 배열을 그리는 반복문을 만듭니다:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## 결과
완성된 결과는 아래와 같이 보이게 됩니다:
![Black screen with a hero and 5*5 monsters](../partI-solution.png)
## 솔루션
먼저 직접 해결해보고 문제가 발생한다면, [solution](../solution/app.js)을 보세요
---
## 🚀 도전
2D-중심의 Canvas API로 그리는 방식에 대해 배웠습니다; [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API)를 살펴보고, 3D 개체를 그려보세요.
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=ko)
## 리뷰 & 자기주도 학습
[reading about it](https://developer.mozilla.org/docs/Web/API/Canvas_API)을 통해 Canvas API에 대해 자세히 알아보세요.
## 과제
[Play with the Canvas API](../assignment.md)

View File

@@ -1,216 +0,0 @@
# Bina Permainan Angkasa Bahagian 2: Menarik Wira dan Monster ke Kanvas
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31)
## Kanvas
Kanvas adalah elemen HTML yang secara lalai tidak mempunyai isi; itu batu tulis kosong. Anda perlu menambahkannya dengan melukisnya.
✅ Baca [lebih lanjut mengenai Canvas API](https://developer.mozilla.org/docs/Web/API/Canvas_API) di MDN.
Inilah cara ia dinyatakan secara umum, sebagai bahagian badan halaman:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
Di atas kita menetapkan `id`, `lebar` dan `tinggi`.
- `id`: tetapkan ini supaya anda dapat memperoleh rujukan apabila anda perlu berinteraksi dengannya.
- `lebar`: ini adalah lebar elemen.
- `tinggi`: ini adalah ketinggian elemen.
## Melukis geometri ringkas
Canvas menggunakan sistem koordinat kartesian untuk menarik sesuatu. Oleh itu ia menggunakan paksi-x dan paksi-y untuk menyatakan di mana sesuatu berada. Lokasi `0,0` adalah kedudukan kiri atas dan kanan bawah adalah yang anda katakan sebagai Luas dan TINGGI kanvas.
![grid kanvas](../canvas_grid.png)
> Imej dari [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
Untuk menggunakan elemen kanvas, anda perlu melalui langkah-langkah berikut:
1. **Dapatkan rujukan** ke elemen Kanvas.
1. **Dapatkan rujukan** pada elemen Konteks yang terletak di elemen kanvas.
1. **Lakukan operasi menggambar** menggunakan elemen konteks.
Kod untuk langkah di atas biasanya kelihatan seperti:
```javascript
// melukis sebuah segi empat tepat berwarna merah
// 1. dapatkan rujukan kanvas
canvas = document.getElementById("myCanvas");
//2. tetapkan konteks ke 2D untuk melukis bentuk asas
ctx = canvas.getContext("2d");
//3. isi dengan warna merah
ctx.fillStyle = 'red';
//4. dan lukis sebuah segi empat tepat dengan parameter ini, tetapkan lokasi dan ukuran
ctx.fillRect(0,0, 200, 200) // x,y,luas,tinggi
```
✅ Canvas API kebanyakannya memfokuskan pada bentuk 2D, tetapi anda juga dapat menarik elemen 3D ke laman web; untuk ini, anda mungkin menggunakan [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API).
Anda boleh menarik pelbagai perkara dengan Canvas API seperti:
- **Bentuk geometri**, kami sudah menunjukkan cara melukis segi empat tepat, tetapi masih banyak lagi yang dapat anda lukis.
- **Teks**, anda boleh menggambar teks dengan fon dan warna yang anda inginkan.
- **Imej**, anda boleh melukis gambar berdasarkan aset gambar seperti .jpg atau .png misalnya.
✅ Cubalah! Anda tahu melukis segi empat, bolehkah anda melukis bulatan ke halaman? Lihatlah beberapa lukisan Kanvas yang menarik di CodePen. Berikut adalah [contoh yang sangat mengagumkan](https://codepen.io/dissimulate/pen/KrAwx).
## Muat dan lukiskan aset gambar
Anda memuat aset gambar dengan membuat objek `Imej` dan menetapkan sifat `src`nya. Kemudian anda mendengar acara `load` untuk mengetahui kapan ia siap digunakan. Kodnya seperti ini:
### Muatkan aset
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// gambar dimuat dan siap digunakan
}
```
### Muatkan corak aset
Sebaiknya bungkus perkara di atas dengan konstruk seperti itu, jadi lebih mudah digunakan dan anda hanya mencuba memanipulasinya apabila dimuat sepenuhnya:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// gambar dimuat dan siap digunakan
resolve(img);
}
})
}
// use like so
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
Untuk menarik aset permainan ke skrin, kod anda akan kelihatan seperti ini:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## Kini tiba masanya untuk mula membina permainan anda
### Apa yang hendak dibina
Anda akan membina laman web dengan elemen Canvas. Ia harus menjadikan skrin hitam `1024*768` . Kami telah memberi anda dua gambar:
- Kapal wira
![Kapal wira](../solution/assets/player.png)
- 5 * 5 raksasa
![Monster ship](solution/assets/enemyShip.png)
### Langkah-langkah yang disarankan untuk memulakan pembangunan
Cari fail yang telah dibuat untuk anda dalam sub folder `your-work`. Ia harus mengandungi yang berikut:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Buka salinan folder ini dalam Visual Studio Code. Anda perlu membuat persediaan persekitaran pembangunan tempatan, lebih baik dengan Visual Studio Code dengan NPM dan Node dipasang. Sekiranya anda tidak memasang `npm` di komputer anda, [berikut cara melakukannya](https://www.npmjs.com/get-npm).
Mulakan projek anda dengan menavigasi ke folder `your_work`:
```bash
cd your-work
npm start
```
Perkara di atas akan memulakan Pelayan HTTP pada alamat `http: // localhost: 5000`. Buka penyemak imbas dan masukkan alamat itu. Ini adalah halaman kosong sekarang, tetapi itu akan berubah
> Catatan: untuk melihat perubahan pada layar anda, muat semula penyemak imbas anda.
### Tambah Kod
Tambahkan kod yang diperlukan ke `your-work / app.js` untuk menyelesaikan perkara di bawah
1. ** Lukiskan kanvas dengan latar belakang hitam
> tip: tambahkan dua baris di bawah TODO yang sesuai di `/ app.js`, tetapkan elemen` ctx` menjadi hitam dan koordinat atas / kiri berada pada 0,0 dan tinggi dan lebarnya sama dengan kanvas .
2. ** Muatkan tekstur **
> tip: tambahkan gambar pemain dan musuh dengan menggunakan `menunggu loadTexture` dan melewati jalan gambar. Anda belum akan melihatnya di skrin!
3. ** Draw ** wira di tengah skrin di bahagian bawah
> tip: gunakan API `drawImage` untuk menarik heroImg ke skrin, tetapkan` canvas.width / 2 - 45` dan `canvas.height - canvas.height / 4)`;
4. ** Draw ** 5 * 5 monster
> tip: Sekarang anda boleh melepaskan kod untuk menarik musuh di layar. Seterusnya, pergi ke fungsi `createEnemies` dan bangunkan.
Pertama, sediakan beberapa pemalar:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
kemudian, buat gelung untuk menarik susunan monster ke skrin:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## Keputusan
Hasil akhirnya akan kelihatan seperti:
![Skrin hitam dengan wira dan 5 * 5 monster](../partI-solution.png)
## Penyelesaian
Cuba selesaikannya sendiri terlebih dahulu tetapi jika anda buntu, lihatlah [solution](../solution/app.js)
---
## 🚀 Cabaran
Anda telah belajar melukis dengan Canvas API 2D; lihatlah [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API), dan cuba lukis objek 3D.
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32)
## Mengkaji & Belajar Sendiri
Ketahui lebih lanjut mengenai Canvas API dengan [membaca mengenainya](https://developer.mozilla.org/docs/Web/API/Canvas_API).
## Tugasan
[Main dengan Canvas API](assignment.ms.md)

View File

@@ -1,216 +0,0 @@
# 建立太空遊戲 Part 2在畫布上繪製英雄與怪物
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/31?loc=zh_tw)
## Canvas
Canvas 是 HTML 中的元素,預設上不帶有任何內容,就如一塊白板。你需要自己彩繪上去。
✅ 在 MDN 上閱讀[更多關於 Canvas API](https://developer.mozilla.org/docs/Web/API/Canvas_API)。
這是它典型的宣告方式,位在頁面的 body 中:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
上面我們設定了 `id``width``height`
- `id`:讓你在處理物件時,能快速地取得參考位置。
- `width`:物件的寬度。
- `height`:物件的高度。
## 繪製簡單幾何圖樣
Canvas 使用了笛卡爾座標系繪製圖案。因此有 x 軸與 y 軸來表達物件的所在地點。座標點 `0,0` 位在畫布的左上方;而右下方則是我們定義畫布的寬度與高度。
![畫布網格](../canvas_grid.png)
> 圖片出自於 [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
要在 Canvas 物件上繪製圖案,你需要執行下列步驟:
1. **取得 Canvas 物件的參考位置**
1. **取得 Context 物件的參考位置**,定義在 Canvas 元素中。
1. 使用 context 元素**進行繪製動作**。
以程式碼表達上述步驟會呈現成:
```javascript
// 繪製紅色矩形
//1. 取得 canvas 參考點
canvas = document.getElementById("myCanvas");
//2. 設定 context 為 2D 以繪製基本圖形
ctx = canvas.getContext("2d");
//3. 填入色彩紅色
ctx.fillStyle = 'red';
//4. 利用這些參數決定位置與大小,繪製矩形
ctx.fillRect(0,0, 200, 200) // x,y,width, height
```
✅ Canvas API 主要是處理 2D 圖形,但你也可以在網頁中繪製 3D 圖形。要完成這個需求,你可以使用 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API)。
你可以使用 Canvas API 繪製出這些物件:
- **幾何圖形**,我們已經展示繪製矩形的流程,還有許多種形狀可以使用。
- **文字**,你可以繪製文字,決定你想要的字型及顏色。
- **圖片**,你可以依據圖片檔繪製圖案,舉例來說像是 .jpg 或是 .png 檔。
✅ 試試看!你知道如何繪製矩形,你能在頁面中繪製圓形嗎?看看在 CodePen 上有趣的 Canvas 塗鴉。這邊有一樣[特別令人驚豔的例子](https://codepen.io/dissimulate/pen/KrAwx)。
## 讀取並繪製圖片檔
建立 `Image` 物件並設定其 `src` 屬性,你可以讀取圖片檔。接著監聽 `load` 事件,了解圖片何時已經可以被使用。程式碼如下:
### 讀取檔案
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// 圖片載入完成,準備使用
}
```
### 讀取檔案之模式
建議上可以將上述程式打包起來,建立成完整的結構,判斷圖片是否載入完成,也方便未來的使用:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
const img = new Image();
img.src = path;
img.onload = () => {
// 圖片載入完成,準備使用
resolve(img);
}
})
}
// 實際用法
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
}
```
要在畫面上繪製遊戲物件,你的程式碼會如下所示:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
}
```
## 是時候來建立你的遊戲了
### 建立目標
你需要建立包含 Canvas 元素的網頁。它會是 `1024*768` 的黑色畫面。我們提供了兩張圖片:
- 英雄艦艇
![英雄艦艇](../solution/assets/player.png)
- 5*5 隻怪物
![敵軍艦艇](../solution/assets/enemyShip.png)
### 開始開發的建議步驟
在你的 `your-work` 子資料夾中,確認檔案是否建立完成。它應該包括:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
在 Visual Studio Code 中開啟這個資料夾的副本。你需要建立本地端的開發環境,建議為 Visual Studio Code 與安裝好的 NPM 與 Node。如果你的電腦中還沒設定好 `npm`[這是它的設定流程](https://www.npmjs.com/get-npm)。
前往 `your_work` 資料夾,開始你的專案:
```bash
cd your-work
npm start
```
這會啟動 HTTP 伺服器,網址為 `http://localhost:5000`。開啟瀏覽器並輸入該網址。目前會是空白的頁面,但不久後就會不一樣了。
> 筆記:想觀察畫面的改變,請重新整理你的頁面。
### 加入程式碼
`your-work/app.js` 中加入程式碼以解決下列目標:
1. 在 Canvas **繪製**黑色背景
> 要點:在 `/app.js` 中,加入兩行程式在 TODO 下方:設定 `ctx` 元素為黑色,左上方座標點為 0,0 且大小與 Canvas 相等。
2. **讀取**材質
> 要點:使用 `await loadTexture` 導入圖片位置以新增玩家與敵軍圖片。你還沒辦法在畫面上看到它們!
3. 在畫面的正下方**繪製**英雄
> 要點:使用 `drawImage` API 來繪製 heroImg 到畫面上,設定位置為 `canvas.width / 2 - 45` 與 `canvas.height - canvas.height / 4)`。
4. **繪製** 5*5 隻怪物
> 要點:現在移除註解,在畫面上繪製敵人。接著編輯函式 `createEnemies`。
首先,設定幾個常數:
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
接著,利用迴圈在畫面上繪製矩陣型態的怪物:
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
## 結果
完成後的成果應該如下所示:
![黑畫面上有英雄與 5*5 隻怪物](../partI-solution.png)
## 解答
試著自己先完成程式碼,但如果你遭遇到困難,請參考[解答](../solution/app.js)。
---
## 🚀 挑戰
你已經學會繪製 2D 圖形的 Canvas API。看看 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API),試著繪製 3D 物件。
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/32?loc=zh_tw)
## 複習與自學
[閱讀更多資料](https://developer.mozilla.org/docs/Web/API/Canvas_API),學習更多有關 Canvas API 的用法。
## 作業
[把玩 Canvas API](assignment.zh-tw.md)

View File

@@ -1,11 +0,0 @@
Juega con la API Canvas
## Instrucciones
Elija un elemento de la API de Canvas y cree algo interesante a su alrededor. ¿Puedes crear una pequeña galaxia de estrellas repetidas? ¿Puedes crear una textura interesante de líneas de colores? Puede buscar inspiración en CodePen (pero no copiar)
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | -------------------------------------------------- ------- | ----------------------------------- | --------------------- |
| | El código se envía mostrando una textura o forma interesante | Se envía el código, pero no se ejecuta | No se envía el código |

View File

@@ -1,11 +0,0 @@
# Jouer avec l'API Canvas
## Instructions
Choisissez un élément de l'API Canvas et créez quelque chose d'intéressant autour de lui. Pouvez-vous créer une petite galaxie d'étoiles répétées ? Pouvez-vous créer une texture intéressante de lignes colorées ? Vous pouvez vous inspirer de CodePen (mais ne copiez pas).
## Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | Le code est soumis montrant une texture ou une forme intéressante | Le code est soumis, mais ne s'exécute pas | Le code n'est pas soumis |

View File

@@ -1,11 +0,0 @@
# कैनवस एपीआई के साथ खेले
## अनुदेश
कैनवास एपीआई का एक तत्व चुनें और इसके चारों ओर कुछ दिलचस्प बनाएं. क्या आप बार-बार तारों की एक छोटी आकाशगंगा बना सकते हैं? क्या आप रंगीन लाइनों की एक दिलचस्प बनावट बना सकते हैं? आप प्रेरणा के लिए कोडपेन को देख सकते हैं (लेकिन कॉपी न करें)
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | ------------------------------------------------------------- | ----------------------------------------- | ----------------------------- |
| | एक दिलचस्प बनावट या आकार दिखाते हुए कोड प्रस्तुत किया जाता है | कोड सबमिट किया गया है, लेकिन नहीं चलता है | कोड प्रस्तुत नहीं किया गया है |

View File

@@ -1,11 +0,0 @@
# Giocare con l'API Canvas
## Istruzioni
Sceglire un elemento dell'API Canvas e creare qualcosa di interessante attorno ad esso. Si è in grado di creare una piccola galassia di stelle ripetute? Si riesce a creare una interessante struttura di linee colorate? Si puoi guardare CodePen per l'ispirazione (ma non copiare)
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | Il codice viene inviato mostrando una struttura o una forma interessante | Il codice viene inviato, ma non viene eseguito | Il codice non è stato inviato |

View File

@@ -1,11 +0,0 @@
# Canvas API で遊ぶ
## 説明書
Canvas API の要素を 1 つ選んで、その周りに何か面白いものを作りましょう。星が繰り返される小さな銀河を作ることができますか? 色のついた線で面白いテクスチャを作ることができますか? インスピレーションを得るために CodePen を見ることができます (ただし、コピーはしないでください)。
## ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | 興味深い質感や形状を示すコードが提出されている | コードは送信されたが実行されない | コードが提出されていない |

View File

@@ -1,11 +0,0 @@
# Canvas API 사용하기
## 설명
Canvas API에서 한 요소를 선택하고 그 주위에 재미있는 요소를 추가해 봅시다. 수많은 별이 모인 은하수를 만들수 있나요? 색깔이 있는 선으로 특별한 질감을 만들 수 있나요? 영감이 필요한다면 CodePen을 참고해보세요. (복사하지는 마세요.)
## 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| 특별한 질감 또는 모양을 보여주는 코드를 제출한 경우 | 코드가 제출되었지만 실행되지 않는 경우 | 코드가 제출되지 않은 경우

View File

@@ -1,11 +0,0 @@
# Main dengan Canvas API
## Arahan
Pilih satu elemen API Canvas dan buat sesuatu yang menarik di sekitarnya. Bolehkah anda membuat galaksi kecil bintang berulang? Bolehkah anda membuat tekstur garis berwarna yang menarik? Anda boleh melihat CodePen untuk mendapatkan inspirasi (tetapi jangan menyalin)
## Rubrik
| Kriteria | Contoh | Mencukupi | Usaha Lagi |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | Kod dihantar menunjukkan tekstur atau bentuk yang menarik | Kod dihantar, tetapi tidak dijalankan | Kod tidak dihantar |

View File

@@ -1,11 +0,0 @@
# Speel met de Canvas API
## Instructies
Kies een element van de Canvas API en maak er iets interessants omheen. Kunt u een kleine melkweg van herhaalde sterren creëren? Kunt u een interessante textuur van gekleurde lijnen maken? U kunt CodePen bekijken voor inspiratie (maar kopieer niet)
## Rubriek
| Criteria | Voorbeeldig | Voldoende | Moet worden verbeterd |
| -------- | --------------------------------------------------------- | ----------------------------------- | --------------------- |
| | Code wordt ingediend met een interessante textuur of vorm| Code is ingediend, maar wordt niet uitgevoerd | Code is niet verzonden |

View File

@@ -1,11 +0,0 @@
# 把玩 Canvas API
## 簡介
挑選一款 Canvas API 上的元素,為它建立一些有趣的設定。你能利用重複的星星建立銀河嗎?你能建立有特殊材質的線條嗎?你可以觀察 CodePen 上的範本激發想法,但請不要抄襲。
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | -------------------------- | ---------------------------- | ------------ |
| | 程式碼呈現有趣的材質與圖案 | 有提交程式碼,但無法正常執行 | 未提交程式碼 |

View File

@@ -1,385 +0,0 @@
# Build a Space Game Part III: Adding Motion
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33)
¡Los juegos no son muy divertidos hasta que tienes extraterrestres corriendo por la pantalla! En este juego haremos uso de dos tipos de movimientos:
- **Keyboard/Mouse movement** (Movimiento del teclado / mouse): cuando el usuario interactúa con el teclado o el mouse para mover un objeto en la pantalla.
- **Game induced movement** (Movimiento inducido por el juego): cuando el juego mueve un objeto con un intervalo de tiempo determinado.
Entonces, ¿cómo movemos las cosas en una pantalla? Se trata de coordenadas cartesianas: cambiamos la ubicación (x, y) del objeto y luego redibujamos la pantalla.
Normalmente, necesita los siguientes pasos para lograr *movimiento* en una pantalla:
1. **Set a new location** (Establecer una nueva ubicación) para un objeto; esto es necesario para percibir que el objeto se ha movido.
2. **Clear the screen** (Limpiar la pantalla), la pantalla debe limpiarse entre sorteos. Podemos borrarlo dibujando un rectángulo que llenamos con un color de fondo.
3. **Redraw object** (Redibujar objeto) en una nueva ubicación. Al hacer esto, finalmente logramos mover el objeto de un lugar a otro.
Así es como puede verse en el código:
```javascript
//establecer la ubicación del héroe
hero.x += 5;
// limpia el rectángulo que alberga al héroe
ctx.clearRect(0, 0, canvas.width, canvas.height);
// vuelve a dibujar el fondo del juego y el héroe
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ ¿Puedes pensar en una razón por la que volver a dibujar a tu héroe muchos fotogramas por segundo podría generar costos de rendimiento? Lea acerca de [alternativas a este patrón](https://www.html5rocks.com/en/tutorials/canvas/performance/).
## Manejar eventos de teclado
Maneja eventos adjuntando eventos específicos al código. Los eventos del teclado se activan en toda la ventana, mientras que los eventos del mouse como un `click` se pueden conectar a hacer clic en un elemento específico. Usaremos eventos de teclado a lo largo de este proyecto.
Para manejar un evento, debe usar el método `addEventListener()` de la ventana y proporcionarle dos parámetros de entrada. El primer parámetro es el nombre del evento, por ejemplo, `keyup`. El segundo parámetro es la función que debe invocarse como resultado del evento que tiene lugar.
He aquí un ejemplo:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = representación de cadena de la clave
if (evt.key === 'ArrowUp') {
// hacer algo
}
})
```
Para los eventos clave, hay dos propiedades en el evento que puede usar para ver qué tecla se presionó:
- `key`, esta es una representación de cadena de la tecla presionada, por ejemplo `ArrowUp`
- `keyCode`, esta es una representación numérica, por ejemplo, `37`, corresponde a `ArrowLeft`.
✅ La manipulación de eventos clave es útil fuera del desarrollo del juego. ¿Qué otros usos se le ocurren para esta técnica?
### Teclas especiales: una advertencia
Hay algunas teclas * especiales * que afectan la ventana. Eso significa que si estás escuchando un evento `keyup` y usas estas teclas especiales para mover a tu héroe, también realizará un desplazamiento horizontal. Por esa razón, es posible que desee *apagar* este comportamiento integrado del navegador a medida que desarrolla su juego. Necesitas un código como este:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Teclas de flecha
case 32:
e.preventDefault();
break; // Space
default:
break; // no bloquee otras llaves
}
};
window.addEventListener('keydown', onKeyDown);
```
El código anterior asegurará que las teclas de flecha y la tecla de espacio tengan su comportamiento *predeterminado* apagado. El mecanismo de *apagado* ocurre cuando llamamos a `e.preventDefault()`.
## Movimiento inducido por el juego
Podemos hacer que las cosas se muevan por sí mismas usando temporizadores como la función `setTimeout()` o `setInterval()` que actualizan la ubicación del objeto en cada tic o intervalo de tiempo. Esto es lo que puede parecer:
```javascript
let id = setInterval(() => {
//mover al enemigo en el eje y
enemy.y += 10;
})
```
## El bucle del juego
El bucle de juego es un concepto que es esencialmente una función que se invoca a intervalos regulares. Se llama bucle de juego, ya que todo lo que debería ser visible para el usuario se incluye en el bucle. El bucle del juego hace uso de todos los objetos del juego que son parte del juego, dibujándolos todos a menos que por alguna razón ya no deban ser parte del juego. Por ejemplo, si un objeto es un enemigo que fue golpeado por un láser y explota, ya no forma parte del ciclo del juego actual (aprenderás más sobre esto en lecciones posteriores).
Así es como suele verse un bucle de juego, expresado en código:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
El ciclo anterior se invoca cada `200` milisegundos para volver a dibujar el lienzo. Tiene la capacidad de elegir el mejor intervalo que tenga sentido para su juego.
## Continuando con el juego espacial
Tomarás el código existente y lo extenderás. Empiece con el código que completó durante la parte I o use el código en [Part II-starter](your-work).
- **Moving the hero** (Mover al héroe): agregará código para asegurarse de que puede mover al héroe con las teclas de flecha.
- **Move enemies** (Mover enemigos): también necesitarás agregar código para asegurarte de que los enemigos se muevan de arriba hacia abajo a un ritmo determinado.
## Pasos recomendados
Busque los archivos que se han creado para usted en la subcarpeta `your-work`. Debe contener lo siguiente:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Comienzas tu proyecto en la carpeta `your_work` escribiendo:
```bash
cd your-work
npm start
```
Lo anterior iniciará un servidor HTTP en la dirección `http://localhost:5000`. Abra un navegador e ingrese esa dirección, ahora mismo debería representar al héroe y a todos los enemigos; nada se mueve, ¡todavía!
### Agregar código
1. **Agrega objetos dedicados** para `hero` y `enemy` y `game object`, deben tener propiedades `x` e `y`. (Recuerde la parte sobre [Herencia o composición](../README.md)).
* SUGERENCIA* `game object` debe ser el que tenga `x` e `y` y la capacidad de dibujarse a sí mismo en un lienzo.
> consejo: comience agregando una nueva clase GameObject con su constructor delineado como se muestra a continuación, y luego dibuje en el lienzo:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Ahora, amplíe este GameObject para crear el héroe y el enemigo.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Agregue controladores de eventos clave** para manejar la navegación de teclas (mueva el héroe hacia arriba / abajo a la izquierda / derecha)
* RECUERDE* es un sistema cartesiano, la parte superior izquierda es `0,0`. También recuerde agregar código para detener *comportamiento predeterminado*.
> consejo: cree su función onKeyDown y adjúntela a la ventana:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...//agregue el código de la lección anterior para detener el comportamiento predeterminado
}
};
window.addEventListener("keydown", onKeyDown);
```
Compruebe la consola de su navegador en este punto y observe cómo se registran las pulsaciones de teclas.
3. **Implemente** el [subpatrón Pub](../README.md), esto mantendrá su código limpio mientras sigue las partes restantes.
Para hacer esta última parte, puede:
1. **Agregue un detector de eventos** en la ventana:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Cree una clase EventEmitter** para publicar y suscribirse a mensajes:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Agregue constantes** y configure el EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Inicializa el juego**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Configura el bucle del juego**
Refactorice la función window.onload para inicializar el juego y configurar un bucle de juego en un buen intervalo. También agregará un rayo láser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Agregar código** para mover enemigos en un cierto intervalo
Refactorice la función `createEnemies()` para crear los enemigos y empujarlos a la nueva clase gameObjects:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
y agregue una función `createHero()` para hacer un proceso similar para el héroe.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
y finalmente, agregue una función `drawGameObjects()` para comenzar el dibujo:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
¡Tus enemigos deberían comenzar a avanzar en tu nave espacial heroica!
🚀 Desafío: como puede ver, su código puede convertirse en 'código espagueti' cuando comienza a agregar funciones, variables y clases. ¿Cómo puede organizar mejor su código para que sea más legible? Esboce un sistema para organizar su código, incluso si todavía reside en un archivo.
## [Post-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34)
## Revisión y autoestudio
Mientras escribimos nuestro juego sin usar marcos, existen muchos marcos de lienzo basados en JavaScript para el desarrollo de juegos. Tómate un tiempo para hacer algo [leer sobre estos](https://github.com/collections/javascript-game-engines).
**Tarea**: [Comenta tu código](assignment.es.md)

View File

@@ -1,388 +0,0 @@
# Construisez un jeu spatial, partie 3: Ajout de mouvement
## Quiz préalable
[Quiz préalable](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=fr)
Les jeux ne sont pas très amusants tant que vous n'avez pas des extraterrestres qui courent à l'écran! Dans ce jeu, nous utiliserons deux types de mouvements:
- **Mouvement clavier/souris**: lorsque l'utilisateur interagit avec le clavier ou la souris pour déplacer un objet à l'écran.
- **Mouvement induit par le jeu**: lorsque le jeu déplace un objet avec un certain intervalle de temps.
Alors, comment déplacer les choses sur un écran? Tout est question de coordonnées cartésiennes: nous changeons l'emplacement (x,y) de l'objet puis redessinons l'écran.
Généralement, vous devez suivre les étapes suivantes pour accomplir un *mouvement* sur un écran:
1. **Définir un nouvel emplacement** pour un objet; cela est nécessaire pour percevoir l'objet comme ayant bougé.
2. **Effacer l'écran**, l'écran doit être dégagé entre les tirages. Nous pouvons l'effacer en dessinant un rectangle que nous remplissons avec une couleur de fond.
3. **Redessiner l'objet** au nouvel emplacement. En faisant cela, nous accomplissons finalement le déplacement de l'objet d'un endroit à l'autre.
Voici à quoi cela peut ressembler dans le code:
```javascript
//définir l'emplacement du héros
hero.x += 5;
// effacer le rectangle qui accueille le héros
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redessiner le fond du jeu et le héros
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ Pouvez-vous penser à une raison pour laquelle redessiner votre héros plusieurs images par seconde pourrait entraîner des coûts de performance? Apprenez en plus sur les [alternatives à ce modèle](https://www.html5rocks.com/en/tutorials/canvas/performance/).
## Gérer les événements du clavier
Vous gérez les événements en attachant des événements spécifiques au code. Les événements de clavier sont déclenchés sur toute la fenêtre tandis que les événements de souris comme un `clic` peuvent être liés au clic sur un élément spécifique. Nous utiliserons des événements de clavier tout au long de ce projet.
Pour gérer un événement, vous devez utiliser la méthode `addEventListener()` de la fenêtre et lui fournir deux paramètres d'entrée. Le premier paramètre est le nom de l'événement, par exemple `keyup`. Le deuxième paramètre est la fonction qui doit être invoquée à la suite de l'événement en cours.
Voici un exemple:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = représentation sous forme de chaîne de la clé
if (evt.key === 'ArrowUp') {
// faire quelque chose
}
})
```
Pour les événements clés, il existe deux propriétés sur l'événement que vous pouvez utiliser pour voir quelle touche a été pressée:
- `key`, il s'agit d'une représentation sous forme de chaîne de la touche enfoncée, par exemple `ArrowUp`
- `keyCode`, c'est une représentation numérique, par exemple `37`, correspond à `ArrowLeft`.
✅ La manipulation des événements clés est utile en dehors du développement de jeux. A quelles autres utilisations pensez-vous pour cette technique?
### Touches spéciales: une mise en garde
Il y a des touches *spéciales* qui affectent la fenêtre. Cela signifie que si vous écoutez un événement `keyup` et que vous utilisez ces touches spéciales pour déplacer votre héros, il effectuera également un défilement horizontal. Pour cette raison, vous voudrez peut-être *désactiver* ce comportement de navigateur intégré lorsque vous créez votre jeu. Vous avez besoin d'un code comme celui-ci:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Touches directionnelles
case 32:
e.preventDefault();
break; // Espace
default:
break; // ne pas bloquer d'autres clés
}
};
window.addEventListener('keydown', onKeyDown);
```
Le code ci-dessus garantira que les touches fléchées et la touche espace ont leur comportement *par défaut* désactivé. Le mécanisme *shut-off* se produit lorsque nous appelons `e.preventDefault()`.
## Mouvement induit par le jeu
Nous pouvons faire bouger les choses d'elles-mêmes en utilisant des minuteries telles que la fonction `setTimeout()` ou `setInterval()` qui met à jour l'emplacement de l'objet à chaque tick ou intervalle de temps. Voici à quoi cela peut ressembler:
```javascript
let id = setInterval(() => {
//déplacer l'ennemi sur l'axe y
enemy.y += 10;
})
```
## La boucle du jeu
La boucle de jeu est un concept qui est essentiellement une fonction invoquée à intervalles réguliers. C'est ce qu'on appelle la boucle de jeu car tout ce qui doit être visible pour l'utilisateur est attiré dans la boucle. La boucle de jeu utilise tous les objets de jeu qui font partie du jeu, les dessinant tous à moins que, pour une raison quelconque, ils ne fassent plus partie du jeu. Par exemple, si un objet est un ennemi qui a été touché par un laser et qui explose, il ne fait plus partie de la boucle de jeu en cours (vous en apprendrez plus à ce sujet dans les leçons suivantes).
Voici à quoi ressemble généralement une boucle de jeu, exprimée en code:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
La boucle ci-dessus est invoquée toutes les `200` millisecondes pour redessiner le canevas. Vous avez la possibilité de choisir le meilleur intervalle qui a du sens pour votre jeu.
## Continuer le jeu spatial
Vous allez prendre le code existant et l'étendre. Soit commencez par le code que vous avez complété au cours de la partie I, soit utilisez le code de la [Partie II - starter](../your-work).
- **Déplacer le héros**: vous ajouterez du code pour vous assurer de pouvoir déplacer le héros à l'aide des touches fléchées.
- **Déplacer les ennemis**: vous devrez également ajouter du code pour vous assurer que les ennemis se déplacent de haut en bas à un rythme donné.
## Étapes recommandées
Localisez les fichiers qui ont été créés pour vous dans le sous-dossier `your-work`. Il doit contenir les éléments suivants:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Vous démarrez votre projet dans le dossier `your_work` en tapant:
```bash
cd your-work
npm start
```
Ce qui précède démarrera un serveur HTTP à l'adresse `http://localhost:5000`. Ouvrez un navigateur et entrez cette adresse, pour le moment, cela devrait rendre le héros et tous les ennemis; rien ne bouge - pour l'instant!
### Ajouter un code
1. **Ajouter des objets dédiés** pour `hero` et `enemy` et `game object`, ils doivent avoir les propriétés `x` et `y`. (Rappelez-vous la partie sur l'[héritage ou la composition](../../translations/README.fr.md)).
*CONSEIL* `game object` (l'objet de jeu) doit être celui avec `x` et `y` et la possibilité de se dessiner sur un canevas.
>astuce: commencez par ajouter une nouvelle classe GameObject avec son constructeur délimité comme ci-dessous, puis dessinez-la sur le canevas:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Maintenant, étendez ce GameObject pour créer le héros et l'ennemi.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Ajoutez des gestionnaires d'événements de touche** pour gérer la navigation des touches (déplacez le héros vers le haut/bas vers la gauche/la droite)
*RAPPELEZ-VOUS* que c'est un système cartésien, le haut à gauche est `0,0`. N'oubliez pas également d'ajouter du code pour arrêter *le comportement par défaut*
>astuce: créez votre fonction onKeyDown et attachez-la à la fenêtre:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
Vérifiez la console de votre navigateur à ce stade et regardez les frappes enregistrées.
3. **Implémentez** le [modèle Pub Sub](../../translations/README.fr.md), cela gardera votre code propre pendant que vous suivez les parties restantes.
Pour faire cette dernière partie, vous pouvez:
1. **Ajouter un écouteur d'événement** sur la fenêtre:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Créez une classe EventEmitter** pour publier et vous abonner aux messages:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Ajoutez des constantes** et configurez l'EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Initialiser le jeu**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -= 5;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Configurer la boucle de jeu**
Refactorisez la fonction window.onload pour initialiser le jeu et mettre en place une boucle de jeu sur un bon intervalle. Vous ajouterez également un faisceau laser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Ajoutez du code** pour déplacer les ennemis à un certain intervalle
Refactoriser la fonction `createEnemies()` pour créer les ennemis et les pousser dans la nouvelle classe gameObjects:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
et ajoutez une fonction `createHero()` pour faire un processus similaire pour le héros.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
et enfin, ajoutez une fonction `drawGameObjects()` pour démarrer le dessin:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Vos ennemis devraient commencer à avancer sur votre vaisseau spatial héros!
---
## 🚀 Challenge
Comme vous pouvez le constater, votre code peut se transformer en 'code spaghetti' lorsque vous commencez à ajouter des fonctions, des variables et des classes. Comment pouvez-vous mieux organiser votre code pour qu'il soit plus lisible? Esquissez un système pour organiser votre code, même s'il réside toujours dans un seul fichier.
## Quiz de validation des connaissances
[Quiz de validation des connaissances](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=fr)
## Révision et étude personnelle
Pendant que nous écrivons notre jeu sans utiliser de frameworks, il existe de nombreux frameworks de canevas basés sur JavaScript pour le développement de jeux. Prenez le temps de faire quelques [lectures à ce sujet](https://github.com/collections/javascript-game-engines).
## Affectation
[Commentez votre code](assignment.fr.md)

View File

@@ -1,387 +0,0 @@
# एक अंतरिक्ष खेल भाग 3 बनाएँ: गति जोड़ना
## प्री-लेक्चर क्विज
[प्री-लेक्चर क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=hi)
जब तक आप परदे पर चारों ओर एलियंस चल रहे हैं तब तक गेम्स बहुत मज़ेदार नहीं हैं! इस खेल में, हम दो प्रकार के आंदोलनों का उपयोग करेंगे:
- **Keyboard/Mouse movement**: जब उपयोगकर्ता स्क्रीन पर किसी ऑब्जेक्ट को स्थानांतरित करने के लिए कीबोर्ड या माउस के साथ इंटरैक्ट करता है.
- **Game induced movement**: जब खेल एक निश्चित समय अंतराल के साथ एक वस्तु को स्थानांतरित करता है.
तो हम चीजों को स्क्रीन पर कैसे स्थानांतरित करते हैं? यह सब कार्तीय निर्देशांक के बारे में है:हम ऑब्जेक्ट का स्थान (x, y) बदलते हैं और फिर स्क्रीन को फिर से खोलते हैं।
आमतौर पर आपको स्क्रीन पर _गति_ को पूरा करने के लिए निम्न चरणों की आवश्यकता होती है:
1. एक वस्तु के लिए **एक नया स्थान निर्धारित करें**; यह वस्तु को स्थानांतरित करने के रूप में अनुभव करने के लिए आवश्यक है.
2. **स्क्रीन को साफ़ करें**, ड्रॉ के बीच स्क्रीन को साफ़ करना होगा. We can clear it by drawing a rectangle that we fill with a background color.
3. नए स्थान पर **Redraw ऑब्जेक्ट**। ऐसा करने से हम अंत में वस्तु को एक स्थान से दूसरे स्थान तक ले जाते हैं.
यहाँ यह कोड में कैसा दिख सकता है:
```javascript
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ क्या आप एक कारण के बारे में सोच सकते हैं कि अपने नायक को प्रति सेकंड कई फ़्रेमों को फिर से तैयार करने से प्रदर्शन की लागत बढ़ सकती है? [इस पैटर्न के विकल्प](https://www.html5rocks.com/en/tutorials/canvas/performance/) के बारे में पढ़ें.
## कीबोर्ड घटनाओं को संभालें
आप विशिष्ट घटनाओं को कोड में संलग्न करके घटनाओं को संभालते हैं. कीबोर्ड की घटनाओं को पूरी विंडो पर ट्रिगर किया जाता है जबकि एक `click` जैसे माउस इवेंट को एक विशिष्ट तत्व को क्लिक करने के लिए जोड़ा जा सकता है. हम इस पूरे प्रोजेक्ट में कीबोर्ड इवेंट का उपयोग करेंगे.
एक घटना को संभालने के लिए आपको विंडो के `addEventListener()` विधि का उपयोग करना होगा और इसे दो इनपुट मापदंडों के साथ प्रदान करना होगा. पहला पैरामीटर घटना का नाम है, उदाहरण के लिए `keyup`. दूसरा पैरामीटर वह फ़ंक्शन होता है, जिसे ईवेंट होने के परिणामस्वरूप लागू किया जाना चाहिए.
यहाँ एक उदाहरण है:
```javascript
window.addEventListener("keyup", (evt) => {
// `evt.key` = string representation of the key
if (evt.key === "ArrowUp") {
// do something
}
});
```
प्रमुख घटनाओं के लिए, इस घटना पर दो गुण हैं जिन्हें आप यह देखने के लिए उपयोग कर सकते हैं कि किस कुंजी को दबाया गया था:
- `key`, यह दबाए गए कुंजी का एक स्ट्रिंग प्रतिनिधित्व है, उदाहरण के लिए` ArrowUp`
- `KeyCode`, यह एक संख्या प्रतिनिधित्व है, उदाहरण के लिए` 37`, `ArrowLeft` से मेल खाती है.
✅ की इवेंट हेरफेर खेल के विकास के बाहर उपयोगी है। इस तकनीक के लिए आप और क्या उपयोग कर सकते हैं?
### विशेष कुंजी: एक चेतावनी
कुछ _special_ किइस हैं जो विंडोज को प्रभावित करती हैं. इसका मतलब है कि यदि आप एक `keyup` घटना सुन रहे हैं और आप अपने नायक को स्थानांतरित करने के लिए इन विशेष किइस का उपयोग करते हैं तो यह क्षैतिज स्क्रॉलिंग भी करेगा.इस कारण से आप अपने गेम को बनाते समय इस बिल्ट-इन ब्राउज़र व्यवहार को बंद कर सकते हैं। आपको इस तरह कोड की आवश्यकता है:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener("keydown", onKeyDown);
```
उपरोक्त कोड यह सुनिश्चित करेगा कि एरो किइस और स्पेस की का डिफ़ॉल्ट व्यवहार बंद हो। शट-ऑफ तंत्र तब होता है जब हम `e.preventDefault ()` कहते हैं.
## खेल प्रेरित चाल
हम टाइम सेट का उपयोग करके चीजों को खुद से आगे बढ़ा सकते हैं जैसे `setTimeout()` या `setInterval()` फ़ंक्शन जो प्रत्येक टिक पर वस्तु के स्थान को अपडेट करते हैं, या समय अंतराल।. यहां ऐसा है जो दिख सकता है:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
});
```
## गेम लूप
गेम लूप एक अवधारणा है जो अनिवार्य रूप से एक फ़ंक्शन है जिसे नियमित अंतराल पर लागू किया जाता है. इसे गेम लूप कहा जाता है क्योंकि उपयोगकर्ता को दिखाई देने वाली सभी चीज़ों को लूप में खींचा जाना चाहिए. गेम लूप उन सभी गेम ऑब्जेक्ट्स का उपयोग करता है जो गेम का हिस्सा हैं, उन सभी को ड्रॉ करना जब तक कि किसी कारण से गेम का हिस्सा नहीं होना चाहिए. उदाहरण के लिए यदि कोई वस्तु एक शत्रु है जो लेजर से टकराती है और उड़ती है, तो यह वर्तमान गेम लूप का हिस्सा नहीं है (बाद के पाठों में आप इस पर और अधिक सीखेंगे).
यहाँ एक गेम लूप आमतौर पर कैसा दिख सकता है, कोड में व्यक्त किया गया है:
```javascript
let gameLoopId = setInterval(
() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
},
200
);
```
उपरोक्त लूप को कैनवास को फिर से बनाने के लिए प्रत्येक `200` मिलीसेकेंड पर लगाया जाता है. आपके पास सबसे अच्छा अंतराल चुनने की क्षमता है जो आपके खेल के लिए समझ में आता है.
## अंतरिक्ष खेल को जारी रखना
आप मौजूदा कोड लेंगे और उसे विस्तारित करेंगे। या तो उस कोड से शुरू करें जो आपने भाग I के दौरान पूरा किया था या [भाग II- स्टार्टर](../your-work) में कोड का उपयोग करें.
- **Moving the hero**: आप यह सुनिश्चित करने के लिए कोड जोड़ेंगे कि आप तीर किइस का उपयोग करके नायक को स्थानांतरित कर सकते हैं.
- **Move enemies**: दुश्मनों को किसी दिए गए दर पर ऊपर से नीचे ले जाने के लिए आपको कोड जोड़ने की भी आवश्यकता होगी.
## अनुशंसित कदम
उन फ़ाइलों का पता लगाएँ जो आपके लिए `your-work` सब फ़ोल्डर में बनाई गई हैं. इसमें निम्नलिखित शामिल होना चाहिए:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
आप टाइप करके अपना प्रोजेक्ट `your_work` फ़ोल्डर शुरू करें:
```bash
cd your-work
npm start
```
उपरोक्त पते पर एक HTTP सर्वर शुरू होगा `http: // localhost: 5000`. एक ब्राउज़र खोलें और उस पते को इनपुट करें, अभी उसे नायक और सभी दुश्मनों को प्रस्तुत करना चाहिए; कुछ भी नहीं चल रहा है - फिर भी!
### कोड जोड़ें
1. `हीरो` और `दुश्मन` और `गेम ऑब्जेक्ट` के लिए **समर्पित ऑब्जेक्ट्स जोड़ें**, उनके पास` x` और `y` गुण होने चाहिए ([इन्हेरिटेंस या कम्पोजीशन](../../README.md) पर भाग याद रखें ).
_संकेत_ `गेम ऑब्जेक्ट` `x` और `y` के साथ एक होना चाहिए और एक कैनवास पर खुद को आकर्षित करने की क्षमता होनी चाहिए
> टिप: नीचे के रूप में देलिनेटेड कंस्ट्रक्टर के साथ एक नया ग़मओब्जेक्ट वर्ग जोड़कर शुरू करें, और फिर इसे कैनवास पर ड्रा करें:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
अब, इस गेमओब्जेक्ट को हीरो और दुश्मन बनाने के लिए विस्तारित करें.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log("Stopped at", this.y);
clearInterval(id);
}
}, 300);
}
}
```
2. **की ईवेंट हैंडलर जोड़ें** की नेविगेशन को संभालने के लिए (बाएं / दाएं हीरो ऊपर ले जाएं)
_REMEMBER_ यह कार्टेशियन सिस्टम है, टॉप-लेफ्ट `0,0` है. यह भी याद रखें कि _डिफ़ॉल्ट व्यवहार_ को रोकने के लिए कोड जोड़ना
> टिप: अपने onKeyDown फ़ंक्शन को बनाएं और इसे विंडो में अटैच करें:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
इस बिंदु पर अपने ब्राउज़र कंसोल की जाँच करें, और लॉग किए जा रहे कीस्ट्रोक्स को देखें.
3. \*\* लागू करें [[पब उप पैटर्न](../../ README.md), शेष भागों का पालन करते हुए यह आपके कोड को साफ रखेगा.
यह अंतिम भाग करने के लिए, आप कर सकते हैं:
1. विंडो पर **एक घटना श्रोता जोड़ें**:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
2. संदेशों को प्रकाशित करने और सदस्यता लेने के लिए **एक EventEmitter वर्ग बनाएं**:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
3. **स्थिरांक जोड़ें** और EventEmitter सेट करें:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas,
ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
4. **खेल को प्रारंभ करें**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
4. **गेम लूप सेट करें**
गेम को प्रारंभ करने के लिए window.onload फ़ंक्शन को रिफ्लेक्टर करें और एक अच्छे अंतराल पर गेम लूप सेट करें। आप एक लेजर बीम भी जोड़ जोड़ सकते है:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100);
};
```
5. एक निश्चित अंतराल पर दुश्मनों को स्थानांतरित करने के लिए **कोड जोड़ें**
शत्रुओं को बनाने और उन्हें नए गेमऑब्जेक्ट्स क्लास में धकेलने के लिए `createEnemies()` फ़ंक्शन को रिफलेक्‍टर करें:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
और हीरो के लिए एक समान प्रक्रिया करने के लिए एक `createHero()` फ़ंक्शन जोड़ें.
```javascript
function createHero() {
hero = new Hero(canvas.width / 2 - 45, canvas.height - canvas.height / 4);
hero.img = heroImg;
gameObjects.push(hero);
}
```
और अंत में, ड्राइंग शुरू करने के लिए एक `drawGameObjects()` फ़ंक्शन जोड़ें:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach((go) => go.draw(ctx));
}
```
अपने दुश्मनों को अपने नायक अंतरिक्ष यान पर आगे बढ़ना शुरू कर देना चाहिए!
---
## 🚀 चुनौती
जैसा कि आप देख सकते हैं, जब आप फ़ंक्शन और चर और कक्षाएं जोड़ना शुरू करते हैं तो आपका कोड 'स्पेगेटी कोड' में बदल सकता है. आप अपने कोड को बेहतर तरीके से कैसे व्यवस्थित कर सकते हैं ताकि यह अधिक पठनीय हो? अपने कोड को व्यवस्थित करने के लिए एक सिस्टम स्केच करें, भले ही वह अभी भी एक फ़ाइल में रहता हो.
## व्याख्यान के बाद की क्विज
[व्याख्यान के बाद की क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=hi)
## समीक्षा और स्व अध्ययन
जब हम फ्रेमवर्क का उपयोग किए बिना अपना गेम लिख रहे हैं, तो गेम के विकास के लिए कई जावास्क्रिप्ट-आधारित कैनवास फ्रेमवर्क हैं. कुछ करने के लिए कुछ समय ले लो [इन के बारे में पढ़ना](https://github.com/collections/javascript-game-engines).
## असाइनमेंट
[अपना कोड कमेंट करें](assignment.hi.md)

View File

@@ -1,388 +0,0 @@
# Costruire un Gioco Spaziale parte 3: Aggiungere il Movimento
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=it)
I giochi non sono molto divertenti finché non si hanno alieni che scorazzano per lo schermo! In questo gioco, si utilizzeranno due tipi di movimenti:
- **Movimento tastiera/mouse**: quando l'utente interagisce con la tastiera o il mouse per spostare un oggetto sullo schermo.
- **Movimento indotto dal gioco**: quando il gioco sposta un oggetto con un certo intervallo di tempo.
Quindi come si spostano le cose su uno schermo? Dipende tutto dalle coordinate cartesiane: si cambia la posizione (x, y) di un oggetto, poi si ridisegna lo schermo.
In genere sono necessari i seguenti passaggi per eseguire il *movimento* su uno schermo:
1. **Impostare una nuova posizione** per un oggetto; questo è necessario per percepire l'oggetto come se si fosse spostato.
2. **Cancellare lo schermo, lo** schermo deve essere cancellato tra un disegno e un altro. Si può cancellarlo disegnando un rettangolo che viene riempito con un colore di sfondo.
3. **Ridisegnare l'oggetto** in una nuova posizione. In questo modo si può finalmente spostare l'oggetto da una posizione all'altra.
Ecco come può apparire nel codice:
```javascript
//imposta la posizione dell'eroe
hero.x += 5;
// pulisce il rettangolo che ospita l'eroe
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ridisegna lo sfondo del gioco e l'eroe
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ Si riesce a pensare a un motivo per cui ridisegnare il proprio eroe con molti fotogrammi al secondo potrebbe far aumentare i costi delle prestazioni? Leggere le [alternative a questo modello](https://www.html5rocks.com/en/tutorials/canvas/performance/).
## Gestire eventi da tastiera
Gli eventi si gestiscono allegando eventi specifici al codice. Gli eventi della tastiera vengono attivati sull'intera finestra mentre gli eventi del mouse come un `clic` possono essere collegati al clic su un elemento specifico. Si useranno gli eventi della tastiera durante questo progetto.
Per gestire un evento è necessario utilizzare il metodo `addEventListener()` dell'oggetto window e fornirgli due parametri di input. Il primo parametro è il nome dell'evento, ad esempio `keyup`. Il secondo parametro è la funzione che dovrebbe essere invocata come risultato dell'evento in corso.
Ecco un esempio:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = rappresentazione stringa del tasto
if (evt.key === 'ArrowUp') {
// fa qualcosa
}
})
```
Per gli eventi da tastiera ci sono due proprietà sull'evento che si possono usare usare per vedere quale tasto è stato premuto:
- `key`, questa è una rappresentazione di stringa del tasto premuto, ad esempio `ArrowUp`
- `keyCode`, questa è una rappresentazione numerica, ad esempio `37`, corrisponde a `ArrowLeft`.
✅ La manipolazione degli eventi da tastiera è utile al di fuori dello sviluppo del gioco. Quali altri usi possono venire in mente per questa tecnica?
### Tasti speciali: un avvertimento
Ci sono alcuni tasti *speciali* che influenzano la finestra. Ciò significa che se si sta ascoltando un evento `keyup` e si usano questi tasti speciali per muovere l'eroe, verrà eseguito anche lo scorrimento orizzontale. Per questo motivo si potrebbe voler *disattivare* questo comportamento del browser integrato mentre si sviluppa il gioco. Serve un codice come questo:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Tasti freccia
case 32:
e.preventDefault();
break; // Barra spazio
default:
break; // non bloccare altri tasti
}
};
window.addEventListener('keydown', onKeyDown);
```
Il codice precedente assicurerà che i tasti freccia e la barra spaziatrice abbiano il loro comportamento *predefinito* disattivato. Il meccanismo *di disattivazione* si verifica quando si chiama `e.preventDefault()`.
## Movimento indotto dal gioco
E' possibile far muovere le cose da sole utilizzando timer come la funzione `setTimeout()` o `setInterval()` che aggiornano la posizione dell'oggetto a ogni tick o intervallo di tempo. Ecco come può apparire:
```javascript
let id = setInterval(() => {
//sposta il nemico sull'asse y
enemy.y += 10;
})
```
## Il ciclo di gioco
Il ciclo di gioco è un concetto che è essenzialmente una funzione che viene invocata a intervalli regolari. Si chiama ciclo di gioco poiché tutto ciò che dovrebbe essere visibile all'utente viene disegnato nel ciclo. Il ciclo di gioco utilizza tutti gli oggetti che fanno parte del gioco, disegnandoli tutti a meno che per qualche motivo non debbano più far parte del gioco. Ad esempio, se un oggetto è un nemico che è stato colpito da un laser ed esplode, non fa più parte del ciclo di gioco corrente (maggiori informazioni nelle lezioni successive).
Ecco come può apparire tipicamente un ciclo di gioco, espresso in codice:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
Il ciclo precedente viene richiamato ogni `200` millisecondi per ridisegnare il canvas. Si ha la possibilità di scegliere l'intervallo migliore che abbia senso per il proprio gioco.
## Continuare il Gioco Spaziale
Si prenderà il codice esistente per estenderlo. Si inizia con il codice che si è completato durante la parte I o si usa il codice nella [parte II-starter](../your-work).
- **Muovere l'eroe**: si aggiungerà un codice per assicurarsi di poter muovere l'eroe usando i tasti freccia.
- **Muovere i nemici**: si dovrà anche aggiungere del codice per assicurarsi che i nemici si muovano dall'alto verso il basso a una determinata velocità.
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work` Dovrebbe contenere quanto segue:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto nella cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici; niente si muove - ancora!
### Aggiungere codice
1. **Aggiungere oggetti dedicati** per `eroe`, `nemico` e `oggetto di gioco`, dovrebbero avere proprietà `x` e `y` . (Ricorda la parte su [ereditarietà o composizione](../../1-introduction/translations/README.it.md).
*SUGGERIMENTO* l'`oggetto di gioco` (GameObject) dovrebbe essere quello con `x` e `y` e la capacità di disegnare se stesso sul canvas.
> suggerimento: iniziare aggiungendo una nuova classe GameObject con il suo costruttore delineato come di seguito, quindi disegnarlo sul canvas:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Ora, si estende questo GameObject per creare eroe (classe Hero) e nemico (clsse Enemy).
```javascript
class Hero extends GameObject {
constructor(x, y) {
...servono x, y, tipo, e velocità
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Aggiungere gestori di eventi di tastiera** per gestire la navigazione con i tasti (spostare l'eroe su/giù, sinistra/destra)
*RICORDARE* che è un sistema cartesiano, la posizione in alto a sinistra è `0,0`. Ricordare anche di aggiungere il codice per interrompere *il comportamento predefinito*
> suggerimento: creare la funzione onKeyDown e attaccarla all'oggetto window:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...aggiungere il codice dalla lezione più sopra per fermare il comportamento predefinito
}
};
window.addEventListener("keydown", onKeyDown);
```
Controllare la console del browser a questo punto e osservare le sequenze di tasti che vengono registrate.
3. **Implementare** il [modello Pub/Sub](../../1-introduction/translations/README.it.md), questo manterrà il codice pulito mentre si seguono le parti rimanenti.
Per fare quest'ultima parte, si può:
1. **Aggiungere un event listener** all'oggetto window:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Creare una classe EventEmitter** per pubblicare e sottoscrivere i messaggi:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Aggiungere costanti** e impostare EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Inizializzare il gioco**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Impostare il ciclo di gioco**
Rifattorizzare la funzione window.onload per inizializzare il gioco e impostare un ciclo di gioco su un buon intervallo. Aggiungere anche un raggio laser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Aggiungere il codice** per spostare i nemici a un certo intervallo
Rifattorizzare la funzione `createEnemies()` per creare i nemici e inserirli nella nuova classe gameObjects:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
e aggiungere una `funzione createHero()` per eseguire un processo simile per l'eroe.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
infine, aggiungere una funzione `drawGameObjects()` per avviare il disegno:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
I nemici dovrebbero iniziare ad avanzare verso l'astronave dell'eroe!
---
## 🚀 Sfida
Come si può vedere, il proprio codice può trasformarsi in ["spaghetti code"](https://it.wikipedia.org/wiki/Spaghetti_code) quando si inizia ad aggiungere funzioni, variabili e classi. Come si puo organizzare meglio il codice in modo che sia più leggibile? Disegnare un sistema per organizzare il proprio codice, anche se risiede ancora in un file.
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=it)
## Revisione e Auto Apprendimento
Mentre questo gioco viene scritto senza utilizzare infrastutture Javascript (framework), ci sono molti framework canvas basati su JavaScript per lo sviluppo di giochi. Ci si prenda un po' di tempo per [leggere qualcosa su questi](https://github.com/collections/javascript-game-engines).
## Compito
[Commentare il proprio codice](assignment.it.md)

View File

@@ -1,387 +0,0 @@
# スペースゲーム構築プロジェクト その 3: モーションの追加
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=ja)
ゲームは、あなたが画面上を走り回るエイリアンを持っているまでは、あまり楽しいものではありません! このゲームでは、2種類の動きを使用しています。このゲームでは、2種類の動きを利用していきます。
- **キーボード/マウスの動き**: ユーザーがキーボードやマウスを操作して画面上のオブジェクトを移動させたとき
- **ゲームで誘導された動き**: ゲームが一定の時間間隔でオブジェクトを移動させたとき
では、どのようにして画面上で物を動かすのでしょうか? それはすべて直交座標に基づいています。オブジェクトの位置 (x,y) を変更してから、画面を再描画します。
通常、画面上で *移動* を行うには、以下の手順が必要です。
1. オブジェクトの**新しい位置を設定します**。これはオブジェクトが移動したと認識するために必要です
2. **画面をクリアします**が、これは描画の合間に画面をクリアする必要があります。背景色で塗りつぶす矩形を描くことでクリアできます
3. 新しい場所にオブジェクトを**再描画します**。これにより、ある場所から別の場所にオブジェクトを移動させることができます
コードではこんな感じになります。
```javascript
// hero の場所を決めます。
hero.x += 5;
// hero がいる長方形をクリアします。
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ゲームの背景と hero 描画し直します。
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ ヒーローを毎秒何フレームも描き直すとパフォーマンスコストが発生する理由が思いつきますか? [このパターンの代替案](https://www.html5rocks.com/ja/tutorials/canvas/performance/)を読んでみてください。
## キーボードイベントの処理
コードに特定のイベントをアタッチすることでイベントを処理します。キーボードイベントはウィンドウ全体でトリガーされますが、`click` のようなマウスイベントは特定の要素をクリックすることに接続することができます。このプロジェクトではキーボードイベントを使用します。
イベントを処理するには、ウィンドウの `addEventListener()` メソッドを使用し、2つの入力パラメータを指定する必要があります。最初のパラメータはイベントの名前で、例えば `keyup` のようなものです。2 番目のパラメータは、イベントの結果として呼び出される関数です。
以下に例を示します。
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = キーの文字列表現
if (evt.key === 'ArrowUp') {
// 何か処理をします。
}
})
```
キーイベントには、どのキーが押されたかを確認するために使用できる2つのプロパティがあります。
- `key`、これは押されたキーの文字列表現で、例えば `ArrowUp` のようなものです
- `keyCode`、これは数値表現であり、例えば `37``ArrowLeft` に対応します
✅ キーイベントの操作はゲーム開発以外でも有用です。他にはどのような用途が考えられますか?
### 特殊なキー: 注意事項
ウィンドウに影響を与える *特殊な* キーがあります。つまり、`keyup` イベントを聞いているときに、これらの特別なキーを使ってヒーローを動かした場合、水平スクロールも行われるということです。そのため、ゲームを構築する際には、このビルトインブラウザの動作を *shut-off* した方が良いかもしれません。このようなコードが必要です。
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // 矢印キー
case 32:
e.preventDefault();
break; // スペース
default:
break; // 他のキーをブロックしないでください。
}
};
window.addEventListener('keydown', onKeyDown);
```
上記のコードでは、矢印キーとスペースキーの *デフォルト* の動作が確実にシャットオフされます。*shut-off* メカニズムは `e.preventDefault()` を呼び出すときに発生します。
## ゲームで誘導された動き
`setTimeout()``setInterval()` 関数のようなタイマーを使うことで、オブジェクトの位置を目盛りや時間間隔ごとに更新することができます。これは次のようなものです。
```javascript
let id = setInterval(() => {
//敵を Y 軸で動かす
enemy.y += 10;
})
```
## ゲームループ
ゲームループとは、基本的には一定の間隔で呼び出される関数の概念です。ユーザーに見えるべきものはすべてループに描画されるので、ゲームループと呼ばれています。ゲームループはゲームの一部であるすべてのゲームオブジェクトを利用し、何らかの理由でゲームの一部ではない場合を除いて、すべてのオブジェクトを描画します。例えば、あるオブジェクトがレーザーで撃たれて吹き飛んでしまった場合、そのオブジェクトは現在のゲームループの一部ではなくなります (これについては後のレッスンで詳しく説明します)。
ゲームループがどのようなものか、コードで表現すると次のようになります。
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
上記のループは `200` ミリ秒ごとに呼び出され、キャンバスを再描画します。あなたのゲームに合った最適な間隔を選択することができます。
## スペースゲームの続き
既存のコードを使って、それを拡張していきます。パート で完成させたコードから始めるか、[パートⅡのスターター](../your-work)のコードを使います。
- **ヒーローの移動**: 矢印キーを使ってヒーローを移動できるようにコードを追加します
- **敵を移動する**: また、敵が与えられたレートで上から下に移動することを確認するためにコードを追加する必要があります
## 推奨される手順
あなたのために作成されたファイルを `your-work` サブフォルダ内で探します。以下のファイルが含まれているはずです。
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
次のコマンドをタイピングして、あなたのプロジェクトを `your_work` フォルダから開始します。
```bash
cd your-work
npm start
```
上記は、アドレス `http://localhost:5000` の HTTP サーバーを起動します。ブラウザを開いてそのアドレスを入力すると、今はヒーローと全ての敵が表示されるはずです。ただしまだ何も動いていません。
### コードの追加
1. `hero``enemy``game object`のための**オブジェクトを追加し**、それらは `x``y` のプロパティを持っている必要があります。([継承や合成](../../translations/README.ja.md)の部分を覚えておいてください)
*ヒント* `game object``x``y` を持ち、それ自身をキャンバスに描画する機能を持つものでなければなりません。
> tip: 以下のようにコンストラクタを定義した新しい GameObject クラスを追加してから、キャンバスに描画します。
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
次に、この GameObject を拡張して、ヒーローと敵を作成します。
```javascript
class Hero extends GameObject {
constructor(x, y) {
...x, y, type, speedが必要です。
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. (ヒーローを上下左右に動かす) キーナビゲーションを処理するための**キーイベントハンドラを追加します**
*REMEMBER* これは直交座標系で、左上は `0,0` です。また、*デフォルトの動作*を止めるコードを追加することも忘れないでください*。
> tip: onKeyDown 関数を作成して、それをウィンドウにアタッチします。
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
この時点でブラウザのコンソールを確認し、キー入力がログに記録されているかどうかを確認します。
3. [Pub Sub パターン](../../translations/README.ja.md)を**実装する**と、残りの部分に続くようにコードをきれいに保つことができます
この最後の部分を行うには
1. ウィンドウに**イベントリスナーを追加します**
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. メッセージを発行して購読するための **EventEmitter クラスを作成します**
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **定数を追加**して EventEmitter を設定します
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **ゲームを初期化します**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **ゲームのループを設定します**
window.onload 関数をリファクタリングしてゲームを初期化し、良い間隔でゲームループを設定します。レーザービームも追加します。
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. 一定間隔で敵を移動させる**コードを追加します**
関数 `createEnemies()` をリファクタリングして敵を作成し、それを新しい gameObjects クラスにプッシュします。
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
そして `createHero()` 関数を追加して hero にも同様の処理を行います。
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
最後に `drawGameObjects()` 関数を追加して描画を開始します。
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
あなたの敵はあなたのヒーローの宇宙船で前進を開始する必要があります!
---
## 🚀 チャレンジ
ご覧のように、関数や変数、クラスを追加し始めると、あなたのコードは「スパゲッティコード」になってしまうことがあります。コードをより読みやすく整理するにはどうしたらいいでしょうか? 1つのファイルに存在していても、あなたのコードを整理するためのシステムをスケッチしてみましょう。
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=ja)
## 復習と自己学習
フレームワークを使わずにゲームを書いているうちに、JavaScript を使ったゲーム開発用の canvas フレームワークがたくさん出てきました。時間をかけて[これらについて読む](https://github.com/collections/javascript-game-engines)。
## 課題
[コードをコメントする](assignment.ja.md)

View File

@@ -1,389 +0,0 @@
# Space 게임 제작하기 파트 3: 모션 추가하기
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=ko)
외계인이 화면을 돌아다니기 전까지는 게임이 재미 없습니다! 이 게임에서는, 두 가지 타입의 동작을 씁니다:
- **키보드/마우스 동작**: 사용자가 키보드 또는 마우스와 상호작용하여 화면에서 개체를 움질일 때.
- **게임으로 움직이는 동작**: 게임이 일정 시간 간격으로 객체를 움직일 때.
그러면 화면에서 물건을 어떻게 움직일까요? 그것은 모두 직교 좌표에 관한 것입니다: 객체의 위치 (x, y)를 변경 한 뒤에 화면을 다시 그립니다.
일반적으로 화면에서 *이동*을 하려면 다음 단계가 필요합니다:
1. 객체의 **새로운 위치 설정하기**; 이는 객체가 움직인 것으로 인식하는 데 필요합니다.
2. **화면 비우기**, 화면은 그려지는 사이에 비워져야 합니다. 배경색으로 채운 사각형을 그려서 지울 수 있습니다.
3. 새로운 위치에서 **개체를 다시 그리기**. 최종적으로 한 위치에서 다른 위치로 객체를 이동합니다.
코드에서 다음과 같이 보일 수 있습니다:
```javascript
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ 영웅을 초당 수 많은 프레임으로 다시 그리게 될 때 성능 비용이 발생하는 이유를 알 수 있나요? [alternatives to this pattern](https://www.html5rocks.com/en/tutorials/canvas/performance/)에 대하여 읽어보세요.
## 키보드 이벤트 제어하기
특정 이벤트를 코드에 연결하여 이벤트를 처리합니다. 키보드 이벤트는 전체 윈도우에서 연결되는 반면에 `click`과 같은 마우스 이벤트는 클릭하는 특정 요소에 연결할 수 있습니다. 이 프로젝트에서는 키보드 이벤트를 사용합니다.
이벤트를 처리하려면 윈도우의 `addEventListener ()` 메소드를 사용하고 두 개의 입력 파라미터를 제공해야 합니다. 첫 번째 파라미터는 이벤트의 이름입니다, 예시를 들자면 `keyup`과 같습니다. 두 번째 파라미터는 이벤트가 발생함에 따라 호출될 함수입니다.
여기는 예시입니다:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
```
키 이벤트의 경우에는 어떤 키를 눌렀는지 확인할 때 쓸 수 있는 이벤트로 두 가지 속성이 있습니다:
- `key`, 눌린 키의 문자열 표현입니다, 예를 들어 `ArrowUp` 입니다.
- `keyCode`, 숫자 표현입니다. 예를 들어 `37``ArrowLeft`에 해당합니다.
✅ 키 이벤트 조작은 게임 개발 외부에서 유용합니다. 이 기술의 다른 사용법은 무엇일까요?
### 특별한 키: a caveat
윈도우에 영향을 주는 몇 가지 *특별한* 키가 있습니다. 즉 `keyup` 이벤트를 듣고 이 특별한 키를 사용하면 영웅을 이동하여 가로 스크롤도 할 수 있다는 것을 의미합니다. 따라서 게임을 제작할 때는 이 내장 브라우저 동작을 *차단* 할 수 있습니다. 다음과 같은 코드가 필요합니다:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener('keydown', onKeyDown);
```
위 코드는 화살표-키와 스페이스 키의 *기본* 동작을 막습니다. *차단* 메커니즘은 `e.preventDefault()`를 호출할 때 발생합니다.
## 게임의 움직임
각 틱 또는 시간 간격에서 객체의 위치를 업데이트하는 `setTimeout()` 또는 `setInterval()` 함수 같은 타이머를 사용하여 스스로 움직일 수 있습니다. 다음과 같이 보입니다:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
```
## 게임 루프
게임 루프는 기본적으로 일정한 간격마다 호출되는 함수인 개념입니다. 사용자에게 보여줄 모든 것이 루프에 그려지므로 이것을 게임 루프라고 합니다. 게임 루프는 게임의 일부인 모든 게임 객체를 사용하여, 모종의 이유로 더 이상 게임의 일부가 아니지 않는 이상 다 그립니다. 예를 들면 객체가 레이저에 맞아 폭발한 적이 있다면 더 이상 현재 게임 루프의 일부가 아닙니다 (다음 단원에서 자세히 알아 볼 것입니다).
다음은 일반적으로 코드로 표현된 게임 루프의 모습입니다:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
캔버스를 다시 그리기 위해 위의 루프가 `200` milliseconds 마다 호출됩니다. 게임에 가장 적합한 간격을 고를 수 있습니다.
## Space 게임 계속하기
기존 코드를 가져와 확장합니다. 파트 I 에서 작성한 코드로 시작하거나 [Part II- starter](../your-work)의 코드를 사용합니다.
- **영웅을 움직이기**: 화살표 키를 사용하여 영웅을 이동할 수 있도록 코드를 추가합니다.
- **적을 움직이기**: 적들이 주어진 속도로 상단에서 하단으로 이동할 수 있도록 코드를 추가합니다.
## 권장 단계
`your-work` 하위 폴더에 생성된 파일을 찾습니다. 다음을 포함해야 합니다:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
타이핑하여 `your_work` 폴더에서 프로젝트를 시작합니다:
```bash
cd your-work
npm start
```
위 코드는 `http://localhost:5000` 주소에서 HTTP 서버를 시작합니다. 브라우저를 열고 해당 주소를 입력하면 영웅과 모든 적을 렌더링 해야하지만; 아무것도 움직이지 않습니다 - 아직!
### 코드 추가하기
1. `영웅``적` 그리고 `게임 객체`에 대한 **전용 객체를 추가합니다**. `x` 혹은 `y` 속성이 필요합니다. ([Inheritance or composition](../README.md) 파트를 기억하세요).
*힌트* `game object``x``y`가 있으면서 canvas에 그릴 수 있는 능력이 되어야 합니다.
>tip: 생성자가 아래와 같이 이루어진 새로운 GameObject 클래스를 추가하여, 시작한 뒤에 canvas로 그립니다:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
이제, GameObject를 확장하여 영웅과 적을 생성합니다.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **키-이벤트 핸들러를 추가**하여 키 탐색을 처리합니다 (Hero를 상/하 좌/우로 이동).
*기억합시다* 데카르트 시스템이고, 좌측 상단은 `0,0`입니다. 또한 *기본 동작*을 중지하는 코드를 추가해야 합니다
>tip: onKeyDown 함수를 만들고 윈도우에 붙입니다.
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
이 지점에서 브라우저 콘솔을 확인해봅니다, 그리고 로깅되는 키 입력을 봅니다.
3. [Pub sub pattern](../README.md)으로 **구현합니다**, 이는 남은 파트를 따라가면서 코드를 깨끗하게 유지할 수 있습니다.
이 마지막 파트를 진행하면, 다음을 할 수 있습니다:
1. 윈도우에 **Add an 이벤트 리스너를 추가합니다**:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. publish하고 메시지를 subscribe할 **EventEmitter 클래스를 생성합니다**:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. EventEmitter를 설정하고 **constants를 추가합니다**:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **게임을 초기화합니다**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **게임 루프를 설정합니다**
window.onload 함수를 리팩터링하여 게임을 초기화하고 적절한 간격으로 게임 루프를 설정합니다. 레이저 빔도 추가합니다:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. 일정 간격으로 움직이는 적에 대한 **코드를 작성합니다**
`createEnemies()`함수를 리팩터링하여 적을 생성하고 새로운 gameObjects 클래스로 푸시합니다:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
그리고 비슷한 과정으로 영웅에 대한 `createHero()` 함수를 추가합니다.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
그리고 마지막으로, 그리기를 시작할 `drawGameObjects()` 함수를 추가합니다:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
적들이 영웅 spaceship의 앞으로 나아가려고 합니다!
---
## 🚀 도전
보다가, 함수와 변수 및 클래스를 추가하기 시작하면 코드가 '스파게티 코드'로 변할 수 있습니다. 코드를 더 읽기 쉽게 구성하려면 어떻게 해야 될까요? 코드가 여전히 하나의 파일에 있어도, 어울리는 시스템을 기획하시기 바랍니다.
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=ko)
## 리뷰 & 자기주도 학습
프레임워크를 사용하지 않고 게임을 작성하는 동안, 게임 개발을 위한 JavaScript-기반 canvas 프레임워크가 많이 존재하고 있습니다. 시간을 내어 [about these](https://github.com/collections/javascript-game-engines)를 보시기 바랍니다.
## 과제
[Comment your code](../assignment.md)

View File

@@ -1,388 +0,0 @@
# Bina Permainan Angkasa Bahagian 3: Menambah Gerakan
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33)
Permainan tidak begitu menyeronokkan sehingga anda mempunyai makhluk asing di layar! Dalam permainan ini, kami akan menggunakan dua jenis pergerakan:
- **Pergerakan papan kekunci / Tetikus**: ketika pengguna berinteraksi dengan papan kekunci atau tetikus untuk menggerakkan objek di layar.
- **Pergerakan yang disebabkan oleh permainan**: ketika permainan menggerakkan objek dengan selang waktu tertentu.
Jadi bagaimana kita memindahkan sesuatu di skrin? Ini semua mengenai koordinat kartesian: kami menukar lokasi (x, y) objek dan kemudian melukis semula skrin.
Biasanya anda memerlukan langkah-langkah berikut untuk menyelesaikan *pergerakan* di skrin:
1. **Tetapkan lokasi baru** untuk objek; ini diperlukan untuk melihat objek sebagai bergerak.
2. **Kosongkan skrin**, skrin perlu dibersihkan di antara undian. Kita dapat membersihkannya dengan melukis sebuah segi empat tepat yang kita isi dengan warna latar belakang.
3. **Lukis semula objek** di lokasi baru. Dengan melakukan ini kita akhirnya dapat memindahkan objek dari satu lokasi ke lokasi lain.
Inilah rupa bentuknya dalam kod:
```javascript
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ Bolehkah anda memikirkan sebab mengapa menggambar semula pahlawan anda dengan banyak bingkai sesaat mungkin menambah kos prestasi? Baca mengenai [alternatif untuk corak ini](https://www.html5rocks.com/en/tutorials/canvas/performance/).
## Mengendalikan acara papan kekunci
Anda mengendalikan acara dengan melampirkan acara tertentu ke kod. Peristiwa papan kekunci dipicu di seluruh tetingkap sedangkan peristiwa tetikus seperti `click` dapat dihubungkan dengan mengklik elemen tertentu. Kami akan menggunakan acara papan kekunci sepanjang projek ini.
Untuk menangani suatu peristiwa, anda perlu menggunakan kaedah ``addEventListener()` tetingkap dan memberikannya dua parameter input. Parameter pertama adalah nama acara, misalnya `keyup`. Parameter kedua adalah fungsi yang harus dipanggil sebagai akibat dari peristiwa yang berlaku.
Inilah contohnya:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
```
Untuk acara utama terdapat dua sifat pada acara yang boleh anda gunakan untuk melihat kunci apa yang ditekan:
- `key`, ini adalah representasi rentetan dari kekunci yang ditekan, misalnya `ArrowUp`
- `keyCode`, ini adalah representasi angka, misalnya `37`, sesuai dengan `ArrowLeft`.
✅ Manipulasi acara utama berguna di luar pengembangan permainan. Apa kegunaan lain yang dapat anda fikirkan untuk teknik ini?
### Kekunci khas: peringatan
Terdapat beberapa *kunci* khas yang mempengaruhi tetingkap. Ini bermaksud bahawa jika anda sedang mendengar acara `keyup` dan anda menggunakan kekunci khas ini untuk menggerakkan wira anda, ia juga akan melakukan tatal mendatar. Untuk itu anda mungkin mahu *mematikan* tingkah laku penyemak imbas terbina dalam ini semasa anda membina permainan anda. Anda memerlukan kod seperti ini:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener('keydown', onKeyDown);
```
Kod di atas akan memastikan bahawa anak panah dan kekunci spasi mempunyai tingkah laku *lalai* mereka. Mekanisme *shut-off* berlaku apabila kita memanggil `e.preventDefault()`.
## Pergerakan yang disebabkan oleh permainan
Kita dapat membuat sesuatu bergerak dengan menggunakan pemasa seperti fungsi `setTimeout()` atau `setInterval()` yang mengemas kini lokasi objek pada setiap centang, atau selang waktu. Begini rupa:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
```
## Gelung permainan
Gelung permainan adalah konsep yang pada dasarnya adalah fungsi yang dipanggil pada selang waktu yang tetap. Ia dipanggil gelung permainan kerana segala sesuatu yang dapat dilihat oleh pengguna ditarik ke dalam gelung. Gelung permainan menggunakan semua objek permainan yang menjadi bagian dari permainan, menggambar semuanya kecuali untuk beberapa alasan tidak seharusnya menjadi bagian dari permainan lagi. Contohnya jika objek adalah musuh yang terkena laser dan meletup, ia bukan lagi bahagian dari gelung permainan semasa (anda akan mengetahui lebih lanjut mengenai perkara ini dalam pelajaran berikutnya).
Inilah rupa gelung permainan, yang dinyatakan dalam kod:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
Gelung di atas dipanggil setiap `200` milisaat untuk menggambar semula kanvas. Anda mempunyai kemampuan untuk memilih selang terbaik yang sesuai untuk permainan anda.
## Meneruskan Permainan Angkasa
Anda akan mengambil kod yang ada dan memanjangkannya. Mulakan dengan kod yang anda lengkapkan semasa bahagian I atau gunakan kod di [Bahagian II- starter] (karya anda).
- **Memindahkan pahlawan**: anda akan menambah kod untuk memastikan anda dapat memindahkan pahlawan menggunakan kekunci anak panah.
- **Pindahkan musuh**: anda juga perlu menambahkan kod untuk memastikan musuh bergerak dari atas ke bawah pada kadar tertentu.
## Langkah yang disyorkan
Cari fail yang telah dibuat untuk anda dalam sub folder `your-work`. Ia harus mengandungi yang berikut:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
Anda memulakan projek anda folder `your_work` dengan mengetik:
```bash
cd your-work
npm start
```
Perkara di atas akan memulakan Pelayan HTTP pada alamat `http: // localhost: 5000`. Buka penyemak imbas dan masukkan alamat itu, sekarang ia harus menjadikan pahlawan dan semua musuh; tidak ada yang bergerak - namun!
### Tambah kod
1. **Tambahkan objek khusus** untuk `hero` dan `musuh` dan `objek permainan`, mereka harus mempunyai sifat `x` dan `y`. ( Ingat bahagian pada [Warisan atau komposisi](../../translations/README.ms.md) ).
*HINT* `objek permainan` harus menjadi objek dengan `x` dan `y` dan kemampuan untuk menarik dirinya ke kanvas.
> tip: mulakan dengan menambahkan kelas GameObject baru dengan konstruktornya digambarkan seperti di bawah, dan kemudian lukiskannya ke kanvas:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
Sekarang, panjangkan GameObject ini untuk membuat Wira dan Musuh.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **Tambahkan pengendali acara utama** untuk mengendalikan navigasi utama (pusing pahlawan ke atas / bawah kiri / kanan)
*INGAT* ini adalah sistem kartesian, kiri atas adalah `0,0`. Juga ingat untuk menambah kod untuk menghentikan *tingkah laku lalai*
> tip: buat fungsi onKeyDown anda dan pasangkannya ke tetingkap:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
Periksa konsol penyemak imbas anda pada ketika ini, dan perhatikan penekanan kekunci dicatat.
3. **Terapkan** the [Pub sub pattern](../../translations/README.ms.md), ini akan memastikan kod anda tetap bersih semasa anda mengikuti bahagian yang tinggal.
Untuk melakukan bahagian terakhir ini, anda boleh:
1. **Tambahkan pendengar acara** di tetingkap:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **Buat kelas EventEmitter** untuk menerbitkan dan melanggan mesej:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **Tambah pemalar** dan sediakan EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **Memulakan permainan**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **Siapkan gelung permainan**
Memfaktorkan semula fungsi window.onload untuk memulakan permainan dan mengatur gelung permainan pada selang waktu yang baik. Anda juga akan menambah sinar laser:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **Tambahkan kod** untuk memindahkan musuh pada selang waktu tertentu
Refactor fungsi `createEnemies()` untuk membuat musuh dan mendorong mereka ke kelas gameObjects yang baru:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
dan tambahkan fungsi `createHero()` untuk melakukan proses yang serupa untuk wira.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
dan akhirnya, tambahkan fungsi `drawGameObjects()` untuk memulakan lukisan:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
Musuh anda harus mula maju di kapal angkasa wira anda!
---
## 🚀 Cabaran
Seperti yang anda lihat, kod anda boleh berubah menjadi 'spaghetti code' apabila anda mula menambahkan fungsi dan pemboleh ubah dan kelas. Bagaimana anda dapat mengatur kod anda dengan lebih baik agar lebih mudah dibaca? Lakarkan sistem untuk mengatur kod anda, walaupun masih terdapat dalam satu fail.
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34)
## Mengkaji & Belajar Sendiri
Semasa kami menulis permainan kami tanpa menggunakan kerangka kerja, ada banyak kerangka kanvas berdasarkan JavaScript untuk pengembangan permainan. Luangkan sedikit masa untuk melakukan [membaca mengenai perkara ini](https://github.com/collections/javascript-game-engines).
## Tugasan
[Komen kod anda](assignment.ms.md)

View File

@@ -1,388 +0,0 @@
# 建立太空遊戲 Part 3加入動作
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/33?loc=zh_tw)
有外星人在移動的遊戲才會好玩!在這款遊戲中,我們會建立兩種移動模式:
- **鍵盤滑鼠的移動**:當使用者控制鍵盤或滑鼠時,能移動畫面上的物件。
- **遊戲內建的移動**:遊戲能自動地在一定時間內,移動其中的物件。
那我們該如何移動畫面上的物件呢?這都取決於笛卡爾座標系:我們改變物件的座標 (x,y),並在畫面上重新繪製出來。
通常你需要下列流程來*移動*畫面上的物件:
1. **設定物件的新地點**,你才能察覺到物件有所移動。
2. **清除畫面**,每一次的繪製間都需要將畫面清除乾淨。我們可以繪製一張背景色的矩形來覆蓋畫面。
3. **在新地點重新繪製物件**,我們就能移動物件,從 A 點移動到 B 點。
合理的程式碼如下所示:
```javascript
// 設定英雄位置
hero.x += 5;
// 利用矩形清除英雄
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重新繪製背景與英雄
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ 你能了解為什麼在同一秒內多次重新繪製英雄會影響效能的原因嗎?閱讀[其他種同目的之設計模式](https://www.html5rocks.com/en/tutorials/canvas/performance/)。
## 處理鍵盤事件
連接特定事件到程式中,你就能處理遊戲事件。鍵盤事件可以在視窗被選擇時觸發,而滑鼠事件如 `click`,則要點擊特定的物件。我們會在這個專案中,使用鍵盤物件。
要處理一種事件,需要使用視窗的 `addEventListener()` 方法,並提供給它兩個參數。第一個參數是事件的名稱,例如: `keyup`。第二個參數是回應事件結果的被呼叫函式。
下列是一種例子:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = 按鍵字串
if (evt.key === 'ArrowUp') {
// 做某事
}
})
```
鍵盤事件有兩個屬性來判別被按壓的按鍵:
- `key`,使用字串名稱表達該按鍵,例如: `ArrowUp`
- `keyCode`,使用數字呈現,例如 `37` 會對應到 `ArrowLeft`
✅ 除了遊戲開發以外,鍵盤事件也是十分實用的功能。你能想到其他使用相同技術的應用嗎?
### 特殊按鍵之限制
有許多*特殊*按鍵會影響視窗。這代表若我們正監聽著 `keyup` 事件,這個按鍵同時也會執行視窗的滾動行為。某些時候你會需要*關閉*這些瀏覽器中預設的行為,好比是建立這款遊戲時。你需要下列的程式:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // 方向鍵
case 32:
e.preventDefault();
break; // 空白鍵
default:
break; // 不阻止其他按鍵
}
};
window.addEventListener('keydown', onKeyDown);
```
上述的程式碼能確保方向鍵與空白鍵關閉*預設*的行為。這個*關閉*機制會在我們呼叫 `e.preventDefault()` 時觸發。
## 遊戲內建的移動
我們可以讓物件自己移動,利用計時器如 `setTimeout()` 或是 `setInterval()` 這兩個函式,隨著秒數間隔更新物件的位置。如下方呈現:
```javascript
let id = setInterval(() => {
// 在 y 軸上移動敵人
enemy.y += 10;
})
```
## 遊戲迴圈
遊戲迴圈是個重要概念,定期地呼叫必須執行的函式。之所以被稱作遊戲迴圈也是基於所有東西會在一個迴圈中呈現給玩家。遊戲迴圈會利用到所有的遊戲物件,並依據各個情況與理由決定是否要繪製出它們。舉例來說,當一個敵人被雷射擊中,爆炸了。他就不應該存在於現在的遊戲迴圈中。你會在後續的課程學到更多此概念。
這是一個遊戲迴圈的基本格式,以程式碼表達如下:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
上述的迴圈會每 `200` 毫秒重新繪製 Canvas。你能自由地判斷哪種時長更適合套用在你的遊戲中。
## 繼續我們的太空遊戲
你會利用現有的程式碼來擴增我們的專案。你可以使用你在 Part I 完成的程式,或是使用 [Part II - Starter](../your-work) 這包程式。
- **移動英雄**:你需要加入程式,確保你可以使用方向鍵來移動主角。
- **移動敵人**:你也需要加入程式,確保敵人能定期地由上往下移動。
## 建議步驟
在你的 `your-work` 子資料夾中,確認檔案是否建立完成。它應該包括:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
開始 `your_work` 資料夾中的專案,輸入:
```bash
cd your-work
npm start
```
這會啟動 HTTP 伺服器並發布網址 `http://localhost:5000`。開啟瀏覽器並輸入該網址,現在它能呈現英雄以及所有的敵人,但它們還沒辦法移動!
### 加入程式碼
1. **加入特定物件** `hero``enemy``game object`,它們皆有 `x``y` 位置屬性。(記得課程[繼承與組合](../../translations/README.zh-tw.md)中的片段)。
*提示* `game object` 要有 `x``y`,以及繪製到畫布上的能力。
>要點:開始建立 GameObject class ,結構如下所示,再繪製到畫布上:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
現在,延伸 GameObject 來建立英雄與敵人。
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **加入鍵盤事件處理器**以處理鍵盤輸入(移動英雄的上下左右)
*記住* 這是笛卡爾座標系,左上方為 `0,0`。也請記得關閉鍵盤的*預設行為*
>要點:建立函式 onKeyDown 並連接到視窗中:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
這時候檢查你的瀏覽器命令欄,看看是否能偵測到鍵盤輸入。
3. **建立**[發布訂閱模式](../../translations/README.zh-tw.md),這能讓剩下的程式段落保持乾淨。
要做到此步驟,你可以:
1. **建立視窗的事件監聽者**
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **建立 EventEmitter class** 以發布及訂閱訊息:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **建立常數**並設定 EventEmitter
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **初始化遊戲**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **設定遊戲迴圈**
重構函式 window.onload 來初始化遊戲,設定遊戲迴圈的定時間隔。你還需要加入雷射光:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **加入程式**來定期地移動敵人
重構函式 `createEnemies()` 以建立敵人們,接到 gameObjects 中:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
新增函式 `createHero()` 來為英雄做相同的事情。
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
最後,建立函式 `drawGameObjects()` 以開始繪製:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
你的敵人開始會朝你的英雄艦艇前進!
---
## 🚀 挑戰
如你所見,在加入零零總總的函式、變數與 class 後,你的程式變成了「麵條式代碼(spaghetti code)」。你能有效的編排你的程式,讓它更容易被閱讀?勾劃出一個系統來組織你的程式碼,即使所有東西都在一個檔案中。
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/34?loc=zh_tw)
## 複習與自學
我們並沒有使用框架(frameworks)來編寫我們的遊戲,現在有許多 JavaScript 基底的 Canvas 框架,提供給遊戲開發使用。花點時間[閱讀這些框架](https://github.com/collections/javascript-game-engines)。
## 作業
[為你的程式做註解](assignment.zh-tw.md)

View File

@@ -1,11 +0,0 @@
# Comente su código
## Instrucciones
Revisa tu archivo /app.js actual en la carpeta de tu juego y busca formas de comentarlo y ordenarlo. Es muy fácil que el código se salga de control y ahora es una buena oportunidad para agregar comentarios para asegurarse de que tiene un código legible para que pueda usarlo más tarde.
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | ---------------- | ------------------------------------- | ------------ |
| | El código `app.js` está completamente comentado y organizado en bloques lógicos | El código `app.js` está adecuadamente comentado | El código `app.js` está algo desorganizado y carece de buenos comentarios |

View File

@@ -1,11 +0,0 @@
# Commentez votre code
## Instructions
Parcourez votre fichier /app.js actuel dans votre dossier de jeu et trouvez des moyens de le commenter et de l'organiser. Il est très facile pour le code de devenir incontrôlable, et c'est maintenant une bonne occasion d'ajouter des commentaires pour s'assurer que vous disposez d'un code lisible afin que vous puissiez l'utiliser plus tard.
## Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Le code `app.js` est entièrement commenté et organisé en blocs logiques | Le code `app.js` est correctement commenté | Le code `app.js` est quelque peu désorganisé et manque de bons commentaires |

View File

@@ -1,11 +0,0 @@
# अपना कोड कमेंट करें
## अनुदेश
अपने गेम फ़ोल्डर में अपने वर्तमान /app.js फ़ाइल पर जाएं, और इसे टिप्पणी करने और इसे साफ करने के तरीके खोजें. कोड को नियंत्रण से बाहर करना बहुत आसान है, और अब यह सुनिश्चित करने के लिए टिप्पणियां जोड़ने का एक अच्छा मौका है कि आपके पास पठनीय कोड है ताकि आप इसे बाद में उपयोग कर सकें.
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | -------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------- |
| | `app.js` कोड पूरी तरह से टिप्पणी की है और तार्किक ब्लॉकों में व्यवस्थित है | `app.js` कोड पर्याप्त रूप से टिप्पणी की है | `app.js` कोड कुछ हद तक अव्यवस्थित है और अच्छी टिप्पणियों का अभाव है |

View File

@@ -1,11 +0,0 @@
# Commentare il proprio codice
## Istruzioni
Eseminare il file /app.js corrente nella cartella del gioco e trovare i modi per commentarlo e riordinarlo. È molto facile che il codice sfugga al controllo e ora è una buona occasione per aggiungere commenti per assicurarsi di avere codice leggibile in modo da poterlo utilizzare in seguito.
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Il codice in `app.js` è completamente commentato e organizzato in blocchi logici | Il codice in `app.js` è adeguatamente commentato | Il codice in `app.js` è in qualche modo disorganizzato e manca di buoni commenti |

View File

@@ -1,11 +0,0 @@
# コードをコメントする
## 説明書
ゲームフォルダ内の現在の /app.js ファイルに目を通し、コメントを付けて片付ける方法を見つけてください。コードはいとも簡単に制御不能になります。今はコメントを追加して、後で使えるように読みやすいコードにする良い機会です。
## ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | `app.js` のコードは完全にコメントされ、論理的なブロックに整理されています。 | `app.js` のコードは適切にコメントされています。 | `app.js` のコードはやや乱れていて、良いコメントがありません。 |

View File

@@ -1,11 +0,0 @@
# 코드 주석 달기
## 설명
게임 폴더에 있는 /app.js 파일을 살펴보고, 주석을 달고 정리해 봅시다. 코드가 많아지면 통제하기가 어려워질 수 있습니다. 지금 주석을 추가해서 나중에도 코드를 쉽게 파악할 수 있도록 조치해 봅시다.
## 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| `app.js` 코드가 주석으로 충분히 설명되어 있고 논리적으로 설계된 경우 | `app.js` 코드가 주석으로 적절히 설명된 경우 | `app.js` 코드가 주석으로 충분히 설명되지 않고 복잡하게 설계된 경우

View File

@@ -1,11 +0,0 @@
# Komen Kod Anda
## Arahan
Periksa fail /app.js semasa anda di folder permainan anda, dan cari cara untuk mengomentarinya dan rapi. Sangat mudah bagi kod untuk tidak terkawal, dan sekarang adalah peluang yang baik untuk menambahkan komen untuk memastikan bahawa anda mempunyai kod yang dapat dibaca sehingga anda dapat menggunakannya kemudian.
## Rubrik
| Kriteria | Contoh | Mencukupi | Usaha Lagi |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Kod `app.js` dikomentari sepenuhnya dan disusun menjadi blok logik | Kod `app.js` diberi komen yang mencukupi | Kod `app.js` agak tidak teratur dan tidak mempunyai komen yang baik |

View File

@@ -1,11 +0,0 @@
# Becommentarieer uw code
## Instructies
Bekijk uw huidige /app.js-bestand in uw gamemap en zoek manieren om er commentaar op te geven en het op te ruimen. Het is heel gemakkelijk dat code uit de hand loopt, en dit is een goede kans om opmerkingen toe te voegen om ervoor te zorgen dat u leesbare code heeft, zodat u deze later kunt gebruiken.
## Rubriek
| Criteria | Voorbeeldig | Voldoende | Moet worden verbeterd |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | `app.js` code is volledig becommentarieerd en georganiseerd in logische blokken | `app.js` code is voldoende becommentarieerd | De code `app.js` is enigszins ongeorganiseerd en er ontbreken goede commentaren |

View File

@@ -1,11 +0,0 @@
# 為你的程式做註解
## 簡介
打開遊戲資料夾中目前的 /app.js 檔案,試著幫它做上註解並整理乾淨。程式碼很容易脫離掌控,現在是個好機會來確保你的程式是容易去閱讀的,在未來還可以被使用。
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | ----------------------------- | ----------------------- | ----------------------- |
| | `app.js` 完整地註解且分塊整理 | `app.js` 有做充分的註解 | `app.js` 凌亂且缺乏註解 |

View File

@@ -1,293 +0,0 @@
# Construye un juego espacial Parte IV: Adición de un láser y detección de colisiones
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35)
¡En esta lección aprenderá a disparar láseres con JavaScript! Agregaremos dos cosas a nuestro juego:
- **Un láser**: este láser se dispara desde la nave de tu héroe y verticalmente hacia arriba
- **Detección de colisiones**, como parte de la implementación de la capacidad de *disparar*, también agregaremos algunas reglas de juego interesantes:
- **El láser golpea al enemigo**: El enemigo muere si es golpeado por un láser
- **El láser golpea la pantalla superior**: un láser se destruye si golpea la parte superior de la pantalla
- **Colisión de héroe y enemigo**: Un enemigo y el héroe son destruidos si se golpean entre sí.
- **El enemigo golpea la parte inferior de la pantalla**: un enemigo y un héroe son destruidos si el enemigo golpea la parte inferior de la pantalla.
En resumen, tú, *el héroe*, debes golpear a todos los enemigos con un láser antes de que logren moverse a la parte inferior de la pantalla.
✅ Investiga un poco sobre el primer juego de computadora jamás escrito. ¿Cuál fue su funcionalidad?
¡Seamos heroicos juntos!
## Detección de colisiones
¿Cómo hacemos la detección de colisiones? Necesitamos pensar en los objetos de nuestro juego como rectángulos que se mueven. ¿Por qué podrías preguntar? Bueno, la imagen utilizada para dibujar un objeto de juego es un rectángulo: tiene una `x`,` y`, `width` y `height`.
Si dos rectángulos, es decir, un héroe y un enemigo *se cruzan*, tienes una colisión. Lo que debería suceder entonces depende de las reglas del juego. Para implementar la detección de colisiones, por lo tanto, necesita lo siguiente:
1. Una forma de obtener una representación rectangular de un objeto de juego, algo como esto:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Una función de comparación, esta función puede verse así:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## ¿Cómo destruimos las cosas?
Para destruir cosas en un juego, debes informarle al juego que ya no debería pintar este elemento en el bucle del juego que se activa en un intervalo determinado. Una forma de hacer esto es marcar un objeto del juego como *muerto* cuando algo sucede, así:
```javascript
// ocurrió la colisión
enemy.dead = true
```
Luego, puede proceder a clasificar los objetos *muertos* antes de volver a pintar la pantalla, así:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## ¿Cómo disparamos un láser?
Disparar un láser se traduce en responder a un evento clave y crear un objeto que se mueve en una dirección determinada. Por lo tanto, debemos realizar los siguientes pasos:
1. **Crea un objeto láser**: desde la parte superior de la nave de nuestro héroe, que al ser creado comienza a moverse hacia la parte superior de la pantalla.
2. **Adjuntar código a un evento clave**: necesitamos elegir una tecla en el teclado que represente al jugador disparando el láser.
3. **Crea un objeto de juego que parezca un láser** cuando se presiona la tecla.
## Enfriamiento en nuestro láser
El láser debe dispararse cada vez que presione una tecla, como *espacio*, por ejemplo. Para evitar que el juego produzca demasiados láseres en poco tiempo, debemos solucionar este problema. La solución es implementar un llamado *enfriamiento*, un temporizador, que garantiza que un láser solo se pueda disparar con cierta frecuencia. Puede implementar eso de la siguiente manera:
```javascript
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.
}
}
}
```
✅ Consulte la lección 1 de la serie de juegos espaciales para recordar los *tiempos de reutilización*.
## Qué construir
Tomará el código existente (que debería haber limpiado y refactorizado) de la lección anterior y lo extenderá. Empiece con el código de la parte II o utilice el código en [Part III- starter](/your-work).
> consejo: el láser con el que trabajará ya está en su carpeta de activos y su código hace referencia a él
- **Agregue detección de colisiones**, cuando un láser choca con algo, se deben aplicar las siguientes reglas:
1. **El láser golpea al enemigo**: el enemigo muere si es golpeado por un láser
2. **El láser golpea la pantalla superior**: un láser se destruye si golpea la parte superior de nuestra pantalla
3. **Colisión de héroe y enemigo**: un enemigo y el héroe son destruidos si se golpean entre sí
4. **El enemigo golpea la parte inferior de la pantalla**: un enemigo y un héroe son destruidos si el enemigo golpea la parte inferior de la pantalla.
## Pasos recomendados
Busque los archivos que se han creado para usted en la subcarpeta `su-trabajo`. Debe contener lo siguiente:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Comienzas tu proyecto en la carpeta `your_work` escribiendo:
```bash
cd your-work
npm start
```
Lo anterior iniciará un servidor HTTP en la dirección `http: // localhost: 5000`. Abra un navegador e ingrese esa dirección, ahora mismo debería representar al héroe y a todos los enemigos, nada se mueve, todavía :).
### Agregar código
1. **Configure una representación rectangular de su objeto de juego, para manejar la colisión** El siguiente código le permite obtener una representación rectangular de un `GameObject`. Edite su clase GameObject para extenderla:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Agregar código que verifica la colisión** Esta será una nueva función que prueba si dos rectángulos se cruzan:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Agregar capacidad de disparo láser**
1. **Agregar mensaje de evento clave**. La tecla *espacio* debería crear un láser justo encima de la nave del héroe. Agregue tres constantes en el objeto Mensajes:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Tecla de espacio en el mango**. Edite la función de teclado `window.addEventListener` para manejar espacios:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Agregar oyentes**. Edite la función ʻinitGame () `para asegurarse de que el héroe pueda disparar cuando se presione la barra espaciadora:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
y agregue una nueva función `eventEmitter.on ()` para asegurar el comportamiento cuando un enemigo choca con un láser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1.**Mover objeto**, asegúrese de que el láser se mueva gradualmente hacia la parte superior de la pantalla. Creará una nueva clase Laser que amplíe `GameObject`, como lo hizo antes:
```javascript
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)
}
}
```
1. **Manejar colisiones**, implementar reglas de colisión para el láser. Agregue una función `updateGameObjects()` que prueba los objetos en colisión en busca de hits
```javascript
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);
}
```
Asegúrate de agregar `updateGameObjects()` en tu bucle de juego en `window.onload`.
4. **Implemente el tiempo de reutilización** en el láser, para que solo se pueda disparar con cierta frecuencia.
Finalmente, edite la clase Hero para que pueda enfriarse:
```javascript
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;
}
}
```
En este punto, ¡tu juego tiene algunas funciones! Puedes navegar con tus flechas, disparar un láser con tu barra espaciadora y los enemigos desaparecen cuando los golpeas. ¡Bien hecho!
🚀 Desafío: ¡Agrega una explosión! Echa un vistazo a los recursos del juego en [el repositorio de Space Art](../solution/spaceArt/readme.txt) e intenta agregar una explosión cuando el láser golpea a un extraterrestre.
## [Post-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36)
## Revisión y autoestudio
Experimente con los intervalos de su juego hasta ahora. ¿Qué pasa cuando los cambias? Más información sobre [eventos de tiempo de JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
**Asignación**: [Explorar colisiones](assignment.es.md)

View File

@@ -1,297 +0,0 @@
# Construire un jeu spatial Partie 4: Ajouter un laser et détecter les collisions
## Quiz préalable
[Quiz préalable](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=fr)
Dans cette leçon, vous apprendrez à tirer des lasers avec JavaScript! Nous ajouterons deux choses à notre jeu:
- **Un laser**: ce laser est tiré depuis le vaisseau de vos héros et verticalement vers le haut
- **La détection de collision**, dans le cadre de la mise en œuvre de la possibilité de *tirer*, nous ajouterons également quelques règles de jeu intéressantes:
- **Le laser frappe l'ennemi**: L'ennemi meurt s'il est touché par un laser
- **Le laser touche l'écran supérieur**: un laser est détruit s'il touche la partie supérieure de l'écran
- **Collision entre ennemis et héros**: Un ennemi et le héros sont détruits s'ils se frappent
- **L'ennemi touche le bas de l'écran**: Un ennemi et un héros sont détruits si l'ennemi touche le bas de l'écran
En bref, vous -- *le héros* -- devez frapper tous les ennemis avec un laser avant qu'ils ne parviennent à se déplacer vers le bas de l'écran.
✅ Faites une petite recherche sur le tout premier jeu vidéo jamais écrit. Quelle était sa fonctionnalité?
Soyons héroïques ensemble!
## La détection de collision
Comment fait-on la détection de collision? Nous devons penser à nos objets de jeu comme des rectangles se déplaçant. Pourquoi vous pourriez vous demander? Eh bien, l'image utilisée pour dessiner un objet de jeu est un rectangle: il a un `x`, `y`, `width` et `height`.
Si deux rectangles, c'est-à-dire un héros et un ennemi *se croisent*, vous avez une collision. Ce qui devrait arriver alors dépend des règles du jeu. Pour implémenter la détection de collision, vous avez donc besoin des éléments suivants:
1. Un moyen d'obtenir une représentation rectangulaire d'un objet de jeu, quelque chose comme ça:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Une fonction de comparaison, cette fonction peut ressembler à ceci:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## Comment détruire les objets
Pour détruire des objets dans un jeu, vous devez faire savoir au jeu qu'il ne doit plus afficher cet objet dans la boucle de jeu qui se déclenche à un certain intervalle. Une façon de faire est de marquer un objet de jeu comme *mort* quand quelque chose se passe, comme ceci:
```javascript
// la collision s'est produite
enemy.dead = true
```
Ensuite, vous procédez au tri des objets *morts* avant de repeindre l'écran, comme ceci:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## Comment tirons-nous un laser
Tirer un laser se traduit par répondre à un événement clé et créer un objet qui se déplace dans une certaine direction. Nous devons donc effectuer les étapes suivantes:
1. **Créez un objet laser**: du haut du vaisseau de notre héros, celui-ci commence à se déplacer vers le haut de l'écran lors de sa création.
2. **Attacher un code à un événement clé**: nous devons choisir une touche du clavier qui représente le joueur tirant le laser.
3. **Créez un objet de jeu qui ressemble à un laser** lorsque la touche est enfoncée.
## Cooldown sur notre laser
Le laser doit se déclencher à chaque fois que vous appuyez sur une touche, comme *espace* par exemple. Pour éviter que le jeu ne produise trop de lasers en peu de temps, nous devons résoudre ce problème. La solution consiste à implémenter un soi-disant *cooldown*(temps de recharge), une minuterie, qui garantit qu'un laser ne peut être tiré aussi souvent. Vous pouvez implémenter cela de la manière suivante:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produire un laser
this.cooldown = new Cooldown(500);
} else {
// ne rien faire - il n'a pas encore refroidi.
}
}
}
```
✅ Reportez-vous à la leçon 1 de la série de jeux spatiaux pour vous rappeler des *temps de recharge*
## Que construire
Vous allez prendre le code existant (que vous avez dû nettoyer et refactoriser) de la leçon précédente et l'étendre. Commencez par le code de la partie II ou utilisez le code de la [partie III - démarreur](../../your-work).
> astuce: le laser avec lequel vous allez travailler est déjà dans votre dossier assets et référencé par votre code
- **Ajoutez la détection de collision**, lorsqu'un laser entre en collision avec quelque chose, les règles suivantes doivent s'appliquer:
1. **Le laser touche l'ennemi**: l'ennemi meurt s'il est touché par un laser
2. **Le laser touche l'écran supérieur**: un laser est détruit s'il touche la partie supérieure de notre écran
3. **Collision ennemi et héros**: un ennemi et le héros sont détruits s'ils se frappent
4. **L'ennemi touche le bas de l'écran**: un ennemi et un héros sont détruits si l'ennemi touche le bas de l'écran
## Étapes recommandées
Localisez les fichiers qui ont été créés pour vous dans le sous-dossier `your-work`. Il doit contenir les éléments suivants:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Vous démarrez votre projet dans le dossier `your_work` en tapant:
```bash
cd your-work
npm start
```
Ce qui précède démarrera un serveur HTTP à l'adresse `http://localhost:5000`. Ouvrez un navigateur et saisissez cette adresse, pour le moment, cela devrait rendre le héros et tous les ennemis, rien ne bouge - pour le moment :).
### Ajouter un code
1. **Configurez une représentation rectangulaire de votre objet de jeu, pour gérer les collisions** Le code ci-dessous vous permet d'obtenir une représentation rectangulaire d'un `GameObject`. Modifiez votre classe GameObject pour l'étendre:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Ajouter du code qui vérifie la collision** Il s'agira d'une nouvelle fonction qui teste si deux rectangles se croisent:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Ajouter une capacité de tir laser **
1. **Ajouter un message d'événement clé**. La touche *espace* devrait créer un laser juste au-dessus du vaisseau héros. Ajoutez trois constantes dans l'objet Messages:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Manipuler la touche espace**. Modifiez la fonction keyup `window.addEventListener` pour gérer les espaces:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Ajouter des auditeurs**. Modifiez la fonction `initGame()` pour vous assurer que le héros peut se déclencher lorsque la barre d'espace est enfoncée:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
et ajoutez une nouvelle fonction `eventEmitter.on()` pour assurer le comportement lorsqu'un ennemi entre en collision avec un laser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **Déplacez l'objet**, assurez-vous que le laser se déplace progressivement vers le haut de l'écran. Vous allez créer une nouvelle classe Laser qui étend `GameObject`, comme vous l'avez fait auparavant:
```javascript
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)
}
}
```
1. **Gérer les collisions**, Implémenter les règles de collision pour le laser. Ajouter une fonction `updateGameObjects()` qui teste les objets en collision pour les hits
```javascript
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);
}
```
Assurez-vous d'ajouter `updateGameObjects()` dans votre boucle de jeu dans `window.onload`.
4. **Mettez en place un temps de recharge (cooldown)** sur le laser, de sorte qu'il ne peut être tiré qu'un certain temps.
Enfin, modifiez la classe de héros afin qu'elle puisse se recharger:
```javascript
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;
}
}
```
À ce stade, votre jeu a quelques fonctionnalités! Vous pouvez naviguer avec vos touches fléchées, tirer un laser avec votre barre d'espace et les ennemis disparaissent lorsque vous les frappez. Bien joué!
---
## 🚀 Challenge
Ajoutez une explosion! Jetez un œil aux ressources du jeu dans [le référentiel Space Art](../../solution/spaceArt/readme.txt) et essayez d'ajouter une explosion lorsque le laser frappe un extraterrestre
## Quiz de validation des connaissances
[Quiz de validation des connaissances](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=fr)
## Révision et étude personnelle
Faites des expériences avec les intervalles de votre jeu à ce stade. Que se passe-t-il lorsque vous les modifiez? En savoir plus sur les [événements de minutage JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Affectation
[Explorer les collisions](assignment.fr.md)

View File

@@ -1,305 +0,0 @@
# एक अंतरिक्ष खेल भाग 4 बनाएँ: एक लेजर और टकराव का पता लगाए
## लेक्चरसे पहलेकी क्विज
[लेक्चरसे पहलेकी क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=hi)
इस पाठ में आप सीखेंगे कि जावास्क्रिप्ट के साथ लेज़रों को कैसे शूट किया जाए! हम अपने खेल में दो चीजें जोड़ेंगे:
- **A laser**: इस लेज़र को आपके नायकों के जहाज से और ऊपर की ओर सीधा खड़ा किया गया है
- **Collision detection**, _शूट_ करने की क्षमता को लागू करने के हिस्से के रूप में हम कुछ अच्छे गेम नियम भी जोड़ेंगे:
- **Laser hits enemy**: लेजर से मारने पर दुश्मन की मौत हो जाती है
- **Laser hits top screen**: एक लेजर यदि स्क्रीन के शीर्ष भाग से टकराने के नष्ट हो जाता है
- **Enemy and hero collision**:एक दुश्मन और नायक नष्ट होजाता है अगर एक दूसरे को मारते हैं
- **Enemy hits bottom of the screen**: एक दुश्मन और एक नायक को नष्ट कर दिया जाता है अगर दुश्मन स्क्रीन के नीचे हिट करता है
संक्षेप में, आप - _ नायक _ - इससे पहले कि वे स्क्रीन के निचले भाग में जाने का प्रबंधन करें, सभी दुश्मनों को एक लेजर के साथ हिट करने की आवश्यकता है.
✅ कभी लिखे गए पहले कंप्यूटर गेम पर थोड़ा शोध करें। इसकी कार्यक्षमता क्या थी?
चलो एक साथ वीर बने !
## टक्कर की पहचान
हम टकराव का पता कैसे लगाते हैं? हमें अपने खेल की वस्तुओं के बारे में सोचने की जरूरत है क्योंकि आयतें चलती हैं. आप ऐसा क्यों पूछ सकते हैं? खैर, किसी गेम ऑब्जेक्ट को खींचने के लिए उपयोग की जाने वाली छवि एक आयत है: इसमें एक `x`,` y`, `चौड़ाई` और `ऊँचाई` है।.
यदि दो आयतें, अर्थात् एक नायक और शत्रु प्रतिच्छेद करते हैं, तो आपकी टक्कर होती है.
तब क्या होना चाहिए यह खेल के नियमों पर निर्भर है. टक्कर का पता लगाने को लागू करने के लिए आपको निम्न की आवश्यकता है:
1. एक खेल वस्तु का एक आयत प्रतिनिधित्व पाने का एक तरीका, कुछ इस तरह:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. एक तुलनात्मक फ़ंक्शन, यह फ़ंक्शन इस तरह दिख सकता है:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
## हम चीजों को कैसे नष्ट करते हैं
एक खेल में चीजों को नष्ट करने के लिए आपको इस खेल को जानने देना चाहिए कि यह गेम लूप में इस आइटम को पेंट नहीं करना चाहिए जो एक निश्चित अंतराल पर ट्रिगर होता है. ऐसा करने का एक तरीका यह है कि गेम ऑब्जेक्ट को _मृत_ के रूप में चिह्नित किया जाए जब कुछ ऐसा होता है, जैसे:
```javascript
// collision happened
enemy.dead = true;
```
फिर आप स्क्रीन को फिर से खोलने से पहले _ मृत _ वस्तुओं को छाँटने के लिए आगे बढ़ सकते हैं, जैसे कि:
```javascript
gameObjects = gameObject.filter((go) => !go.dead);
```
## हम एक लेजर को कैसे फायर करते हैं
लेजर को फायर करना एक महत्वपूर्ण घटना का जवाब देने और एक निश्चित दिशा में जाने वाली वस्तु बनाने के लिए अनुवाद करता है. इसलिए हमें निम्नलिखित चरणों को पूरा करने की आवश्यकता है:
1. **एक लेज़र ऑब्जेक्ट बनाएँ**: हमारे नायक के जहाज के ऊपर से, कि सृजन स्क्रीन के ऊपर की ओर बढ़ने लगता है.
2. **एक महत्वपूर्ण घटना के लिए कोड संलग्न करें**: हमें कीबोर्ड पर एक की चुनने की आवश्यकता है जो लेजर की शूटिंग करने वाले खिलाड़ी का प्रतिनिधित्व करता है.
3. **एक गेम ऑब्जेक्ट बनाएं जो लेजर की तरह दिखता है**:जब की दबाया जाता है.
## हमारे लेजर पर शांत हो जाओ
उदाहरण के लिए, हर बार जब आप एक की दबाते हैं, तो लेज़र को फायर करना पड़ता है. खेल को कम समय में कई लेज़रों के उत्पादन को रोकने के लिए हमें इसे ठीक करने की आवश्यकता है.
फिक्स एक तथाकथित _कुलदावन_ को लागू करके है, एक टाइमर, जो यह सुनिश्चित करता है कि एक लेजर को केवल इतनी बार निकाल दिया जा सकता है। आप इसे निम्नलिखित तरीके से लागू कर सकते हैं
```javascript
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.
}
}
}
```
✅ अपने आप को _कूलदावन_ की याद दिलाने के लिए अंतरिक्ष खेल श्रृंखला में सबक 1 का संदर्भ लें.
## क्या बनना है
आप पिछले पाठ से मौजूदा कोड (जिसे आपको साफ करना चाहिए और रिफलेक्ट करना चाहिए था) ले लेंगे, और इसे विस्तारित करेंगे। या तो भाग II के कोड से शुरू करें या [Part III-Starter](/your-work) में कोड का उपयोग करें.
> टिप: वह लेजर जिसके साथ आप काम करेंगे, वह पहले से ही आपके असेट्स फ़ोल्डर में है और आपके कोड द्वारा संदर्भित है
- **टक्कर की पहचान जोड़े**, जब एक लेजर कुछ के साथ टकराता है तो निम्नलिखित नियम लागू होने चाहिए:
1. **लेजर दुश्मन को मारता है**: लेजर से मारने पर दुश्मन मर जाता है
2. **लेजर शीर्ष स्क्रीन को मारता है**: यदि हमारी स्क्रीन का शीर्ष भाग हिट हो जाता है तो एक लेजर नष्ट हो जाता है
3. **दुश्मन और हीरो की टक्कर**: एक दूसरे को मारने पर एक दुश्मन और नायक नष्ट हो जाता है
4. **स्क्रीन के नीचे दुश्मन मारा**: एक दुश्मन और एक नायक नष्ट हो जाता है अगर दुश्मन स्क्रीन के नीचे मारता है
## अनुशंसित कदम
उन फ़ाइलों का पता लगाएँ जो आपके लिए `your-work` सब फ़ोल्डर में बनाई गई हैं। इसमें निम्नलिखित शामिल होना चाहिए:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
आप टाइप करके अपना प्रोजेक्ट `your_work` फ़ोल्डर शुरू करें:
```bash
cd your-work
npm start
```
उपरोक्त पते पर एक HTTP सर्वर शुरू होगा `http: // localhost: 5000`। एक ब्राउज़र खोले और उस पते को डाले, अभी उसे नायक और सभी दुश्मनों को सौंप देना चाहिए, कुछ भी नहीं चल रहा है - अभी तक :).
### कोड डाले
1. **टकराव को संभालने के लिए अपने गेम ऑब्जेक्ट के आयत प्रतिनिधित्व को सेट करें** नीचे दिए गए कोड से आप 'GameObject` का आयत प्रतिनिधित्व प्राप्त कर सकते हैं। इसे बढ़ाने के लिए अपने गेमऑब्जेक्ट क्लास को संपादित करें:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **कोड जोड़ें जो टकराव की जाँच करता है** यह एक नया कार्य होगा जो परीक्षण करता है कि क्या दो आयतें प्रतिच्छेद करती हैं::
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **लेजर फायरिंग क्षमता जोड़ें**
1. **की-ईवेंट संदेश जोड़ें**. _ स्पेस _ की को हीरो शिप के ठीक ऊपर एक लेजर बनाना चाहिए। संदेश ऑब्जेक्ट में तीन स्थिरांक जोड़ें:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **अंतरिक्ष की संभालें**. रिक्त स्थान को संभालने के लिए `window.addEventListener` कीअप फंक्शन संपादित करें:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **श्रोताओं को जोड़ें**.`InitGame()` फ़ंक्शन को संपादित करे यह सुनिश्चित करने के लिए कि स्पेस बार हिट होने पर हीरो फायर कर सकता है:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
और एक नया `eventEmitter.on()` समारोह जोड़ने के लिए व्यवहार सुनिश्चित करने के लिए जब एक दुश्मन एक लेजर के साथ टकराता है:
```javascript
eventEmitter.on(
Messages.COLLISION_ENEMY_LASER,
(_, { first, second }) => {
first.dead = true;
second.dead = true;
}
);
```
1. **बस्तु चाल**, धीरे-धीरे स्क्रीन के शीर्ष पर लेजर चाल सुनिश्चित करें। आप एक नया लेजर वर्ग बनाएंगे जो `GameObject` का विस्तार करता है, जैसा कि आपने पहले किया है:
```javascript
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);
}
}
```
1. **तकराब संभालना**,लेजर के लिए टकराव के नियमों को लागू करें। एक `updateGameObjects()` फ़ंक्शन जोड़ें जो हिट के लिए वस्तुओं को टकराने का परीक्षण करता है
```javascript
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);
}
```
`Window.onload` में अपने गेम लूप में`updateGameObjects()`जोड़ना सुनिश्चित करें.
1. लेजर पर **कुलदावन लागू** करें, इसलिए यह बार बार थोक सकता है.
अंत में, हीरो वर्ग को संपादित करें ताकि यह कुलडाउन हो सके:
```javascript
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;
}
}
```
इस बिंदु पर, आपके गेम में कुछ कार्यक्षमता है! आप अपने तीर की के साथ नेविगेट कर सकते हैं, अपने स्पेस बार के साथ एक लेजर फायर कर सकते हैं, और जब आप उन्हें मारते हैं तो दुश्मन गायब हो जाते हैं। बहुत बढ़िया!
---
## 🚀 चुनौती
एक विस्फोट जोड़ें! [स्पेस आर्ट रेपो](../solution/spaceArt/readme.txt) में खेल की परिसंपत्तियों पर एक नज़र डालें और जब कोई एलियन टकराता है तो विस्फोट को जोड़ने का प्रयास करें
## पोस्ट-व्याख्यान प्रश्नोत्तरी
[पोस्ट-व्याख्यान प्रश्नोत्तरी](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=hi)
## समीक्षा और स्व अध्ययन
इस प्रकार अपने खेल में अंतराल के साथ प्रयोग करें. जब आप उन्हें बदलते हैं तो क्या होता है? [जावास्क्रिप्ट समय घटनाओं](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/) के बारे में और पढ़ें.
## असाइनमेंट
[टक्करों का अन्वेषण करें](assignment.hi.md)

View File

@@ -1,297 +0,0 @@
# Costruire un Gioco Spaziale Parte 4: Aggiungere un Laser e Rilevare le Collisioni
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=it)
In questa lezione si imparererà come fare fuoco con i laser con JavaScript! Verranno aggiunte due cose al gioco:
- **Un laser**: questo laser viene sparato dall'astronave del proprio eroe e verticalmente verso l'alto
- **Rilevamento delle collisioni**, come parte dell'implementazione della capacità di *sparare* , si aggiungeranno anche alcune simpatiche regole di gioco:
- Il **laser colpisce il nemico**: il nemico muore se colpito da un laser
- Il **laser raggiunge lo schermo superiore**: un laser viene distrutto se raggiunge la parte superiore dello schermo
- **Collisione tra nemico ed eroe**: un nemico e l'eroe vengono distrutti se entrano in collisione
- Il **nemico raggiunge la parte inferiore dello schermo**: un nemico e un eroe vengono distrutti se il nemico raggiunge la parte inferiore dello schermo
In breve, il giocatore - *l'eroe* - deve colpire tutti i nemici con un laser prima che riescano a raggiungere la parte inferiore dello schermo.
✅ Fare una piccola ricerca sul primissimo gioco per computer mai scritto. Qual era la sua funzionalità?
Diventiamo eroici insieme!
## Rilevamento della collisione.
Come si rilevano le collisioni? Occorre pensare agli oggetti di gioco come rettangoli che si muovono. Perché, ci si potrebbe chiedere? Bene, l'immagine usata per disegnare un oggetto di gioco è un rettangolo: ha una `x`, una `y`, una larghezza (`width`) e un'altezza (`height`).
Se due rettangoli, cioè un eroe e un nemico *si intersecano*, si verifica una collisione. Ciò che dovrebbe accadere dipende dalle regole del gioco. Per implementare il rilevamento delle collisioni è quindi necessario quanto segue:
1. Un modo per ottenere una rappresentazione rettangolare di un oggetto di gioco, qualcosa del genere:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Una funzione di confronto, questa funzione può assomigliare a questa:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## Come si distruggono le cose
Per distruggere le cose in un gioco si deve far sapere al gioco stesso che non dovrebbe più disegnare un certo oggetto nel ciclo di gioco che si attiva in un certo intervallo. Un modo per farlo è contrassegnare un oggetto del gioco come *morto* quando succede qualcosa, in questo modo:
```javascript
// si è verificata una collisione
enemy.dead = true
```
Quindi si procede a filtrare gli oggetti *morti* prima di ridipingere lo schermo, in questo modo:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## Come si spara un laser
Sparare un laser si traduce nel rispondere a un evento da tastiera e creare un oggetto che si muove in una certa direzione. Occorre quindi eseguire i seguenti passaggi:
1. **Creare un oggetto laser**: dalla cima dell' astronave dell'eroe, che al momento della creazione inizia a muoversi in alto verso la parte superiore dello schermo.
2. **Allegare codice a un evento da tastiera**: si deve scegliere un tasto sulla tastiera che rappresenti il giocatore che spara il laser.
3. **Creare un oggetto di gioco che assomigli a un laser** quando viene premuto il tasto.
## Raffreddamento del laser
Il laser deve attivarsi ogni volta che si preme un tasto, come ad esempio la *barra spazio*. Per evitare che il gioco produca troppi laser in breve tempo, si deve risolvere questo problema. La soluzione è implementando un cosiddetto raffreddamento (*cooldown*), un timer, che garantisce che un laser possa essere sparato solo a intervallo determinato. Su può implementare nel modo seguente:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce un laser
this.cooldown = new Cooldown(500);
} else {
// non fa nulla - non si è ancora raffreddato
}
}
}
```
✅ Fare riferimento alla lezione 1 della serie gioco spaziale per quanto riguarda i tempi di *cooldown* (raffreddamento).
## Cosa costruire
Si prende il codice esistente (che dovrebbe essere stato ripulito e rifattorizzato) dalla lezione precedente e lo si estende Si inizia con il codice della parte II o si usa il codice [della parte III-starter](../your-work).
> suggerimento: il laser con cui lavorare è già nella cartella asset ed è referenziato nel proprio codice
- **Aggiungere il rilevamento delle collisioni**, quando un laser entra in collisione con qualcosa dovrebbero essere applicate le seguenti regole:
1. Il **laser colpisce il nemico**: il nemico muore se colpito da un laser
2. Il **laser raggiunge lo schermo superiore**: un laser viene distrutto se raggiunge la parte superiore dello schermo
3. **Collisione tra nemico ed eroe**: un nemico e l'eroe vengono distrutti se entrano in collisione
4. Il **nemico colpisce la parte inferiore dello schermo**: un nemico e un eroe vengono distrutti se il nemico raggiunge la parte inferiore dello schermo
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere quanto segue:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto nella cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP sull'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici.
### Aggiungere codice
1. **Impostare una rappresentazione di un rettangolo di un oggetto di gioco, per gestire la collisione**. Il codice seguente consente di ottenere una rappresentazione di un rettangolo di un `GameObject`. Modificare la classe GameObject per estenderla:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Aggiungere il codice che controlla la collisione** Questa sarà una nuova funzione che verifica se due rettangoli si intersecano:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Aggiungere capacità di fuoco laser**
1. **Aggiungere messaggio per un evento da tastiera**. Il tasto spazio (*space*) dovrebbe creare un laser appena sopra la astronave dell'eroe. Aggiungere tre costanti nell'oggetto Messages:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Gestire il tasto barra spazio** Modificare la funzione keyup di `window.addEventListener` per gestire la barra spazio:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Aggiungere listener**. Modificare la funzione `initGame()` per assicurarsi che l'eroe possa sparare quando viene premuta la barra spazio:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
e aggiungere una nuova funzione `eventEmitter.on()` per garantire il comportamento quando un nemico si scontra con un laser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **Spostare l'oggetto**, assicurarsi che il laser si muova gradualmente verso parte superiore dello schermo. Creare una nuova classe Laser che estende `GameObject`, come fatto precedentemente:
```javascript
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)
}
}
```
1. **Gestire le collisioni**, implementare le regole di collisione per il laser. Aggiungere una funzione `updateGameObjects()` che verifica gli oggetti in collisione per quelli colpiti
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// il laser ha colpito qualcosa
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);
}
```
Assicurarsi di aggiungere `updateGameObjects()` nel ciclo di gioco in `window.onload`.
4. **Implementare il** raffreddamento sul laser, in modo che possa essere sparato solo a determinati intervalli.
Infine, modificare la classe Hero in modo che possa eseguire il raffreddamento:
```javascript
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;
}
}
```
A questo punto, il tuo gioco ha alcune funzionalità! Si può navigare con i tasti freccia, sparare un laser con la barra spaziatrice e i nemici scompaiono quando colpiti. Ottimo lavoro!
---
## 🚀 Sfida
Aggiungere un'esplosione! Dare un'occhiata alle risorse di gioco [nel repository Space Art](../solution/spaceArt/readme.txt) e provare ad aggiungere un'esplosione quando il laser colpisce un alieno
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=it)
## Revisione e Auto Apprendimento
Sperimentare con gli intervalli nel proprio gioco fino ad ora. Cosa succede quando si cambiano? Ulteriori informazioni sugli [eventi di temporizzazione JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Compito
[Esplorare le collisioni](assignment.it.md)

View File

@@ -1,298 +0,0 @@
# スペースゲーム構築プロジェクト その 4: レーザーを追加して衝突を検出する
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=ja)
このレッスンでは、JavaScript でレーザーを撃つ方法を学びます! 私たちはゲームに2つのものを追加します。
- **レーザー**: このレーザーはあなたのヒーローの宇宙船から垂直に上向きに撃たれます
- **衝突検出**、発射する能力を実装する一環として、我々はまたいくつかの素敵なゲームのルールを追加します
- **レーザーが敵に当たる**: レーザーが当たると敵が破壊される
- **レーザーがトップ画面に当たる**: 画面上部に当たるとレーザーが破壊されます
- **敵とヒーローの衝突**: 敵とヒーローがぶつかると破壊されます
- **敵が画面の下に当たる**: 敵が画面下に当たると敵とヒーローが破壊されます
要するに、あなた - *ヒーロー* - は画面の下部に移動するために管理する前に、レーザーですべての敵をヒットする必要があります。
✅ これまでに書かれた最初のコンピュータゲームについて少し調べてみてください。その機能は何だったのでしょうか?
一緒にヒーローになりましょう!
## 衝突検出
どのようにして衝突を検出するのか? ゲームオブジェクトを移動する長方形と考える必要があります。なぜでしょうか? ゲームオブジェクトの描画に使われる画像は矩形です。`x``y``width``height` を持っています。
2 つの長方形、つまりヒーローと敵が交差すると、衝突します。そのときに何が起こるかは、ゲームのルール次第です。したがって、衝突検出を実装するためには以下のものが必要です。
1. ゲームオブジェクトの矩形表現を取得する方法。こんな感じです
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. 比較関数で、この関数は次のようになります
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## オブジェクトをどうやって破壊するか
ゲーム内のオブジェクトを破壊するには、一定の間隔で発生するゲームループの中で、このアイテムをペイントしないようにゲームに知らせる必要があります。これを行うには、以下のように、何かが起こったときにゲームオブジェクトを *dead* としてマークする方法があります。
```javascript
// 衝突が発生します
enemy.dead = true
```
その後、画面を再描画する前に、このように、*dead* オブジェクトをソートしていきます。
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## レーザーの撃ち方
レーザーを発射することは、キーイベントに反応して、特定の方向に移動するオブジェクトを作成することになります。そのため、以下のような作業を行う必要があります。
1. **レーザーオブジェクトを作成します**: ヒーローの船の上部から、作成時に画面の上部に向かって上向きに移動を開始します
2. **キーイベントにコードを添付します**: レーザーを撮影しているプレイヤーを表すキーボードのキーを選択する必要があります
3. キーを押すと**レーザーのように見えるゲームオブジェクトを作成します**
## レーザーのクールダウン
レーザーは、例えば*スペース*のように、キーを押すたびに発射する必要があります。短時間で多くのレーザーが発射されるのを防ぐために、この問題を解決する必要があります。これを修正するには、いわゆる *クールダウン*、タイマーを実装することで、レーザーはそれほど頻繁にしか発射することができないことを保証します。以下の方法で実装できます。
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// レーザーを作る
this.cooldown = new Cooldown(500);
} else {
// 何もしない-まだクールダウンしていない。
}
}
}
```
✅ スペースゲームシリーズのレッスン1を参照して、*クールダウン*について思い出してください。
## 何を構築するか
前回のレッスンで使用した既存のコード (クリーンアップしてリファクタリングしたはずのコード) を使用して、それを拡張します。
パート II のコードから始めるか、[パートIII - スターター](../../your-work)のコードを使用してください。
> ヒント: 作業に使用するレーザーはすでにアセットフォルダにあり、コードで参照されています。
- **衝突検出を追加します**、レーザーが何かに衝突するときは、次のルールを適用する必要があります
1. **レーザーが敵に当たる**: レーザーが当たると敵が破壊される
2. **レーザーが画面上部に当たる**: レーザーが画面の上部に当たると破壊される
3. **敵とヒーローの衝突**: 敵とヒーローがぶつかると破壊される
4. **画面下に敵が当たる**: 敵が画面下部に当たると敵とヒーローが破壊される
## 推奨される手順
あなたのために作成されたファイルを `your-work` サブフォルダ内で探します。以下のファイルが含まれているはずです。
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
次のコマンドをタイピングして、あなたのプロジェクトを `your_work` フォルダから開始します。
```bash
cd your-work
npm start
```
上記は、`http://localhost:5000` というアドレスで HTTP サーバを起動します。ブラウザを開いてそのアドレスを入力すると、今はヒーローと全ての敵が表示されるはずですが、まだ何も動いていません。
### コードの追加
1. **衝突を処理するためにゲームオブジェクトの矩形表現を設定する** 以下のコードでは、`GameObject` の矩形表現を取得することができます。GameObject クラスを編集して拡張します
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **衝突をチェックするコードの追加** これは、2 つの矩形が交差するかどうかをテストする新しい関数になります
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **レーザー発射機能の追加**
1. **キーイベントメッセージを追加します**。*スペース*キーはヒーローの宇宙船の真上にレーザーを作成する必要があります。Messages オブジェクトに3つの定数を追加します
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **スペースキーを処理します**。スペースを扱うために、`window.addEventListener` のキーアップ関数を編集します
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **リスナーを追加します**。スペースバーがヒットした時にヒーローが発射できるように `initGame()` 関数を編集します
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
そして、敵がレーザーに衝突したときの動作を保証するために、新しい `eventEmitter.on()` 関数を追加します
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **オブジェクトを移動させ**、レーザーが徐々に画面上部に移動するようにします。先ほどと同じように `GameObject`を継承した Laser クラスを新たに作成します
```javascript
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)
}
}
```
1. **衝突の処理**、レーザーの衝突ルールの実装 衝突したオブジェクトのヒットをテストする `updateGameObjects()` 関数を追加します
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// レーザーが当たる
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);
}
```
必ず `window.onload` のゲームループに `updateGameObjects()` を追加してください。
4. レーザーに**クールダウンを実装**して、それがそんなに頻繁にしか発射できないようにします
最後に、Hero クラスをクールダウンできるように編集します
```javascript
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;
}
}
```
この時点で、あなたのゲームはいくつかの機能を持っています! 矢印キーで移動したり、スペースバーでレーザーを発射したり、敵はあなたがそれらを打つときに消えることができます。よくできました!
---
## 🚀 チャレンジ
爆発を追加しましょう! [スペースアートレポ](../../solution/spaceArt/readme.txt)のゲームアセットを見て、レーザーがエイリアンに当たったときに爆発を追加してみてください。
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=ja)
## 復習と自己学習
これまでのゲームの間隔を実験してみてください。それらを変更するとどうなるでしょうか? [JavaScript のタイミングイベント](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)についての詳細はこちら。
## 課題
[衝突を調べる](assignment.ja.md)

View File

@@ -1,297 +0,0 @@
# Space 게임 제작하기 파트 4: 레이저 추가하고 충돌 감지하기
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=ko)
이 강의에서는 JavaScript로 레이저를 쏘는 방법을 배웁니다! 게임에 다음 두 가지를 추가합니다:
- **레이저**: 이 레이저는 영웅 우주선에서 수직 위쪽으로 발사되며
- **충돌 감지**, *쏘는* 기능 구현의 부분으로 몇 가지 멋진 게임 규칙을 추가할 예정입니다:
- **레이저로 적 때리기**: 레이저에 맞으면 적은 사망합니다
- **레이저로 화면 상단 도달하기**: 화면의 상단 부분을 맞으면 레이저는 파괴됩니다
- **적과 영웅 충돌하기**: 적과 영웅이 부딪히면 파괴됩니다
- **적이 화면 하단 도달하기**: 적이 화면 하단에 부딪히면 적과 영웅이 파괴됩니다
짧게 요약해보면, 여러분은 -- *영웅* -- 모든 적들이 화면 아래로 내려오기 전에 레이저로 모든 적을 때려야 합니다.
✅ 지금까지 작성된 최초의 컴퓨터 게임에 대해 약간 알아보새요. 어떻게 작동하나요?
함께 영웅이 됩시다!
## 충돌 감지하기
충돌은 어떻게 감지할까요? 게임 객체를 움직이는 직사각형으로 생각해야 합니다. 왜 물어볼까요? 게임 객체를 그리는 데 사용되는 이미지는 직사각형이기 때문입니다: `x`, `y`, `width` 그리고 `height`가 있습니다.
만약 두 직사각형이, 즉 영웅과 적이 *교차*하면, 충돌합니다. 그런 뒤에 발생할 일은 게임의 룰에 달려 있습니다. 따라서 충돌 감지를 구현하려면 다음이 필요합니다:
1. 게임 객체의 직사각형 표현을 얻는 방법은, 다음과 같습니다:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. 비교 함수는, 다음 함수와 같습니다:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## 어떻게 파괴할까요
게임에서 물건을 파괴하려면 특정 간격으로 연결되는 게임 루프에서 이 아이템을 더 이상 그리지 않아야 한다고 게임에 알려야 합니다. 이 방법은 다음과 같이, 어떤 일이 발생했을 때 게임 객체를 *dead*으로 표시합니다:
```javascript
// collision happened
enemy.dead = true
```
그러고 다음과 같이, 화면을 다시 그리기 전에 *dead* 객체를 정렬합니다:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## 어떻게 레이저를 발사할까요
레이저를 발사하는 것은 키-이벤트에 반응하고 특정 방향으로 움직이는 객체를 만드는 것으로 바뀝니다. 따라서 다음 단계를 해야합니다:
1. **레이저 객체 생성**: 영웅 함선의 상단에서, 생성되고 화면 상단으로 올라가기 시작합니다.
2. **키 이벤트에 코드 첨부**: 레이저를 쏘는 플레이어로 특정할 키보드의 키를 선택해야 합니다.
3. 키를 누를 때, **레이저처럼 보이는 게임 객체를 생성합니다**.
## 레이저 쿨다운
레이저는 *space*와 같은 키를 누를 때마다 발사되어야 합니다. 게임이 짧은 시간에 너무 많은 레이저를 생성하는 것을 막으려면 이를 해결해야 합니다. 해결 방법은 레이저를 자주 발사하도록 보장하는 타이머인, *cooldown*을 구현하는 것입니다. 다음과 같이 구현할 수 있습니다:
```javascript
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.
}
}
}
```
✅ *cooldowns*에 대해 복습하려면 space 게임 시리즈의 1강을 참조하세요.
## 무엇을 만드나요
이전 강의에 존재한 기존 코드 (정리하고 리팩토링함)를 가져와서, 확장합니다. 파트 II에서 코드를 시작하거나 [Part III- starter](../your-work) 코드를 사용합니다.
> tip: 작업할 레이저는 이미 어셋 폴더에 있으므로 코드에서 참조합니다
- **충돌 감지를 추가합니다**, 레이저가 무언가 부딪칠 때 다음 규칙이 적용되어야 합니다:
1. **레이저가 적 때리기**: 레이저에 맞으면 적은 사망합니다
2. **레이저로 화면 상단 도달하기**: 화면의 상단 부분을 맞으면 레이저는 부서집니다
3. **적과 영웅 충돌하기**: 적과 영웅이 부딪히면 파괴됩니다
4. **적이 화면 하단 도달하기**: 적이 화면 하단에 부딪히면 적과 영웅이 파괴됩니다
## 권장 단계
`your-work` 하위 폴더에 생성된 파일을 찾습니다. 이는 다음을 포함하고 있어야 합니다:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
타이핑하여 `your_work` 폴더에서 프로젝트를 시작합니다:
```bash
cd your-work
npm start
```
위 코드는 `http://localhost:5000` 주소에서 HTTP 서버를 시작합니다. 브라우저를 열고 해당 주소를 입력하세요, 지금 바로 영웅과 모든 적을 렌더링해야합니다, 하지만 다 움직이지 않습니다 - 아직 :).
### 코드 추가하기
1. ***충돌을 처리하기 위해 게임 객체의 사각형 표현을 설정합니다** 아래 코드를 쓰면 `GameObject`의 사각형 표현을 얻을 수 있습니다. GameObject 클래스를 편집하여 확장합니다:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **충돌을 확인하는 코드를 추가합니다** 이것은 두 개의 직사각형이 교차되는가에 대한 여부를 테스트하는 새로운 함수입니다:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **레이저 발사 기능 추가**
1. **키-이벤트 메시지 추가하기**. *space* 키는 영웅 함선 바로 위에 레이저를 만들어줘야 합니다. Messages 객체에 세 개의 상수를 추가합니다:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **space 키 제어하기**. `window.addEventListener` 키업 함수로 spaces를 제어합니다:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **리스너 추가하기**. `initGame()` 함수를 편집해서 space 바를 눌렀을 때 hero가 발사할 수 있도록 합니다:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
새로운 `eventEmitter.on ()` 함수를 추가해서 적이 레이저와 부딪칠 때 동작하도록 합니다:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **객체 움직이기**, 레이저가 화면 상단으로 조금씩 이동하고 있는지 확인합니다. 저번처럼, `GameObject`를 확장하는 새로운 Laser 클래스를 만듭니다:
```javascript
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)
}
}
```
1. **충돌 제어하기**, 레이저에 대한 충돌 규칙을 구현합니다. 적에 충돌하는 객체를 테스트하는 `updateGameObjects ()` 함수를 추가합니다:
```javascript
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);
}
```
`window.onload`의 게임 루프에 `updateGameObjects()`를 추가해야 합니다.
4. 레이저의 **cooldown을 구현합니다**, 그래서 자주 발사할 수 있습니다.
마지막으로, cooldown을 할 수 있도록 Hero 클래스를 편집합니다:
```javascript
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;
}
}
```
여기에서 핵심은, 게임이 몇 가지 기능을 가지고 있다는 사실입니다! 화살표 키로 탐색하고, 스페이스 바로 레이저를 발사할 수 있으며, 적을 치면 사라지게 합니다. 잘 하셨습니다!
---
## 🚀 도전
폭발을 추가합니다! [the Space Art repo](../../solution/spaceArt/readme.txt)에서 게임 어셋을 살펴보고 레이저가 외계인을 칠 때 폭발하도록 추가해보세요
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=ko)
## 리뷰 & 자기주도 학습
지금까지 게임의 간격을 실험 해보세요. 바꾸면 어떻게 되나요? [JavaScript timing events](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)에 대하여 더 읽어보시기 바랍니다.
## 과제
[Explore collisions](../assignment.md)

View File

@@ -1,297 +0,0 @@
# Bina Permainan Angkasa Bahagian 4: Menambah Laser dan Mengesan Perlanggaran
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35)
Dalam pelajaran ini, anda akan belajar bagaimana menembak laser dengan JavaScript! Kami akan menambah dua perkara pada permainan kami:
- **Laser**: laser ini ditembak dari kapal wira anda dan menegak ke atas
- **Pengesanan perlanggaran**, sebagai bagian dari menerapkan kemampuan untuk *menembak* kami juga akan menambahkan beberapa peraturan permainan yang bagus:
- **Laser menyerang musuh**: Musuh mati jika terkena laser
- **Laser mencecah skrin atas**: Laser hancur jika memukul bahagian atas skrin
- **Pertempuran musuh dan pahlawan**: Musuh dan pahlawan musnah jika saling memukul
- **Musuh memukul bahagian bawah layar**: Musuh dan pahlawan hancur jika musuh memukul bahagian bawah layar
Pendek kata, anda - *pahlawan* - perlu memukul semua musuh dengan laser sebelum mereka berjaya bergerak ke bahagian bawah skrin.
✅ Lakukan sedikit kajian mengenai permainan komputer pertama yang pernah ditulis. Apa fungsinya?
Mari sama-sama menjadi wira!
## Pengesanan perlanggaran
Bagaimanakah kita melakukan pengesanan perlanggaran? Kita perlu memikirkan objek permainan kita sebagai segi empat tepat bergerak. Mengapa anda bertanya? Nah, gambar yang digunakan untuk menggambar objek permainan adalah persegi panjang: ia memiliki `x`, `y`, `width` dan` height`.
Sekiranya dua segi empat tepat, iaitu pahlawan dan musuh *berpotongan*, anda akan bertembung. Apa yang harus berlaku adalah mengikut peraturan permainan. Oleh itu, untuk melaksanakan pengesanan perlanggaran anda memerlukan perkara berikut:
1. Cara untuk mendapatkan representasi segi empat tepat dari objek permainan, seperti ini:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Fungsi perbandingan, fungsi ini boleh kelihatan seperti ini:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## Bagaimanakah kita memusnahkan perkara
Untuk menghancurkan sesuatu dalam permainan, anda perlu memberitahu permainan bahawa permainan ini tidak lagi harus melukis item ini dalam gelung permainan yang mencetuskan pada selang waktu tertentu. Cara untuk melakukannya adalah dengan menandakan objek permainan sebagai *mati* apabila sesuatu berlaku, seperti:
```javascript
// collision happened
enemy.dead = true
```
Kemudian anda meneruskan untuk menyusun *mati* objek sebelum mengecat semula skrin, seperti:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## Bagaimanakah kita menghidupkan laser
Menyalakan laser diterjemahkan untuk bertindak balas terhadap peristiwa penting dan membuat objek yang bergerak ke arah tertentu. Oleh itu, kita perlu melakukan langkah-langkah berikut:
1. **Buat objek laser**: dari bahagian atas kapal pahlawan kita, bahawa setelah penciptaan mula bergerak ke atas menuju bahagian atas layar.
2. **Lampirkan kod ke acara utama**: kita perlu memilih kunci pada papan kekunci yang mewakili pemain yang menembak laser.
3. **Buat objek permainan yang kelihatan seperti laser** apabila kekunci ditekan.
## Cooldown menggunakan laser kami
Laser perlu menyala setiap kali anda menekan kekunci, seperti *ruang* misalnya. Untuk mengelakkan permainan menghasilkan terlalu banyak laser dalam masa yang singkat, kita perlu memperbaikinya. Penyelesaiannya adalah dengan menerapkan *cooldown*, pemasa, yang memastikan bahawa laser hanya dapat dipecat begitu kerap. Anda boleh melaksanakannya dengan cara berikut:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// menghasilkan laser
this.cooldown = new Cooldown(500);
} else {
// buat apa-apa - ia belum cooldown.
}
}
}
```
✅ Rujuk pelajaran 1 dalam siri permainan ruang untuk mengingatkan diri anda mengenai *cooldowns*.
## Apakah yang hendak dibina
Anda akan mengambil kod yang ada (yang seharusnya anda bersihkan dan refactored) dari pelajaran sebelumnya, dan memperpanjangnya. Mulakan dengan kod dari bahagian II atau gunakan kod di [Bahagian III-starter](/your-work)
> tip: laser yang akan anda gunakan sudah ada di folder aset anda dan dirujuk oleh kod anda
- **Tambahkan pengesanan perlanggaran**, apabila laser bertabrakan dengan sesuatu peraturan berikut harus berlaku:
1. **Laser memukul musuh**: musuh mati jika terkena laser
2. **Laser mencecah skrin atas**: Laser hancur jika menyentuh bahagian atas skrin kita
3. **Pertembungan musuh dan pahlawan**: musuh dan pahlawan musnah jika saling memukul
4. **Musuh memukul bahagian bawah skrin**: Musuh dan pahlawan hancur jika musuh memukul bahagian bawah skrin
## Langkah-langkah yang disyorkan
Cari fail yang telah dibuat untuk anda dalam sub folder `your-work`. Ia harus mengandungi yang berikut:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Anda memulakan projek anda folder `your_work` dengan mengetik:
```bash
cd your-work
npm start
```
Perkara di atas akan memulakan Pelayan HTTP pada alamat `http: // localhost: 5000`. Buka penyemak imbas dan masukkan alamatnya, sekarang ia harus menjadikan pahlawan dan semua musuh, tidak ada yang bergerak - namun :).
### Tambah kod
1. **Siapkan representasi segi empat tepat dari objek permainan anda, untuk menangani perlanggaran** Kod di bawah ini membolehkan anda mendapatkan representasi segi empat tepat dari `GameObject`. Edit kelas GameObject anda untuk memperluasnya:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Tambahkan kod yang memeriksa perlanggaran** Ini akan menjadi fungsi baru yang menguji sama ada dua segi empat tepat bersilang:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Tambahkan keupayaan menembak laser**
1. **Tambah mesej peristiwa penting**. Kekunci *ruang* harus membuat laser tepat di atas kapal wira. Tambahkan tiga pemalar dalam objek Mesej:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Kendalikan kekunci ruang**. Edit fungsi kekunci `window.addEventListener` untuk menangani ruang:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Tambah pendengar**. Edit fungsi `initGame ()` untuk memastikan bahawa pahlawan dapat melepaskan tembakan ketika spasi terkena:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
and add a new `eventEmitter.on()` function to ensure behavior when an enemy collides with a laser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **Pindahkan objek**, Pastikan laser bergerak ke bahagian atas skrin secara beransur-ansur. Anda akan membuat kelas Laser baru yang meluaskan `GameObject`, seperti yang telah anda lakukan sebelumnya:
```javascript
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)
}
}
```
1. ** Tangani perlanggaran **, Terapkan peraturan perlanggaran untuk laser. Tambahkan fungsi `updateGameObjects ()` yang menguji objek bertembung untuk hits
```javascript
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);
}
```
Pastikan untuk menambah `updateGameObjects ()` ke gelung permainan anda di `window.onload`.
4. ** Terapkan cooldown ** pada laser, sehingga hanya dapat dipecat begitu kerap.
Akhir sekali, edit kelas Wira sehingga dapat dikembalikan:
```javascript
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;
}
}
```
Pada ketika ini, permainan anda mempunyai beberapa fungsi! Anda boleh menavigasi dengan kekunci anak panah anda, menembak laser dengan bar ruang anda, dan musuh hilang ketika anda memukulnya. Bagus!
---
## 🚀 Cabaran
Tambah letupan! Lihat aset permainan di [Space Art repo](../../solution/spaceArt/readme.txt) dan cuba tambahkan letupan ketika laser memukul makhluk asing
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36)
## Mengkaji & Belajar Sendiri
Percubaan dengan selang permainan anda setakat ini. Apa yang berlaku apabila anda menukarnya? Baca lebih lanjut mengenai [acara pemasaan JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Tugasan
[Menerokai Perlanggaran](assignment.ms.md)

View File

@@ -1,297 +0,0 @@
# 建立太空遊戲 Part 4加入雷射與碰撞偵測
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/35?loc=zh_tw)
這堂課中,你會學會如何在 JavaScript 上發射雷射光!我們需要在遊戲中新增兩項東西:
- **雷射光**:這束雷射會從英雄艦艇垂直往上移動
- **碰撞偵測**,除了完成*射擊*這項能力,我們還會新增幾項遊戲規則:
- **雷射擊中敵人**:被雷射擊中的敵人會死亡
- **雷射擊中畫面最上方**:雷射擊中到畫面最上方會消失
- **敵人碰觸到英雄**:敵人與英雄在互相碰觸時皆會被摧毀
- **敵人碰觸到畫面最下方**:敵人碰觸到畫面最下方時,該敵人與英雄會被摧毀
簡單來說,你這位*英雄*需要在敵人到達畫面最下方之前,使用雷射擊毀它們。
✅ 做點關於第一款電腦遊戲的研究。它有哪些功能?
讓我們成為英雄吧!
## 碰撞偵測
我們該如何進行碰撞偵測呢?我們需要將遊戲物件視為移動中的矩形。你或許會問為什麼?這是因為,繪製遊戲物件的圖片皆為矩形:它有一組 `x``y``width``height`
若兩個矩形,舉例來說:英雄與敵人*相交*了,這就是一次碰撞。至於會發生什麼事取決於遊戲規則。要製作碰撞偵測,你需要這些步驟:
1. 取得表達遊戲物件為矩形的方法,好比是:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. 一個比較函式,這個函式可以如下:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## 我們該如何摧毀物件
要摧毀遊戲物件,你需要讓遊戲知道,它不再需要在定期的遊戲迴圈中繪製該物件。一種方法是在情況發生時,我們可以標記遊戲物件為*死亡*,例如:
```javascript
// 碰撞發生
enemy.dead = true
```
接著,你在重新繪製畫面前,排除掉這些*死亡*的物件,例如:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## 我們該如何發射雷射
發射雷射可以被視作回應一件鍵盤事件,並建立往特定方向移動的物件。因此我們需要列出下列步驟:
1. **建立雷射物件**:從英雄艦艇的正上方,建立往畫面上方移動的物件。
2. **連接該程式到鍵盤事件**:我們需要在鍵盤中挑選一個按鍵,表達玩家發射雷射光。
3. 在按鍵按壓時,**建立看起來像雷射光的遊戲物件**。
## 雷射的冷卻時間
在每次按壓按鍵時,好比說*空白鍵*,雷射光都需要被發射出來。為了讓遊戲不要在短時間內發射太多組雷射光,我們需要修正它。修法為建立*冷卻時間* ── 一個計時器確保雷射在一定期間內只能被發射一次。你可以藉由下列方式建立:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// 產生雷射光
this.cooldown = new Cooldown(500);
} else {
// 什麼事都不做,冷卻中。
}
}
}
```
✅ 根據太空遊戲系列課程的第一章,回想關於*冷卻時間*。
## 建立目標
你會利用上一堂課中現成的程式碼(你應該有整理並重構過)做延伸。使用來自 Part II 的檔案或是使用 [Part III - Starter](../your-work)。
> 要點:你需要使用的雷射光已經在資料夾中,並已匯入到程式碼中。
- **加入碰撞偵測**,建立下列規則給各個雷射碰觸到東西的情況:
1. **雷射擊中敵人**:被雷射擊中的敵人會死亡
2. **雷射擊中畫面最上方**:雷射擊中到畫面最上方會消失
3. **敵人碰觸到英雄**:敵人與英雄在互相碰觸時皆會被摧毀
4. **敵人碰觸到畫面最下方**:敵人碰觸到畫面最下方時,該敵人與英雄會被摧毀
## 建議步驟
在你的 `your-work` 子資料夾中,確認檔案是否建立完成。它應該包括:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
開始 `your_work` 資料夾中的專案,輸入:
```bash
cd your-work
npm start
```
這會啟動 HTTP 伺服器並發布網址 `http://localhost:5000`。開啟瀏覽器並輸入該網址,現在它能呈現英雄以及所有的敵人,但它們還沒辦法移動!
### 建立程式碼
1. **設定表達遊戲物件為矩形的方法,以處理碰撞狀況** 下列的程式表達 `GameObject` 的矩形呈現方式。編輯你的 GameObject class
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **加入程式碼來檢查碰撞** 這會是新函式來測試兩矩形是否相交:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **加入雷射發射功能**
1. **加入鍵盤事件訊息**。 *空白鍵*要能在英雄艦艇上方建立雷射光。加入三個常數到 Messages 物件中:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **處理空白鍵**。 編輯 `window.addEventListener` 的 keyup 函式來處理空白鍵:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **加入監聽者**。 編輯函式 `initGame()` 來確保英雄可以在空白鍵按壓時,發射雷射光:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
建立新的函式 `eventEmitter.on()` 確保敵人碰觸到雷射光時,能更新死亡狀態:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **移動物件**。 確保雷射逐步地向畫面上方移動。建立新的 class Laser 延伸自 `GameObject`,你應該有做過:
```javascript
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)
}
}
```
1. **處理碰撞**。 建立雷射的碰撞規則。加入函式 `updateGameObjects()` 來確認被碰撞的物件。
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// 雷射擊中某物
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);
}
```
記得在 `window.onload` 裡的遊戲迴圈中加入 `updateGameObjects()`。
4. **設定雷射的冷卻時間**,它只能在定期內發射一次。
最後,編輯 Hero class 來允許冷卻:
```javascript
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;
}
}
```
到這裡,你的遊戲有了些功能!你可以測試方向鍵,使用空白鍵發射雷射。當你擊中敵人時它們會消失。幹得漂亮!
---
## 🚀 挑戰
加入爆炸特效! 看看 [Space Art Repo](../solution/spaceArt/readme.txt) 中的檔案,試著在雷射擊中外星人時,加入爆炸畫面。
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/36?loc=zh_tw)
## 複習與自學
對遊戲中的迴圈定時做點實驗。當你改變數值時,發生了什麼事?閱讀更多關於 [JavaScript 計時事件](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)。
## 作業
[探索碰撞](assignment.zh-tw.md)

View File

@@ -1,11 +0,0 @@
# Explorar colisiones
## Instrucciones
Para comprender mejor cómo funcionan las colisiones, cree un juego muy pequeño con algunos elementos que chocan. Haz que se muevan presionando las teclas o haciendo clic con el mouse, y haz que algo le suceda a uno de los elementos cuando se golpea. Podría ser algo como un meteoro golpeando la tierra o autos chocadores. ¡Se creativo!
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | -------------------------------------------------- -------------------------------------------------- -------------------- | ------------------------------ | ----------------- |
| | Se produce una muestra de código de trabajo completa, con elementos dibujados en el lienzo, ocurriendo una colisión básica y ocurriendo reacciones | El código está incompleto de alguna manera | Fallos del código |

View File

@@ -1,11 +0,0 @@
# Explorer les collisions
## Instructions
Pour mieux comprendre le fonctionnement des collisions, créez un très petit jeu avec quelques objets qui entrent en collision. Faites-les bouger en appuyant sur des touches ou en cliquant sur la souris, et faites en sorte que quelque chose arrive à l'un des éléments lorsqu'il est touché. Cela pourrait être quelque chose comme un météore frappant la terre, ou des autos tamponneuses. Faites preuve de créativité!
## Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- |
| | Un échantillon de code de travail complet est produit, avec des éléments dessinés sur le canevas, une collision de base et des réactions se produisant | Le code est incomplet d'une certaine manière | Dysfonctionnements du code |

View File

@@ -1,11 +0,0 @@
# टकरावों का अन्वेषण करें
## अनुदेश
बेहतर ढंग से समझने के लिए कि टकराव कैसे काम करता है, कुछ वस्तुओं के साथ बहुत छोटे खेल का निर्माण करें जो टकराते हैं. उन्हें कीप्रेसेस या माउस क्लिक के माध्यम से ले जाएँ, और यह हिट होने पर आइटम में से किसी एक के साथ कुछ करें . यह कुछ ऐसा हो सकता है जैसे उल्का पिंड पृथ्वी से टकराता है, या बम्पर-कार। रचनात्मक बनो!
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | --------------------------------------------------------------------------------------------------------------- | ------------------------ | -------------- |
| | पूरा वर्किंग कोड सैंपल उत्पादित होता है, जिसमें कैनवस को खींची गई चीजें, बेसिक टक्कर, और प्रतिक्रियाएँ होती हैं | कोड किसी तरह से अधूरा है | कोड की खराबी |

View File

@@ -1,11 +0,0 @@
# Esplorare Collisioni
## Istruzioni
Per una migliore comprensione del funzionamento delle collisioni, costruire un gioco molto piccolo con alcuni elementi che collidono. Farli muovere tramite pressioni di tasto o clic di mouse, fare accadere qualcosa a uno di questi elementi quando viene colpito. Potrebbe essere qualcosa tipo un meteorite che colpisce la terra oppure un autoscontro. Si usi la propria creatività!
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| | Viene prodotto un campione di codice completo e funzionante con elementi disegnati sul canvas, verificarsi di collisioni basiche e reazioni| Il codice è incompleto in qualche modo | Malfunzionamenti del codice |

View File

@@ -1,11 +0,0 @@
# 衝突を調べる
## 説明書
衝突がどのように機能するかを理解するために、衝突するいくつかのアイテムで非常に小さなゲームを構築します。キープレスやマウスクリックでアイテムを移動させ、アイテムが当たったときに何かが起こるようにします。隕石が地球に衝突したり、バンパーカーに衝突したりします。創造力を発揮してください。
## ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- |
| | アイテムがキャンバスに描画され、基本的な衝突が発生し、反応が発生する完全な動作コードサンプルが作成されます。 | コードが不完全な場合 | コードの誤動作 |

View File

@@ -1,11 +0,0 @@
# 충돌 감지
## 설명
충돌이 어떻게 작용하는지 더 잘 이해하기 위해 충돌하는 몇 가지 항목으로 아주 작은 게임을 만들어 봅시다. 키를 누르거나 마우스 클릭으로 이동하게 하고, 부딪혔을 때는 아이템들 중 하나에 무슨 일이 일어나게 해보세요. 부딪히는 것은 지구를 강타하는 유성이거나, 범퍼카 같은 것일 수도 있습니다. 창의력을 보여주세요!
## 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| 항목을 캔버스에 끌어다 놓거나, 기본 충돌이 발생하고, 반응이 있는 샘플 코드를 만든 경우 | 코드가 일부 미완성인 경우 | 코드에 오류가 있는 경우

View File

@@ -1,11 +0,0 @@
# Menorokai Perlanggaran
## Arahan
Untuk lebih memahami bagaimana perlanggaran berfungsi, bina permainan yang sangat kecil dengan beberapa item yang bertembung. Buat mereka bergerak melalui menekan kekunci atau klik tetikus, dan membuat sesuatu berlaku pada salah satu item ketika dipukul. Ini mungkin seperti meteor yang menghantam bumi, atau kereta bumper. Dapatkan kreatif!
## Rubrik
| Kriteria | Contoh | Mencukupi | Usaha Lagi |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- |
| | Sampel kod kerja lengkap dihasilkan, dengan item dilukis ke kanvas, perlanggaran asas berlaku, dan reaksi berlaku | Kod tidak lengkap dalam beberapa cara | Kod Tidak Berfungsi |

View File

@@ -1,11 +0,0 @@
# Ontdek botsingen
## Instructies
Om beter te begrijpen hoe botsingen werken, bouwt u een heel klein spel met een paar items die botsen. Laat ze bewegen door middel van toetsaanslagen of muisklikken, en zorg dat er iets gebeurt met een van de items wanneer deze wordt geraakt. Het kan zoiets zijn als een meteoor die de aarde raakt, of botsauto's. Wees creatief!
## Rubriek
| Criteria | Voorbeeldig | Voldoende | Moet worden vebeterd |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- |
| | Er wordt een volledig werkend codevoorbeeld geproduceerd, met items op het canvas getekend, er vindt een fundamentele botsing plaats en er vinden reacties plaats | Code is op de een of andere manier onvolledig | Code storingen |

View File

@@ -1,11 +0,0 @@
# 探索碰撞
## 簡介
為了更了解碰撞是如何運作的,建立一款小遊戲包含物件的碰撞。利用鍵盤或是滑鼠來移動物件,當物件碰撞時執行某些行為。它可以像是彗星撞地球,或是碰碰車。發揮你的創意!
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | -------------------------------------------------------------- | ------------------ | ---------- |
| | 建立出完整的程式:有在畫面上繪製物件、有基本的碰撞與對應的行為 | 程式有部分尚未完成 | 程式有瑕疵 |

View File

@@ -1,182 +0,0 @@
# Construye un juego espacial Parte V: Puntuación y vidas
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37)
En esta lección, aprenderá cómo agregar puntos a un juego y calcular vidas.
## Dibujar texto en la pantalla
Para poder mostrar la puntuación de un juego en la pantalla, necesitará saber cómo colocar texto en la pantalla. La respuesta es usar el método `fillText()` en el objeto de lienzo. También puedes controlar otros aspectos como qué tipo de letra usar, el color del texto e incluso su alineación (izquierda, derecha, centro). A continuación se muestra un código que dibuja un texto en la pantalla.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ Lea más sobre [cómo agregar texto a un lienzo](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text), ¡y siéntase libre de hacer que el suyo se vea más elegante!
## La vida, como concepto de juego
El concepto de tener una vida en un juego es solo un número. En el contexto de un juego espacial, es común asignar un conjunto de vidas que se deducen una por una cuando tu nave sufre daños. Es bueno si puede mostrar una representación gráfica de esto como miniships o corazones en lugar de un número.
## Qué construir
Agreguemos lo siguiente a tu juego:
- **Game score** (Puntuación del juego): por cada barco enemigo que sea destruido, el héroe debería recibir algunos puntos, sugerimos 100 puntos por barco. La puntuación del juego debe mostrarse en la parte inferior izquierda.
- **Life** (Vida): Tu nave tiene tres vidas. Pierdes una vida cada vez que un barco enemigo choca contigo. Se debe mostrar un puntaje de vida en la parte inferior derecha y estar compuesto por el siguiente gráfico: [life image](solution/assets/life.png).
## Pasos recomendados
Busque los archivos que se han creado para usted en la subcarpeta `your-work`. Debe contener lo siguiente:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Comienzas tu proyecto en la carpeta `your_work` escribiendo:
```bash
cd your-work
npm start
```
Lo anterior iniciará un servidor HTTP en la dirección `http://localhost:5000`. Abre un navegador e ingresa esa dirección, en este momento debería representar al héroe y a todos los enemigos, y cuando presionas las flechas izquierda y derecha, el héroe se mueve y puede derribar enemigos.
### Agregar código
1. **Copie los recursos necesarios** de la carpeta `solution/assets/` a la carpeta `your-work`; agregará un activo `life.png`. Agregue el lifeImg a la función window.onload:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Agregue el `lifeImg` a la lista de activos:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2.**Agregar variables**. Agregue un código que represente su puntaje total (0) y las vidas restantes (3), muestre estos puntajes en una pantalla.
3. **Amplíe la función `updateGameObjects()`**. Amplíe la función `updateGameObjects()` para manejar las colisiones enemigas:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **Agrega `life` y `points`**.
1. **Inicializar variables**. En `this.cooldown = 0` en la clase `Hero`, establece la vida y los puntos:
```javascript
this.life = 3;
this.points = 0;
```
1. **Dibujar variables en pantalla**. Dibuja estos valores en la pantalla:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **Agregar métodos al bucle del juego**. Asegúrese de agregar estas funciones a su función window.onload en `updateGameObjects ()`:
```javascript
drawPoints();
drawLife();
```
1. **Implementa las reglas del juego**. Implementa las siguientes reglas del juego:
1. **Por cada colisión entre héroes y enemigos**, resta una vida.
Extiende la clase `Hero` para hacer esta deducción:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **Por cada láser que golpea a un enemigo**, aumenta la puntuación del juego con 100 puntos.
Extiende la clase Hero para hacer este incremento:
```javascript
incrementPoints() {
this.points += 100;
}
```
Agregue estas funciones a sus Collision Event Emitters:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ Investigue un poco para descubrir otros juegos creados con JavaScript / Canvas. ¿Cuáles son sus rasgos comunes?
Al final de este trabajo, deberías ver las pequeñas naves de 'vida' en la parte inferior derecha, los puntos en la parte inferior izquierda, y deberías ver que tu cuenta de vidas disminuye cuando chocas con enemigos y tus puntos aumentan cuando disparas a los enemigos. ¡Bien hecho! Tu juego está casi completo.
🚀Challenge: Tu código está casi completo. ¿Puedes imaginar tus próximos pasos?
## [Post-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38)
## Revisión y autoestudio
Investigue algunas formas en las que puede incrementar y disminuir las puntuaciones y vidas del juego. Hay algunos motores de juegos interesantes como [PlayFab](https://playfab.com). ¿Cómo podría mejorar tu juego el uso de uno de estos?
**Tarea**: [Crear un juego de puntuación](assignment.es.md)

View File

@@ -1,189 +0,0 @@
# Construire un jeu de l'espace - Partie 5: scores et vies
## Quiz de pré-lecture
[Quiz de pré-lecture](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=fr)
Dans cette leçon, vous apprendrez à ajouter des points à un jeu et à calculer des vies.
## Dessiner du texte à l'écran
Pour pouvoir afficher un score de jeu à l'écran, vous devez savoir comment placer du texte à l'écran. La réponse est d'utiliser la méthode `fillText()` sur l'objet canvas. Vous pouvez également contrôler d'autres aspects comme la police à utiliser, la couleur du texte et même son alignement (gauche, droite, centre). Vous trouverez ci-dessous du code permettant d'afficher du texte à l'écran.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ En savoir plus sur [comment ajouter du texte à un canvas ](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text), et n'hésitez pas à rendre le vôtre plus sophistiqué!
## La vie, comme concept de jeu
Le concept d'avoir une vie dans un jeu n'est qu'un nombre. Dans le contexte d'un jeu spatial, il est courant d'attribuer un ensemble de vies qui sont réduites une par une lorsque votre vaisseau subit des dégâts. C'est bien si vous pouvez montrer une représentation graphique de ceci comme des mini vaisseaux ou des cœurs au lieu d'un nombre.
## Ce qu'il faut construire
Ajoutons les éléments suivants à votre jeu:
- **Score de jeu**: Pour chaque navire ennemi détruit, le héros devrait recevoir quelques points, nous suggérons 100 points par navire. Le score du jeu devrait être affiché en bas à gauche.
- **Vie**: Votre navire a trois vies. Vous perdez une vie chaque fois qu'un navire ennemi entre en collision avec vous. Un score de vie devrait être affiché en bas à droite et être composé du graphique suivant ![image de vie](../solution/assets/life.png).
## Étapes recommandées
Localisez les fichiers qui ont été créés pour vous dans le sous-répertoire `your-work`. Il devrait contenir les éléments suivants:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Vous démarrez votre projet dans le dossier `your_work` en tapant:
```bash
cd your-work
npm start
```
Ce qui précède va démarrer un serveur HTTP sur l'adresse `http://localhost:5000`. Ouvrez un navigateur et entrez cette adresse, pour l'instant il devrait afficher le héros et tous les ennemis, et lorsque vous appuyez sur vos flèches gauche et droite, le héros se déplace et peut abattre les ennemis.
### Ajouter du code
1. **Copiez les éléments nécessaires** du dossier `solution/assets/` dans le dossier `your-work` ; vous allez ajouter un élément `life.png`. Ajoutez l'image lifeImg à la fonction window.onload:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Ajoutez le `lifeImg` à la liste des éléments:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **Ajoutez des variables**. Ajoutez le code qui représente votre score total (0) et les vies restantes (3), affichez ces scores sur un écran.
3. **Étendre la fonction `updateGameObjects()`**. Étendre la fonction `updateGameObjects()` pour gérer les collisions avec les ennemis:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **Ajouter `life` et `points`**.
1. **Initialiser les variables**. Sous `this.cooldown = 0` dans la classe `Hero` , définissez la vie et les points:
```javascript
this.life = 3;
this.points = 0;
```
1. **Dessinez les variables à l'écran**. Dessinez ces valeurs à l'écran:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **Ajouter des méthodes à la boucle de jeu**. Assurez-vous d'ajouter ces fonctions à votre fonction window.onload sous `updateGameObjects()`:
```javascript
drawPoints();
drawLife();
```
1. **Mettre en œuvre les règles du jeu**. Mettez en œuvre les règles du jeu suivantes:
1. **Pour chaque collision entre héros et ennemis**, retirez une vie.
Étendez la classe `Hero` pour faire cette suppression:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **Pour chaque laser qui touche un ennemi**, augmentez le score du jeu de 100 points.
Étendez la classe Hero pour faire cet ajout:
```javascript
incrementPoints() {
this.points += 100;
}
```
Ajoutez ces fonctions à vos émetteurs d'événements de collision:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ Faites une petite recherche pour découvrir d'autres jeux créés à l'aide de JavaScript/Canvas. Quels sont leurs points communs?
À la fin de ce travail, vous devriez voir les petits vaisseaux de "vie" en bas à droite, les points en bas à gauche, et vous devriez voir votre compte de vie diminuer lorsque vous entrez en collision avec des ennemis et vos points augmenter lorsque vous tirez sur des ennemis. Bien joué! Votre jeu est presque terminé.
---
## 🚀 Challenge
Votre code est presque complet. Pouvez-vous envisager vos prochaines étapes?
## Quiz post-lecture
[Quiz post-lecture](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=fr)
## Révision et autoformation
Recherchez des moyens d'incrémenter et de décrémenter les scores et les vies. Il existe des moteurs de jeu intéressants comme [PlayFab](https://playfab.com). Comment l'utilisation de l'un d'eux pourrait-elle améliorer votre jeu?
## Travail à faire
[Construire un jeu de score](assignment.fr.md)

View File

@@ -1,189 +0,0 @@
# एक अंतरिक्ष खेल बनाएँ भाग ५: स्कोरिंग और जीवन
## लेक्टरसे पहलेकी क्विज़
[लेक्टरसे पहलेकी क्विज़](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=hi)
इस पाठ में, आप सीखेंगे कि किसी खेल में स्कोरिंग कैसे जोड़ें और जीवन की गणना करें.
## स्क्रीन पर टेक्स्ट ड्रा करें
स्क्रीन पर गेम स्कोर प्रदर्शित करने में सक्षम होने के लिए, आपको यह जानना होगा कि स्क्रीन पर टेक्स्ट कैसे रखा जाए. उत्तर कैनवास ऑब्जेक्ट पर `fillText()` विधि का उपयोग कर रहा है। आप अन्य पहलुओं को भी नियंत्रित कर सकते हैं जैसे कि किस फ़ॉन्ट का उपयोग करना है, पाठ का रंग और यहां तक ​​कि इसके संरेखण (बाएं, दाएं, केंद्र)। नीचे कुछ कोड है जो स्क्रीन पर कुछ टेक्स्ट खींच रहा है.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ [एक कैनवास में पाठ कैसे जोड़ें](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text) इसके बारे में और पढ़ें और बेझिझक अपने को आकर्षक बनाने के लिए!
## जीवन, एक खेल अवधारणा के रूप में
एक खेल में एक जीवन होने की अवधारणा केवल एक संख्या है। एक अंतरिक्ष खेल के संदर्भ में यह सामान्य है कि आपके जहाज को नुकसान होने पर एक-एक करके कटे हुए जीवन का एक सेट आवंटित किया जाए। यदि आप एक नंबर के बजाय मिनीशिप या दिल की तरह इस का एक चित्रमय प्रतिनिधित्व दिखा सकते हैं तो अच्छा है.
## क्या बनाना है
चलो अपने खेल में निम्नलिखित जोड़ते हैं:
- **खेल का स्कोर**: नष्ट होने वाले प्रत्येक दुश्मन जहाज के लिए, नायक को कुछ बिंदुओं से सम्मानित किया जाना चाहिए, हम प्रति जहाज १०० अंक का सुझाव देते हैं। खेल का स्कोर नीचे बाईं ओर दिखाया जाना चाहिए.
- **जीबन**: आपके जहाज में तीन जीवन हैं। जब भी कोई दुश्मन जहाज आपसे टकराता है तो आप एक जीवन खो देते हैं। एक जीवन स्कोर नीचे दाईं ओर प्रदर्शित किया जाना चाहिए और निम्नलिखित ग्राफ़िक से बाहर किया जाना चाहिए! [जीवन छवि](../solution/assets/life.png).
## अनुशंसित कदम
उन फ़ाइलों का पता लगाएँ जो आपके लिए `your-work` सब फ़ोल्डर में बनाई गई हैं। इसमें निम्नलिखित शामिल होना चाहिए:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
आप टाइप करके अपना प्रोजेक्ट `your_work` फ़ोल्डर शुरू करें:
```bash
cd your-work
npm start
```
उपरोक्त पते पर एक HTTP सर्वर शुरू होगा `http://localhost:5000`। एक ब्राउज़र खोले और उस पते को डाले , अभी उसे नायक और सभी दुश्मनों को प्रस्तुत करना चाहिए, और जैसे ही आप अपने बाएँ और दाएँ तीर मारते हैं, नायक चलता है और दुश्मनों को मार सकता है।.
### कोड जोड़े
1. `solution/assets/` फ़ोल्डर से `your-work` फ़ोल्डर में आवश्यक असेट्स की प्रतिलिपि बनाएँ;आप एक `life.png` एसेट जोड़ देंगे। LifeImg को window.onload फ़ंक्शन में जोड़ें:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. असेट्स की सूची में `lifeImg` जोड़ें:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
1. **चर जोड़ें** वह कोड जोड़ें जो आपके कुल स्कोर(0) का प्रतिनिधित्व करता है और बाईं ओर (3) रहता है, इन स्कोर को स्क्रीन पर प्रदर्शित करता है.
1. **`updateGameObjects()` फ़ंक्शन बढ़ाएँ**। दुश्मन टकराव को संभालने के लिए `updateGameObjects()` फ़ंक्शन बढ़ाएँ:
```javascript
enemies.forEach((enemy) => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
});
```
1. **`life` ओर `points` जोड़े**.
1. **आरंभिक चर**. `हीरो` वर्ग में `this.cooldown = 0` के तहत, जीवन और बिंदुओ निर्धारित करें:
```javascript
this.life = 3;
this.points = 0;
```
1. **स्क्रीन पर चर बनाए**. इन मूल्यों को स्क्रीन पर बनाए:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for (let i = 0; i < hero.life; i++) {
ctx.drawImage(lifeImg, START_POS + 45 * (i + 1), canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height - 20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **गेम लूप के तरीके जोड़ें**. सुनिश्चित करें कि आप इन अपडेट्स को अपने विंडो में जोड़ें। फ़ंक्शन को `updateGameObjects()` के तहत लोड करें:
```javascript
drawPoints();
drawLife();
```
1. **खेल के नियमों को लागू करें**. निम्नलिखित खेल नियमों को लागू करें:
1. **हर हीरो और दुश्मन की टक्कर के लिए**, जीवन घटाए.
इस कटौती को करने के लिए `हीरो` क्लास का विस्तार करें:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **हर लेजर के लिए जो दुश्मन को मारता है**, १०० अंकों के साथ गेम स्कोर बढ़ाता है.
इस वेतन वृद्धि को करने के लिए हीरो क्लास बढ़ाएँ:
```javascript
incrementPoints() {
this.points += 100;
}
```
अपने कॉलिसन ईवेंट एमीटर्स में इन कार्यों को जोड़ें:
```javascript
eventEmitter.on(
Messages.COLLISION_ENEMY_LASER,
(_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
}
);
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ जावास्क्रिप्ट/कैनवस का उपयोग करके बनाए गए अन्य खेलों की खोज के लिए थोड़ा शोध करें। उनके सामान्य लक्षण क्या हैं?
इस काम के अंत तक, आपको छोटे 'जीवन' जहाजों को नीचे दाईं ओर, बिंदुओं को नीचे बाईं ओर देखना चाहिए, और जब आप दुश्मनों को गोली मारते हैं, तो आपको अपने जीवन की गिनती में कमी और दुश्मनों से टकराते हुए देखना चाहिए। बहुत बढ़िया! आपका खेल लगभग पूरा हो गया है.
---
## 🚀 चुनोती
आपका कोड लगभग पूरा हो चुका है। क्या आप अपने अगले चरणों की कल्पना कर सकते हैं?
## पोस्ट-व्याख्यान प्रश्नोत्तरी
[पोस्ट-व्याख्यान प्रश्नोत्तरी](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=hi)
## समीक्षा और स्व अध्ययन
कुछ तरीकों पर शोध करें जो आप खेल के स्कोर और जीवन को बढ़ा सकते हैं। [PlayFab](https://playfab.com) जैसे कुछ दिलचस्प गेम इंजन हैं। इनमें से किसी एक का उपयोग करने से आपका खेल कैसे बढ़ेगा?
## असाइनमेंट
[एक स्कोरिंग गेम बनाएँ](assignment.hi.md)

View File

@@ -1,189 +0,0 @@
# Costruire un Gioco Spaziale parte 5: Punteggio e Vite
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=it)
In questa lezione si imparerà come aggiungere punteggi a una partita e calcolare le vite.
## Disegnare testo sullo schermo
Per poter visualizzare il punteggio di una partita sullo schermo, serve sapere come posizionare il testo sullo schermo. La risposta è usando il metodo `fillText()` sull'oggetto canvas. Si possono anche controllare altri aspetti come il tipo di carattere da usare, il colore del testo e anche il suo allineamento (sinistra, destra, centro). Di seguito è riportato del codice che disegna testo sullo schermo.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ Si legga di più su [come aggiungere testo a un oggetto canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text), ci si senta liberi di far sembrare il proprio più elaborato!
## La vita, come concetto di gioco
Il concetto di avere una vita in un gioco è solo un numero. Nel contesto di un gioco spaziale è comune assegnare una serie di vite che vengono detratte una per una quando la propria astronave subisce danni. Sarebbe bello se si potesse mostrare una rappresentazione grafica di questo come mini astronavi o cuori invece di un numero.
## Cosa costruire
Si aggiunge quanto segue al gioco:
- **Punteggio del gioco**: per ogni astronave nemica che viene distrutta, l'eroe dovrebbe ricevere alcuni punti, si suggerisce 100 punti per astronave. Il punteggio del gioco dovrebbe essere mostrato in basso a sinistra.
- **Vita**: la propria astronave ha tre vite. Si perde una vita ogni volta che una astronave nemica si scontra con la propria. Un punteggio di vita dovrebbe essere visualizzato in basso a destra ed essere ricavato dalla seguente immagine ![immagine grafica di una vita](../solution/assets/life.png).
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto dalla cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo, in questo momento dovrebbe rendere l'eroe e tutti i nemici, e premendo le frecce sinistra e destra, si fa muovere l'eroe che può abbattere i nemici.
### Aggiungere codice
1. **Copiare le risorse necessarie** dalla cartella `solution/assets` nella cartella `your-work`; aggiungere una risorsa `life.png`. Aggiungere lifeImg alla funzione window.onload:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Aggiungere `lifeImg` all'elenco delle risorse:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **Aggiungere variabili**. Aggiungere il codice che rappresenta il punteggio totale (0) e le vite rimaste (3), visualizzare questi punteggi sullo schermo.
3. **Estendere la funzione `updateGameObjects()`** . Estendere la funzione `updateGameObjects()` per gestire le collisioni con il nemico:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **Aggiungere vita (`life`) e punti (`points`)**.
1. **Inizializzare le variabili**. Sotto `this.cooldown = 0` nella classe `Hero`, impostare la vita e i punti:
```javascript
this.life = 3;
this.points = 0;
```
1. **Disegnare variabili sullo schermo**. Disegnare questi valori sullo schermo:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **Aggiungere metodi al ciclo di gioco**. Assicurarsi di aggiungere queste funzioni alla funzione window.onload sotto `updateGameObjects()`:
```javascript
drawPoints();
drawLife();
```
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
1. **Per ogni collisione di eroi e nemici**, togliere una vita.
Estendere la classe `Hero` per eseguire questa sottrazione:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **Per ogni laser che colpisce un nemico**, aumentare il punteggio del gioco di 100 punti.
Estendere la classe Hero per fare questo incremento:
```javascript
incrementPoints() {
this.points += 100;
}
```
Aggiungere queste funzioni agli event listener di Collision:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ Fare una piccola ricerca per scoprire altri giochi creati utilizzando JavaScript/Canvas. Quali sono i loro tratti comuni?
Alla fine di questo lavoro, si dovrebbero vedere le piccole astronavi che rappresentano le vite in basso a destra, i punti in basso a sinistra, e si dovrebbe vedere il numero di vite diminuire quando si entra in collisione con i nemici e i punti aumentare quando si colpiscono i nemici. Ottimo lavoro! Il gioco è quasi completo.
---
## 🚀 Sfida
Il codice è quasi completo. Si riescono a immaginare i prossimi passi?
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=it)
## Revisione e Auto Apprendimento
Cercare alcuni modi per aumentare e diminuire i punteggi e le vite del gioco. Ci sono alcuni motori di gioco interessanti come [PlayFab](https://playfab.com). In che modo l'utilizzo di uno di questi potrebbe migliorare il proprio gioco?
## Compito
[Costruire un Gioco di Punteggio](assignment.it.md)

View File

@@ -1,190 +0,0 @@
# スペースゲーム構築プロジェクト その 5: スコアリングとライフ
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=ja)
このレッスンでは、ゲームに得点を加えてライフを計算する方法を学びます。
## 画面上にテキストを描画
ゲームのスコアを画面に表示できるようにするには、画面にテキストを配置する方法を知っておく必要があります。その答えは、canvas オブジェクトの `fillText()` メソッドを使用することです。また、使用するフォント、テキストの色、配置 (左、右、中央) などの他の要素を制御することもできます。以下は、画面上にテキストを描画するコードです。
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ [canvas にテキストを追加する方法](https://developer.mozilla.org/ja/docs/Drawing_text_using_a_canvas)について詳しくはこちらをご覧ください。そうすれば自由にファンシーな外観を作ることができます。
## ゲームの概念としてのライフ
ゲームにおけるライフの概念は数字でしかありません。スペースゲームの文脈では、自分の宇宙船がダメージを受けたときに一つずつ差し引かれるライフを割り当てるのが一般的です。これを数字ではなく、ミニシップやハートのようにグラフィカルに表現できるといいですね。
## 何を構築するか
以下のようなものをゲームに追加してみましょう。
- **ゲームのスコア**: 敵艦が破壊されるごとに、ヒーローにはいくつかのポイントが与えられるはずですが、私たちは1隻につき100ポイントをお勧めします。ゲームスコアは左下に表示されます
- **ライフ**: あなたの宇宙船には3つのライフがあります。敵の船が衝突するたびにライフを失います。ライフスコアは右下に表示され、次のグラフィックで作成されます。![life image](../solution/assets/life.png)
## 推奨される手順
あなたのために作成されたファイルを `your-work` サブフォルダ内で探します。以下のファイルが含まれているはずです。
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
次のように入力して、`your_work` フォルダからプロジェクトを起動します。
```bash
cd your-work
npm start
```
上記のようにすると、`http://localhost:5000` というアドレスに HTTP サーバーが起動します。ブラウザを開いてそのアドレスを入力すると、ヒーローと敵が表示され、左右の矢印を打つとヒーローが動き、敵を撃ち落とすことができます。
### コードの追加
1. `solution/assets/` フォルダから `your-work` フォルダに**必要なアセットをコピー**して、`life.png` アセットを追加します。lifeImg を window.onload 関数に追加します
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. アセットのリストに `lifeImg` を追加します
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **変数を追加します**。あなたの合計スコア (0) とその左に (3) のライフを表すコードを追加し、画面上にこれらのスコアを表示します
3. **`updateGameObjects()` 関数を拡張します**。`updateGameObjects()` 関数を拡張し、敵の衝突に対応するようにします
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **`life` と `points` を追加します**
1. **変数を初期化します**。`Hero` クラスの `this.cooldown = 0` の下で、ライフとポイントを設定します
```javascript
this.life = 3;
this.points = 0;
```
1. **画面上に変数を描画します**。これらの値を画面に描画します
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **Game ループにメソッドを追加します**。`updateGameObjects()` の下の window.onload 関数にこれらの関数を追加します
```javascript
drawPoints();
drawLife();
```
1. **ゲームのルールを実装します**。以下のゲームルールを実装します
1. **ヒーローと敵の衝突ごとに**、ライフを差し引きます
`Hero` クラスを拡張してこの差し引きを行います
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **レーザーが敵に当たるたびに**、ゲームスコアを100点アップさせます
このインクリメントを行うために Hero クラスを拡張します
```javascript
incrementPoints() {
this.points += 100;
}
```
これらの機能を衝突イベントエミッタに追加します
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ JavaScript/Canvas を使用して作成された他のゲームを少し調べてみてください。これらのゲームに共通する特徴は何ですか?
この作品が終わる頃には、右下に小さな「ライフ」の船、左下にポイントが表示され、敵と衝突するとライフカウントが減少し、敵を撃つとポイントが増加するのがわかるはずです。よくできました! これでゲームはほぼ完成です。
---
## 🚀 チャレンジ
あなたのコードはほぼ完成しています。次のステップをイメージできますか?
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=ja)
## 復習と自己学習
ゲームのスコアやライフを増減させる方法をいくつか研究してみましょう。[PlayFab](https://playfab.com) のような面白いゲームエンジンがあります。これらのエンジンを使用することで、どのようにゲームを強化することができるでしょうか?
## 課題
[スコアリングゲームの構築](assignment.ja.md)

View File

@@ -1,189 +0,0 @@
# Space 게임 제작하기 파트 5: 점수내고 살기
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=ko)
이 강의에서는 어떻게 게임에서 점수를 내고 생명을 구하는 가에 대하여 배웁니다.
## 화면에 텍스트 그리기
화면에 게임 점수를 표시하려면, 화면에 텍스트를 두는 방법을 알아야 합니다. 답변은 canvas 객체에 `fillText()` 메소드를 사용한다고 할 수 있습니다. 사용할 글꼴, 텍스트 색상과 정렬(왼쪽, 오른쪽, 가운데)처럼 다른 측면으로 제어할 수 있습니다. 다음은 화면에 텍스트를 그리는 코드입니다:
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ [how to add text to a canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text)에 대하여 더 읽어보세요, 그리고 더 멋지게 보이도록 자유롭게 느껴보세요!
## 게임 컨셉에서 생명
게임에서 생명을 가진다는 개념은 숫자에 불과합니다. space 게임의 맥락에서는 배가 피해를 입었을 때마다 생명을 하나씩 빼는 것이 일반적입니다. 숫자 대신 miniships이나 하트와 같은 그래픽으로 표현할 수 있다면 좋습니다.
## 무엇을 만드나요
게임에 다음을 추가하겠습니다:
- **게임 점수**: 적의 배가 파괴될 때마다, 영웅은 점수를 받아야하고, 하나의 배마다 100점을 제안합니다. 게임 점수는 좌측 하단에 보여야 합니다.
- **생명**: 여러분의 배는 세 생명이 있습니다. 적의 배로 부딪칠 때마다 생명을 잃습니다. 생명 점수는 우측 하단에 보여야되고 ![life image](../solution/assets/life.png)로 만들어야 합니다.
## 권장 단계
`your-work` 하위 폴더에서 생성된 파일을 찾습니다. 다음을 포함해야 합니다:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
타이핑해서 `your_work` 폴더에 프로젝트를 시작합니다:
```bash
cd your-work
npm start
```
위 코드는 `http://localhost:5000` 주소에서 HTTP 서버를 시작합니다. 브라우저를 열고 해당 주소를 입력하면, 바로 영웅과 모든 적을 렌더링해야하며, 왼쪽과 오른쪽 화살표를 누르면, 영웅이 움직이고 적을 격추할 수 있습니다.
### 코드 추가하기
1. `solution/assets/` 폴더에서 `your-work` 폴더로 **필요한 어셋을 복사합니다**; `life.png` 어셋을 추가합니다. window.onload 함수에 lifeImg를 추가합니다:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. 어셋의 배열에 `lifeImg`를 추가합니다:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **변수를 추가합니다**. 총 점수(0)과 남은 생명(3)을 나타내는 코드를 추가하고, 이 점수를 화면에 출력합니다.
3. **`updateGameObjects()` 함수를 확장합니다**. `updateGameObjects()` 함수를 확장하여 적 충돌을 제어합니다:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **`life`과 `points`를 추가하기**.
1. **변수를 초기화합니다**. `Hero` 클래스의 `this.cooldown = 0` 아래에 생명과 점수를 설정합니다:
```javascript
this.life = 3;
this.points = 0;
```
1. **화면에 점수를 그립니다**. 이 값을 화면에 그립니다:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **게임 루프에 메소드를 추가합니다**. `updateGameObjects()` 아래의 window.onload 함수에 다음 함수를 추가해야 합니다:
```javascript
drawPoints();
drawLife();
```
1. **게임 규칙을 구현합니다**. 다음 게임 규칙을 구현합니다:
1. **모든 영웅과 적의 충돌**에 대해 생명을 깍습니다.
깍기 위해서 `Hero` 클래스를 확장합니다:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **적을 공격하는 모든 레이저는**, 게임 점수 100점을 올립니다.
올리기 위해서 `Hero` 클래스를 확장합니다:
```javascript
incrementPoints() {
this.points += 100;
}
```
Collision Event Emitter에 다음 함수를 추가합니다:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ JavaScript/Canvas를 사용하여 만든 다른 게임을 찾으려면 약간 알아보세요. 공통된 특징은 무엇일까요?
이 작업이 끝날 즈음, 우측 하단에 작은 '생명' 배, 좌측 하단에 점수를 보여줘야 하며, 적과 부딪칠 때마다 생명의 개수가 감소하고 적을 쏠 때마다 점수가 증가하는 것을 볼 수 있습니다. 잘 했습니다! 게임이 거의 완료되었습니다.
---
## 🚀 도전
코드는 거의 완성되었습니다. 다음 단계를 상상할 수 있나요?
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=ko)
## 리뷰 & 자기주도 학습
게임 점수와 생명을 늘리거나 줄일 수 있는 몇 가지 방법을 조사해보세요. [PlayFab](https://playfab.com)처럼 흥미로운 게임 엔진이 있습니다. 이 중 하나를 사용하면 어떻게 게임을 향상시킬 수 있을까요?
## 과제
[Build a Scoring Game](../assignment.md)

View File

@@ -1,189 +0,0 @@
# Membina Permainan Angkasa Bahagian 5: Pemarkahan dan Kehidupan
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37)
Dalam pelajaran ini, anda akan belajar bagaimana menambahkan skor pada permainan dan mengira kehidupan.
## Lukis teks di skrin
Untuk dapat memaparkan skor permainan di layar, Anda harus tahu cara meletakkan teks di layar. Jawapannya adalah menggunakan kaedah `fillText()` pada objek kanvas. Anda juga dapat mengawal aspek lain seperti fon apa yang hendak digunakan, warna teks dan juga penjajarannya (kiri, kanan, tengah). Di bawah ini terdapat beberapa kod yang melukis beberapa teks di skrin.
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ Baca lebih lanjut mengenai [cara menambahkan teks ke kanvas](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text), dan jangan ragu untuk menjadikan teks anda kelihatan lebih menarik!
## Hidup, sebagai konsep permainan
Konsep mempunyai kehidupan dalam permainan hanya bilangan. Dalam konteks permainan ruang angkasa adalah biasa untuk menetapkan satu set nyawa yang dipotong satu demi satu ketika kapal anda mengalami kerosakan. Senang jika anda dapat menunjukkan gambaran grafik seperti miniships atau hati dan bukannya angka.
## Apakah yang anda perlu bina
Mari tambahkan yang berikut ke permainan anda:
- **Skor permainan**: Untuk setiap kapal musuh yang musnah, pahlawan harus diberikan beberapa mata, kami mencadangkan 100 mata setiap kapal. Skor permainan harus ditunjukkan di kiri bawah.
- **Kehidupan**: Kapal anda mempunyai tiga nyawa. Anda kehilangan nyawa setiap kali kapal musuh bertembung dengan anda. Skor hidup mesti dipaparkan di kanan bawah dan dibuat berdasarkan grafik berikut! [Gambar hidup](../solution/assets/life.png).
## Langkah yang disyorkan
Cari fail yang telah dibuat untuk anda dalam sub folder `your-work`. Ia harus mengandungi yang berikut:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Anda memulakan projek anda folder `your_work` dengan mengetik:
```bash
cd your-work
npm start
```
Perkara di atas akan memulakan Pelayan HTTP pada alamat `http://localhost:5000`. Buka penyemak imbas dan masukkan alamatnya, sekarang ia akan menjadikan pahlawan dan semua musuh, dan ketika anda menekan anak panah kiri dan kanan, pahlawan bergerak dan dapat menembak musuh.
### Tambahkan Kod
1. **Salin atas aset yang diperlukan** dari folder `solution/aset/` ke folder `your-work`; anda akan menambah aset `life.png`. Tambahkan lifeImg ke fungsi window.onload:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Tambahkan `lifeImg` ke senarai aset:
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **Tambah pemboleh ubah**. Tambahkan kod yang mewakili jumlah skor anda (0) dan tinggal (3), paparkan skor ini di skrin.
3. **Panjangkan fungsi `updateGameObjects ()`**. Panjangkan fungsi `updateGameObjects ()` untuk menangani perlanggaran musuh:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **Tambahkan `life` dan` point`**.
1. **Memulakan pemboleh ubah**. Di bawah `this.cooldown = 0` di kelas `Hero`, tetapkan hidup dan mata:
```javascript
this.life = 3;
this.points = 0;
```
1. **Lukis pemboleh ubah pada skrin**. Lukiskan nilai-nilai ini ke skrin:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. ** Tambahkan kaedah ke gelung Permainan **. Pastikan anda menambahkan fungsi ini ke fungsi window.onload anda di bawah `updateGameObjects()`:
```javascript
drawPoints();
drawLife();
```
1. **Melaksanakan peraturan permainan**. Laksanakan peraturan permainan berikut:
1. **Untuk setiap pertembungan pahlawan dan musuh**, tolaklah nyawa.
Lanjutkan kelas `Hero` untuk melakukan pemotongan ini:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **Untuk setiap laser yang menyerang musu**, tingkatkan skor permainan dengan 100 mata.
Panjangkan kelas Wira untuk melakukan kenaikan ini:
```javascript
incrementPoints() {
this.points += 100;
}
```
Tambahkan fungsi ini ke Collision Event Emitter anda:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ Lakukan sedikit penyelidikan untuk mengetahui permainan lain yang dibuat menggunakan JavaScript/Canvas. Apakah sifat umum mereka?
Menjelang akhir kerja ini, anda akan melihat kapal-kapal kecil 'kehidupan' di kanan bawah, titik di kiri bawah, dan anda akan melihat pengurangan jumlah hayat anda semasa anda bertembung dengan musuh dan kenaikan mata anda ketika anda menembak musuh. Bagus! Permainan anda hampir selesai.
---
## 🚀 Cabaran
Kod anda hampir lengkap. Bolehkah anda membayangkan langkah seterusnya?
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38)
## Mengkaji & Belajar Sendiri
Teliti beberapa cara yang boleh anda tambah dan mengurangkan skor dan kehidupan permainan. Terdapat beberapa enjin permainan yang menarik seperti [PlayFab](https://playfab.com). Bagaimana penggunaan salah satu daripadanya dapat meningkatkan permainan anda?
## Tugasan
[Bina Permainan Pemarkahan](assignment.ms.md)

View File

@@ -1,189 +0,0 @@
# 建立太空遊戲 Part 5分數與生命數
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/37?loc=zh_tw)
在這堂課中,你會學習如何為遊戲加入計分功能與計算性命數。
## 在畫面上繪製文字
為了在畫面上顯示遊戲分數,你需要了解如何配置文字。答案是在 Canvas 物件上使用方法 `fillText()`。你也可以控制其他特徵,例如文字字型、文字顏色甚至文字對齊方向(左、右、置中)。下面是在畫面中繪製一些文字。
```javascript
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ 閱讀更多關於[在畫布上建立文字](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text),你可以自由地豐富你的文字!
## 性命,一個遊戲概念
遊戲中的生命概念只是一個數字。在太空遊戲中,在你的船艦受到攻擊時,扣除生命值是一種常見的方式。如果能以圖像的方式顯示生命值也是很好的方式,例如船艦圖示、心臟圖像,而非單純使用數字。
## 建立目標
我們在遊戲中新增下列功能:
- **遊戲分數**:當每一艘敵軍艦艇被摧毀時,英雄應該取得一些獎勵分數,我們建議一艘敵艦一百分。遊戲總分應該顯示在畫面的左下角。
- **生命值**:你的船艦有三條性命。在每一次敵軍艦艇撞擊你時,你會損失一條性命。生命數應該顯示在畫面的右下角,以下列圖像顯示出來。 ![性命圖片](../solution/assets/life.png)
## 建議步驟
在你的 `your-work` 子資料夾中,確認檔案是否建立完成。它應該包括:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
開始 `your_work` 資料夾中的專案,輸入:
```bash
cd your-work
npm start
```
這會啟動 HTTP 伺服器並發布網址 `http://localhost:5000`。開啟瀏覽器並輸入該網址,現在它能顯示出英雄與所有的敵人,在你操作方向鍵後,英雄能移動並擊落敵人。
### 加入程式碼
1. 從資料夾 `solution/assets/` **複製你需要的檔案** 到資料夾 `your-work` 中。你會加入檔案 `life.png`。在函式 window.onload 中加入 lifeImg
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. 在檔案清單中加入 `lifeImg`
```javascript
let heroImg,
...
lifeImg,
...
eventEmitter = new EventEmitter();
```
2. **新增變數**。 加入程式碼表達你的遊戲總分(0)和剩餘性命(3),並顯示在畫面上。
3. **擴增函式 `updateGameObjects()`**。 擴增函式 `updateGameObjects()` 來處理敵軍碰撞:
```javascript
enemies.forEach(enemy => {
const heroRect = hero.rectFromGameObject();
if (intersectRect(heroRect, enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_HERO, { enemy });
}
})
```
4. **加入 `life` 與 `points`**.
1. **初始化變數**。 在 `Hero` class 的 `this.cooldown = 0` 下方,設定性命與分數:
```javascript
this.life = 3;
this.points = 0;
```
1. **在畫面上顯示變數內容**。 在畫面上繪製這些數值:
```javascript
function drawLife() {
// TODO, 35, 27
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
lifeImg,
START_POS + (45 * (i+1) ),
canvas.height - 37);
}
}
function drawPoints() {
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "left";
drawText("Points: " + hero.points, 10, canvas.height-20);
}
function drawText(message, x, y) {
ctx.fillText(message, x, y);
}
```
1. **在遊戲迴圈中加入呼叫**。 請確保你加入這些函式到 `updateGameObjects()` 下方的 window.onload 內:
```javascript
drawPoints();
drawLife();
```
1. **制定遊戲規則**。 制定下列的遊戲規則:
1. **在英雄與敵人發生碰撞時**,扣除一條生命。
擴增 `Hero` class 來執行這段減法:
```javascript
decrementLife() {
this.life--;
if (this.life === 0) {
this.dead = true;
}
}
```
2. **當雷射擊中敵人時**,增加遊戲總分一百分。
擴增 Hero class 來執行這段加法:
```javascript
incrementPoints() {
this.points += 100;
}
```
加入這些函式到碰撞事件發送器中:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
})
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
});
```
✅ 做點研究,探索其他使用到 JavaScript 與 Canvas 的遊戲。他們有什麼共同特徵?
在這個工作的尾聲,你應該能在右下方看到「性命」小船;左下方看到遊戲總分。當你碰撞到敵人時會扣除生命;當你擊落敵人時會增加分數。做得好!你的遊戲就快完成了。
---
## 🚀 挑戰
你的程式就快完成了。你能預測到下一步嗎?
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/38?loc=zh_tw)
## 複習與自學
研究其他種增加與減少分數與生命數的方法。這邊有一些有趣的遊戲引擎,例如:[PlayFab](https://playfab.com)。使用這些引擎能如何增進你的遊戲?
## 作業
[建立計分遊戲](assignment.zh-tw.md)

View File

@@ -1,11 +0,0 @@
# Construye un juego de puntuación
## Instrucciones
Crea un juego en el que muestres la vida y los puntos de forma creativa. Una sugerencia es mostrar la vida como corazones y los puntos como un gran número en la parte inferior central de la pantalla. Eche un vistazo aquí para ver [Recursos de juegos gratuitos](https://www.kenney.nl/)
# Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | se presenta el juego completo | se presenta parcialmente el juego | juego parcial contiene errores |

View File

@@ -1,11 +0,0 @@
# Développer un jeu de score
## Instructions
Créez un jeu où vous affichez la vie et les points de manière créative. Une suggestion est de montrer la vie sous forme de cœurs et les points sous forme de grand nombre dans la partie centrale inférieure de l'écran. Jetez un coup d'œil ici pour des [ressources de jeu gratuites](https://www.kenney.nl/)
# Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | le jeu complet est présenté | le jeu est partiellement présenté | le jeu partiel contient des bugs |

View File

@@ -1,11 +0,0 @@
# एक स्कोरिंग गेम बनाएँ
## अनुदेश
एक खेल बनाएं जहां आप रचनात्मक तरीके से जीवन और बिंदुओं को प्रदर्शित करते हैं। एक सुझाव है कि जीवन को दिलों के रूप में दिखाना और स्क्रीन के निचले केंद्र भाग में एक बड़ी संख्या के रूप में अंक। [मुक्त खेल संसाधनों](https://www.kenney.nl/) के लिए यहाँ एक नज़र डाले ।
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | -------------------- | ------------------------------------- | --------------------------- |
| | पूरा खेल प्रस्तुत है | खेल आंशिक रूप से प्रस्तुत किया गया है | आंशिक खेल में बग्स होते हैं |

View File

@@ -1,11 +0,0 @@
# Costruire un Gioco di Punteggio
## Istruzioni
Creare un gioco in cui si mostrano vite e punti in modo creativo. Un suggerimento è quello di mostrare le vite come cuori e i punti come un numero grande nella parte centrale in basso dello schermo. Dare un'occhiata qui per [risorse di gioco gratuite](https://www.kenney.nl/)
# Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |

View File

@@ -1,11 +0,0 @@
# スコアリングゲームの構築
## 説明書
ライフとポイントを独創的な方法で表示するゲームを作成します。提案としては、ライフをハートで表示し、ポイントを画面中央下部の大きな数字で表示することです。[無料のゲームリソース](https://www.kenney.nl/)はこちらをご覧ください。
# ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | フルゲームが提示されています | 部分的に提示されたゲーム | 部分的なゲームにはバグが含まれています |

View File

@@ -1,11 +0,0 @@
# 득점 게임 만들기
## 설명
생명과 포인트를 창의적으로 보여주는 게임을 만들어 봅시다. 화면 가운데 하단에는 생명과 포인트를 큰 숫자로 나타내보세요. [무료 게임 목록](https://www.kenney.nl/)을 참고해보세요.
# 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| 완성도 있는 게임을 만든 경우 | 일부만 작동하는 게임을 만든 경우 | 게임에 오류가 있는 경우

View File

@@ -1,11 +0,0 @@
# Bina Permainan Pemarkahan
## Arahan
Buat permainan di mana anda menampilkan kehidupan dan mata dengan cara yang kreatif. Satu cadangan adalah untuk menunjukkan kehidupan sebagai hati dan poin sebagai angka yang besar di bahagian tengah bawah skrin. Lihat di sini [Sumber permainan percuma](https://www.kenney.nl/)
# Rubrik
| Kriteria | Contoh | Mencukupi | Usaha Lagi |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | permainan penuh dipersembahkan | permainan dipersembahkan sebahagiannya | permainan separa mengandungi pepijat |

View File

@@ -1,11 +0,0 @@
# Bouw een scorespel
## Instructies
Creëer een spel waarin u leven en punten op een creatieve manier weergeeft. Een suggestie is om het leven te laten zien als harten en de punten als een groot getal in het midden onderaan het scherm. Kijk hier voor [gratis spelbronnen](https://www.kenney.nl/)
# Rubriek
| Criteria | Voorbeeldig | Voldoende | Moet worden verbeterd |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | volledige game wordt gepresenteerd | game wordt gedeeltelijk gepresenteerd | gedeeltelijke game bevat bugs |

View File

@@ -1,11 +0,0 @@
# 建立計分遊戲
## 簡介
建立一款遊戲,能有創意地顯示生命值與分數。建議上能在畫面的正下方,以心型圖示顯示生命值,或是斗大的數字顯示分數。看看這些[免費的遊戲資源](https://www.kenney.nl/)。
## 學習評量
| 作業內容 | 優良 | 普通 | 待改進 |
| -------- | ---------------- | ---------------- | ------------ |
| | 呈現出完整的遊戲 | 遊戲提供部分內容 | 遊戲存在問題 |

View File

@@ -1,214 +0,0 @@
# Construye un juego espacial Parte VI: Finalizar y reiniciar
![video](video-url)
## [Pre-lecture prueba](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39)
Hay diferentes formas de expresar y *condición final* en un juego. Depende de usted, como creador del juego, decir por qué ha terminado. Aquí hay algunas razones, si asumimos que estamos hablando del juego espacial que has estado construyendo hasta ahora:
- **Las naves `N` enemigas han sido destruidas**: Es bastante común si divides un juego en diferentes niveles que necesites destruir las naves `N` Enemy para completar un nivel
- **Tu nave ha sido destruida**: Definitivamente hay juegos en los que pierdes el juego si tu nave es destruida. Otro enfoque común es que tienes el concepto de vidas. Cada vez que un barco es destruido, se descuenta una vida. Una vez que se hayan perdido todas las vidas, perderá el juego.
- **Has acumulado `N` puntos**: Otra condición final común es que acumules puntos. La forma de obtener puntos depende de usted, pero es bastante común asignar puntos a diversas actividades, como destruir una nave enemiga o tal vez recolectar elementos que los elementos *sueltan* cuando son destruidos.
- **Completa un nivel**: Esto puede implicar varias condiciones como "X" barcos enemigos destruidos, puntos "Y" acumulados o tal vez que se haya recogido un objeto específico.
## Reiniciando
Si las personas disfrutan de tu juego, es probable que quieran volver a jugarlo. Una vez que el juego termina por cualquier motivo, debes ofrecer una alternativa para reiniciar.
✅ Piensa un poco en las condiciones en las que encuentras que termina un juego y luego cómo se te pide que reinicies
## Qué construir
Agregará estas reglas a su juego:
1. **Ganar el juego**. Una vez que todas las naves enemigas hayan sido destruidas, ganas el juego. Además, muestra algún tipo de mensaje de victoria.
1. **Reiniciar**. Una vez que hayas perdido todas tus vidas o hayas ganado el juego, debes ofrecer una forma de reiniciar el juego. ¡Recuerda! Deberá reinicializar el juego y se debe borrar el estado anterior del juego.
## Pasos recomendados
Busque los archivos que se han creado para usted en la subcarpeta `your-work`. Debe contener lo siguiente:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
Comienzas tu proyecto en la carpeta `your_work` escribiendo:
```bash
cd your-work
npm start
```
Lo anterior iniciará un servidor HTTP en la dirección `http://localhost:5000`. Abra un navegador e ingrese esa dirección. Tu juego debe estar en un estado jugable.
> consejo: para evitar advertencias en Visual Studio Code, edite la función `window.onload` para llamar a `gameLoopId` tal cual (sin `let`), y declare el gameLoopId en la parte superior del archivo, independientemente: `let gameLoopId;`
### Agregar código
1. **Condición de fin de pista**. Agregue un código que realice un seguimiento de la cantidad de enemigos o si el barco héroe ha sido destruido agregando estas dos funciones:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **Agregar lógica a los controladores de mensajes**. Edite el `eventEmitter` para manejar estas condiciones:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **Agregar nuevos tipos de mensajes**. Agregue estos mensajes al objeto de constantes:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **Agregar código de reinicio**. Código que reinicia el juego con solo presionar un botón seleccionado.
1. **Escuche la tecla "Enter"**. Edite el eventListener de su ventana para escuchar esta acción:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **Agregar mensaje de reinicio**. Agregue este mensaje a su constante de Mensajes:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **Implementar las reglas del juego**. Implementa las siguientes reglas del juego:
1. **Condición de victoria del jugador**. Cuando todos los barcos enemigos sean destruidos, muestra un mensaje de victoria.
1. Primero, cree una función `displayMessage()`:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. Cree una función `endGame()`:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// establece una demora para estar seguros de que las pinturas han terminado
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **Reiniciar la lógica**. Cuando se pierdan todas las vidas o el jugador haya ganado el juego, muestre que el juego se puede reiniciar. Además, reinicie el juego cuando se presione la tecla *reiniciar* (puede decidir qué tecla debe asignarse para reiniciar).
1. Cree la función `resetGame()`:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. Agrega una llamada al `eventEmitter` para reiniciar el juego en `initGame()`:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. Agregue una función `clear()` al EventEmitter:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 ¡Felicitaciones, Capitán! ¡Tu juego está completo! ¡Bien hecho! 🚀 💥 👽
🚀 Desafío: ¡Agrega un sonido! ¿Puedes agregar un sonido para mejorar tu juego, tal vez cuando hay un golpe de láser, o el héroe muere o gana? Eche un vistazo a este [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) para aprender a reproducir sonido con JavaScript.
## [Post-lecture prueba]((https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40)
## Revisión y autoestudio
Tu tarea es crear un juego de muestra nuevo, así que explora algunos de los juegos interesantes que existen para ver qué tipo de juego podrías construir.
**Tarea**: [Crear un juego de muestra](assignment.es.md)

View File

@@ -1,223 +0,0 @@
# Construire un jeu de l'espace Partie 6: Fin et redémarrage
## Quiz de prélecture
[Quiz de prélecture](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=fr)
Il existe différentes façons d'exprimer et de terminer une condition dans un jeu. C'est à vous, en tant que créateur du jeu, de dire pourquoi le jeu est terminé. Voici quelques raisons, si nous supposons que nous parlons du jeu spatial que vous avez construit jusqu'à présent:
- **`N`Les navires ennemis ont été détruits**: Si vous divisez un jeu en plusieurs niveaux, il est assez fréquent que vous deviez détruire `N` vaisseaux ennemis pour terminer un niveau.
- **Votre navire a été détruit**: Il y a certainement des jeux où vous perdez la partie si votre vaisseau est détruit. Une autre approche commune est que vous avez le concept de vies. Chaque fois que votre vaisseau est détruit, une vie est déduite. Une fois que toutes les vies ont été perdues, vous perdez la partie.
- **Vous avez collecté `N` points**: Une autre condition de fin commune est que vous accumuliez des points. La façon dont vous obtenez des points dépend de vous, mais il est assez courant d'attribuer des points à diverses activités comme la destruction d'un navire ennemi ou la collecte d'objets que les objets *laissent tomber* lorsqu'ils sont détruits.
- **Compléter un niveau**: Cela peut impliquer plusieurs conditions telles que `X` navires ennemis détruits, `Y` points collectés ou peut-être qu'un objet spécifique a été collecté.
## Redémarrage
Si les gens apprécient votre jeu, ils auront probablement envie d'y rejouer. Lorsque le jeu se termine, quelle qu'en soit la raison, vous devez proposer une alternative pour recommencer.
✅ Réfléchissez un peu aux conditions dans lesquelles vous constatez qu'une partie se termine, puis à la manière dont vous êtes invité à la reprendre.
## Ce qu'il faut construire
Vous allez ajouter ces règles à votre jeu:
1. **Gagner la partie**. Une fois que tous les navires ennemis ont été détruits, vous gagnez la partie. Affichez en outre une sorte de message de victoire.
1. **Démarrer**. Une fois que toutes les vies sont perdues ou que la partie est gagnée, vous devez proposer un moyen de redémarrer la partie. N'oubliez pas! Vous devrez réinitialiser le jeu et l'état précédent du jeu devra être effacé.
## Mesures recommandées
Localisez les fichiers qui ont été créés pour vous dans le sous-répertoire `your-work`. Il devrait contenir les éléments suivants:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
Vous démarrez votre projet dans le dossier `your_work` en tapant:
```bash
cd your-work
npm start
```
Ce qui précède va démarrer un serveur HTTP sur l'adresse `http://localhost:5000`. Ouvrez un navigateur et entrez cette adresse. Votre jeu devrait être dans un état jouable.
> astuce: pour éviter les avertissements dans Visual Studio Code, modifiez la fonction `window.onload` pour appeler `gameLoopId` tel quel (sans `let`), et déclarez le gameLoopId en haut du fichier, indépendamment: `let gameLoopId;`
### Ajouter du code
1. **Condition de fin de parcours**. Ajoutez du code qui garde la trace du nombre d'ennemis, ou si le vaisseau du héros a été détruit en ajoutant ces deux fonctions:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **Ajouter de la logique aux gestionnaires de messages**. Modifiez le `eventEmitter` pour gérer ces conditions:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **Ajouter de nouveaux types de messages**. Ajoutez ces messages à l'objet constants:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
1. **Ajouter le code de redémarrage** code qui redémarre le jeu à la pression d'un bouton sélectionné.
1. **Écouter la touche pressée `Enter`**. Modifiez l'eventListener de votre fenêtre pour écouter cette pression:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **Ajouter un message de redémarrage**. Ajoutez ce message à votre constante Messages:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **Mettre en œuvre les règles du jeu**. Mettez en œuvre les règles du jeu suivantes:
1. **Condition de victoire du joueur**. Lorsque tous les navires ennemis sont détruits, affichez un message de victoire.
1. Tout d'abord, créez une fonction `displayMessage()`:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. Créez une fonction `endGame()`:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// set a delay so we are sure any paints have finished
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **Logique de redémarrage**. Lorsque toutes les vies sont perdues ou que le joueur a gagné la partie, affichez que le jeu peut être redémarré. En outre, redémarrez le jeu lorsque la touche *redémarrage* est enfoncée (vous pouvez décider quelle touche doit être affectée au redémarrage).
1. Créez la fonction `resetGame()`:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. Ajoutez un appel au `eventEmitter` pour réinitialiser le jeu dans `initGame()`:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. Ajoutez une fonction `clear()` à l'EventEmitter:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 Félicitations, capitaine! Ton jeu est terminé! Bien joué! 🚀 💥 👽
---
## 🚀 Défi
Ajoutez un son! Pouvez-vous ajouter un son pour améliorer votre jeu, par exemple lorsqu'un laser est touché, ou lorsque le héros meurt ou gagne? Jetez un coup d'œil à ce [bac à sable](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) pour apprendre à jouer un son en utilisant JavaScript.
## Quiz post-lecture
[Quiz post-lecture](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=fr)
## Révision et autoformation
Votre mission consiste à créer un nouveau jeu type. Explorez donc certains des jeux intéressants qui existent pour voir quel type de jeu vous pourriez créer.
## Travail à faire
[Créer un jeu type](assignment.fr.md)

View File

@@ -1,224 +0,0 @@
# एक अंतरिक्ष खेल बनाएँ भाग ६: अंत और पुनः आरंभ
## प्री-रीडिंग क्विज
[प्री-रीडिंग क्विज](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=hi)
एक खेल में व्यक्त करने और _अंतिम स्थिति_ के विभिन्न तरीके हैं। यह गेम के निर्माता के रूप में यह कहना है कि खेल क्यों समाप्त हो गया है। यहाँ कुछ कारण हैं, अगर हम मान लें कि हम उस अंतरिक्ष खेल के बारे में बात कर रहे हैं जो आप अभी तक बना रहे हैं:
- **`N` दुश्मन के जहाज तबाह हो गए हैं**: यदि आप एक गेम को विभिन्न स्तरों में विभाजित करते हैं तो यह काफी सामान्य है कि आपको एक स्तर पूरा करने के लिए `N` दुश्मन जहाजों को नष्ट करने की आवश्यकता है
- **आपका जहाज नष्ट हो गया है**: यदि आपका जहाज नष्ट हो जाता है तो निश्चित रूप से ऐसे खेल हैं जहाँ आप खेल को खो देते हैं। एक और आम दृष्टिकोण यह है कि आपके पास जीवन की अवधारणा है। हर बार जब आपका जहाज नष्ट हो जाता है तो यह जीवन काट देता है। एक बार जब सभी जान चली गई तो आप खेल खो देते हैं.
- **आपने `N` अंक एकत्र किए हैं**: एक और सामान्य अंतिम स्थिति आपके लिए अंक एकत्र करने की है। आप कैसे अंक प्राप्त करते हैं, यह आपके ऊपर है, लेकिन दुश्मन के जहाज को नष्ट करने या शायद वस्तुओं को इकट्ठा करने जैसी विभिन्न गतिविधियों के लिए अंक प्रदान करना काफी सामान्य है, जब वे _गिर_ जाते हैं।.
- **एक स्तर पूरा करें**: इसमें कई स्थितियां शामिल हो सकती हैं जैसे कि `X` दुश्मन के जहाज नष्ट,` Y` अंक एकत्र या शायद एक विशिष्ट आइटम एकत्र किया गया है.
## पुनरारंभ
यदि लोग आपके खेल का आनंद लेते हैं, तो वे इसे फिर से खेलना चाहते हैं। एक बार खेल जो भी कारण से आप को पुनरारंभ करने के लिए एक विकल्प की पेशकश के लिए समाप्त होता है.
✅ इस बारे में थोड़ा सोचें कि आपको किन परिस्थितियों में गेम समाप्त होता है, और फिर आपको कैसे पुनरारंभ करने के लिए प्रेरित किया जाता है
## क्या बनना है
आप इन नियमों को अपने खेल में शामिल करेंगे:
1. **खेल जीतना**. एक बार सभी दुश्मन जहाजों को नष्ट कर दिए जाने के बाद, आप गेम जीतते हैं। इसके अतिरिक्त किसी प्रकार का विजय संदेश प्रदर्शित करें.
1. **पुनरारंभ**. एक बार जब आपका सारा जीवन खो जाता है या खेल जीत जाता है, तो आपको खेल को पुनः आरंभ करने का एक तरीका पेश करना चाहिए। याद है! आपको खेल को फिर से संगठित करना होगा और पिछले खेल की स्थिति को साफ करना चाहिए.
## अनुशंसित कदम
उन फ़ाइलों का पता लगाएँ जो आपके लिए `your-work` सब फ़ोल्डर में बनाई गई हैं। इसमें निम्नलिखित शामिल होना चाहिए:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
आप टाइप करके अपना प्रोजेक्ट `your_work` फ़ोल्डर शुरू करें:
```bash
cd your-work
npm start
```
उपरोक्त पते पर एक HTTP सर्वर शुरू होगा `http: // localhost: 5000`। एक ब्राउज़र खोले और उस पतेको खोलें। आपका खेल खेलने योग्य अवस्था में होना चाहिए
> टिप: विज़ुअल स्टूडियो कोड में चेतावनियों से बचने के लिए, `window.onload` फ़ंक्शन को` gameLoopId` के रूप में (`let` के बिना) के रूप में संपादित करने के लिए संपादित करें, और स्वतंत्र रूप से फ़ाइल के शीर्ष पर gameLoopId की घोषणा करें, `let gameLoopId`;
### कोड जोड़े
1. **ट्रैक एंड कंडीशन**. उन कोडों को जोड़ें जो दुश्मनों की संख्या का ट्रैक रखते हैं, या यदि इन दो कार्यों को जोड़ते हुए नायक जहाज को नष्ट कर दिया गया है:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter(
(go) => go.type === "Enemy" && !go.dead
);
return enemies.length === 0;
}
```
1. **संदेश संचालकों में तर्क जोड़ें**. इन स्थितियों को संभालने के लिए `EventEmitter` संपादित करें:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **नए संदेश प्रकार जोड़ें**. इन संदेशों को स्थिरांक वस्तु में जोड़ें:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
1. **पुनः आरंभ कोड जोड़ें** कोड जो चयनित बटन के प्रेस पर गेम को पुनरारंभ करता है.
1. **`Enter` की प्रेस सुनो**. इस प्रेस को सुनने के लिए अपनी विंडो के इवेंटलिस्ट को एडिट करें:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **पुनः आरंभ संदेश जोड़ें**. इस संदेश को अपने संदेशों में लगातार जोड़ें:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **खेल के नियमों को लागू करें**. निम्नलिखित खेल नियमों को लागू करें:
1. **खिलाड़ी शर्त जीता**. जब सभी दुश्मन जहाज नष्ट हो जाते हैं, तो एक जीत संदेश प्रदर्शित करें.
1. सबसे पहले, एक `displayMessage()` फ़ंक्शन बनाएँ:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. एक `endGame()` फ़ंक्शन बनाएँ:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// set a delay so we are sure any paints have finished
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200);
}
```
1. **तर्क पुनः आरंभ**. जब सभी जीवन खो जाते हैं या खिलाड़ी खेल जीत जाता है, तो प्रदर्शित करें कि खेल को फिर से शुरू किया जा सकता है। इसके अलावा खेल को पुनरारंभ करें जब _पुनरारंभ_ की हिट होती है (आप तय कर सकते हैं कि पुनरारंभ करने के लिए किस की को मैप किया जाना चाहिए).
1. `ResetGame()` फ़ंक्शन बनाएँ:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. `InitGame()` में गेम को रीसेट करने के लिए` EventEmitter` में कॉल जोड़ें:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. EventEmitter में `clear()` फ़ंक्शन जोड़ें:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 बधाई हो, कैप्टन! आपका खेल पूरा हो गया है! बहुत बढ़िया! 🚀 💥 👽
---
## 🚀 चुनौती
एक ध्वनि जोड़ें! क्या आप अपने गेम खेलने को बढ़ाने के लिए एक ध्वनि जोड़ सकते हैं, हो सकता है कि जब कोई लेजर हिट हो, या नायक मर जाए या जीत जाए? जावास्क्रिप्ट का उपयोग करके ध्वनि कैसे खेलें, यह जानने के लिए इस [सैंडबॉक्स](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) पर एक नज़र डालें
## पोस्ट-व्याख्यान प्रश्नोत्तरी
[पोस्ट-व्याख्यान प्रश्नोत्तरी](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=hi)
## समीक्षा और स्व अध्ययन
आपका असाइनमेंट एक फ्रेश सैंपल गेम बनाना है, इसलिए वहां के कुछ दिलचस्प गेम्स को देखें कि आप किस प्रकार के गेम का निर्माण कर सकते हैं.
## असाइनमेंट
[एक नमूना खेल बनाएँ](assignment.hi.md)

View File

@@ -1,222 +0,0 @@
# Costruire un Gioco Spaziale Parte 6: Fine e Riavvio
## Quiz Pre-Lezione
[Quiz Pre-Lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=it)
Esistono diversi modi per esprimere una *condizione di fine gioco*. Spetta al creatore del gioco dire perché il gioco è finito. Ecco alcuni motivi, si supponga di parlare del gioco spaziale costruito finora:
- Un **numero `N` di astronavi nemiche sono state distrutte**: è abbastanza comune se il gioco viene diviso in diversi livelli che si debba distruggere `N` astronavi nemiche per completare un livello
- **La propria nave è stata distrutta**: ci sono sicuramente giochi in si perde la partita se la propria astronave viene distrutta. Un altro approccio comune è il concetto di vite. Ogni volta che una propria astronave viene distrutta, sottrae una vita. Una volta perse tutte le vite, si perde la partita.
- **Sono stati raccolti `N` punti**: un'altra condizione finale comune è che il giocatore raccolga punti. Il modo in cui ottenere punti dipende dallo sviluppatore, ma è abbastanza comune assegnare punti a varie attività come distruggere una astronave nemica o forse raccogliere elementi che vengono *rilasciati* quando gli oggetti vengono distrutti.
- **Completare un livello**: questo potrebbe coinvolgere diverse condizioni come `X` astronavi nemiche distrutte, `Y` punti raccolti o forse che è stato raccolto un oggetto specifico.
## Riavvio
Se le persone apprezzano il gioco, è probabile che vogliano rigiocarlo. Una volta che il gioco finisce per qualsiasi motivo, si dovrebbe offrire un'alternativa per il riavvio.
✅ Si pensi alle condizioni per le quali si ritiene che un gioco finisca e poi a come viene chiesto di riavviare
## Cosa costruire
Aggiungere queste regole al gioco:
1. **Vincere il gioco**. Una volta che tutte le navi nemiche sono state distrutte, si vince la partita. Mostrare inoltre una sorta di messaggio di vittoria.
1. **Riavvio**. Una volta perse tutte le vite o si è vinto il gioco, si dovrebbe offrire un modo per riavviare il gioco. Ricordare! Si dovrà reinizializzare il gioco e lo stato del gioco precedente dovrebbe essere cancellato.
## Passaggi consigliati
Individuare i file che già sono stati creati nella sottocartella `your-work`. Dovrebbe contenere le seguenti informazioni:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
Si fa partire il progetto dalla cartella `your_work` digitando:
```bash
cd your-work
npm start
```
Quanto sopra avvierà un server HTTP all'indirizzo `http://localhost:5000`. Aprire un browser e inserire quell'indirizzo. Il gioco dovrebbe essere in uno stato giocabile.
> suggerimento: per evitare avvertimenti in Visual Studio Code, modificare la funzione `window.onload` per chiamare `gameLoopId` così com'è (senza `let`) e dichiarare gameLoopId all'inizio del file, indipendentemente: `let gameLoopId;`
### Aggiungere codice
1. **Tracciare la condizione di fine**. Aggiungere codice che tenga traccia del numero di nemici o se l'astronave dell'eroe è stata distrutta aggiungendo queste due funzioni:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **Aggiungere logica ai gestori di messaggi**. Modificare `eventEmitter` per gestire queste condizioni:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **Aggiungere nuovi tipi di messaggi**. Aggiungere questi messaggi alle costanti assegnate agli oggetti:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **Aggiungere il codice di riavvio** che fa ripartire il gioco con la semplice pressione di un pulsante selezionato.
1. **Mettersi in ascolto per la pressione del tasto `Invio`**. Modificare l'eventListener di window per ascoltare questa pressione
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **Aggiungere messaggio di riavvio**. Aggiungere questo messaggio alle costanti in Messages:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **Implementare le regole del gioco**. Implementare le seguenti regole di gioco:
1. **Condizione di vittoria del giocatore**. Quando tutte le astronavi nemiche vengono distrutte, mostrare un messaggio di vittoria.
1. Innanzitutto, creare una funzione `displayMessage()` :
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. Creare una funzione `endGame()` :
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// imposta un ritardo per assicurarsi che tutte sia stato disegnato
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **Logica di riavvio**. Quando tutte le vite sono perse o il giocatore ha vinto la partita, mostrare che il gioco può essere riavviato. Inoltre, riavviare il gioco quando viene premuto il tasto di *riavvio* (si può decidere quale tasto deve essere mappato per il riavvio).
1. Creare la funzione `resetGame()` :
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. Aggiungere una chiamata a `eventEmitter` per riazzerare il gioco in `initGame()`:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. Aggiungere una funzione `clear()` a EventEmitter:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 Congratulazioni, Capitano! Il gioco è completo! Ottimo lavoro! 🚀 💥 👽
---
## 🚀 Sfida
Aggiungere un suono! Si può aggiungere un suono per migliorare il gioco, magari quando un colpo di laser va a segno, o l'eroe muore o vince? Si dia un'occhiata a questo [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) per imparare a riprodurre il suono utilizzando JavaScript
## Quiz Post-Lezione
[Quiz post-lezione](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=it)
## Revisione e Auto Apprendimento
Il compito è creare un nuovo gioco di esempio, quindi esplorare alcuni dei giochi interessanti esistenti per vedere che tipo di gioco si potrebbe costruire.
## Compito
[Creare un Gioco di Esempio](assignment.it.md)

View File

@@ -1,223 +0,0 @@
# スペースゲーム構築プロジェクト その 6: 終了と再起動
## レッスン前の小テスト
[レッスン前の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=ja)
ゲーム内での表現方法や *終了条件* の表現方法は様々です。なぜゲームが終了したのかは、ゲームを作る側のあなた次第です。ここでは、あなたが今まで作ってきた宇宙ゲームの話をしていると仮定して、いくつかの理由を挙げてみましょう。
- **`N` 隻の敵の宇宙船を撃破しました**: それはあなたがレベルを完了するために N 隻の敵の宇宙船を破壊する必要があることを別のレベルにゲームを分割する場合はかなり一般的です
- **あなたの船が破壊されました**: 自分の船が破壊されるとゲームに負けるゲームは間違いなくあります。もう一つの一般的なアプローチは、ライフの概念を持っているということです。あなたの宇宙船が破壊されるたびに、それはライフを差し引きます。すべてのライフが失われると、ゲームを失うことになります
- **`N` ポイントを集めました**: もう一つの一般的な終了条件は、ポイントを集めることです。どのようにポイントを獲得するかはあなた次第ですが、敵の宇宙船を破壊したり、破壊された時にドロップするアイテムを集めたりと、様々な活動にポイントを割り当てるのが一般的です
- **レベルをクリアしました**: これには、`X` 隻の敵の宇宙船を破壊したり、`Y` ポイントを集めたり、特定のアイテムを集めたりするなど、いくつかの条件が含まれている場合があります
## 再起動
人々があなたのゲームを楽しめば、彼らはそれを再プレイしたいと思う可能性が高いです。何らかの理由でゲームが終了したら、再起動するための代替手段を提供すべきです。
✅ どのような条件でゲームが終了したか、そしてどのように再起動を促されるかを少し考えてみてください。
## 何を構築するか
これらのルールをゲームに追加していくことになります。
1. **ゲームの勝利**。全ての敵の宇宙船を撃破したらゲームの勝利です。さらに、何らかの勝利メッセージを表示します
1. **再起動すること**。全てのライフが失われたり、ゲームに勝利したら、ゲームを再起動する方法を提供する必要があります。覚えておいてください! ゲームを再初期化する必要があり、以前のゲームの状態をクリアする必要があります
## 推奨される手順
あなたのために作成されたファイルを `your-work` サブフォルダ内で探します。以下のファイルが含まれているはずです。
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
次のように入力して、`your_work` フォルダからプロジェクトを起動します。
```bash
cd your-work
npm start
```
上記のようにすると、`http://localhost:5000` というアドレスに HTTP サーバーが起動します。ブラウザを開いて、そのアドレスを入力してください。ゲームがプレイ可能な状態になっているはずです。
> ヒント: Visual Studio Code の警告を避けるためには、`window.onload` 関数を編集して `gameLoopId` をそのまま (`let` を省略して) 呼び出し、ファイルの先頭に gameLoopId を `let gameLoopId;` と独立して宣言します。
### コードの追加
1. **終了条件を追跡します**。敵の数を追跡したり、ヒーローの宇宙船が破壊されたかどうかを追跡するコードを追加します
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **メッセージハンドラにロジックを追加します**。これらの条件を処理するために `eventEmitter` を編集します
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // 勝利前に敗北
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **新しいメッセージタイプを追加します**。これらのメッセージを定数オブジェクトに追加します
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. 選択したボタンの押下でゲームを再起動する**再起動コードを追加します**
1. **`Enter` キーの押下を待ち受けます**。この押下を待ち受けるために、ウィンドウの eventListener を編集します
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **再起動メッセージを追加します**。このメッセージをメッセージ定数に追加します
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **ゲームルールの実装** 以下のゲームルールを実装します
1. **プレイヤーの勝利条件**。敵の宇宙船を全て撃破した場合、勝利のメッセージを表示します
1. まず、関数 `displayMessage()` を作成します
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. 関数 `endGame()` を作成します
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// 塗り終わったことを確認するために遅延を設定します
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **再起動ロジック**。すべてのライフが失われたとき、またはプレイヤーが勝ったときに、ゲームを再起動できることを表示します。さらに、リスタートキーが押されるとゲームを再起動します (どのキーをリスタートにマッピングするかはあなたが決めることができます)
1. 関数 `resetGame()` を作成します
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. `initGame()` でゲームをリセットするために `eventEmitter` の呼び出しを追加します
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. EventEmitter に `clear()` 関数を追加します
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 おめでとうございます、隊長! あなたのゲームは完成しました! よくできました! 🚀 💥 👽
---
## 🚀 チャレンジ
音を追加しましょう!レーザーが当たった時や、ヒーローが死んだ時、勝った時など、ゲームを盛り上げるために音を追加することはできますか? JavaScript を使ってサウンドを再生する方法については、こちらの [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) をご覧ください。
## レッスン後の小テスト
[レッスン後の小テスト](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=ja)
## 復習と自己学習
あなたの課題は新鮮なサンプルゲームを作成することです。だから、どんなゲームを作れそうかを確認するために、そこにある面白いゲームのいくつかを探索してください。
## 課題
[サンプルゲームを作る](assignment.ja.md)

View File

@@ -1,222 +0,0 @@
# Space 게임 제작하기 파트 6: 끝과 재시작
## 강의 전 퀴즈
[Pre-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=ko)
게임에서 *조건을 표현하고 종료*하는 여러 방식이 있습니다. 게임이 종료된 이유를 말하는 것은 게임 크리에이터의 일입니다. 지금까지 만든 space 게임에 대해 말하고 있다고 가정하면, 몇 가지 이유가 있습니다:
- **`N`개의 적 배가 파괴되었습니다**: 게임을 여러 레벨로 나누면 레벨을 완료하기 위해 `N`개의 적 배를 부숴야하는 경우가 매우 흔합니다.
- **배가 파괴되었습니다**: 배가 부서지면 지는 게임이 분명 있습니다. 또 다른 일반적인 접근 방식은 생명의 컨셉을 가지고 있다는 점입니다. 배가 부서질 때마다 생명이 깍입니다. 모든 목숨을 잃으면 게임에서 집니다.
- **`N` 점수를 모았습니다**: 또 다른 종료 조건은 점수를 모으는 것입니다. 점수를 얻는 방법으로 각자 배를 파괴하는 것처럼 다양한 활동에 점수를 할당하거나 아이템이 부서질 때마다 *떨구는* 아이템을 수집하는 것은 매우 일반적입니다.
- **레벨을 완료했습니다**: 적 배를 `X` 번 부시거나, `Y` 점수를 수집하거나 특정 아이템을 수집하는 것처럼 여러 조건들을 여기에 포함할 수 있습니다.
## 다시 시작하기
사람들이 게임을 즐기고 있다면 다시 플레이하고 싶어합니다. 어떤 이유든지 게임이 끝나면 다시 시작할 수 있는 대안을 줘야합니다.
✅ 어떤 조건에서 게임이 끝나는 지에 대하여 찾고, 다시 시작이라는 메시지가 어떻게 보일지 생각해보세요
## 무엇을 만드나요
게임에 다음 규칙을 추가합니다:
1. **게임에 우승합니다**. 모든 적의 배가 부서지면, 게임에서 승리합니다. 추가로 일종의 승리 메시지를 출력합니다.
1. **다시 시작합니다**. 모든 생명을 잃거나 게임에서 이긴다면, 게임을 다시 시작할 방법을 제공해야 합니다. 생각해보세요! 게임을 다시 초기화하고 이전 게임 상태를 깨끗이 지워야 합니다.
## 권장 단계
`your-work` 하위 폴더에서 생성된 파일을 찾습니다. 다음을 포함해야 합니다:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
타이핑해서 `your_work` 폴더에 프로젝트를 시작합니다:
```bash
cd your-work
npm start
```
위 코드는 `http://localhost:5000` 주소에서 HTTP 서버를 시작합니다. 브라우저를 열고 해당 주소를 입력합니다. 게임은 플레이 가능한 상태여야 합니다.
> tip: Visual Studio Code에서 경고를 보이지 않게 하려면, `gameLoopId`를 (`let`없이) 그대로 호출하도록 `window.onload` 함수를 편집하고, 파일 최상단에 gameLoopId를 독립적으로 선언합니다: `let gameLoopId;`
### 코드 추가하기
1. **종료 조건을 추적합니다**. 다음 두 함수를 추가하여 적의 수를 추적하거나, 영웅의 배가 부서진 경우도 추적해주는 코드를 추가합니다:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **메시지 핸들러에 로직을 추가합니다**. 이 조건을 제어하도록 `eventEmitter`를 편집합니다:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **새로운 메시지 타입을 추가합니다**. 상수 객체에 이 메시지를 추가합니다:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **재시작 코드를 추가합니다** 선택한 버튼을 누르면 게임을 다시 시작하는 코드입니다.
1. **`Enter` 누를 키를 수신합니다**. 누르는 것을 수신하도록 윈도우의 이벤트 리스너를 편집합니다:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **재시작 메시지 추가하기**. 메시지를 메시지 상수에 추가합니다:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **게임 규칙을 구현합니다**. 다음 게임 규칙을 구현합니다:
1. **플레이어 승리 조건입니다**. 적 배가 모두 파괴되면, 승리 메시지를 출력합니다.
1. 먼저, `displayMessage()` 함수를 만듭니다:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. `endGame()` 함수를 만듭니다:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// set a delay so we are sure any paints have finished
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **로직을 다시 시작합니다**. 모든 생명을 잃거나 플레이어가 게임에서 이긴다면, 게임을 다시 시작할 수 있다고 출력합니다. 추가로 *restart* 키를 누르면 게임을 다시 시작합니다 (다시 시작하기 위해 매핑할 키를 고를 수 있습니다).
1. `resetGame()` 함수를 만듭니다:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. `initGame()`에서 게임을 다시 설정하기 위해 `eventEmitter`에 호출하도록 추가합니다:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. EventEmitter에 `clear()` 힘수를 추가합니다:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 축하합니다, 대장! 게임이 완성되었습니다! 잘 하셨습니다! 🚀 💥 👽
---
## 🚀 도전
소리를 추가해보세요! 레이저가 때리거나, 영웅이 죽고 이길 때, 소리를 추가하여 게임 플레이를 향상시킬 수 있나요? [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play)에서 JavaScript로 소리를 재생하는 방법에 대하여 알아보세요
## 강의 후 퀴즈
[Post-lecture quiz](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=ko)
## 리뷰 & 자기주도 학습
과제는 새로운 샘플 게임을 만드는 것이므로, 어떤 타입의 게임을 만들 수 있는지 알아보고 흥미로운 게임을 찾아보세요.
## 과제
[Build a Sample Game](../assignment.md)

View File

@@ -1,222 +0,0 @@
# Bina Permainan Angkasa Bahagian 6: Tamat dan Mulakan Semula
## Kuiz Pra Kuliah
[Kuiz Pra Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39)
Terdapat pelbagai cara untuk menyatakan dan *keadaan akhir* dalam permainan. Terserah anda sebagai pencipta permainan untuk mengatakan mengapa permainan ini berakhir. Berikut adalah beberapa sebab, jika kita menganggap bahawa kita sedang membincangkan permainan ruang angkasa yang anda buat sejauh ini:
- **Kapal musuh `N` telah hancur**: Sangat biasa jika anda membahagikan permainan ke dalam tahap yang berbeza yang anda perlukan untuk menghancurkan kapal `N` Musuh untuk menyelesaikan level
- **Kapal anda hancur**: Pasti ada permainan di mana anda kehilangan permainan jika kapal anda musnah. Pendekatan umum lain ialah anda mempunyai konsep kehidupan. Setiap kali kapal anda musnah, ia akan meragut nyawa. Setelah semua nyawa telah hilang maka anda akan kehilangan permainan.
- **Anda telah mengumpulkan mata `N`**: Syarat akhir yang umum ialah anda mengumpulkan mata. Cara anda memperoleh mata adalah bergantung kepada anda tetapi agak biasa untuk memberikan mata kepada pelbagai aktiviti seperti memusnahkan kapal musuh atau mungkin mengumpulkan barang-barang yang item *turun* ketika mereka musnah.
- **Lengkapkan tahap**: Ini mungkin melibatkan beberapa syarat seperti kapal musuh `X` musnah, mata `Y` dikumpulkan atau mungkin item tertentu telah dikumpulkan.
## Memulakan semula
Sekiranya orang menikmati permainan anda, mereka mungkin ingin memainkannya semula. Setelah permainan berakhir dengan alasan apa pun, anda harus menawarkan alternatif untuk dimulakan semula.
✅ Fikirkan sedikit dalam keadaan apa permainan anda berakhir, dan kemudian bagaimana anda diminta untuk memulakan semula
## Apakah yang anda perlu bina
Anda akan menambahkan peraturan ini ke permainan anda:
1. **Memenangi permainan**. Setelah semua kapal musuh musnah, anda memenangi permainan. Selain itu memaparkan semacam mesej kemenangan.
1. **Mulakan semula**. Setelah semua nyawa anda hilang atau permainan dimenangi, anda harus menawarkan cara untuk memulakan semula permainan. Ingat! Anda perlu memulakan semula permainan dan keadaan permainan sebelumnya harus dibersihkan.
## Langkah-langkah yang disyorkan
Cari fail yang telah dibuat untuk anda dalam sub folder `your-work`. Ia harus mengandungi yang berikut:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
Anda memulakan projek anda folder `your_work` dengan mengetik:
```bash
cd your-work
npm start
```
Perkara di atas akan memulakan Pelayan HTTP pada alamat `http://localhost:5000`. Buka penyemak imbas dan masukkan alamat itu. Permainan anda mestilah dalam keadaan boleh dimainkan.
> tip: untuk mengelakkan amaran dalam Visual Studio Code, edit fungsi `window.onload` untuk memanggil `gameLoopId` sebagaimana adanya (tanpa `let`), dan nyatakan gameLoopId di bahagian atas fail, secara bebas: `let gameLoopId;`
### Tambahkan kod
1. **Keadaan hujung trek**. Tambahkan kod yang memantau jumlah musuh, atau jika kapal pahlawan telah musnah dengan menambahkan dua fungsi ini:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **Tambahkan logik ke pengendali mesej**. Edit `eventEmitter` untuk menangani keadaan berikut:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **Tambahkan jenis mesej baru**. Tambahkan Mesej ini ke objek pemalar:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **Tambah kod restart** kod yang memulakan semula permainan dengan menekan butang yang dipilih.
1. **Dengarkan kekunci tekan `Enter`**. Edit eventListener tetingkap anda untuk mendengar akhbar ini:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **Tambah semula mesej**. Tambahkan Mesej ini ke pemalar Mesej anda:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **Melaksanakan peraturan permainan**. Laksanakan peraturan permainan berikut:
1. **Syarat kemenangan pemain**. Apabila semua kapal musuh hancur, tunjukkan mesej kemenangan.
1. Pertama, buat fungsi `displayMessage()`:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. Buat fungsi `endGame()`:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// atur kelewatan supaya kami yakin sebarang cat telah selesai
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **Mulakan semula logik**. Apabila semua nyawa hilang atau pemain memenangi permainan, tunjukkan bahawa permainan dapat dimulakan semula. Selain itu mulakan semula permainan apabila butang * restart * dipukul (anda boleh menentukan kunci apa yang harus dipetakan untuk dimulakan semula).
1. Buat fungsi `resetGame ()`:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. Tambahkan panggilan ke `eventEmitter` untuk menetapkan semula permainan di `initGame()`:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. Tambahkan fungsi `clear()` ke EventEmitter:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 Tahniah, Kapten! Permainan anda selesai! Bagus! 🚀 💥 👽
---
## 🚀 Cabaran
Tambahkan suara! Bolehkah anda menambahkan suara untuk meningkatkan permainan anda, mungkin ketika ada pancaran laser, atau wira mati atau menang? Lihatlah [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) untuk belajar bagaimana memainkan suara menggunakan JavaScript
## Kuiz Pasca Kuliah
[Kuiz Pasca Kuliah](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40)
## Mengkaji & Belajar Sendiri
Tugas anda adalah membuat permainan contoh baru, jadi terokai beberapa permainan menarik di luar sana untuk melihat jenis permainan yang mungkin anda bina.
## Tugasan
[Bina Contoh Permainan](assignment.ms.md)

View File

@@ -1,222 +0,0 @@
# 建立太空遊戲 Part 6結束與重來
## 課前測驗
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/39?loc=zh_tw)
有許多方式可以表達遊戲中的*結束狀態*。這都取決於你這位遊戲開發者,定義遊戲結束的理由。假設我們討論這款已經開發許久的太空遊戲,以下是遊戲結束的理由:
- **`N` 艘敵軍艦艇被擊毀**:如果你想將遊戲分成許多關卡,一種常見的方式是將每一關的破關門檻,定為擊毀 `N` 艘敵軍艦艇。
- **你的船艦已被擊毀**:一定有遊戲,只要你的船艦被擊毀一次時,便判定你輸了這場遊戲。另一種可行概念是加入生命值系統。每次你的船艦被擊毀時,會扣除一條生命。一但你損失了所有性命,你便輸了這場遊戲。
- **你已經取得 `N` 點分數**:另一種常見的結束狀態為分數門檻。取得分數的機制取決在你,常見的條件為摧毀敵艦、或是收集敵艦所*掉落*的道具。
- **完成關卡**:這或許會涉及到許多種狀態,好比說: `X` 艘艦艇已被擊毀、已取得 `Y` 點分數或是收集特定的道具。
## 重新遊戲
如果玩家很享受你的遊戲,他們會想再重新遊玩一次。一旦因任何原因結束遊戲時,你應該要提供重新遊戲的方法。
✅ 想想看,什麼條件下會結束一款遊戲,而它們又是如何提示你重新遊玩。
## 建立目標
你需要為你的遊戲新增這些規則:
1. **贏得遊戲**。 一旦所有敵軍艦艇被擊毀時,你便贏得這場遊戲。請額外地顯示勝利訊息。
1. **重新開始**。 一旦你損失了所有性命,或是贏得了勝利,你應該提供方法來重新遊戲。記住!你需要重新初始化你的遊戲,所有遊戲的歷史紀錄會被移除。
## 建議步驟
在你的 `your-work` 子資料夾中,確認檔案是否建立完成。它應該包括:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| life.png
-| index.html
-| app.js
-| package.json
```
開始 `your_work` 資料夾中的專案,輸入:
```bash
cd your-work
npm start
```
這會啟動 HTTP 伺服器並發布網址 `http://localhost:5000`。開啟瀏覽器並輸入該網址。你的遊戲應該能被遊玩。
> 要點: 要避免在 Visual Studio Code 裡出現警告訊息,編輯函式 `window.onload` 以 is而非 let 的方式呼叫 `gameLoopId`;並在檔案正上方獨立地宣告 gameLoopId `let gameLoopId;`。
### 加入程式碼
1. **追蹤結束狀態**。 新增程式碼來追蹤敵人的數量,利用下列函式判斷英雄艦艇是否被擊毀:
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
1. **加入訊息處理器**。 編輯 `eventEmitter` 以處理這些狀態:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // 遊戲失敗,提前結束
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
1. **加入新的訊息**。 新增這些訊息到 Messages 常數中:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
2. **加入重新開始的功能** 在按下特定按鈕後,程式會重新開始遊戲。
1. **監聽 `Enter` 按鈕之按壓**。 編輯視窗的 eventListener ,監聽按鍵的按壓:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
1. **加入重新遊戲的訊息**。 加入這段訊息到 Messages 常數中:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
1. **制定遊戲規則**。 編制下列的遊戲規則:
1. **玩家勝利條件**。 當所有敵軍艦艇被擊毀時,顯示勝利訊息。
1. 首先,建立函式 `displayMessage()`
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
1. 建立函式 `endGame()`
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// 設定延遲以確保所有圖像皆繪製完成
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
1. **重新遊戲的邏輯**。 當玩家損失所有的性命,或是贏下這場遊戲,顯示遊戲重來的提示。此外,在*重新遊玩*按鍵被按壓時,重新遊戲(你可以自己決定任一個鍵盤按鍵)。
1. 建立函式 `resetGame()`
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
1. 在 `initGame()` 內呼叫 `eventEmitter` 來重新設定遊戲:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
1. 在 EventEmitter 加入函式 `clear()`
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 恭喜你,艦長!你的遊戲已經完成了!幹得好! 🚀 💥 👽
---
## 🚀 挑戰
加入遊戲音效!你能加入音效來提升遊戲品質嗎?或許在雷射擊中敵人,或是在英雄死亡、勝利時發出音效。看看這套[沙盒](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play),了解如何使用 JavaScript 播放音效。
## 課後測驗
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/40?loc=zh_tw)
## 複習與自學
你的功課是建立一款新的小遊戲。去探索一些有趣的遊戲,決定你想建造的遊戲類型。
## 作業
[建立一款遊戲](assignment.zh-tw.md)

View File

@@ -1,19 +0,0 @@
# Crea un juego de muestra
## Instrucciones
Intente crear un juego pequeño en el que practique en diferentes condiciones finales. Varía entre obtener una cantidad de puntos, el héroe pierde todas las vidas o todos los monstruos son derrotados. Construye algo simple como un juego de aventuras basado en consola. Utilice el siguiente flujo de juego como inspiración:
''
Hero> Golpea con espada ancha: el orco recibe 3p de daño
Orc> Golpes con garrote: el héroe recibe 2p de daño
Hero> Patadas: el orco recibe 1p de daño
Game> Orc es derrotado - Hero recoge 2 monedas
Game> ****No más monstruos, has conquistado la fortaleza del mal****
''
## Rúbrica
| Criterios | Ejemplar | Adecuado | Necesita mejorar |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | se presenta el juego completo | se presenta parcialmente el juego | juego parcial contiene errores |

View File

@@ -1,19 +0,0 @@
# Développer un exemple de jeu
## Instructions
Essayez de créer un petit jeu où vous vous entraînez dans différentes conditions finales. Variez entre le fait d'obtenir un certain nombre de points, que le héros perde toutes les vies ou que tous les monstres soit vaincus. Construisez quelque chose de simple comme un jeu d'aventure sur console. Utilisez le flux de jeu ci-dessous comme source d'inspiration :
```
Héros> Frappe avec une épée large - l'orc subit 3p de dégâts
Orc> Frappe avec un club - le héros subit 2p de dégâts
Héros> Donne un coups de pied - l'orc subit 1p de dégâts
Jeu> L'orc est vaincu - le héros récupère 2 pièces
Jeu> ****Plus de monstres, vous avez conquis la forteresse maléfique****
```
## Rubrique
| Critères | Exemplaire | Adéquat | Besoin d'amélioration |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | le jeu complet est présenté | le jeu est partiellement présenté | le jeu partiel contient des bugs |

View File

@@ -1,19 +0,0 @@
# एक नमूना खेल बनाएँ
## अनुदेश
एक छोटा गेम बनाने की कोशिश करें जहां आप विभिन्न अंत स्थितियों पर अभ्यास करते हैं। कई अंकों के बीच भिन्न, नायक सभी जीवन खो देता है या सभी राक्षसों को हराया जाता है। कंसोल आधारित साहसिक गेम की तरह कुछ सरल बनाएं। प्रेरणा के रूप में नीचे के खेल प्रवाह का उपयोग करें:
```
Hero> Strikes with broadsword - orc takes 3p damage
Orc> Hits with club - hero takes 2p damage
Hero> Kicks - orc takes 1p damage
Game> Orc is defeated - Hero collects 2 coins
Game> ****No more monsters, you have conquered the evil fortress****
```
## शीर्ष
| मानदंड | उदाहरणात्मक | पर्याप्त | सुधार की जरूरत |
| ------ | -------------------- | ------------------------------------- | --------------------------- |
| | पूरा खेल प्रस्तुत है | खेल आंशिक रूप से प्रस्तुत किया गया है | आंशिक खेल में बग्स होते हैं |

View File

@@ -1,19 +0,0 @@
# Creare un Gioco di Esempio
## Istruzioni
Provare a costruire un piccolo gioco in cui esercitarsi in diverse condizioni di fine. Variare tra ottenere un numero di punti, l'eroe perde tutte le vite o tutti i mostri vengono sconfitti. Costruire qualcosa di semplice come un gioco di avventura basato su console. Usare il seguente flusso di gioco come ispirazione:
```
Eroe> Colpisce con lo spadone - l'orco riceve 3 punti di danno
Orco> Colpisce con la mazza - l'eroe riceve 2 punti di danno
Eroe> Calcia - l'orco takes 1 punto di danno
Gioco> l'orco è sconfitto - L'eroe guadagna 2 monete
Gioco> ****Non ci sono più mostri, la fortezza del male è stata conquistata****
```
## Rubrica
| Criteri | Ottimo | Adeguato | Necessita miglioramento |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | Viene presentato un gioco completo | Il gioco è presentato parzialmente | il gioco parziale contiene bug |

View File

@@ -1,19 +0,0 @@
# サンプルゲームを作る
## インストラクション
異なる終了条件で練習する小さなゲームを構築してみてください。ポイントの数を取得する間に変化し、主人公はすべてのライフを失うか、またはすべてのモンスターが退治されてしています。コンソールベースのアドベンチャーゲームのようなシンプルなものを作ってみてください。インスピレーションとして以下のゲームの流れを使用してください。
```
Hero> Strikes with broadsword - orc takes 3p damage
Orc> Hits with club - hero takes 2p damage
Hero> Kicks - orc takes 1p damage
Game> Orc is defeated - Hero collects 2 coins
Game> ****No more monsters, you have conquered the evil fortress****
```
## ルーブリック
| 基準 | 模範的な例 | 適切な | 改善が必要 |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | フルゲームが提示されています | 部分的に提示されたゲーム | 部分的なゲームにはバグが含まれています |

View File

@@ -1,19 +0,0 @@
# 샘플 게임 만들기
## 설명
게임이 끝나는 다양한 조건을 연습할 수 있는 작은 게임을 만들어 봅시다. 점수를 얼마나 얻어야 하는지에 따라 주인공이 목숨을 다 잃거나 모든 몬스터들이 패배합니다. 콘솔 기반 어드벤처 게임과 같은 간단한 것을 만들어봅시다. 아래 예시를 참고해서 영감을 얻어 보세요.
```
영웅> 양날검 휘두르기 - 오크는 3포인트의 데미지를 입는다.
오크> 클럽 휘두르기 - 영웅은 2포인트의 데미지를 입는다.
영웅> 발차기 - 오크는 1포인트의 데미지를 입는다.
게임> 오크가 지는 경우 - 영웅은 2코인을 얻는다.
게임> ****해치울 몬스터가 더 이상 없으면 당신은 악마의 요새를 정복한 것입니다****
```
## 평가 기준
기준 | 모범 답안 | 적당한 답안 | 개선이 필요한 답안
--- | --- | --- | ---
| 완성도 있는 게임을 만든 경우 | 부분적인 게임을 구현한 경우 | 게임에 일부 버그가 있는 경우

View File

@@ -1,19 +0,0 @@
# Bina Contoh Permainan
## Arahan
Cuba buat permainan kecil di mana anda berlatih dengan keadaan akhir yang berbeza. Berbeza antara mendapatkan sejumlah mata, wira kehilangan semua nyawa atau semua raksasa dikalahkan. Bina sesuatu yang mudah seperti permainan pengembaraan berasaskan konsol. Gunakan aliran permainan di bawah sebagai inspirasi:
```
Wira> Serangan dengan pedang lebar - orc mengalami kerosakan 3p
Orc> Hits with club - wira mengalami kerosakan 2p
Wira> Tendangan - orc mengalami kerosakan 1p
Permainan> Orc dikalahkan - Hero mengumpulkan 2 syiling
Permainan> ****Tidak ada lagi raksasa, anda telah menakluki kubu jahat****
```
## Rubrik
| Kriteria | Contoh | Mencukupi | Usaha Lagi |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | permainan penuh dipersembahkan| permainan dipersembahkan sebahagiannya | permainan separa mengandungi pepijat |

Some files were not shown because too many files have changed in this diff Show More