📜 ⬆️ ⬇️

Canvas step by step: PONG

Today we will try to write a small game Pong using the canvas html5 tag. Those who do not want to read the post can immediately play .
If you believe Wikipedia , you can find out that Pong is the simplest table tennis simulator. A small square replacing the ping-pong ball moves along the screen along a linear trajectory. If it hits the perimeter of the playing field or one of the drawn rackets, its trajectory changes in accordance with the angle of collision.
The gameplay is that players move their rackets vertically to protect their gates. A player gets one point if he manages to send the ball for the opponent's racket ...

We will try to implement the game so that it could be played with the mouse, and the opponent was controlled by the computer. So let's get started. To begin with, we will decide on the fact that, in order to fully understand what is happening in this record, it is advisable to get acquainted with the post describing the basics .

Our megaproject will consist of two files, respectively pong.htm and pong.js, which actually need to be created and saved in one folder.
Content of html file:
< html >
< head >
< meta charset = "utf-8" >
< title > html5Pong < / title >
< script src = "pong.js" >< / script >
< / head >
< body >
<canvas id = "pong" > wtf?! < / canvas>
< script > init() < / script >
< / body >
< / html >

Actually, the whole mechanics of the game will be moved to the pong.js file and we will add the first lines of our future game to it:
//
function init ( ) {
canvas = document. getElementById ( "pong" ) ;
canvas. width = 480 ; //
canvas. height = 320 ; //
context = canvas. getContext ( '2d' ) ;
draw ( ) ;
}
//
function draw ( ) {
context. fillStyle = "#000" ;
context. fillRect ( 0 , 0 , 480 , 320 ) ;
}
init ( ) ;

If you open the html file with a browser, then you can see the work of this script that actually paints our canvas in black.

Game objects


All game objects in Pong are rectangles and this will greatly facilitate our task. Let's set a small class Rect which will contain all the necessary fields for drawing the rectangle, as well as the draw method:
function rect ( color , x , y , width , height ) {
this . color = color ; //
this . x = x ; //
this . y = y ; //
this . width = width ; //
this . height = height ; //
this . draw = function ( ) //
{
context. fillStyle = this . color ;
context. fillRect ( this . x , this . y , this . width , this . height ) ;
}
}

Now let's slightly change the contents of the initialization and rendering functions by adding objects of the playing field, players, “ball”
//
function init ( ) {
//
game = new rect ( "#000" , 0 , 0 , 480 , 320 ) ;
// -
ai = new rect ( "#fff" , 10 , game. height / 2 - 40 , 20 , 80 ) ;
player = new rect ( "#fff" , game. width - 30 , game. height / 2 - 40 , 20 , 80 ) ;
//
ai. scores = 0 ;
player. scores = 0 ;
// ""
ball = new rect ( "#fff" , 40 , game. height / 2 - 10 , 20 , 20 ) ;
canvas = document. getElementById ( "pong" ) ;
canvas. width = game. width ;
canvas. height = game. height ;
context = canvas. getContext ( "2d" ) ;
draw ( ) ;
}
//
function draw ( ) {
game. draw ( ) ; //
//
context. font = 'bold 128px courier' ;
context. textAlign = 'center' ;
context. textBaseline = 'top' ;
context. fillStyle = '#ccc' ;
context. fillText ( ai. scores , 100 , 0 ) ;
context. fillText ( player. scores , game. width - 100 , 0 ) ;
for ( var i = 10 ; i < game. height ; i += 45 ) //
{
context. fillStyle = "#ccc" ;
context. fillRect ( game. width / 2 - 10 , i , 20 , 30 ) ;
}
ai. draw ( ) ; //
player. draw ( ) ; //
ball. draw ( ) ; //
}

If you now open the file, you can see all the elements of our game.
')

Let there be life


