简介
人工智能(Artificial Intelligence) ,英文缩写为AI。它是研究、开发用于模拟、延伸和扩展智能的理论、方法、技术及应用系统的一门新的技术科学。本篇从严格意义上说属于人工智能的范畴,但也是基础中的基础。本篇的目的是要赋予小球解散和集合两项基本指令(智商),本篇内容中相关算法适用于子弹追踪等塔防类游戏当中。
基础类
二维向量(2D vector)可谓2D游戏或是动画里最常用型别了。这里二维向量用Vector2类实现,用(x, y)表示。 Vector2亦用来表示空间中的点(point),而不另建类。先看代码:
1 (function(window) {
2
var Vector2 =
function(x, y) {
3
this.x = x || 0;
4
this.y = y || 0;
5 };
6 Vector2.prototype = {
7 set:
function(x, y) {
8
this.x = x;
9
this.y = y;
10
return
this;
11 },
12 sub:
function(v) {
13
return
new Vector2(
this.x - v.x,
this.y - v.y);
14 },
15 multiplyScalar:
function(s) {
16
this.x *= s;
17
this.y *= s;
18
return
this;
19 },
20 divideScalar:
function(s) {
21
if (s) {
22
this.x /= s;
23
this.y /= s;
24 }
else {
25
this.set(0, 0);
26 }
27
return
this;
28 },
29 length:
function() {
30
return Math.sqrt(
this.lengthSq());
31 },
32 normalize:
function() {
33
return
this.divideScalar(
this.length());
34 },
35 lengthSq:
function() {
36
return
this.x *
this.x +
this.y *
this.y;
37 },
38 distanceToSquared:
function(v) {
39
var dx =
this.x - v.x,
40 dy =
this.y - v.y;
41
return dx * dx + dy * dy;
42 },
43 distanceTo:
function(v) {
44
return Math.sqrt(
this.distanceToSquared(v));
45 },
46 setLength:
function(l) {
47
return
this.normalize().multiplyScalar(l);
48 }
49 };
50 window.Vector2 = Vector2;
51 } (window));
使用该类需要特别注意和区分的地方是:
它什么时候代表点、什么时候代表向量。
当其代表向量的时候,它的几何意义是什么?
不能把其当成一个黑盒来调用,需要知其然并知其所以然。
在下面的使用的过程当中,我会特别标注其代表点还是向量;代表向量时,其几何意义是什么?
给小球赋予智商,顾名思义需要小球类:
(
function(window) {
var Ball =
function(r, v, p, cp) {
this.radius = r;
this.velocity = v;
this.position = p;
this.collectionPosition = cp
}
Ball.prototype = {
collection:
function(v) {
this.velocity =
this.collectionPosition.sub(
this.position).setLength(v)
},
disband:
function() {
this.velocity =
new Vector2(MathHelp.getRandomNumber( - 230, 230), MathHelp.getRandomNumber( - 230, 230))
}
}
window.Ball = Ball
} (window));
其中
小球拥有4个属性,分别是:radius半径、velocity速度(Vector2)、position位置(Vector2)、collectionPosition集合点/小球的家(Vector2)。
小球拥有2个方法,分别是:collection集合、disband解散。
小球的集合方法所传递的参数为集合的速度,因为小球都有一个集合点的属性,所以这里不用再传入集合点/家给小球。
这里详细分析一下collection方法,这也是整个demo的关键代码。
collection:
function (v) {
this.velocity =
this.collectionPosition.sub(
this.position).setLength(v);
},
因为setLength设置向量的长度:
setLength:
function (l) {
return
this.normalize().multiplyScalar(l);
}
所以collection可以改成:
this.velocity =
this.collectionPosition.sub(
this.position).normalize().multiplyScalar(v);
normalize是获取单位向量,也可以改成:
this.collectionPosition.sub(
this.position).divideScalar(
this.length()).multiplyScalar(v);
整个Vector2黑盒就全部展现出来,其整个过程都是向量的运算,代表含义如下所示:
this.collectionPosition
.sub(this.position) 获取小球所在位置指向小球集合位置的向量;
.divideScalar(this.length()) 得到该向量的单位向量;
.multiplyScalar(v); 改变该向量的长度。
最后把所得到的向量赋给小球的速度。
上面我们还是用到了解散方法,其过程是帮小球生成一个随机速度,用到了MathHelp类的一个静态方法:
(
function (window) {
var MathHelp = {};
MathHelp.getRandomNumber =
function (min, max) {
return (min + Math.floor(Math.random() * (max - min + 1)));
}
window.MathHelp = MathHelp;
} (window));
粒子生成
写了Vector2、Ball、MathHeper三个类之后,终于可以开始实现一点东西出来!
1
var ps = [],
2 balls = [];
3
function init(tex) {
4 balls.length = 0;
5 ps.length = 0;
6 cxt.clearRect(0, 0, canvas.width, canvas.height);
7 cxt.fillStyle = "rgba(0,0,0,1)";
8 cxt.fillRect(0, 0, canvas.width, canvas.height);
9 cxt.fillStyle = "rgba(255,255,255,1)";
10 cxt.font = "bolder 160px 宋体";
11 cxt.textBaseline = 'top';
12 cxt.fillText(tex, 20, 20);
13
14
//
收集所有像素
15
for (y = 1; y < canvas.height; y += 7) {
16
for (x = 1; x < canvas.width; x += 7) {
17 imageData = cxt.getImageData(20 + x, 20 + y, 1, 1);
18
if (imageData.data[0] > 170) {
19 ps.push({
20 px: 20 + x,
21 py: 20 + y
22 })
23 }
24 }
25 };
26 cxt.fillStyle = "rgba(0,0,0,1)";
27 cxt.fillRect(20, 20, canvas.width, canvas.height);
28
29
//
像素点和小球转换
30
for (
var i
in ps) {
31
var ball =
new Ball(2,
new Vector2(0, 0),
new Vector2(ps[i].px, ps[i].py),
new Vector2(ps[i].px, ps[i].py));
32 balls.push(ball);
33 };
34
35 cxt.fillStyle = "#fff";
36
for (i
in balls) {
37 cxt.beginPath();
38 cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2,
true);
39 cxt.closePath();
40 cxt.fill();
41 }
42
43
//
解散:生成随机速度
44
for (
var i
in balls) {
45 balls[i].disband();
46 }
47 }
其中分三个步骤:收集所有像素、 像素点和小球转换、生成随机速度。整个demo我们需要一个loop:
1
var time = 0;
2
var cyc = 15;
3
var a = 80;
4
var collectionCMD =
false;
5 setInterval(
function() {
6 cxt.fillStyle = "rgba(0, 0, 0, .3)";
7 cxt.fillRect(0, 0, canvas.width, canvas.height);
8 cxt.fillStyle = "#fff";
9 time += cyc;
10
for (
var i
in balls) {
11
if (collectionCMD ===
true && balls[i].position.distanceTo(balls[i].collectionPosition) < 2) {
12 balls[i].velocity.y = 0;
13 balls[i].velocity.x = 0;
14 }
15 }
16
17
if (time === 3000) {
18 collectionCMD =
true;
19
for (
var i
in balls) {
20 balls[i].collection(230);
21 }
22 }
23
if (time === 7500) {
24 time = 0;
25 collectionCMD =
false;
26
for (
var i
in balls) {
27 balls[i].disband();
28 }
29 }
30
31
for (
var i
in balls) {
32 cxt.beginPath();
33 cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2,
true);
34 cxt.closePath();
35 cxt.fill();
36 balls[i].position.y += balls[i].velocity.y * cyc / 1000;
37 balls[i].position.x += balls[i].velocity.x * cyc / 1000;
38 }
39 },
40 cyc);
这里使用time整体控制,使其无限loop。ps:这里还有一点不够OO的地方就是应当为ball提供一个draw方法。
其中的balls[i].position.distanceTo(balls[i].collectionPosition) 代表了点与点之间的距离,这里判断小球是否到了集合点或家。这里其几何意义就不再向量了。
在线演示
这你也敢叫人工智能?ok,未完待续......