1
0
Fork 0

Add sounds, config loader and the base of score & lives display

This commit is contained in:
Ethanell 2021-03-23 10:27:15 +01:00
parent 829926dddf
commit 4d0c09fa1f
7 changed files with 112 additions and 47 deletions

View file

@ -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">
<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">
</head>
<body>
<h1 class="linear-wipe">KyFlo Snake</h1>
@ -22,6 +21,8 @@
<div id="game">
<canvas id="canvas" class="invisible"></canvas>
</div>
<p id="score"></p>
<p id="lives"></p>
<script type="module" src="sources/js/index.js"></script>
</body>

View file

@ -6,44 +6,27 @@ export class Game {
/**
* Generate a new game of Snake
* @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")
this.ctx = canvas.getContext("2d");
else
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 = 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");
this.size = [15, 15];
this.direction = directions.RIGHT;
this.snakeSpeed = 500;
this.appleSpeed = 5000;
this.lives = 3;
this.world = [];
this.apple = false;
this.score = 0;
this.lives = lives;
this.onStart = null;
this.onStop = null;
this.onEat = null;
this.onDie = null;
this.onGameOver = null;
}
/**
@ -62,7 +45,7 @@ export class Game {
* Init the canvas
*/
initCanvas() {
this.ctx.canvas.ownerDocument.addEventListener("keyup", ev => {
this.ctx.canvas.ownerDocument.addEventListener("keydown", ev => {
switch (ev.key) {
case "ArrowUp":
if (this.lastDirection !== directions.DOWN)
@ -146,10 +129,14 @@ export class Game {
if (err instanceof GameOver) {
this.lives--;
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();
} else
} else {
if (this.onDie && typeof this.onDie === "function")
this.onDie(this.lives);
this.restart();
}
} else {
console.error(err);
alert("An error occurred !");
@ -170,6 +157,8 @@ export class Game {
this.snake.eat();
this.apple = false;
this.score++;
if (this.onEat && typeof this.onEat === "function")
this.onEat(this.score);
}
t.type = tiles.SNAKE;
}
@ -218,6 +207,41 @@ export class Game {
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 {

View file

@ -4,7 +4,17 @@ const canvas = document.getElementById("canvas");
const menu = document.getElementById("menu");
const menuLevel = document.querySelector(".menu-level");
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.onerror = () => console.error("Fail to load XML request");
@ -16,10 +26,37 @@ req.onload = () => {
else
levels = null;
loadLevels(levels);
}
};
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) {
if (!levels) {
menuLevel.innerHTML = "";
@ -35,25 +72,28 @@ function loadLevels(levels) {
}
}
const loadGame = data => {
game = new Game(canvas, data);
game.onStart = () => {
menu.classList.add("invisible");
canvas.classList.remove("invisible");
};
game.onStop = () => {
menu.classList.remove("invisible");
canvas.classList.add("invisible");
};
function loadGame(data) {
game.load(data);
game.start();
startSound.play();
};
function updateScore(s) {
score.innerText = s;
}
function updateLives(l) {
lives.innerText = l;
}
document.addEventListener('DOMContentLoaded', function() {
M.AutoInit();
canvas.width = gameZone.offsetWidth;
canvas.height = gameZone.offsetHeight;
canvas.width = gameZone.clientWidth;
canvas.height = gameZone.clientHeight;
});
audio.addEventListener("canplaythrough", () => {
audio.volume = 0.1;
audio.play();
});

BIN
sources/sound/onDie.mp3 Normal file

Binary file not shown.

BIN
sources/sound/onEat.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
sources/sound/onStart.mp3 Normal file

Binary file not shown.