JS.Interact.Animate.Game.HitTheBall.v1
body{
background: linear-gradient(35deg, #CCFFFF, #FFCCCC);
}
#myCanvas{
border: 1px solid black;
float: left;
}
#controls{
float: left;
}
let canvas, ctx, w, h;
let mousePos;
let balls = [];
let initialNumberOfBalls;
let number;
let globalSpeedMutiplier = 0.035;
let score = 0;
let level = 0;
let colorToEat = 'red';
let wrongBallsEaten = goodBallsEaten = 0;
let numberOfGoodBalls;
let ballEatenSound;
let player = {
x:10,
y:10,
width:20,
height:20,
color:'red',
move(x, y){
this.x = x;
this.y = y;
},
draw(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.fillStyle = this.color;
ctx.fillRect(0, 0, this.width, this.height);
ctx.restore();
}
};
window.onload = function init(){
playBackgroundMusic();
canvas = document.querySelector("#myCanvas");
w = canvas.width;
h = canvas.height;
ctx = canvas.getContext('2d');
startGame(10);
canvas.addEventListener('mousemove', mouseMoved);
//nextLevel();
ballEatenSound = new Howl({
urls: ['https://mainline.i3s.unice.fr/mooc/SkywardBound/assets/sounds/plop.mp3'],
onload: function() {
nextLevel();
}
});
};
function playBackgroundMusic() {
let audioPlayer = document.querySelector("#audioPlayer");
audioPlayer.play();
}
function pausebackgroundMusic() {
let audioPlayer = document.querySelector("#audioPlayer");
audioPlayer.pause();
}
function startGame(nb){
do{
balls = createBalls(nb);
initialNumberOfBalls = nb;
numberOfGoodBalls = countNumberOfGoodBalls(balls, colorToEat);
}while(numberOfGoodBalls === 0);
wrongBallsEaten = goodBallsEaten = 0;
}
function nextLevel(){
level += 1;
cancelAnimationFrame(mainLoop);
nextLevelPage();
wrongBallsEaten = 0;
globalSpeedMutiplier += 0.075;
startGame(10 + level);
setTimeout(mainLoop, 3300);
}
function nextLevelPage(){
ctx.clearRect(0, 0, w, h);
ctx.save();
ctx.font = "50px Arial";
ctx.fillText("Level " + level, 110, 200);
ctx.fillStyle = "black";
ctx.font = "20px Arial";
ctx.fillText("Eat all of the " + colorToEat + " balls", 100, 250);
ctx.fillText("Do NOT eat more than 3 other balls", 50, 280);
ctx.restore();
}
function gameOver(){
gameOverPage();
balls = [];
window.addEventListener("keydown", playAgain, false);
}
function gameOverPage(){
ctx.clearRect(0, 0, w, h);
ctx.save();
ctx.fillStyle = "black";
ctx.font = "50px Arial";
ctx.fillText("Game Over!", 70, 170);
ctx.font = "20px Arial";
ctx.fillText("You made it to Level: " + level, 110, 205);
ctx.fillText("Your final score is: " + score, 110, 240);
ctx.fillText("-- Hit Space Bar to Play Again --", 60, 275);
ctx.restore();
}
function playAgain(e) {
if (e.keyCode == 32) {
globalSpeedMutiplier = 0.035;
wrongBallsEaten = 0;
startGame(10);
level = 1;
canvas.style.border="3px grey solid";
requestAnimationFrame(mainLoop);
removeEventListener("keydown", playAgain, false);
}
}
function countNumberOfGoodBalls(balls, colorToEat){
let nb = 0;
balls.forEach(function(b){
if(b.color === colorToEat){
nb++;
}
});
return nb;
}
function changeNbBalls(nb){
startGame(nb);
}
function changeColorToEat(color){
colorToEat = color;
}
function changePlayerColor(color){
player.color = color;
}
function changeBallSpeed(coef){
globalSpeedMutiplier = coef;
}
function mouseMoved(evt){
mousePos = getMousePos(canvas, evt);
}
function getMousePos(canvas, evt){
let rect = canvas.getBoundingClientRect();
return{
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function mainLoop(){
ctx.clearRect(0, 0, w, h);
player.draw(ctx);
drawAllBalls(balls);
info(balls);
moveAllBalls(balls);
if(mousePos != undefined){
player.move(mousePos.x, mousePos.y);
}
if(goodBallsEaten === numberOfGoodBalls){
score = (initialNumberOfBalls - wrongBallsEaten)*10;
nextLevel();
} else if(wrongBallsEaten === 3){
gameOver();
} else{
requestAnimationFrame(mainLoop);
}
}
function circRectsOverlap(x0, y0, w0, h0, cx, cy, r){
let testX = cx;
let testY = cy;
if(testX < x0) testX = x0;
if(testX > (x0+w0)) testX = (x0+w0);
if(testY < y0) testY = y0;
if(testY > (y0+h0)) testY = (y0+h0);
return(((cx-testX)*(cx-testX) + (cy-testY)*(cy-testY)) < r*r);
}
class Ball{
constructor(x, y, radius, color, speedX, speedY){
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.speedX = speedX;
this.speedY = speedY;
}
draw(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.radius, 0, 2*Math.PI);
ctx.fill();
ctx.restore();
}
move(){
this.x += this.speedX;
this.y += this.speedY;
}
}
function createBalls(n){
var ballArray = [];
for(var i=0; i<n; i++){
let x = w/2;
let y = h/2;
let radius = 5 + 30*Math.random();
let speedX = -5 + 10*Math.random();
let speedY = -5 + 10*Math.random();
let color = getARandomColor();
let b = new Ball(x, y, radius, color, speedX, speedY);
ballArray.push(b);
}
return ballArray;
}
function getARandomColor(){
let colors = ['red', 'blue', 'cyan', 'purple', 'pink', 'green', 'yellow'];
let colorIndex = Math.round((colors.length - 1)*Math.random());
let c = colors[colorIndex];
return c;
}
function info(balls){
ctx.save();
ctx.font = "20px Arial";
ctx.fillText("Balls still alive: " + balls.length, 210, 30);
ctx.fillText("Good balls eaten: " + goodBallsEaten, 210, 50);
ctx.fillText("Wrong balls eaten: " + wrongBallsEaten, 210, 70);
ctx.fillText("Level: " + level, 10, 30);
ctx.restore();
}
function drawAllBalls(ballArray){
ballArray.forEach(function(b){
b.draw(ctx);
});
}
function moveAllBalls(ballArray){
balls.forEach(function(b, index){
b.move();
testCollisionBallWithWalls(b);
testCollisionWithPlayer(b, index);
});
}
function testCollisionWithPlayer(b, index){
if(circRectsOverlap(player.x, player.y, player.width, player.height, b.x, b.y, b.radius)){
ballEatenSound.play();
if(b.color === colorToEat){
goodBallsEaten += 1;
} else{
wrongBallsEaten += 1;
}
balls.splice(index, 1);
}
}
function testCollisionBallWithWalls(b){
if((b.x + b.radius) > w){
b.speedX = -b.speedX;
b.x = w - b.radius;
}else if((b.x - b.radius) < 0){
b.speedX = -b.speedX;
b.x = b.radius;
}
if((b.y + b.radius) > h){
b.speedY = -b.speedY;
b.y = h - b.radius;
}else if((b.y - b.radius) < 0){
b.speedY = -b.speedY;
b.y = b.radius;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Hit the ball</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/1.1.28/howler.min.js">
</script>
</head>
<body>
<audio src = "https://mainline.i3s.unice.fr/mooc/SkywardBound/assets/sounds/humbug.mp3" id="audioPlayer">
</audio>
<div id="controls">
<label for="nbBalls">Number of balls: </label>
<input type="number" min=1 max=30 value=10 id="nbBalls" oninput="changeNbBalls(this.value);">
<p></p>
<label for="colorChooser">Player color: </label>
<input type="color" value='#FF0000' oninput="changePlayerColor(this.value);" id="colorChooser">
<p></p>
<label for="selectColorOffBallToEat">Color of ball to eat: </label>
<select onchange="changeColorToEat(this.value);" id="selectColorOffBallToEat">
<option value='red'>Red</option>
<option value='blue'>Blue</option>
<option value='green'>Green</option>
</select>
<p></p>
<label for="ballSpeed">Change ball speed: </label>
- <input type="range" value='1' min=0.1 max=3 step=0.1 oninput="changeBallSpeed(this.value);" id="ballSpeed"> +
<p></p>
</div>
<canvas id="myCanvas" height="400" width="400"></canvas>
</body>
</html>