这款javascript桌球也是国人写的,体验度非常的好,整体也相当流畅,基本不存在卡顿现象,优化得不错。
cnwander的博客看到一款非常棒的桌球小游戏,用JavaScript实现的,并且可以实现台球的力度、撞击点等。
当鼠标按住时还可以看到右下角的力量进度条实时滑动,非常不错,
1 2 3 4 5 6 7 8 9 10 11 | // ball class function Ball(type,x,y) { ... this.type = type; this.x = x; //位置 this.y = y; this.angle = 0; //角度 this.v = 0; //速度(不包含方向) ... return this; } |
描述小球的四个信息,小球的类型(母球,目标球),坐标,角度,速度
在更新坐标时,读取小球的v,刷新小球的位置
在与边沿碰撞时,更改小球angle
1 2 3 | var formPos = getBallPos(cueBall.elem), toPos = getBallPos(guideBall), angle = Math.atan2(toPos[0] - formPos[0],toPos[1] - formPos[1]); |
计算母球,与参考球之间的角度,其它任意小球之间也是如此
值得注意的是我采用的是Math.atan2,而非Math.atan,这是因为Math.atan2返回的是(0 – Math.PI)和(-Math.PI – 0),可以确定唯一的角度,而Math.atan不唯一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //边缘碰撞 if(ball.x < R || ball.x > W - R) { ball.angle *= -1; ball.v = ball.v * (1 - LOSS); ... if(ball.type == "cue") { if(ball.angle > 0) vy -= rollRight; else vy += rollRight; vx += rollUp; rollUp *= 0.2; rollRight *= 0.2; ball.v = Math.sqrt(vx*vx + vy*vy); ball.angle = Math.atan2(vx,vy); } ... if(ball.y < R || ball.y > H - R) { ball.angle = ball.angle > 0 ? Math.PI - ball.angle : - Math.PI - ball.angle ; ... |
不考虑小球旋转时,边缘碰撞很简单,更改小球angle即可
当小球旋转时,如果碰到固定不动的物体时,那将会把速度作用给自身
并且自身旋转速度减小
我这里计算球与球之间碰撞,并没有将旋转传递,这是不准确的,有待改善
这一段是核心,即小球与小球碰撞后各自的速度,其实并不难
先判断两球距离
1 2 | var dis = Math.sqrt(Math.pow(disX,2)+Math.pow(disY,2)); if(dis <= gap) { |
//如果目标球是静止的,则添加到数组movingBalls
1 2 | if(Math.round(obj.v) == 0) movingBalls.push(obj); |
//还原两球相切状态,用其它方式做我不知道,但用js来做,这一步相当关键,否则误差将相当大
1 2 3 4 | ball.x -= (gap - dis)*sin; ball.y -= (gap - dis)*cos; disX = obj.x - ball.x; disY = obj.y - ball.y; |
// 下面则是先将整个坐标系旋转到相撞的水平方向
// 计算角度和正余弦值
1 2 3 4 5 6 7 | var angle = Math.atan2(disY, disX), hitsin = Math.sin(angle), hitcos = Math.cos(angle), objVx = obj.v * Math.sin(obj.angle), objVy = obj.v * Math.cos(obj.angle); //trace(angle*180/Math.PI); |
// 旋转坐标
1 2 3 4 5 6 7 8 | var x1 = 0, y1 = 0, x2 = disX * hitcos + disY * hitsin, y2 = disY * hitcos - disX * hitsin, vx1 = vx * hitcos + vy * hitsin, vy1 = vy * hitcos - vx * hitsin, vx2 = objVx * hitcos + objVy * hitsin, vy2 = objVy * hitcos - objVx * hitsin; |
// 碰撞后的速度和位置
1 2 3 | var plusVx = vx1 - vx2; vx1 = vx2; vx2 = plusVx + vx1; |
//母球加塞
1 2 3 4 5 6 | if(ball.type == "cue") { vx1 += rollUp; rollUp *= 0.2; } x1 += vx1; x2 += vx2; |
// 将位置旋转回来
1 2 3 4 5 6 7 8 | var x1Final = x1 * hitcos - y1 * hitsin, y1Final = y1 * hitcos + x1 * hitsin, x2Final = x2 * hitcos - y2 * hitsin, y2Final = y2 * hitcos + x2 * hitsin; obj.x = ball.x + x2Final; obj.y = ball.y + y2Final; ball.x = ball.x + x1Final; ball.y = ball.y + y1Final; |
// 将速度旋转回来
1 2 3 4 | vx = vx1 * hitcos - vy1 * hitsin; vy = vy1 * hitcos + vx1 * hitsin; objVx = vx2 * hitcos - vy2 * hitsin; objVy = vy2 * hitcos + vx2 * hitsin; |
//最终速度
1 2 | ball.v = Math.sqrt(vx*vx + vy*vy) * (1 - 0); obj.v = Math.sqrt(objVx*objVx + objVy*objVy) * (1 - 0); |
// 计算角度
1 2 3 | ball.angle = Math.atan2(vx , vy); obj.angle = Math.atan2(objVx , objVy); } |
// 坐标旋转的公式:
1 2 | x1 = Math.cos(angle) * x - Math.sin(angle) * y; y1 = Math.cos(angle) * y + Math.sin(angle) * x; |
// 反坐标旋转:
1 2 | x1 = Math.cos(angle) * x + Math.sin(angle) * y; y1 = Math.cos(angle) * y - Math.sin(angle) * x; |
原文:cnwander博客