mirror of
https://github.com/microsoft/Web-Dev-For-Beginners.git
synced 2025-08-31 18:32:16 +02:00
folder names
This commit is contained in:
18
6-space-game/3-moving-elements-around/.github/post-lecture-quiz.md
vendored
Normal file
18
6-space-game/3-moving-elements-around/.github/post-lecture-quiz.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
*Complete this quiz after the lesson by checking one answer per question.*
|
||||
|
||||
1. You always need to redraw the screen
|
||||
|
||||
- [ ] true
|
||||
- [ ] false
|
||||
|
||||
2. What is a game loop?
|
||||
|
||||
- [ ] A function that ensures the game can be restarted
|
||||
- [ ] A function that decided how fast the game should run
|
||||
- [ ] A function that is invoked at regular intervals and draws what the user should see
|
||||
|
||||
3. A good case for redrawing the screen is
|
||||
|
||||
- [ ] A user interaction happened
|
||||
- [ ] Something has moved
|
||||
- [ ] Time has passed
|
19
6-space-game/3-moving-elements-around/.github/pre-lecture-quiz.md
vendored
Normal file
19
6-space-game/3-moving-elements-around/.github/pre-lecture-quiz.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
*A warm-up quiz about game development*
|
||||
|
||||
Complete this quiz in class
|
||||
|
||||
1. Any object on the screen can receive keyboard events
|
||||
|
||||
- [ ] true
|
||||
- [ ] false
|
||||
|
||||
2. You can use the same method to listen to key events and mouse events
|
||||
|
||||
- [ ] true
|
||||
- [ ] false
|
||||
|
||||
3. To make things happen at a regular interval, you use what function?
|
||||
|
||||
- [ ] setInterval()
|
||||
- [ ] setTimeout()
|
||||
- [ ] sleep()
|
21
6-space-game/3-moving-elements-around/LICENSE
Normal file
21
6-space-game/3-moving-elements-around/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 WebDev-For-Beginners
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
384
6-space-game/3-moving-elements-around/README.md
Normal file
384
6-space-game/3-moving-elements-around/README.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Build a Space Game Part III: Adding Motion
|
||||
|
||||
## [Pre-lecture quiz](.github/pre-lecture-quiz.md)
|
||||
|
||||
Games aren't much fun until you have aliens running around on screen! In this game, we will make use of two types of movements:
|
||||
|
||||
- **Keyboard/Mouse movement**: when the user interacts with the keyboard or mouse to move an object on the screen.
|
||||
- **Game induced movement**: when the game moves an object with a certain time interval.
|
||||
|
||||
So how do we move things on a screen? It's all about cartesian coordinates: we change the location (x,y) of the object and then redraw the screen.
|
||||
|
||||
Typically you need the following steps to accomplish *movement* on a screen:
|
||||
|
||||
1. **Set a new location** for an object; this is needed to perceive the object as having moved.
|
||||
2. **Clear the screen**, the screen needs to be cleared in between draws. We can clear it by drawing a rectangle that we fill with a background color.
|
||||
3. **Redraw object** at new location. By doing this we finally accomplish moving the object from one location to the other.
|
||||
|
||||
Here's what it can look like in code:
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
✅ Can you think of a reason why redrawing your hero many frames per second might accrue performance costs? Read about [alternatives to this pattern](https://www.html5rocks.com/en/tutorials/canvas/performance/).
|
||||
|
||||
## Handle keyboard events
|
||||
|
||||
You handle events by attaching specific events to code. Keyboard events are triggered on the whole window whereas mouse events like a `click` can be connected to clicking a specific element. We will use keyboard events throughout this project.
|
||||
|
||||
To handle an event you need to use the window's `addEventListener()` method and provide it with two input parameters. The first parameter is the name of the event, for example `keyup`. The second parameter is the function that should be invoked as a result of the event taking place.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```javascript
|
||||
window.addEventListener('keyup', (evt) => {
|
||||
// `evt.key` = string representation of the key
|
||||
if (evt.key === 'ArrowUp') {
|
||||
// do something
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For key events there are two properties on the event you can use to see what key was pressed:
|
||||
|
||||
- `key`, this is a string representation of the pressed key, for example `ArrowUp`
|
||||
- `keyCode`, this is a number representation, for example `37`, corresponds to `ArrowLeft`.
|
||||
|
||||
✅ Key event manipulation is useful outside of game development. What other uses can you think of for this technique?
|
||||
|
||||
### Special keys: a caveat
|
||||
|
||||
There are some *special* keys that affect the window. That means that if you are listening to a `keyup` event and you use these special keys to move your hero it will also perform horizontal scrolling. For that reason you might want to *shut-off* this built-in browser behavior as you build out your game. You need code like this:
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
The above code will ensure that arrow-keys and the space key have their *default* behavior shut off. The *shut-off* mechanism happens when we call `e.preventDefault()`.
|
||||
|
||||
## Game induced movement
|
||||
|
||||
We can make things move by themselves by using timers such as the `setTimeout()` or `setInterval()` function that update the location of the object on each tick, or time interval. Here's what that can look like:
|
||||
|
||||
```javascript
|
||||
let id = setInterval(() => {
|
||||
//move the enemy on the y axis
|
||||
enemy.y += 10;
|
||||
})
|
||||
```
|
||||
|
||||
## The game loop
|
||||
|
||||
The game loop is a concept that is essentially a function that is invoked at regular intervals. It's called the game loop as everything that should be visible to the user is drawn into the loop. The game loop makes use of all the game objects that are part of the game, drawing all of them unless for some reason shouldn't be part of the game any more. For example if an object is an enemy that was hit by a laser and blows up, it's no longer part of the current game loop (you'll learn more on this in subsequent lessons).
|
||||
|
||||
Here's what a game loop can typically look like, expressed in 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);
|
||||
```
|
||||
|
||||
The above loop is invoked every `200` milliseconds to redraw the canvas. You have the ability to choose the best interval that makes sense for your game.
|
||||
|
||||
## Continuing the Space Game
|
||||
|
||||
You will take the existing code and extend it. Either start with the code that you completed during part I or use the code in [Part II- starter](your-work).
|
||||
|
||||
- **Moving the hero**: you will add code to ensure you can move the hero using the arrow keys.
|
||||
- **Move enemies**: you will also need to add code to ensure the enemies move from top to bottom at a given rate.
|
||||
|
||||
## Recommended steps
|
||||
|
||||
Locate the files that have been created for you in the `your-work` sub folder. It should contain the following:
|
||||
|
||||
```bash
|
||||
-| assets
|
||||
-| enemyShip.png
|
||||
-| player.png
|
||||
-| index.html
|
||||
-| app.js
|
||||
-| package.json
|
||||
```
|
||||
|
||||
You start your project the `your_work` folder by typing:
|
||||
|
||||
```bash
|
||||
cd your-work
|
||||
npm start
|
||||
```
|
||||
|
||||
The above will start a HTTP Server on address `http://localhost:5000`. Open up a browser and input that address, right now it should render the hero and all the enemies; nothing is moving - yet!
|
||||
|
||||
### Add code
|
||||
|
||||
1. **Add dedicated objects** for `hero` and `enemy` and `game object`, they should have `x` and `y` properties. (Remember the portion on [Inheritance or composition](../README.md) ).
|
||||
|
||||
*HINT* `game object` should be the one with `x` and `y` and the ability to draw itself to a canvas.
|
||||
|
||||
>tip: start by adding a new GameObject class with its constructor delineated as below, and then draw it to the 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, extend this GameObject to create the Hero and Enemy.
|
||||
|
||||
```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. **Add key-event handlers** to handle key navigation (move hero up/down left/right)
|
||||
|
||||
*REMEMBER* it's a cartesian system, top-left is `0,0`. Also remember to add code to stop *default behavior*
|
||||
|
||||
>tip: create your onKeyDown function and attach it to the window:
|
||||
|
||||
```javascript
|
||||
let onKeyDown = function (e) {
|
||||
console.log(e.keyCode);
|
||||
...add the code from the lesson above to stop default behavior
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
```
|
||||
|
||||
Check your browser console at this point, and watch the keystrokes being logged.
|
||||
|
||||
3. **Implement** the [Pub sub pattern](../README.md), this will keep your code clean as you follow the remaining parts.
|
||||
|
||||
To do this last part, you can:
|
||||
|
||||
1. **Add an event listener** on the 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. **Create an EventEmitter class** to publish and subscribe to 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. **Add constants** and set up the 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. **Initialize the game**
|
||||
|
||||
```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. **Setup the game loop**
|
||||
|
||||
Refactor the window.onload function to initialize the game and set up a game loop on a good interval. You'll also add a laser beam:
|
||||
|
||||
```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. **Add code** to move enemies at a certain interval
|
||||
|
||||
Refactor the `createEnemies()` function to create the enemies and push them into the new gameObjects class:
|
||||
|
||||
```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);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and add a `createHero()` function to do a similar process for the hero.
|
||||
|
||||
```javascript
|
||||
function createHero() {
|
||||
hero = new Hero(
|
||||
canvas.width / 2 - 45,
|
||||
canvas.height - canvas.height / 4
|
||||
);
|
||||
hero.img = heroImg;
|
||||
gameObjects.push(hero);
|
||||
}
|
||||
```
|
||||
|
||||
and finally, add a `drawGameObjects()` function to start the drawing:
|
||||
|
||||
```javascript
|
||||
function drawGameObjects(ctx) {
|
||||
gameObjects.forEach(go => go.draw(ctx));
|
||||
}
|
||||
```
|
||||
|
||||
Your enemies should start advancing on your hero spaceship!
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
As you can see, your code can turn into 'spaghetti code' when you start adding functions and variables and classes. How can you better organize your code so that it is more readable? Sketch out a system to organize your code, even if it still resides in one file.
|
||||
|
||||
## [Post-lecture quiz](.github/post-lecture-quiz.md)
|
||||
|
||||
## Review & Self Study
|
||||
|
||||
While we're writing our game without using frameworks, there are many JavaScript-based canvas frameworks for game development. Take some time to do some [reading about these](https://github.com/collections/javascript-game-engines).
|
||||
|
||||
## Assignment
|
||||
|
||||
[Comment your code](assignment.md)
|
11
6-space-game/3-moving-elements-around/assignment.md
Normal file
11
6-space-game/3-moving-elements-around/assignment.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Comment Your Code
|
||||
|
||||
## Instructions
|
||||
|
||||
Go over your current /app.js file in your game folder, and find ways to comment it and tidy it up. It's very easy for code to get out of control, and now's a good chance to add comments to ensure that you have readable code so that you can use it later.
|
||||
|
||||
## Rubric
|
||||
|
||||
| Criteria | Exemplary | Adequate | Needs Improvement |
|
||||
| -------- | ------------------------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
|
||||
| | `app.js` code is fully commented and organized into logical blocks | `app.js` code is adequately commented | `app.js` code is somewhat disorganized and lacks good comments |
|
180
6-space-game/3-moving-elements-around/solution/app.js
Normal file
180
6-space-game/3-moving-elements-around/solution/app.js
Normal file
@@ -0,0 +1,180 @@
|
||||
// @ts-check
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function loadTexture(path) {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = path;
|
||||
img.onload = () => {
|
||||
resolve(img);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// EVENTS
|
||||
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);
|
||||
|
||||
// TODO make message driven
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createHero() {
|
||||
hero = new Hero(canvas.width / 2 - 45, canvas.height - canvas.height / 4);
|
||||
hero.img = heroImg;
|
||||
gameObjects.push(hero);
|
||||
}
|
||||
|
||||
function drawGameObjects(ctx) {
|
||||
gameObjects.forEach((go) => go.draw(ctx));
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
BIN
6-space-game/3-moving-elements-around/solution/assets/enemyShip.png
Executable file
BIN
6-space-game/3-moving-elements-around/solution/assets/enemyShip.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
6-space-game/3-moving-elements-around/solution/assets/laserRed.png
Executable file
BIN
6-space-game/3-moving-elements-around/solution/assets/laserRed.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
6-space-game/3-moving-elements-around/solution/assets/player.png
Executable file
BIN
6-space-game/3-moving-elements-around/solution/assets/player.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<canvas id ="canvas" width="1024" height="768"></canvas>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
13
6-space-game/3-moving-elements-around/solution/package.json
Normal file
13
6-space-game/3-moving-elements-around/solution/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "solution",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "npx http-server -c-1 -p 5000",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
385
6-space-game/3-moving-elements-around/translations/README.es.md
Normal file
385
6-space-game/3-moving-elements-around/translations/README.es.md
Normal file
@@ -0,0 +1,385 @@
|
||||
# Build a Space Game Part III: Adding Motion
|
||||
|
||||

|
||||
|
||||
## [Pre-lecture prueba](.github/pre-lecture-quiz.md)
|
||||
|
||||
¡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);
|
||||
```
|
||||
|
||||
TEl 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](.github/post-lecture-quiz.md)
|
||||
|
||||
## 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.md)
|
@@ -0,0 +1,11 @@
|
||||
# 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 |
|
38
6-space-game/3-moving-elements-around/your-work/app.js
Normal file
38
6-space-game/3-moving-elements-around/your-work/app.js
Normal file
@@ -0,0 +1,38 @@
|
||||
function loadTexture(path) {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = path;
|
||||
img.onload = () => {
|
||||
resolve(img);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createEnemies(ctx, canvas, enemyImg) {
|
||||
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) {
|
||||
ctx.drawImage(enemyImg, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = async () => {
|
||||
canvas = document.getElementById("canvas");
|
||||
ctx = canvas.getContext("2d");
|
||||
const heroImg = await loadTexture("assets/player.png");
|
||||
const enemyImg = await loadTexture("assets/enemyShip.png");
|
||||
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(
|
||||
heroImg,
|
||||
canvas.width / 2 - 45,
|
||||
canvas.height - canvas.height / 4
|
||||
);
|
||||
createEnemies(ctx, canvas, enemyImg);
|
||||
};
|
BIN
6-space-game/3-moving-elements-around/your-work/assets/enemyShip.png
Executable file
BIN
6-space-game/3-moving-elements-around/your-work/assets/enemyShip.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
6-space-game/3-moving-elements-around/your-work/assets/laserRed.png
Executable file
BIN
6-space-game/3-moving-elements-around/your-work/assets/laserRed.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
6-space-game/3-moving-elements-around/your-work/assets/player.png
Executable file
BIN
6-space-game/3-moving-elements-around/your-work/assets/player.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<canvas id ="canvas" width="1024" height="768"></canvas>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
13
6-space-game/3-moving-elements-around/your-work/package.json
Normal file
13
6-space-game/3-moving-elements-around/your-work/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "solution",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "npx http-server -c-1 -p 5000",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
Reference in New Issue
Block a user