Football Game with JavaScript [Part 2]

Football Game with JavaScript [Part 2]

Part 2

You can check the GitHub repository by clicking here

You can play the last version of the game by clicking here

What do I want in Part 2? I want it a little more functional game.

  • Add more players to the field, add a team and rivals
  • Make the players collide with each other.
  • Make the players collide with the limits of the map
  • Make the players score

Adding more players

First I'll update the player and the ball class to this:

class Ball {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.xVel = 0;
    this.yVel = 0;
    this.decel = 0.1;
    this.size = 5;
  }
}

class Player {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.size = 12;
    this.xVel = 0;
    this.yVel = 0;
    this.accel = 2;
    this.decel = 2;
    this.maxSpeed = 2;
  }
}

So now it's time to add more players. I'll create a yellow team and a green team with five players on each. For now, the user will have control of just one of them in the yellow team.

I'm creating them with the class I defined before. I need to write new Player, and declare [x] and [y] positions, and this will give me just one. To create multiple players I declare an array named players. What I need to do is to replace the old var player with this new let players

let players = [
  //yellow
  new Player(canvas.width / 3, canvas.height / 2),
  new Player(30, 300),

  //green  new Player((canvas.width / 3) * 2, canvas.height - canvas.height / 2),
  new Player(1170, 300),
];

 let ball = new Ball(canvas.width / 2, canvas.height / 2); // The position of the ball is right in the center

So for example the first one: new Player(canvas.width / 3, canvas.height / 2),. This means player[0] is positioned canvas.height / 2 in the center of the field canvas.width / 3 more to the left than to the right. Then, the positions on the field are player[n].(x, y)

Since I changed the original name of the variable now I have to update the name in the functions, this change was necessary to avoid creating multiple variables for all the players.

So, updating the keyboardStatus function, now looks like this:

  if (up) {
    if (players[0].yVel > -players[0].maxSpeed) {
      players[0].yVel -= players[0].accel;
    } else {
      players[0].yVel = -players[0].maxSpeed;
    }
  } else {
    if (players[0].yVel < 0) {
      players[0].yVel += players[0].decel;
      if (players[0].yVel > 0) players[0].yVel = 0;
    }
  }

  if (down) {
    if (players[0].yVel < players[0].maxSpeed) {
      players[0].yVel += players[0].accel;
    } else {
      players[0].yVel = players[0].maxSpeed;
    }
  } else {
    if (players[0].yVel > 0) {
      players[0].yVel -= players[0].decel;
      if (players[0].yVel < 0) players[0].yVel = 0;
    }
  }
  if (left) {
    if (players[0].xVel > -players[0].maxSpeed) {
      players[0].xVel -= players[0].accel;
    } else {
      players[0].xVel = -players[0].maxSpeed;
    }
  } else {
    if (players[0].xVel < 0) {
      players[0].xVel += players[0].decel;
      if (players[0].xVel > 0) players[0].xVel = 0;
    }
  }
  if (right) {
    if (players[0].xVel < players[0].maxSpeed) {
      players[0].xVel += players[0].accel;
    } else {
      players[0].xVel = players[0].maxSpeed;
    }
  } else {
    if (players[0].xVel > 0) {
      players[0].xVel -= players[0].decel;
      if (players[0].xVel < 0) players[0].xVel = 0;
    }
  }

The function old functionplayerBounds is renamed to playersBounds and updated with a for loop that embraces all the players

function playersBounds() {
  for (let i = 0; i < 4; i++) {
    if (players[i].x + players[i].size > canvas.width) {
      players[i].x = canvas.width - players[i].size;
      players[i].xVel *= -0.2;
    }
    if (players[i].x - players[i].size < 0) {
      players[i].x = 0 + players[i].size;
      players[i].xVel *= -0.2;
    }
    if (players[i].y + players[i].size > canvas.height) {
      players[i].y = canvas.height - players[i].size;
      players[i].yVel *= -0.2;
    }
    if (players[i].y - players[i].size < 0) {
      players[i].y = 0 + players[i].size;
      players[i].yVel *= -0.2;
    }
  }
}

Now, updating the renderPlayer function, I'll change the name to renderPlayers. I'm adding all the players with this for loop. For the yellow team, I start creating the player 0 with i = 0; and I create (i < 2) yellow players. The same thing with the green team (let i = 2; i < 4; i++) I'm creating two green players. I'll have something like this.

