zz85在这里用简单的代码就生成了一张随机的城市地图。生成地图与画一棵随机的树十分相似,都是先给出初始的元素,每个元素在满足条件时会生成新的子代,最后由所有的元素共同组成了我们想要的地图或树。
地图中的每一条道路对应程序中的一个Boid对象,Boid对象中用两个向量分别表示道路的起点和终点坐标。程序中的向量是使用
Three.js
这个库中的Vector2
对象来表示的。
1 | // 使用three.js中的向量来表示 |
Boid对象还有这些属性:
x,y
:道路上距离起点最远的坐标angle
:道路的角度,会在其父代角度基础上偏转一个随机的角度distance
:这条道路的长度dead
:对象是否已经死亡
Boid还有一个update
方法,它有如下的几个功能:
- 更新 x,y 坐标
1 | this.distance += 2; |
- 检测相交情况,根据更新后的坐标作图。
在程序中需要创建两个数组用于保存Boid对象,boids中存放当前存活的元素,all_boids存放所有(包括存活和死亡)的元素。产生一个新元素时,会被同时放入两个数组,当元素死亡后,将其从boids中移除。
对于一条道路A,它会一直向前延伸,直到与另一条道路相交,这时将A的状态设置为dead。为了检测相交,需要对all_boids数组中的元素进行遍历。如果与其中的元素B出现了交点,可能是以下几种情况:
- A是B的子代
- B是A的子代
- B的终点在A上
- A在延伸过程中遇上了B
这最后一种情况才是我们所需要的,将交点坐标赋给A的终点,将A从boids数组中删去。以上检查交点的过程发生在update()函数中。
在程序开始时,首先创建四个元素来表示画面的边框。
1 | var b1 = new Boid(); |
然后创建第一个boid
,它的坐标在画面的中间
1 | var b = new Boid(width/2, height/2, Math.random() * 2 * Math.PI); |
调用setInterval
函数进入循环,首先检查boids.length
,如果当前没有存活的boid
,则退出循环,程序完成。否则遍历所有存活的Boid
,更新其状态。在满足如下的几个条件时生成子代。
- 没有死亡
- 只有0.1的概率产生子代
- 当前所有存活元素的数量小于50
1 | for (i = 0; i < boids.length; i++) { |
最终当存活的Boid数量为零时,程序运行完毕,就得到了一张随机的城市道路地图。当然,现在的地图还只是 2D 的版本,想生成 3D 的城市,可以查看下面的参考资料中zz85的博客。
在线的demo
参考资料: