←首页

上篇 Canvas学习。

效果看这里。
预览效果
为了让小游戏效果更佳,需要横屏才能玩。(解开手机的锁定屏幕 + 旋转手机)就能看到效果了。

游戏控制

整个游戏操作很简单,只需要按住-释放就ok了,所以这里只需要监听touchstart, touchend事件。

1
2
3
4
function addEventListener() {
window.addEventListener('touchstart', touchstart);
window.addEventListener('touchend', touchend);
}

游戏loop

整个游戏的loop同上篇相似,都是通过 requestAnimationFrame 来循环绘制内容,这样可以保证效果的流畅性。

1
2
3
4
5
6
7
8
9
10
11
12
13
function loop() {
if (isHorizontal) {
requestAnimationFrame(loop);
}
updateRotation();
ctx.clearRect(0, 0, winWidth, winHeight);
drawLever();
drawSemi();
bulletRender();
drawText();
drawBoard();
}

内容绘制

效果
从图片上看,将绘制工作分成了绘制操作杆,发射堡垒,子弹,文字和地板。
主要难点在操作杆和子弹上。

蓄力期

蓄力期小球一直在控制杆上,而控制杆的长度固定,通过旋转角度和控制杆起点就能算出小球位置。
所以这个过程只需要控制杆的旋转角度即可。
计算位置
通过旋转角度计算出a,b两边长,再转换成控制杆尾部坐标。

1
2
3
4
5
6
7
8
9
function calPosByAngle(angle) {
var a = leverHeight * Math.sin(angleHelper(angle));
var B = 90 - angle;
var b = leverHeight * Math.sin(angleHelper(B));
return {
x: leverPos.x - b,
y: leverPos.y - a
};
}

让杆旋转有加速度

1
2
3
4
5
6
function updateRotation(){
// ...
anv += ana;
currentAngle = currentAngle -= anv;
// ...
}

Canvas的绘制图片的起点是左上角,为了让图片中心能处在控制杆末端,需要做特殊处理。
如果使用三角函数会很麻烦,因为图片的与控制杆的旋转角度一直在变化。
庆幸Canvas有 translate 方法,为了让绘制子弹的旋转不影响到其他图形绘制,可以使用隔离(相当于堆栈)。

1
2
3
4
5
6
7
ctx.save();
ctx.translate(_this.x, _this.y);
ctx.translate(-30, -30);
ctx.drawImage(img, 0, 0, img.width* 0.3, img.height*0.3);
ctx.restore();

发射期

当用户松开手指或者旋转角度超过限制时,进入发射器。
发射前期控制杆回到原来的位置,并释放子弹,子弹根据旋转角度进入抛体运动。

为了让杆有弹性效果,增加了点代码,说实话这部分代码我也有点迷糊。
Rachel Smith 她自己也不知道…
但大概值这样的,有兴趣的自己去研究吧。

1
2
3
4
5
6
7
8
9
10
// this the spring magic in the loop - for example the x property of an object
// spring & damper from k (stiffness) and b (damping constant)
var spring_x = k * ( (block.x - target.x));
var damper_x = b * ( block.vx );
// update acceleration
block.ax = ( spring_x + damper_x ) / block.mass;
// update velocity
block.vx += block.ax * (t/1000);
// update position
block.x += block.vx * (t/1000);

小球的抛体运动比较简单,需要考虑重力加速度,空气阻力,碰到墙之后的回弹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function update() = {
_this.vy += gravity;
_this.x += _this.vx;
_this.y += _this.vy;
_this.vx *= 0.98;
_this.vy *= 0.98;
// 碰壁反弹
if(_this.x < 0) {
_this.x = 0;
_this.vx *= -1.4;
}
if (_this.x > winWidth - bulletRadius) {
_this.x = winWidth - bulletRadius;
_this.vx *= -1.4;
}
// ...
}

爆炸效果

爆炸效果
当小球砸在甲板上时会爆炸,为了更生动,通过工具把图片的主要颜色成分取出来。

1
var colors = ['#000000','#b7ada1','#7f7a72','#a4a59d', ...];

然后在爆炸时创建颗粒物, 颜色在 colors 中随机取。
看了HJM的文章,才知道原来Canvas本身也可以取得图片的组成颜色。

1
2
3
4
5
6
7
8
9
10
11
12
function createParticles(num, x, y, color) {
var particles = [];
for (var i = 0; i < num; i++) {
var vx = -2 + Math.random() * 4;
var size = 4 + Math.random() * 4;
var vy = -1 + Math.random() * -2;
var tcolor = colors[Math.floor(Math.random() * colors.length)];
particles.push(new Partical(x, y, vx, vy, size, tcolor, 1));
}
return particles;
}

By The Way

这个Demo大部分代码来自 Rachel Smith - Hack Physics and JavaScript part 2,我仅是以学习的姿势借鉴一下。

还有为了让Canvas能在Retina屏上正常显示,使用了stackoverflow - Canvas Retina display代码。

1
2
3
4
5
6
7
8
9
10
11
if (window.devicePixelRatio > 1) {
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
canvas.width = canvasWidth * window.devicePixelRatio;
canvas.height = canvasHeight * window.devicePixelRatio;
canvas.style.width = canvasWidth;
canvas.style.height = canvasHeight;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}

如需转载,请注明出处: http://w3ctrain.com/2016/07/31/canvas-fire-hole/