function renderPlayers() {
  for (let i = 0; i < 2; i++) {
    context.beginPath();
    context.fillStyle = "yellow"; //player color
    context.arc(players[i].x, players[i].y, players[i].size, 0, Math.PI * 2);
    context.fill();
    context.closePath();
  }
  for (let i = 2; i < 4; i++) {
    context.beginPath();
    context.fillStyle = "green";
    context.arc(players[i].x, players[i].y, players[i].size, 0, Math.PI * 2);
    context.fill();
    context.closePath();
  }

  context.restore();
}

When scoring a goal reset the positions and show sum a point into a score mark.

The game would look like this:

Screenshot from 2020-12-23 06-21-26.png

Since I added more players, I need to update the function playerball_Collision I changed the name to players_Ball_Collision and updated it with a for loop

function players_Ball_Collision() {
  for (let i = 0; i < 4; i++) {
    var ball_distance =
      getDistance(players[i].x, players[i].y, ball.x, ball.y) -
      players[i].size -
      ball.size;
    if (ball_distance < 0) {
      collide(ball, players[i]);
    }
  }
}

Making the players collide with each other

I've had trouble trying to make the players collapse between each other cause I couldn't figure it out. I first realized that something I could do is to use the function getDistance with my player from the other players with something like this:

getDistance(players[0].x, players[0].y, players[1].x, players[1].y) -
        players[0].size -
        players[1].size;

Then, I thought doing this with every case would be too much code so I fixed this by adding three for loop in a new function called playersCollision.

I got a variable called player_distance. What I'm going to do is get the player_distance of every player. So when two players get close (player_distance < 0) enough they will collide.

The first part is dedicated to the collisions of a yellow with another yellow player, then I check on the collision of green players with another green player, and finally collision of a yellow player with a green player.

function playersCollision() {
  //Collision yellow player with another yellow player
  for (let k = 0; k < 2; k++) {
    for (let i = 1; i < 2; i++) {
      var player_distance =
        getDistance(players[k].x, players[k].y, players[i].x, players[i].y) -
        players[k].size -
        players[i].size;
      if (i === k) {
        i++;
      } else if (player_distance < 0) {
        collide(players[k], players[i]);
      }
    }
  }
  // Collision green player with another green player
  for (let k = 2; k < 4; k++) {
    for (let i = 3; i < 4; i++) {
      var player_distance =
        getDistance(players[k].x, players[k].y, players[i].x, players[i].y) -
        players[k].size -
        players[i].size;
      if (i === k) {
        i++;
      } else if (player_distance < 0) {
        collide(players[k], players[i]);
      }
    }
  }
  // Collision yellow player with a green player
  for (let i = 0; i < 2; i++) {
    for (let j = 2; j < 4; j++) {
      var player_distance =
        getDistance(players[i].x, players[i].y, players[j].x, players[j].y) -
        players[i].size -
        players[j].size;
      if (i === j) {
        i++;
      } else if (player_distance < 0) {
        collide(players[i], players[j]);
      }
    }
  }
}

Making the players collide with the limits of the maps

I'll update the old playerBounds function to playersBounds. This function will make the players collide with the limits of the map. What this function does, is to check if the player is reaching a limit in the field, for example.

players[i].x + players[i].size > canvas.width let's suppose players[i].x = 486

then 486 + 15, that is 501, if 501 is bigger than 500

{then, the player just reached a bounce and will collapse}

Now, I added a for loop to make use of this function for every player.

function playersBounds() {
  for (let i = 0; i < 4; i++) {
    if (players[i].x + players[i].size > canvas.width) {
      players[i].x = canvas.width - players[i].size;
      players[i].xVel *= -0.2;
    }
    if (players[i].x - players[i].size < 0) {
      players[i].x = 0 + players[i].size;
      players[i].xVel *= -0.2;
    }
    if (players[i].y + players[i].size > canvas.height) {
      players[i].y = canvas.height - players[i].size;
      players[i].yVel *= -0.2;
    }
    if (players[i].y - players[i].size < 0) {
      players[i].y = 0 + players[i].size;
      players[i].yVel *= -0.2;
    }
  }
}

Make the players score

