当前位置: 首页 > 图文教程 > 网络编程 > Javascript > JS小游戏实例:2D桌面台球

Javascript
JS 文件本身编码转换 图文教程
jQuery Ajax之$.get()方法和$.post()方法
jQuery Ajax之load()方法
JavaScript 核心参考教程 内置对象
JavaScript 核心参考教程 RegExp对象
javascript hashtable实现代码
百度留言本js 大家可以参考下
javascript 判断某年某月有多少天的实现代码 推荐
让iframe子窗体取父窗体地址栏参数(querystring)
jquery pagination插件实现无刷新分页代码
jQuery与javascript对照学习 获取父子前后元素 实现代码
通用javascript脚本函数库 方便开发
JQuery 绑定事件时传递参数的实现方法
支持IE,Firefox的javascript 日历控件
javascript 变速加数功能实现代码
extjs 学习笔记(一) 一些基础知识
extjs 学习笔记(二) Ext.Element类
Jquery 学习笔记(一)
一些技巧性实用js代码小结
jquery 常用操作整理 基础入门篇

Javascript 中的 JS小游戏实例:2D桌面台球


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2010-01-03   浏览: 181 ::
收藏到网摘: n/a

DEMO: http://cnwander.com/demo/billiards/
原文地址:http://cnwander.com/blog/?p=11

先贴上代码:

运行代码框

[Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]

虽厚颜冠名桌球,其实与真实桌球还相差甚远,还有太多需要改进的地方。
具体待解决的问题:

  1. 由于浏览器上刷新频率不可能太高,可能当检测两球距离时,两球已经重叠了大部分,甚至完全越过。
    完全越过的情况先不考虑,重叠部分如果还原到精确的相切状态,运行非常缓慢,所以我只采用了计算量较少的近似值,具体问题主要体现在开球时,多球碰撞时有些诡异。
    (如果哪位有好的优化计算方法,可以拿出来与wander分享,那wander真的感激不尽)
  2. 球自身滚动与桌面摩擦力问题。如正中击球的瞬间,母球滑行状态的摩擦力会大于向前滚动时的摩擦力,小于缩杆时摩擦力等等。这个问题好解决,只是刚开始没考虑进去,之后也没添加了,具体体现在两球正撞后,撞击球将完全静止,这是不正确的。
  3. 能量损耗问题,无论与边沿碰撞还是球与球相撞,都是直接减去一个固定值,这肯定是存在很大问题的。

其它肯定还会有很多问题,担心假期把心玩油了,回头没心思继续,干脆一气呵成,赶得有些匆忙,问题回头再慢慢解决吧,先发上来,对这块有兴趣的同学一块儿探讨探讨。

大学数学基本是过场,高中的物理数学与忘得所剩无几,真正做东西才发现自己这块太薄弱,希望在这方面经验比较丰富的同学不吝赐教。

贴出一些关键代码稍作解释,有兴趣的同学看看。

// 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

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不唯一。

//边缘碰撞
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即可
当小球旋转时,如果碰到固定不动的物体时,那将会把速度作用给自身
并且自身旋转速度减小
我这里计算球与球之间碰撞,并没有将旋转传递,这是不准确的,有待改善

这一段是核心,即小球与小球碰撞后各自的速度,其实并不难
先判断两球距离
var dis = Math.sqrt(Math.pow(disX,2)+Math.pow(disY,2));
if(dis <= gap) {
    //如果目标球是静止的,则添加到数组movingBalls
    if(Math.round(obj.v) == 0)   
    movingBalls.push(obj);
   
    //还原两球相切状态,用其它方式做我不知道,但用js来做,这一步相当关键,否则误差将相当大
    ball.x -= (gap - dis)*sin;
    ball.y -= (gap - dis)*cos;
    disX = obj.x - ball.x;
    disY = obj.y - ball.y;
   
    // 下面则是先将整个坐标系旋转到相撞的水平方向
    // 计算角度和正余弦值
    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);
       
    // 旋转坐标
    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;
   
    // 碰撞后的速度和位置
    var plusVx = vx1 - vx2;
    vx1 = vx2;
    vx2 = plusVx + vx1;
   
    //母球加塞
    if(ball.type == "cue")    {
        vx1 += rollUp;
        rollUp *= 0.2;
    }               
   
    x1 += vx1;
    x2 += vx2;
   
    // 将位置旋转回来
    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;
   
    // 将速度旋转回来
    vx = vx1 * hitcos - vy1 * hitsin;
    vy = vy1 * hitcos + vx1 * hitsin;
    objVx = vx2 * hitcos - vy2 * hitsin;
    objVy = vy2 * hitcos + vx2 * hitsin;
   
    //最终速度
    ball.v = Math.sqrt(vx*vx + vy*vy) * (1 - 0);
    obj.v = Math.sqrt(objVx*objVx + objVy*objVy) * (1 - 0);
   
    // 计算角度
    ball.angle = Math.atan2(vx , vy);
    obj.angle = Math.atan2(objVx , objVy);
}

  • 坐标旋转的公式
    x1 = Math.cos(angle) * x - Math.sin(angle) * y;
    y1 = Math.cos(angle) * y + Math.sin(angle) * x;
  • 反坐标旋转
    x1 = Math.cos(angle) * x + Math.sin(angle) * y;
    y1 = Math.cos(angle) * y - Math.sin(angle) * x;

不一定用坐标旋转,我只是习惯了用这种方式来计算碰撞,只要将正向碰撞的速度相差即可