Add sounds, config loader and the base of score & lives display
This commit is contained in:
parent
829926dddf
commit
4d0c09fa1f
7 changed files with 112 additions and 47 deletions
|
@ -6,7 +6,6 @@
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="sources/css/style.css">
|
<link rel="stylesheet" type="text/css" href="sources/css/style.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 class="linear-wipe">KyFlo Snake</h1>
|
<h1 class="linear-wipe">KyFlo Snake</h1>
|
||||||
|
@ -22,6 +21,8 @@
|
||||||
<div id="game">
|
<div id="game">
|
||||||
<canvas id="canvas" class="invisible"></canvas>
|
<canvas id="canvas" class="invisible"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<p id="score"></p>
|
||||||
|
<p id="lives"></p>
|
||||||
|
|
||||||
<script type="module" src="sources/js/index.js"></script>
|
<script type="module" src="sources/js/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -6,44 +6,27 @@ export class Game {
|
||||||
/**
|
/**
|
||||||
* Generate a new game of Snake
|
* Generate a new game of Snake
|
||||||
* @param {HTMLCanvasElement} canvas
|
* @param {HTMLCanvasElement} canvas
|
||||||
* @param {[int, int]} size
|
|
||||||
* @param {directions} direction
|
|
||||||
* @param {int} snakeSpeed
|
|
||||||
* @param {int} appleSpeed
|
|
||||||
* @param {int} lives
|
|
||||||
*/
|
*/
|
||||||
constructor(canvas, {size = [15, 15], direction = directions.RIGHT, snakeSpeed = 500, appleSpeed = 5000, lives = 3} = {}) {
|
constructor(canvas) {
|
||||||
if (canvas && canvas.nodeName === "CANVAS")
|
if (canvas && canvas.nodeName === "CANVAS")
|
||||||
this.ctx = canvas.getContext("2d");
|
this.ctx = canvas.getContext("2d");
|
||||||
else
|
else
|
||||||
throw new InvalidGameOption("canvas");
|
throw new InvalidGameOption("canvas");
|
||||||
|
|
||||||
if (size && Array.isArray(size) && size.length === 2 && size.filter(s => typeof s === "number" && s > 0 && s % 1 === 0).length === size.length)
|
this.size = [15, 15];
|
||||||
this.size = size;
|
this.direction = directions.RIGHT;
|
||||||
else
|
this.snakeSpeed = 500;
|
||||||
throw new InvalidGameOption("size");
|
this.appleSpeed = 5000;
|
||||||
|
this.lives = 3;
|
||||||
if (direction && Object.values(directions).find(([x,y]) => direction[0] === x && direction[1] === y))
|
|
||||||
this.direction = this.startDirection = this.lastDirection = direction;
|
|
||||||
else
|
|
||||||
throw new InvalidGameOption("direction");
|
|
||||||
|
|
||||||
if (snakeSpeed && typeof snakeSpeed === "number" && snakeSpeed > 0 && snakeSpeed % 1 === 0)
|
|
||||||
this.snakeSpeed = snakeSpeed;
|
|
||||||
else
|
|
||||||
throw new InvalidGameOption("snakeSpeed");
|
|
||||||
|
|
||||||
if (appleSpeed && typeof appleSpeed === "number" && appleSpeed > 0 && appleSpeed % 1 === 0)
|
|
||||||
this.appleSpeed = appleSpeed;
|
|
||||||
else
|
|
||||||
throw new InvalidGameOption("appleSpeed");
|
|
||||||
|
|
||||||
this.world = [];
|
this.world = [];
|
||||||
this.apple = false;
|
this.apple = false;
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
this.lives = lives;
|
|
||||||
this.onStart = null;
|
this.onStart = null;
|
||||||
this.onStop = null;
|
this.onStop = null;
|
||||||
|
this.onEat = null;
|
||||||
|
this.onDie = null;
|
||||||
|
this.onGameOver = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +45,7 @@ export class Game {
|
||||||
* Init the canvas
|
* Init the canvas
|
||||||
*/
|
*/
|
||||||
initCanvas() {
|
initCanvas() {
|
||||||
this.ctx.canvas.ownerDocument.addEventListener("keyup", ev => {
|
this.ctx.canvas.ownerDocument.addEventListener("keydown", ev => {
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case "ArrowUp":
|
case "ArrowUp":
|
||||||
if (this.lastDirection !== directions.DOWN)
|
if (this.lastDirection !== directions.DOWN)
|
||||||
|
@ -146,10 +129,14 @@ export class Game {
|
||||||
if (err instanceof GameOver) {
|
if (err instanceof GameOver) {
|
||||||
this.lives--;
|
this.lives--;
|
||||||
if (this.lives <= 0) {
|
if (this.lives <= 0) {
|
||||||
alert(`Game over !\nYour score is: ${this.score}`);
|
if (this.onGameOver && typeof this.onGameOver === "function")
|
||||||
|
this.onGameOver(this.score);
|
||||||
this.stop();
|
this.stop();
|
||||||
} else
|
} else {
|
||||||
|
if (this.onDie && typeof this.onDie === "function")
|
||||||
|
this.onDie(this.lives);
|
||||||
this.restart();
|
this.restart();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
alert("An error occurred !");
|
alert("An error occurred !");
|
||||||
|
@ -170,6 +157,8 @@ export class Game {
|
||||||
this.snake.eat();
|
this.snake.eat();
|
||||||
this.apple = false;
|
this.apple = false;
|
||||||
this.score++;
|
this.score++;
|
||||||
|
if (this.onEat && typeof this.onEat === "function")
|
||||||
|
this.onEat(this.score);
|
||||||
}
|
}
|
||||||
t.type = tiles.SNAKE;
|
t.type = tiles.SNAKE;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +207,41 @@ export class Game {
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new game of Snake
|
||||||
|
* @param {[int, int]} size
|
||||||
|
* @param {directions} direction
|
||||||
|
* @param {int} snakeSpeed
|
||||||
|
* @param {int} appleSpeed
|
||||||
|
* @param {int} lives
|
||||||
|
*/
|
||||||
|
load({size = [15, 15], direction = directions.RIGHT, snakeSpeed = 500, appleSpeed = 5000, lives = 3} = {}) {
|
||||||
|
if (size && Array.isArray(size) && size.length === 2 && size.filter(s => typeof s === "number" && s > 0 && s % 1 === 0).length === size.length)
|
||||||
|
this.size = size;
|
||||||
|
else
|
||||||
|
throw new InvalidGameOption("size");
|
||||||
|
|
||||||
|
if (direction && Object.values(directions).find(([x,y]) => direction[0] === x && direction[1] === y))
|
||||||
|
this.direction = this.startDirection = this.lastDirection = direction;
|
||||||
|
else
|
||||||
|
throw new InvalidGameOption("direction");
|
||||||
|
|
||||||
|
if (snakeSpeed && typeof snakeSpeed === "number" && snakeSpeed > 0 && snakeSpeed % 1 === 0)
|
||||||
|
this.snakeSpeed = snakeSpeed;
|
||||||
|
else
|
||||||
|
throw new InvalidGameOption("snakeSpeed");
|
||||||
|
|
||||||
|
if (appleSpeed && typeof appleSpeed === "number" && appleSpeed > 0 && appleSpeed % 1 === 0)
|
||||||
|
this.appleSpeed = appleSpeed;
|
||||||
|
else
|
||||||
|
throw new InvalidGameOption("appleSpeed");
|
||||||
|
|
||||||
|
if (lives && typeof lives === "number" && lives > 0 && lives % 1 === 0)
|
||||||
|
this.lives = lives;
|
||||||
|
else
|
||||||
|
throw new InvalidGameOption("lives");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InvalidGameOption extends Error {
|
export class InvalidGameOption extends Error {
|
||||||
|
|
|
@ -4,7 +4,17 @@ const canvas = document.getElementById("canvas");
|
||||||
const menu = document.getElementById("menu");
|
const menu = document.getElementById("menu");
|
||||||
const menuLevel = document.querySelector(".menu-level");
|
const menuLevel = document.querySelector(".menu-level");
|
||||||
const gameZone = document.getElementById("game");
|
const gameZone = document.getElementById("game");
|
||||||
let game;
|
const score = document.getElementById("score");
|
||||||
|
const lives = document.getElementById("lives");
|
||||||
|
const audio = new Audio("sources/sound/main.mp3");
|
||||||
|
const startSound = new Audio("sources/sound/onStart.mp3");
|
||||||
|
const eatSound = new Audio("sources/sound/onEat.mp3");
|
||||||
|
const dieSound = new Audio("sources/sound/onDie.mp3");
|
||||||
|
const gameOverSound = new Audio("sources/sound/onGameOver.mp3");
|
||||||
|
const game = new Game(canvas);
|
||||||
|
|
||||||
|
|
||||||
|
startSound.volume = "0.2";
|
||||||
|
|
||||||
req.open("GET", "sources/levels.json");
|
req.open("GET", "sources/levels.json");
|
||||||
req.onerror = () => console.error("Fail to load XML request");
|
req.onerror = () => console.error("Fail to load XML request");
|
||||||
|
@ -16,10 +26,37 @@ req.onload = () => {
|
||||||
else
|
else
|
||||||
levels = null;
|
levels = null;
|
||||||
loadLevels(levels);
|
loadLevels(levels);
|
||||||
}
|
};
|
||||||
|
|
||||||
req.send();
|
req.send();
|
||||||
|
|
||||||
|
game.onStart = () => {
|
||||||
|
updateLives(game.lives);
|
||||||
|
updateScore(game.score);
|
||||||
|
menu.classList.add("invisible");
|
||||||
|
canvas.classList.remove("invisible");
|
||||||
|
};
|
||||||
|
|
||||||
|
game.onStop = () => {
|
||||||
|
menu.classList.remove("invisible");
|
||||||
|
canvas.classList.add("invisible");
|
||||||
|
};
|
||||||
|
|
||||||
|
game.onEat = (score) => {
|
||||||
|
eatSound.play();
|
||||||
|
updateScore(score);
|
||||||
|
};
|
||||||
|
|
||||||
|
game.onDie = lives => {
|
||||||
|
dieSound.play();
|
||||||
|
updateLives(lives);
|
||||||
|
};
|
||||||
|
|
||||||
|
game.onGameOver = (score) => {
|
||||||
|
gameOverSound.play();
|
||||||
|
alert(`Game over !\nYour score is: ${score}`);
|
||||||
|
};
|
||||||
|
|
||||||
function loadLevels(levels) {
|
function loadLevels(levels) {
|
||||||
if (!levels) {
|
if (!levels) {
|
||||||
menuLevel.innerHTML = "";
|
menuLevel.innerHTML = "";
|
||||||
|
@ -35,25 +72,28 @@ function loadLevels(levels) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadGame = data => {
|
function loadGame(data) {
|
||||||
game = new Game(canvas, data);
|
game.load(data);
|
||||||
|
|
||||||
game.onStart = () => {
|
|
||||||
menu.classList.add("invisible");
|
|
||||||
canvas.classList.remove("invisible");
|
|
||||||
};
|
|
||||||
|
|
||||||
game.onStop = () => {
|
|
||||||
menu.classList.remove("invisible");
|
|
||||||
canvas.classList.add("invisible");
|
|
||||||
};
|
|
||||||
|
|
||||||
game.start();
|
game.start();
|
||||||
|
startSound.play();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateScore(s) {
|
||||||
|
score.innerText = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLives(l) {
|
||||||
|
lives.innerText = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
M.AutoInit();
|
M.AutoInit();
|
||||||
canvas.width = gameZone.offsetWidth;
|
canvas.width = gameZone.clientWidth;
|
||||||
canvas.height = gameZone.offsetHeight;
|
canvas.height = gameZone.clientHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
audio.addEventListener("canplaythrough", () => {
|
||||||
|
audio.volume = 0.1;
|
||||||
|
audio.play();
|
||||||
});
|
});
|
||||||
|
|
BIN
sources/sound/onDie.mp3
Normal file
BIN
sources/sound/onDie.mp3
Normal file
Binary file not shown.
BIN
sources/sound/onEat.mp3
Normal file
BIN
sources/sound/onEat.mp3
Normal file
Binary file not shown.
BIN
sources/sound/onGameOver.mp3
Normal file
BIN
sources/sound/onGameOver.mp3
Normal file
Binary file not shown.
BIN
sources/sound/onStart.mp3
Normal file
BIN
sources/sound/onStart.mp3
Normal file
Binary file not shown.
Reference in a new issue