Now, the user can score goals for the team, how do I do for the rival to score goals? Exactly as I've done for my player to score goals. As a global variable I'll the score when starting the game:

//Initial score
let yellowteam = 0;
let greenteam = 0;

Then, I will update the start function, using jQuery, to update the score counter of the teams:

$(document).ready(
    function()
    {
      document.getElementById("greenteam_score").innerHTML = "Green Team Score: " + greenteam;
      document.getElementById("yellowteam_score").innerHTML = "Yellow Team Score: " + yellowteam;
    }
);

Now that I'm using jQuery I'm going to import it in the HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Then, I'll delete the var score and to leave the user see the update of the score, I replaced that little <div id="score"></div> in the HTML file, for this two elements:

<p id="yellowteam_score" class="text-1xl"></p>
<p id="greenteam_score" class="text-1xl" ></p>

How does the game know that a player scored a goal? Checking with the function ballBounds. If the ball touches my goal gate, that would be a goal for the green team. The function would look like this now:

It'll be useful to declare these two global variables: upon_theGoal and down_theGoal tells exactly the positions of the goal gates in the middle of the field counting with the height of the canvas. These variables will also help me in the future to improve the renderBackground function

var upon_theGoal = canvas.height / 2 - 73; //closest to the top of the map
var down_theGoal = canvas.height / 2 + 73; //closest to the lowest point of the map
function ballBounds() {
  // for the x (axis) parts of the field
  //if it's the goal gate
  // this means a point for the yellow team
  if (ball.x + ball.size > canvas.width) {
    if (ball.y > upon_theGoal && ball.y < down_theGoal) {
      yellowteam++;
      reset();
      console.log("goal for the blueteam");
      return;
    }
    // if it's not a goal gate
    ball.x = canvas.width - ball.size;
    ball.xVel *= -1.5;
  }
  //if it's the goal gate
  // this means a point for the green team
  if (ball.x - ball.size < 0) {
    if (ball.y > upon_theGoal && ball.y < down_theGoal) {
      greenteam++;
      reset();
      console.log("goal for the redteam");
      return;
    }
    ball.x = 0 + ball.size;
    ball.xVel *= -1.5;
  }

  // for the y (axis) parts of the field
  if (ball.y + ball.size > canvas.height) {
    ball.y = canvas.height - ball.size;
    ball.yVel *= -1.5;
  }

  if (ball.y - ball.size < 0) {
    ball.y = 0 + ball.size;
    ball.yVel *= -1.5;
  }
};

Before anything else, I need to add this little function called movePlayers. This was my old function movePlayer, now, with a for loop, I'll make it effective in all the players. This will help me to make the players move all around the field. By making a sum of the player.x and the players xVel, the same with the players y and the players.yVel.

function movePlayers() {
  for (let i = 0; i < 4; i++) {
    players[i].x += players[i].xVel;
    players[i].y += players[i].yVel;
  }
}

The reset function is updated to:

function reset() {
  players = [
    //yellow
    new Player(canvas.width / 3, canvas.height / 2),
    new Player(30, 300),

    //green
    new Player((canvas.width / 3) * 2, canvas.height - canvas.height / 2),
    new Player(1170, 300),
  ];

  ball = new Ball(canvas.width / 2, canvas.height / 2);
  up = false;
  down = false;
  left = false;
  right = false;
}

So, after all the changes, this is how the start() function looks:

function start() {

  $(document).ready(
    function()
    {
      document.getElementById("greenteam_score").innerHTML = "Green Team Score: " + greenteam;
      document.getElementById("yellowteam_score").innerHTML = "Yellow Team Score: " + yellowteam;
    }
);

  clear();

  //Render
  renderBackground();
  renderPlayers();
  renderBall();

  //Moves
  movePlayers();
  moveBall();
  keyboardMoves();

  //Bounce
  playersCollision();
  playersBounds();
  ballBounds();
  players_Ball_Collision();

  requestAnimationFrame(start);
}

Here is a playable result of part 2:

So now everything is working, I want to improve some functions and I want the game to look a little better, the objectives for the next part are:

  • Improve the draw of the field
  • Giving the players ability to shoot and run
  • Make the goalkeepers cover their position.
  • Add a home goal gate and the possibility for the rival to go after the ball, and score a goal.

Here is a link for the third part

End of the second part.