Football Game with JavaScript [Part 4]

Football Game with JavaScript [Part 4]

Part 4

You can check the GitHub repository by clicking here

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

This is the last part of this series of articles where I explained this little project I've been working on.

What do I want in Part 4? Now the main idea is done and the game is working, as a final step I want the game the look better, the objectives for the next part are:

  • I want a Main Menu with the following options:
    • Quick Game Mode.
    • Settings.
    • About. (credits)
  • Sound (Crowd, and a piece of music, for the Main Menu)
  • I want to counter that will set up the full time of the match.
  • To draw the ball.
  • To draw a player for the local team and the rival team.

Adding the Main Menu

Let's start with the Main Menu. I want it to look like this:

Menu.png

To do the main menu I made use of the style display property, I'd make some elements disappear based on their ID, so when a button is clicked, the file will just show the elements I want.

I'll use a little of tailwind and some CSS elements for the style of the menu.

To import tailwind I need to add this to the head of the HTML file.

<link
      href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
      rel="stylesheet"
/>

So, the first thing I'm going to do is to create a heading, here is where the Football Game title and the Main Menu subtitle will be. I need to put them there, but when I start the game, they shall disappear.

<body class="bg-green-700 text-center text-white font-sans">
    <div>
      <div id="theHead">
        <h1 class="mt-16 text-5xl">Football Game</h1>
        <h3 class="mt-8">Main Menu</h3>
        <br />
      </div>

      <div class="from-orange-400 to-orange-700">
        <button
          class="bg-gradient-to-r text-white w-1/4 hover:from-teal-400 hover:to-blue-500 flex items-center justify-center text-white"
          id="newGame"
          onclick="quickMatch();"
        >
          Quick Match
        </button>

... Canvas ...
... Script ...
</div>
</body>

I'll have to hide the instructions. Currently, the CSS file will be this:

#instructions {
  display: none;
}

/* Menu Buttons */

#theHead {
  font-size: xx-large;
}

#newGame {
  text-align: center;
  vertical-align: middle;
  font-size: 30px;
  font-weight: bold;
  font-family: "Courier New";
  margin: 5px auto;
  cursor: pointer;
}

In the JavaScript file, I'll update the function start(). First I'll change the name to quickMatch() then, inside the function, in the beginning, I'll set this to hide the title theHead and to show the instructions and the field of the game to start it.

document.getElementById("theHead").style.display = "none";
document.getElementById("newGame").style.display = "none";

// When the quickMatch function is called (by clicking the button) 
// will display the instructions and the field of the game
  instructions.style.display = "block";
  canvas.style.display = "block";

Now if I click it the match will start. So, the first button is working, one should see something like this:

first part menu.png

When the user clicks the Quick Match button the game will start, the instructions will be displayed on the screen and so the scores.

Let's add the next two buttons, just like I've done with the quick match button.

To make it simpler to read, the structure would look like this:

<button> Settings </button>
<h2>Settings</h2>
<!-- All the settings of the game would appear here -->
<button>About</button>
<div> <!-- All the information related to Gonzalo Simon, the developer of the game will appear here --></div>
<button>Go Back</button>

I'll show how the HTML code looks like. I added comments so it's easier to read.

<body class="bg-green-700 text-center text-white font-sans">
    <div>
      <div id="theHead">
        <h1 class="mt-16 text-5xl">Football Game</h1>
        <h3 class="mt-8">Main Menu</h3>
    <br/> 
    </div>

<!-- Below this comment, I'll set the buttons of the menu -->

    <div class="from-orange-400 to-orange-700"> 
      <button class="bg-gradient-to-r text-white w-1/4 hover:from-teal-400 hover:to-blue-500 flex items-center justify-center text-white" id="newGame" onclick="quickMatch();" > 
      Quick Match 
      </button>

<!-- This is what I had before, below this comment I'll add the other buttons  -->

       <button class="bg-gradient-to-r text-white w-1/4 flex hover:from-teal-400 hover:to-blue-500 items-center justify-center text-white" id="settingsBtn" onclick="showSettings()" > Settings </button>
        <div id="settings">
          <h2 class="text-4xl mt-8">Settings</h2>
        </div>
        <button class="bg-gradient-to-r text-white w-1/4 flex hover:from-teal-400 hover:to-blue-500 items-center justify-center text-white" id="aboutBtn" onclick="showCredits()"> About </button>