Of course, everything at this stage looks very good, but the static picture is not what we need. So now we are going to “revive” the ball. To do this, we will create a new play () function in which we will make a call to the draw () function, and the play itself will be called from init () by means of a timer, namely setInterval (play, 1000/50). The y coordinate of the player will be attached to the coordinate of the mouse moving on the canvas. The update function contains changes that should be made, such as mouse coordinates and other joys. In order not to get confused in the actions performed below, the code of the entire pong.js file that we need to exit:
//
function rect ( color , x , y , width , height ) {
this . color = color ;
this . x = x ;
this . y = y ;
this . width = width ;
this . height = height ;
this . draw = function ( ) {
context. fillStyle = this . color ;
context. fillRect ( this . x , this . y , this . width , this . height ) ;
} ;
}
//
function playerMove ( e ) {
var y = e. pageY ;
if ( player. height / 2 + 10 < y && y < game.height - player.height / 2 - 10 ) {
player. y = y - player. height / 2 ;
}
}
//
function draw ( ) {
game. draw ( ) ; //
//
context. font = 'bold 128px courier' ;
context. textAlign = 'center' ;
context. textBaseline = 'top' ;
context. fillStyle = '#ccc' ;
context. fillText ( ai. scores , 100 , 0 ) ;
context. fillText ( player. scores , game. width - 100 , 0 ) ;
for ( var i = 10 ; i < game. height ; i += 45 )
//
{
context. fillStyle = "#ccc" ;
context. fillRect ( game. width / 2 - 10 , i , 20 , 30 ) ;
}
ai. draw ( ) ; //
player. draw ( ) ; //
ball. draw ( ) ; //
}
//
function update ( ) {
//
ball. x += ball. vX ;
ball. y += ball. vY ;

}
function play ( ) {
draw ( ) ; //
update ( ) ; //
}
//
function init ( ) {
//
game = new rect ( "#000" , 0 , 0 , 480 , 320 ) ;
// -
ai = new rect ( "#fff" , 10 , game. height / 2 - 40 , 20 , 80 ) ;
player = new rect ( "#fff" , game. width - 30 , game. height / 2 - 40 , 20 , 80 ) ;
//
ai. scores = 0 ;
player. scores = 0 ;
// ""
ball = new rect ( "#fff" , 40 , game. height / 2 - 10 , 20 , 20 ) ;
//
ball. vX = 2 ; //
ball. vY = 2 ; //
canvas = document. getElementById ( "pong" ) ;
canvas. width = game. width ;
canvas. height = game. height ;
context = canvas. getContext ( "2d" ) ;
canvas. onmousemove = playerMove ;
setInterval ( play , 1000 / 50 ) ;
}


Game clashes


The most interesting thing will start now, we need to teach the ball not to fly out of the playing field, as well as to come in contact with the rackets, for this I wrote a small function that returns the true value if the two game objects touch. Below is its code
function collision ( objA , objB ) {
if ( objA. x + objA. width > objB. x &&
objA. x < objB. x + objB. width &&
objA. y + objA. height > objB. y &&
objA. y < objB. y + objB. height ) {
return true ;
}
else {
return false ;
}
}

Now, so that the ball would not just fly out of the playing field, we need to slightly adjust the update function, namely
function update ( ) {
//
//
if ( ball. y < 0 || ball. y + ball. height > game. height ) {
//
ball. vY = - ball. vY ;
}
//
if ( ball. x < 0 ) {
//
ball. vX = - ball. vX ;
player. scores ++;
}
if ( ball. x + ball. width > game. width ) {
//
ball. vX = - ball. vX ;
ai. scores ++;
}
//
if ( ( collision ( ai , ball ) && ball. vX < 0 ) || ( collision ( player , ball ) && ball. vX > 0 ) ) {
ball. vX = - ball. vX ;
}
//
ball. x += ball. vX ;
ball. y += ball. vY ;
}

Now our game can practically be played, the ball flies correctly, the rackets can beat it, the opponent is true, we have a slightly corpse, but we will fix this, with a small function

function aiMove ( ) {
var y ;
//
var vY = Math. abs ( ball. vY ) - 2 ;
if ( ball. y < ai. y + ai. height / 2 ) {
y = ai. y - vY ;
}
else {
y = ai. y + vY ;
}
if ( 10 < y && y < game. height - ai. height - 10 ) {
ai. y = y ;
}
}

Call the function should be placed in the update.

Total


Actually, they wrote, it remains only to tinker a bit, and here it is a full-fledged pong. We will display the statistics of the played games in the same way as the score will be displayed on the screen, and we will assign the speed changes to the mouse click. The link can be found PONG with simple statistics, full of comments.

Source: https://habr.com/ru/post/116860/


All Articles