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:
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.