<!-- The following division contains the information that will appear after the button is clicked  -->
         <div id="credits">
          <h2 class="text-4xl mt-8">About this game</h2>
          <h3 class="text-xl mt-8">Made by Gonzalo Simon.</h3>
          <p>I'm a web-developer from Argentina, this is one of my projects.</p>
          <div>
            <br />
           <p> You can learn on my blog how I made this game with JavaScript. </p>
       <p> <a href="https://gonzalosimon.hashnode.dev/football-game-with-javascript-part-1" >Click here to check the first part</a ></p>
       <p> <a href="https://gonzalosimon.hashnode.dev/football-game-with-javascript-part-2-making-botscpu-move" >Click here to check the second part</a></p>
        <br/>
     </div>
    </div>
<!--  And, a 'Go Back' button. This will be only visible if enters any of the two previous sections -->
    <button class="mt-16 bg-gradient-to-r text-white w-1/4 flex hover:from-teal-400 hover:to-blue-500 items-center justify-center text-white" id="backBtn" onclick="goBack()" > Go Back </button>
      </div>
    </div>
... canvas ...
... script ...
<body>

#credits, #settings are the information inside the buttons and #newGame, #aboutBtn, #settingsBtn, #backBtn are the buttons ID. So the information is displayed hidden, thanks to the display: none; property. With JavaScript, I'll make it appear only when the user clicks the button.

So the CSS will be updated with this:

#theHead {
  font-size: xx-large;
}

#newGame,
#aboutBtn,
#settingsBtn,
#backBtn {
  text-align: center;
  vertical-align: middle;
  font-size: 30px;
  font-weight: bold;
  font-family: "Courier New";
  margin: 5px auto;
  cursor: pointer;
}

#backBtn,
#credits,
#settings {
  margin: auto;
  display: none;
}

Now, I'll need to update the JavaScript file. First I'll add this at the beginning of the file. This will make the field of the game disappear when the menu is on the screen. Next, I'll make it appear when the user clicks the quickMatch button.

canvas.style.display = "none";

Then, what I'm going to do is to make a function for every button, then pass it with the document method, getElementById, and declare their property depending on what the button does. For example, if the user touches the about button, the ID that belongs to it will be showCredits because the only elements visible will be 'credits' and the button 'Go Back'.

So I'll do the same for the Settings and for the button go Back. The button Go Back, will appear just when the user is inside of settings or about.

var showCredits = () => {
  document.getElementById("theHead").style.display = "none";
  document.getElementById("aboutBtn").style.display = "none";
  document.getElementById("newGame").style.display = "none";
  document.getElementById("settings").style.display = "none";
  document.getElementById("credits").style.display = "block";
  document.getElementById("backBtn").style.display = "block";
  document.getElementById("settingsBtn").style.display = "none";
};

var showSettings = () => {
  document.getElementById("theHead").style.display = "none";
  document.getElementById("aboutBtn").style.display = "none";
  document.getElementById("newGame").style.display = "none";
  document.getElementById("credits").style.display = "none";
  document.getElementById("settings").style.display = "block";
  document.getElementById("backBtn").style.display = "block";
  document.getElementById("settingsBtn").style.display = "none";
};

var goBack = () => {
  document.getElementById("theHead").style.display = "block";
  document.getElementById("newGame").style.display = "block";
  document.getElementById("aboutBtn").style.display = "block";
  document.getElementById("settingsBtn").style.display = "block";
  document.getElementById("backBtn").style.display = "none";
  document.getElementById("credits").style.display = "none";
  document.getElementById("settings").style.display = "none";
};

function quickMatch() {

  document.getElementById("theHead").style.display = "none";
  document.getElementById("aboutBtn").style.display = "none";
  document.getElementById("newGame").style.display = "none";
  document.getElementById("credits").style.display = "none";
  document.getElementById("settings").style.display = "none";
  document.getElementById("backBtn").style.display = "none";
  document.getElementById("settingsBtn").style.display = "none";

// When the quickMatch function is called (by clicking the button) will display the field of the map and the sintructions
  instructions.style.display = "block";
  canvas.style.display = "block";

  if (restart) {
    reset();
  }

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

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

  //Bounce functions
  players_Ball_Collision();
  playersCollision();
  playersBounds();
  ballBounds();
  document.getElementById("yellowteam_score").innerHTML = "Yellow Team Score: " + yellowteam;
  document.getElementById("greenteam_score").innerHTML = "Green Team Score: " + greenteam;

  requestAnimationFrame(quickMatch);
};

After this update, the main menu should look like the first image.

Adding sound to the game

What I want, is to add a crowd sound during the game, and a piece of music, for the Main Menu that will also be the crowd. This sound will start as soon as the game is loaded and it can be muted from the settings options.

First I'll add the piece of audio I'll use, I'll create a new folder called assets inside the folder of the game, and I'll have this audio file called crowd.mp3.

So, I'll add the function playSound, when the sound icon is clicked it'll reproduce a crowd sound, if it's clicked again, it will stop.

