簡介
人工智能(Artificial Intelligence) ,英文縮寫為AI。它是研究、開發用於模擬、延伸和擴展智能的理論、方法、技術及應用系統的一門新的技術科學。本篇從嚴格意義上說屬於人工智能的范疇,但也是基礎中的基礎。本篇的目的是要賦予小球解散和集合兩項基本指令(智商),本篇內容中相關算法適用於子彈追蹤等塔防類游戲當中。
基礎類
二維向量(2D vector)可謂2D游戲或是動畫里最常用型別了。這里二維向量用Vector2類實現,用(x, y)表示。 Vector2亦用來表示空間中的點(point),而不另建類。先看代碼:
1 (function(window) {
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));
使用該類需要特別注意和區分的地方是:
它什么時候代表點、什么時候代表向量。
當其代表向量的時候,它的幾何意義是什么?
不能把其當成一個黑盒來調用,需要知其然並知其所以然。
在下面的使用的過程當中,我會特別標注其代表點還是向量;代表向量時,其幾何意義是什么?
給小球賦予智商,顧名思義需要小球類:
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的關鍵代碼。
this.velocity = this.collectionPosition.sub( this.position).setLength(v);
},
因為setLength設置向量的長度:
return this.normalize().multiplyScalar(l);
}
所以collection可以改成:
normalize是獲取單位向量,也可以改成:
整個Vector2黑盒就全部展現出來,其整個過程都是向量的運算,代表含義如下所示:
this.collectionPosition
.sub(this.position) 獲取小球所在位置指向小球集合位置的向量;
.divideScalar(this.length()) 得到該向量的單位向量;
.multiplyScalar(v); 改變該向量的長度。
最后把所得到的向量賦給小球的速度。
上面我們還是用到了解散方法,其過程是幫小球生成一個隨機速度,用到了MathHelp類的一個靜態方法:
var MathHelp = {};
MathHelp.getRandomNumber = function (min, max) {
return (min + Math.floor(Math.random() * (max - min + 1)));
}
window.MathHelp = MathHelp;
} (window));
粒子生成
寫了Vector2、Ball、MathHeper三個類之后,終於可以開始實現一點東西出來!
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:
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,未完待續......