onlyforbopi
9/24/2018 - 10:33 AM

JS.Interact.Animate.Game.HitTheBall.v1

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;
  }
}

JS.Interact.Animate.Game.HitTheBall.v1

A Pen by Pan Doul on CodePen.

License.

<!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>