function playSound(sound) {
  var crowd = document.getElementById("crowd");
  crowd.volume = 0.25; // setting the volume to 25% because the sound is loud
  if (crowd.paused) {
    // if crowd is paused
    crowd.play();
  } else {
    crowd.pause();
  }
}

Now, with jQuery, I added a function that changes the icon of the mute sound. If it's clicked it'll mute the crowd and the icon will change.

To something like this:

Mute-Unmute.jpeg

$(function () {
  $("#sound-icon").on("click", function () {
    $(this).toggleClass("fa-volume-up fa-volume-off");
    $(this).hasClass("fa-volume-off")
      ? (document.getElementById("showmute").innerHTML = "Unmute")
      : (document.getElementById("showmute").innerHTML = "Mute");
  });
});

Finally, I'll call the function playSound with the element "crowd".

playSound("crowd");

The element "crowd" will be updated in the HTML file in a div inside the settings section

<div class="sound-section" unselectable="on">
  <audio id="crowd">
    <source src="assets/crowd.mp3" type="audio/mp3" />
  </audio>
  <i
    class="mt-8 sound-icon fa fa-volume-up fa-2x"
    id="sound-icon"
    type="button"
    onclick="playSound('crowd')"
  >
    <span id="showmute">Mute</span>
  </i>
</div>

Making the counter that will set up the time of the match

To do this I declared in the JavaScript file a variable with the number 60. Then I declared the function onTimer that will make them get an element named timer by id, that I'll set in the HTML file, so the variable time will decrease by 1 every second, and when the timer is 0, an alert will appear telling the user the Game is over, after this message the user will be redirected to the main menu.

let time = 60;

function onTimer() {
// I added this, so when the function is called the time will be displayed on the screen
  showTime.style.display = "block"; 

  document.getElementById("timer").innerHTML = time;
  time--;
  if (time < 0) {
    alert("Game Over");
    location.reload(true);
  } else {
    setTimeout(onTimer, 1000);
  }
}

At this moment, after setting that piece of code, I need to update the HTML file by adding an element timer right after the scores element. Also, I have to call the function onTimer when the quickMatch button is clicked, so onClick I'll add the function along with the quickMatch function.

<div id="showTime">The match ends in <span id="timer"></span></div>
<!-- To make it shorter I'm only showing the change I've made, this is the same button with id="newGame" -->
<button onclick="onTimer(); quickMatch();" >

I shall hide this information, the sound, and the time when the user is in the main menu, so I'll add this to the CSS file.

#main, #instructions, #showTime {
  display: none;
}

And this to the quickMatch function:

instructions.style.display = "block";

Drawing players

I wanted to change the players. Instead of being a simple circle, I wanted two players. Although I'm not good at drawing, I wanted to make them on my own, so, I've made them with this website https://make8bitart.com

A yellow player, and a green player. Originally I planned the game to be a blue team and a red team, so I drew the players blue and red.

It cost me literally two clicks to change their colors to yellow and green, I wanted to switch now the color to blue and red, but I'll show all the results.

Esta.png

I'll create a new folder called images inside the assets folder and I'll place these png files inside. To replace the players inside the game I followed a few steps in the JavaScript file. I wrote added so it's easier to read.

var yellowPath = "assets/images/Blue.png";
//The path to the image that we want to add.
var yellowPlayerReady = false;
var yellowImage = new Image();

//Create a new Image object.
yellowImage.onload = function () {
  //Draw the image onto the canvas.
  yellowPlayerReady = true;
};
//Set the src of this Image object.
yellowImage.src = yellowPath;

var greenPath = "assets/images/Red.png";
//The path to the image that we want to add.
var greenPlayerReady = false;
var greenImage = new Image();

//Create a new Image object.
greenImage.onload = function () {
  //Draw the image onto the canvas.
  greenPlayerReady = true;
};
//Set the src of this Image object.
greenImage.src = greenPath;

Then, the renderPlayers() function has to be updated, I commented on the code to delete and added the new.

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();
    if (yellowPlayerReady) {
      context.drawImage(
        BlueImage,
        players[i].x,
        players[i].y,
        players[i].size - 0.5,
        players[i].size * 2.5
      );
    }
  }
  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();
    if (greenPlayerReady) {
      context.drawImage(
        RedImage,
        players[i].x,
        players[i].y,
        players[i].size - 1.5,
        players[i].size * 3.5
      );
  }
}
  context.restore();
}

After all this, the result is this:

Screenshot from 2020-12-30 16-31-18.png

Here is a playable result of part 4

This is the end of part 4 and for now the end of this series of articles, I enjoyed making this game. Coding videogames it's something I've never done before, so probably there are probably many things that could be done better, however, I found very little material, about soccer games of this style with JavaScript. Thanks for getting here. I would greatly appreciate comments.