Home  >  Article  >  Web Front-end  >  Tutorial on making a simple table tennis game with HTML5_html5 tutorial tips

Tutorial on making a simple table tennis game with HTML5_html5 tutorial tips

WBOY
WBOYOriginal
2016-05-16 15:46:431818browse

By the way, this is just a simple DEMO. I haven’t given much thought to gameplay, game rules, etc. If you are interested in refining it, you can refine it, such as refining the rules, game switches, adding a sound, refining the goal detection, and even more rigorous. Check the strength of the shot, the real friction of the table, etc. to make the game more like a game. I'm just giving you some programming ideas, just take a demo and it won't be very enjoyable to play~~
2015512171509746.png (737×458)

Snooker Game
There are two categories in the entire snooker 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 collisions between the ball and corners and goals. The game I made uses the simplest wall collision detection, so there is no collision detection between balls and irregular shapes. If you want to play more complex collisions, you can poke about simple collision detection Cen An The speech was still very good. Okay, let’s do it step by step:

【Ball】

Post the 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);
                                                                                      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.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.moving = false;
                    else {

This.moving = true;

}
}
}[/code]
Ball attributes: x, y ball position, vx, vy ball horizontal speed and vertical speed. , ismine represents whether it is a white ball or another ball (different balls draw different pictures in the _paint method), oldx, oldy are used to save the position of the ball in the previous frame, but they have not been used yet, it should be useful. There is nothing much to say about the _paint method. The _run method is to track the position of the ball and calculate the displacement increment and speed increment of the ball based on the time of each frame of the ball. mcl and pxpm are both constants, mcl is the friction force, and pxpm is approximately Calculate the pixel and reality conversion ratio. . . . Then there is collision detection. This is easy to understand. It calculates whether the position of the ball exceeds the boundary. If it exceeds, it will rebound. However, this kind of collision detection is very loose. If you really want to make a game, it is recommended to use something more complicated. There is also the method of stopping the ball according to its speed.





Copy code

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 a dotted line. This is relatively simple. Get the position of the mouse and the position of the white ball, and then draw a distance between the two. line, and then it becomes a dotted line.


 【Multi-ball collision detection】


Copy code

The code is as follows:
function collision(){
for(var i=0;i for(var j=0;j var b1 = balls[i],b2 = balls[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;
//Correct 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 centers of the two balls. If it is less than the sum of the radii of the two balls, a collision has occurred. If both small balls are stationary, no collision detection will be performed. Otherwise, the speed increment after collision will be calculated. The method of calculating the collision speed increment can be directly seen in Algorithm Design of Small Ball Collision, which explains It's quite detailed, and when combined, we get the series of formulas above.

Assign the speed increment to the collision ball. Because in the frame when the two balls collide, the two balls partially overlap, so the position correction must be performed. Otherwise, the small balls will always be in collision and then stick together. The principle of position correction is also simple. Calculate the distance between the two balls. Center distance, calculate the width of the overlapping area of ​​the two balls through the Pythagorean theorem, and then divide the width by 2 and assign the new position to the ball. The new position is that the radius of the two balls is exactly equal to the distance between the centers of the balls.

 【Mouse action】

Copy code
The code is as follows:
canvas.addEventListener("mousedown" , function(){
if(balls[0].moving) return;

document.querySelector(".shotPower").style.display = "block";
document.querySelector(".shotPower").style.top = balls[0].y-60 "px";
document.querySelector(".shotPower").style.left = balls[0].x-40 "px";
document.getElementById("pow").className = "animate";
var x = event.clientX document.body.scrollLeft document.documentElement.scrollLeft - document.querySelector(".view").offsetLeft;
var y = event.clientY document.body.scrollTop document.documentElement.scrollTop - document.querySelector(".view").offsetTop;
dotline.display = true;
dotline.x0 = balls[0].x;
dotline.y0 = balls[0].y;
dotline.x1 = x;
dotline.y1 = y;

window.addEventListener("mouseup" , muHandle , false);
window.addEventListener("mousemove" , mmHandle , false);

function mmHandle(){
var x = event.clientX document.body.scrollLeft document.documentElement.scrollLeft - document.querySelector(".view").offsetLeft;
var y = event.clientY document.body.scrollTop document.documentElement.scrollTop - document.querySelector(".view").offsetTop;
dotline.x1 = x;
dotline.y1 = y;
}
function muHandle(){
var x = event.clientX document.body.scrollLeft document.documentElement.scrollLeft - document.querySelector(".view").offsetLeft;
var y = event.clientY document.body.scrollTop document.documentElement.scrollTop - document.querySelector(".view").offsetTop;

var angle = Math.atan((y - balls[0].y)/(x - balls[0].x));
var h = document.getElementById("pow").offsetHeight/document.getElementById("powbar").offsetHeight;
var v = 60*h;
document.getElementById("pow").style.height = h*100 "%"

balls[0].vx = x - balls[0].x>0 ? v*Math.abs(Math.cos(angle)) : -v*Math.abs(Math.cos(angle));
balls[0].vy = y - balls[0].y>0 ? v*Math.abs(Math.sin(angle)) : -v*Math.abs(Math.sin(angle));

document.getElementById("pow").className = "";

window.removeEventListener("mouseup" , muHandle , false);
window.removeEventListener("mousemove" , muHandle , false);
dotline.display = false;
document.querySelector(".shotPower").style.display = "none";
}
},false);
 鼠标动作也比较简单,有js基础的基本上都没问题,就是鼠标按下后计算鼠标位置,然后产生辅助虚线,鼠标移动后修改辅助虚线的终点位置。鼠标按下的时候旁边产生一个力量计,我就只用用animation做动画了,然后鼠标按键抬起时通过计算力量计的大小来确定白球的速度,然后再分解成水平速度以及垂直速度赋给白球。同时取消鼠标移动以及鼠标抬起的事件绑定,把辅助虚线以及力量计隐藏。

 

  【动画舞台】

     

复制代码
代码如下:
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height)
var t1 = new Date();
var t = (t1 - t0)/1000;

collision();
balls.foreach(function(){
if(!this.inhole) this._run(t);
});
if(dotline.display){
dotline.x0 = balls[0].x;
dotline.y0 = balls[0].y;
dotline._paint();
}

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("mozRequestAnimationFrame" in window){
mozRequestAnimationFrame(animate);
}
else {
setTimeout(animate , 16);
}
}
}


 这个就是游戏每一帧的逻辑处理现场,如果小球进洞了,就不再进行绘制,如果辅助虚线的display属性设成false,就不进行辅助虚线的绘制,还有就是计算每一帧的时间。 【常量与初始化】
 
复制代码
代码如下:

var canvas = document.getElementById("cas");
var ctx = canvas.getContext('2d');
var mcl = 1 , collarg = 0.8 , ballRadius = 15 , t0 = 0 , balls=[] , tbw = 32 , animateStop = true , powAnimation = false;
var dotline;
pxpm = canvas.width/20;

window.onload = function(){
var myball = new Ball(202 , canvas.height/2 , true);
balls.push(myball);
for(var i=0;i<6;i ){
for(var j=0;j var other = new Ball(520 i*(ballRadius-2)*2 , (canvas.height-i*2*ballRadius)/2 ballRadius 2*ballRadius*j , false);
balls.push(other);
}
}
t0 = new Date();
dotline = new dotLine(0,0,0,0);

animateStop = false;
animate();
}


 实例化所有小球,把小球全部按照规律摆好,然后获取当前时间,实例化辅助虚线,动画开始。

源码地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/snooker

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn