您的位置:首页 > Web前端 > HTML5

HTML5 OO实践

2012-03-29 08:42 239 查看

简介

人工智能(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;

return this;

},

sub: function(v) {

return new Vector2(this.x - v.x, this.y - v.y);

},

multiplyScalar: function(s) {

this.x *= s;

this.y *= s;

return this;

},

divideScalar: function(s) {

if (s) {

this.x /= s;

this.y /= s;

} else {

this.set(0, 0);

}

return this;

},

length: function() {

return Math.sqrt(this.lengthSq());

},

normalize: function() {

return this.divideScalar(this.length());

},

lengthSq: function() {

return this.x * this.x + this.y * this.y;

},

distanceToSquared: function(v) {

var dx = this.x - v.x,

dy = this.y - v.y;

return dx * dx + dy * dy;

},

distanceTo: function(v) {

return Math.sqrt(this.distanceToSquared(v));

},

setLength: function(l) {

return this.normalize().multiplyScalar(l);

}

};

window.Vector2 = Vector2;

} (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)";

cxt.font = "bolder 160px 宋体";

cxt.textBaseline = 'top';

cxt.fillText(tex, 20, 20);

//收集所有像素

for (y = 1; y < canvas.height; y += 7) {

for (x = 1; x < canvas.width; x += 7) {

imageData = cxt.getImageData(20 + x, 20 + y, 1, 1);

if (imageData.data[0] > 170) {

ps.push({

px: 20 + x,

py: 20 + y

})

}

}

};

cxt.fillStyle = "rgba(0,0,0,1)";

cxt.fillRect(20, 20, canvas.width, canvas.height);

//像素点和小球转换

for (var i in ps) {

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));

balls.push(ball);

};

cxt.fillStyle = "#fff";

for (i in balls) {

cxt.beginPath();

cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2, true);

cxt.closePath();

cxt.fill();

}

//解散:生成随机速度

for (var i in balls) {

balls[i].disband();

}
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;

for (var i in balls) {

if (collectionCMD === true && balls[i].position.distanceTo(balls[i].collectionPosition) < 2) {

balls[i].velocity.y = 0;

balls[i].velocity.x = 0;

}

}

if (time === 3000) {

collectionCMD = true;

for (var i in balls) {

balls[i].collection(230);

}

}

if (time === 7500) {

time = 0;

collectionCMD = false;

for (var i in balls) {

balls[i].disband();

}

}

for (var i in balls) {

cxt.beginPath();

cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2, true);

cxt.closePath();

cxt.fill();

balls[i].position.y += balls[i].velocity.y * cyc / 1000;

balls[i].position.x += balls[i].velocity.x * cyc / 1000;

}

},
40 cyc);

这里使用time整体控制,使其无限loop。ps:这里还有一点不够OO的地方就是应当为ball提供一个draw方法。

其中的balls[i].position.distanceTo(balls[i].collectionPosition) 代表了点与点之间的距离,这里判断小球是否到了集合点或家。这里其几何意义就不再向量了。

在线演示

这你也敢叫人工智能?ok,未完待续......

(function (window) { var Vector2 = function (x, y) { this.x = x || 0; this.y = y || 0 }; Vector2.prototype = { set: function (x, y) { this.x = x; this.y = y; return this }, sub: function (v) { return new Vector2(this.x - v.x, this.y - v.y) }, multiplyScalar: function (s) { this.x *= s; this.y *= s; return this }, divideScalar: function (s) { if (s) { this.x /= s; this.y /= s } else { this.set(0, 0) }; return this }, length: function () { return Math.sqrt(this.lengthSq()) }, normalize: function () { return this.divideScalar(this.length()) }, lengthSq: function () { return this.x * this.x + this.y * this.y }, distanceToSquared: function (v) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy }, distanceTo: function (v) { return Math.sqrt(this.distanceToSquared(v)) }, setLength: function (l) { return this.normalize().multiplyScalar(l) } }; window.Vector2 = Vector2 } (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)); (function (window) { var MathHelp = function () { }; MathHelp.getRandomNumber = function (min, max) { return (min + Math.floor(Math.random() * (max - min + 1))) }; window.MathHelp = MathHelp } (window)); canvas = document.getElementById("myCanvas"); cxt = canvas.getContext("2d"); var ps = [], balls = []; function init(tex) { balls.length = 0; ps.length = 0; cxt.clearRect(0, 0, canvas.width, canvas.height); cxt.fillStyle = "rgba(0,0,0,1)"; cxt.fillRect(0, 0, canvas.width, canvas.height); cxt.fillStyle = "rgba(255,255,255,1)"; cxt.font = "bolder 115px 宋体"; cxt.textBaseline = 'top'; cxt.fillText(tex, 20, 20); for (y = 1; y < 200; y += 6) { for (x = 1; x < canvas.width; x += 6) { imageData = cxt.getImageData(20 + x, 20 + y, 1, 1); if (imageData.data[0] > 170) { ps.push({ px: 20 + x, py: 20 + y }) } } }; cxt.fillStyle = "rgba(0,0,0,1)"; cxt.fillRect(20, 20, canvas.width, canvas.height); for (var i in ps) { 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)); balls.push(ball) }; cxt.fillStyle = "#fff"; for (i in balls) { cxt.beginPath(); cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2, true); cxt.closePath(); cxt.fill() }; for (var i in balls) { balls[i].disband() } }; var time = 0; var cyc = 15; var a = 80; var collectionCMD = false; init("当耐特砖家"); setInterval(function () { cxt.fillStyle = "rgba(0, 0, 0, .3)"; cxt.fillRect(0, 0, canvas.width, canvas.height); cxt.fillStyle = "#fff"; time += cyc; for (var i in balls) { if (collectionCMD === true && balls[i].position.distanceTo(balls[i].collectionPosition) < 2) { balls[i].velocity.y = 0; balls[i].velocity.x = 0 } }; if (time === 3000) { collectionCMD = true; for (var i in balls) { balls[i].collection(230) } }; if (time === 7500) { time = 0; collectionCMD = false; for (var i in balls) { balls[i].disband() } }; for (var i in balls) { cxt.beginPath(); cxt.arc(balls[i].position.x, balls[i].position.y, balls[i].radius, 0, Math.PI * 2, true); cxt.closePath(); cxt.fill(); if (balls[i].radius + balls[i].position.x > canvas.width) { balls[i].position.x = canvas.width - balls[i].radius; balls[i].velocity.x *= -1 }; if (balls[i].position.x < balls[i].radius) { balls[i].position.x = balls[i].radius; balls[i].velocity.x *= -1 }; if (balls[i].radius + balls[i].position.y > canvas.height) { balls[i].position.y = canvas.height - balls[i].radius; balls[i].velocity.y *= -1 }; if (balls[i].position.y < balls[i].radius) { balls[i].position.y = balls[i].radius; balls[i].velocity.y *= -1 }; balls[i].position.y += balls[i].velocity.y * cyc / 1000; balls[i].position.x += balls[i].velocity.x * cyc / 1000 } }, 15);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: