Use HTML5 to create a simple table tennis game tutorial and html5 table tennis tutorial
This article mainly introduces how to use HTML5 to create a simple billiard game. It mainly uses the Canvas API of HTML5. For more information, see
This is just a simple DEMO. I have not thought about gameplay, game rules or anything. If you are interested in refining, You can refine the rules, switch the game, add a sound, and refine the goal detection, more rigorous, you can even check the hitting strength and the real friction on the desktop to make the game more like a game. I just gave me a programming idea. I just took a DEMO and thought it would be pretty cool to play ~~
Table Tennis Games
There are two categories of the entire table tennis game: one is the ball and the other is the auxiliary aiming line. If you want to make the game more complex, you can also abstract a shape class to detect the collision between the ball and the corner and the goal. I am playing this game to take the simplest wall collision detection, so there is no collision detection between balls and irregular shapes. If you want to play a more complex collision, we can talk about the simple collision detection. Okay. Next, let's take a step:
[Ball]
Post Code first:
[/Code] var Ball = function (x, y, ismine ){
This. x = x;
This. y = y;
This. ismine = ismine;
This. oldx = x;
This. oldy = y;
This. vx = 0;
This. vy = 0;
This. radius = ballRadius;
This. inhole = false; this. moving = true;
}
Ball. prototype = {
Constructor: Ball,
_ Paint: function (){
Var B = this. ismine? Document. getElementById ("wb"): document. getElementById ("yb ")
If (B. complete ){
Ctx. drawImage (B, this. x-this.radius, this. y-this.radius, 2 * this. radius, 2 * this. radius );
}
Else {
B. onload = function (){
Ctx. drawImage (B, this. x-this.radius, this. y-this.radius, 2 * this. radius, 2 * this. radius );
}
}
},
_ Run: function (t ){
This. oldx = this. x;
This. oldy = this. y;
This. vx = Math. abs (this. vx) <0.1? 0: (this. vx> 0? This. vx-mcl * t: this. vx + mcl * t );
This. vy = Math. abs (this. vy) <0.1? 0: (this. vy> 0? This. vy-mcl * t: this. vy + mcl * t );
// This. vx + = this. vx> 0? -Mcl * t: mcl * t;
// This. vy + = this. vy> 0? -Mcl * t: mcl * t;
This. x + = t * this. vx * pxpm;
This. y + = t * this. vy * pxpm;
If (this. x <50 & this. y <50) | (this. x> 370 & this. x <430 & this. y <50) | (this. x> 758 & this. y <50) | (this. x <46 & this. y> 490) | (this. x> 377 & this. x <420 & this. y> 490) | (this. x> 758 & this. y> 490 )){
This. inhole = true;
If (this. ismine ){
Var that = this;
SetTimeout (function (){
That. x = 202;
That. y = canvas. height/2;
That. vx = 0;
That. vy = 0;
That. inhole = false;
}, 500)
}
Else {
Document. getElementById ("shotNum"). innerHTML = parseInt (document. getElementById ("shotNum"). innerHTML) + 1
}
}
Else {
If (this. y> canvas. height-(ballRadius + tbw) | this. y <(ballRadius + tbw )){
This. y = this. y <(ballRadius + tbw )? (BallRadius + tbw): (canvas. height-(ballRadius + tbw ));
This. derectionY =! This. derectionY;
This. vy =-this. vy * 0.6;
}
If (this. x> canvas. width-(ballRadius + tbw) | this. x <(ballRadius + tbw )){
This. x = this. x <(ballRadius + tbw )? (BallRadius + tbw): (canvas. width-(ballRadius + tbw ));
This. derectionX =! This. derectionX;
This. vx =-this. vx* 0.6;
}
}
This. _ paint ();
If (Math. abs (this. vx) <0.1 & Math. abs (this. vy) <0.1 ){
This. moving = false;
}
Else {
This. moving = true;
}
}
} [/Code]
Ball attributes: the position of x and y balls, the horizontal velocity of vx and vy balls, and the obtained vertical velocity, ismine indicates whether it is a white ball or other balls (different balls draw different images in the _ paint method). oldx and oldy are used to save the last frame of the ball, but they are not used yet, it should be useful. The _ paint method has nothing to say. The _ run method is to track the position of the ball. The incremental displacement and speed of the ball are calculated based on the time of each frame of the ball. Both mcl and pxpm are constants, mcl is the friction, pxpm is the ratio of pixels in the big budget to the actual conversion .... Then there is the collision detection. This is easy to understand. If the position of the computation ball exceeds the boundary, it will rebound if it exceeds the boundary. However, this collision detection is not rigorous. If you really want to play games, we suggest using more complex ones. In addition, the ball can still be made based on the speed of the ball.
The Code is as follows: var dotLine = function (x0, y0, x1, y1 ){
This. x0 = this. x0;
This. y0 = this. y0;
This. x1 = this. x1;
This. y1 = this. y1;
This. dotlength = 3;
This. display = false;
}
DotLine. prototype = {
Constructor: dotLine,
_ Ready: function (){
This. length = Math. sqrt (Math. pow (this. y1-this. y0, 2) + Math. pow (this. x1-this. x0, 2 ));
This. dotNum = Math. ceil (this. length/this. dotlength );
},
_ Paint: function (){
This. _ ready ();
Xadd = this. dotlength * (this. x1-this. x0)/this. length;
Yadd = this. dotlength * (this. y1-this. y0)/this. length;
Ctx. save ();
Ctx. beginPath ();
For (var I = 1; I <= this. dotNum; I ++ ){
If (I % 2! = 0 ){
Ctx. moveTo (this. x0 + (I-1) * xadd, this. y0 + (I-1) * yadd );
Ctx. lineTo (this. x0 + I * xadd, this. y0 + I * yadd );
}
}
Ctx. strokeStyle = "# FFF ";
Ctx. stroke ();
Ctx. beginPath ();
Ctx. arc (this. x1, this. y1, ballRadius-2, 0, 2 * Math. PI );
Ctx. stroke ();
Ctx. restore ();
}
}
Just draw the dotted line. This is relatively simple. Get the mouse position and the white ball position, draw a line at a distance between the two, and then it will become a dotted line.
[Multi-ball collision detection]
The Code is as follows: function collision (){
For (var I = 0; I <bils. length; I ++ ){
For (var j = 0; j <bils. length; j ++ ){
Var b1 = bils [I], b2 = bils [j];
If (b1! = B2 &&! B1.inhole &&! B2.inhole ){
Var rc = Math. sqrt (Math. pow (b1.x-b2.x, 2) + Math. pow (b1.y-b2.y, 2 ));
If (Math. ceil (rc) <(b1.radius + b2.radius )){
If (! B1.moving &&! B2.moving) return;
// Get the speed increment after collision
Var ax = (b1.vx-b2.vx) * Math. pow (b1.x-b2.x), 2) + (b1.vy-b2.vy) * (b1.x-b2.x) * (b1.y-b2.y)/Math. pow (rc, 2)
Var ay = (b1.vy-b2.vy) * Math. pow (b1.y-b2.y), 2) + (b1.vx-b2.vx) * (b1.x-b2.x) * (b1.y-b2.y)/Math. pow (rc, 2)
// Assign the speed increment to the collision ball
B1.vx = b1.vx-ax;
B1.vy = b1.vy-ay;
B2.vx = b2.vx + ax;
B2.vy = b2.vy + ay;
// Modify the ball collision distance
Var clength = (b1.radius + b2.radius)-rc)/2;
Var cx = clength * (b1.x-b2.x)/rc;
Var cy = clength * (b1.y-b2.y)/rc;
B1.x = b1.x + cx;
B1.y = b1.y + cy;
B2.x = b2.x-cx;
B2.y = b2.y-cy;
}
}
}
}
}
Traverse all the balls and calculate the distance between the two balls. If the distance is smaller than the radius of the two balls, a collision occurs. If the two balls are static, the collision detection will not be performed. Otherwise, the speed increment after the collision is calculated. The method of the collision speed increment can directly look at the algorithm design of the ball collision, it is quite detailed in this article. After all, we can get the above series of sub-statements.
Assign the speed increment to the collision ball. Because the two balls collide with the frame, the two balls partially overlap, so you have to modify the position. Otherwise, the ball will remain in the collision and then stick together, the principle of Position Correction is also simple. The distance between the two balls is calculated, and the width of the overlapping areas of the two balls is calculated through the stock theorem, then, divide the width by 2 and assign it to the new position of the ball. The new position is that the radius of the two balls is exactly equal to the ball center distance.
[Mouse Action]
The Code is as follows: canvas. addEventListener ("mousedown", function (){
If (bils [0]. moving) return; </p> <p> document. querySelector (". shotPower"). style. display = "block ";
Document. querySelector (". shotPower"). style. top = bils [0]. y-60 + "px ";
Document. querySelector (". shotPower"). style. left = bils [0]. x-40 + "px ";
Document. getElementById ("pow"). className = "animate ";
Var x = event. clientX + document. body. scrollLeft + document.doc umentElement. scrollLeft-document. querySelector (". view"). offsetLeft;
Var y = event. clientY + document. body. scrollTop + document.doc umentElement. scrollTop-document. querySelector (". view"). offsetTop;
Dotline. display = true;
Dotline. x0 = bils [0]. x;
Dotline. y0 = bils [0]. y;
Dotline. x1 = x;
Dotline. y1 = y; </p> <p> window. addEventListener ("mouseup", muHandle, false );
Window. addEventListener ("mousemove", mmHandle, false); </p> <p> function mmHandle (){
Var x = event. clientX + document. body. scrollLeft + document.doc umentElement. scrollLeft-document. querySelector (". view"). offsetLeft;
Var y = event. clientY + document. body. scrollTop + document.doc umentElement. scrollTop-document. querySelector (". view"). offsetTop;
Dotline. x1 = x;
Dotline. y1 = y;
}
Function muHandle (){
Var x = event. clientX + document. body. scrollLeft + document.doc umentElement. scrollLeft-document. querySelector (". view"). offsetLeft;
Var y = event. clientY + document. body. scrollTop + document.doc umentElement. scrollTop-document. querySelector (". view "). offsetTop; </p> <p> var angle = Math. atan (y-bils [0]. y)/(x-bils [0]. x ));
Var h = document. getElementById ("pow"). offsetHeight/document. getElementById ("powbar"). offsetHeight;
Var v = 60 * h;
Document. getElementById ("pow "). style. height = h * 100 + "%" </p> <p> bils [0]. vx = x-bils [0]. x> 0? V * Math. abs (Math. cos (angle):-v * Math. abs (Math. cos (angle ));
Bils [0]. vy = y-bils [0]. y> 0? V * Math. abs (Math. sin (angle):-v * Math. abs (Math. sin (angle); </p> <p> document. getElementById ("pow "). className = ""; </p> <p> window. removeEventListener ("mouseup", muHandle, false );
Window. removeEventListener ("mousemove", muHandle, false );
Dotline. display = false;
Document. querySelector (". shotPower"). style. display = "none ";
}
}, False );
The mouse action is also relatively simple, and there is basically no problem with the js Foundation, that is, after the mouse is pressed, the mouse position is calculated, and then the auxiliary dotted line is generated. After the mouse is moved, the terminal position of the auxiliary dotted line is modified. When the mouse is pressed, a force meter is generated next to it. I only use animation for animation. Then, when the mouse is lifted, the speed of the white ball is determined by calculating the size of the force meter, then it is decomposed into horizontal velocity and vertical velocity assigned to the white ball. At the same time, you can unbind the mouse movement and mouse lift events to hide the dotted line and power meter.
[Animation stage]
The Code is as follows: function animate (){
Ctx. clearRect (0, 0, canvas. width, canvas. height)
Var t1 = new Date ();
Var t = (t1-t0)/1000; </p> <p> collision ();
Bils. foreach (function (){
If (! This. inhole) this. _ run (t );
});
If (dotline. display ){
Dotline. x0 = bils [0]. x;
Dotline. y0 = bils [0]. y;
Dotline. _ paint ();
} </P> <p> t0 = t1;
If (! AnimateStop ){
If ("requestAnimationFrame" in window ){
RequestAnimationFrame (animate );
}
Else if ("webkitRequestAnimationFrame" in window ){
WebkitRequestAnimationFrame (animate );
}
Else if ("msRequestAnimationFrame" in window ){
MsRequestAnimationFrame (animate );
}
Else if ("Your requestanimationframe" in window ){
Required requestanimationframe (animate );
}
Else {
SetTimeout (animate, 16 );
}
}
}
This is the logic processing field for each frame of the game. If the ball goes into the hole, it will not be drawn. If the display attribute of the auxiliary dotted line is set to false, it will not be drawn, the other is to calculate the time of each frame. [Constants and initialization]
The Code is as follows:
Var canvas = document. getElementById ("cas ");
Var ctx = canvas. getContext ('2d ');
Var mcl = 1, collarg = 0.8, ballRadius = 15, t0 = 0, bils = [], tbw = 32, animateStop = true, powAnimation = false;
Var dotline;
Pxpm = canvas. width/20; </p> <p> window. onload = function (){
Var myball = new Ball (202, canvas. height/2, true );
Bils. push (myball );
For (var I = 0; I <6; I ++ ){
For (var j = 0; j <I; j ++ ){
Var other = new Ball (520 + I * (ballRadius-2) * 2, (canvas. height-I * 2 * ballRadius)/2 + ballRadius + 2 * ballRadius * j, false );
Bils. push (other );
}
}
T0 = new Date ();
Dotline = new dotLine (0, 0, 0); </p> <p> animateStop = false;
Animate ();
}
Instantiate all the balls, set all the balls according to the rules, get the current time, instantiate the auxiliary dotted line, and start the animation.
Source Code address: https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/snooker