码迷,mamicode.com
首页 > 编程语言 > 详细

265行 JavaScript 代码实现第一人称引擎

时间:2014-06-12 20:56:55      阅读:365      评论:0      收藏:0      [点我收藏+]

标签:style   class   blog   code   java   http   

原文:A first-person engine in 265 lines(2014-6-11)
翻译:Jaward华仔


今天,让我们进入一个可以伸手触摸的世界吧。在这篇文章里,我们将从零开始快速完成一次第一人称探索。本文没有涉及复杂的数学计算,只用到了光线投射技术。你可能已经见识过这种技术了,比如《上古卷轴2 : 匕首雨》、《毁灭公爵3D》还有 Notch Persson 最近在 ludum dare 上的参赛作品。Notch 认为它够好,我就认为它够好! 【Demo (arrow keys / touch)】【Source

bubuko.com,布布扣

用了光线投射就像开挂一样,作为一名懒得出油的程序员,我表示非常喜欢。你可以舒畅地浸入到3D环境中而不受“真3D”复杂性的束缚。举例来说,光线投射算法消耗线性时间,所以不用优化也可以加载一个巨大的世界,它执行的速度跟小型世界一样快。水平面被定义成简单的网格而不是多边形网面树,所以即使没有 3D 建模基础或数学博士学位也可以直接投入进去学习。

利用这些技巧很容易就可以做一些让人嗨爆的事情。15分钟之后,你会到处拍下你办公室的墙壁,然后检查你的 HR 文档看有没有规则禁止“工作场所枪战建模”。

玩家

我们从何处投射光线?这就是玩家对象(Palyer)的作用,只需要三个属性 x,y,direction。

function Player(x, y, direction) {
  this.x = x;
  this.y = y;
  this.direction = direction;
}

地图

我们将地图存作简单的二维数组。数组中,0代表没墙,1代表有墙。你还可以做得更复杂些,比如给墙设任意高度,或者将多个墙数据的“故事(stories)”打包进数组。但作为我们的第一次尝试,用0-1就足够了。

function Map(size) {
  this.size = size;
  this.wallGrid = new Uint8Array(size * size);
}

投射一束光线

这里就是窍门:光线投射引擎不会一次性绘制出整个场景。相反,它把场景分成独立的列然后一条一条地渲染。每一列都代表从玩家特定角度投射出的一条光线。如果光线碰到墙壁,引擎会计算玩家到墙的距离然后在该列中画出一个矩形。矩形的高度取决于光线的长度——越远则越短。

bubuko.com,布布扣

绘画的光线越多,显示效果就会越平滑。

1.找到每条光线的角度

我们首先找出每条光线投射的角度。角度取决于三点:玩家面向的方向,摄像机的视野,还有正在绘画的列。

var angle = this.fov * (column / this.resolution - 0.5);
var ray = map.cast(player, player.direction + angle, this.range);

2.通过网格跟踪每条光线

接下来,我们要检查每条光线经过的墙。这里的目标是最终得出一个数组,列出了光线离开玩家后经过的每面墙。

bubuko.com,布布扣

从玩家开始,我们找出最接近的横向(stepX)和纵向(stepY)网格坐标线。移到最近的地方然后检查是否有墙(inspect)。一直重复检查直到跟踪完每条线的所有长度。

   function ray(origin) {
     var stepX = step(sin, cos, origin.x, origin.y);
     var stepY = step(cos, sin, origin.y, origin.x, true);
     var nextStep = stepX.length2 < stepY.length2
       ? inspect(stepX, 1, 0, origin.distance, stepX.y)
       : inspect(stepY, 0, 1, origin.distance, stepY.x);
   
     if (nextStep.distance > range) return [origin];
     return [origin].concat(ray(nextStep));
   }
   

寻找网格交点很简单:只需要对 x 向下取整(1,2,3…),然后乘以光线的斜率(rise/run)得出 y。

   var dx = run > 0 ? Math.floor(x + 1) - x : Math.ceil(x - 1) - x;
   var dy = dx * (rise / run);
   

现在看出了这个算法的亮点没有?我们不用关心地图有多大!只需要关注网格上特定的点——与每帧的点数大致相同。样例中的地图是32×32,而32,000×32,000的地图一样跑得这么快!

3.绘制一列

跟踪完一条光线后,我们就要画出它在路径上经过的所有墙。

 

阅读全文

265行 JavaScript 代码实现第一人称引擎,布布扣,bubuko.com

265行 JavaScript 代码实现第一人称引擎

标签:style   class   blog   code   java   http   

原文地址:http://www.cnblogs.com/jaward/p/3781645.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!