kelele67的学习笔记-html5小游戏

tinyHeart

之前一直在做 swift 在IOS上的一些界面设计啊,动画效果啥的,主要是来自简书上看到的自学 iOS - 三十天三十个 Swift 项目。这个项目我才做了几个。打算做完了再整合成一篇文章发表出来。结果学到一半就。。不过有一点可以辩解的是,之前的确是一直在不断的学啊学。感觉自己身心疲惫,也是有点学不动了,但是现在又充满了动力蛤蛤。

最近为了学(fu)习 web 开发,决定做一个慕课网的教程HTML5小游戏—爱心鱼

想起来还有一件让我充满动力的事就是除了自习室有空调能够暖和过冬以外就是我的sublime换了一个主题,代码颜色变得超好看,我超喜欢的,哈哈哈我是不会告诉你是 Boxy Nova.sublime-theme这个主题的。

这个爱心鱼html5小游戏的制作通过以下几个步骤进行:

  1. 页面搭建
  2. 绘制

页面搭建

主要就是建立src资源文件夹和js文件夹再加上一个html文件就行了

首先是html文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>tinyHeart</title>
<style type="text/css">
body{
padding-top: 10px;
}
.all_bg{
width: 800px;
height: 600px;
margin: 0px auto;
}
#allcanvas{
position: relative;
width: 800px;
height: 600px
margin: 0px;
}
#canvas1{
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
}
#canvas2{
position: absolute;
bottom: 0;
left: 0;
z-index: 0;
}
</style>
</head>
<body>
<div class="all_bg">
<div class="allcanvas">
<canvas id="canvas1" width="800" height="600"></canvas>
<canvas id="canvas2" width="800" height="600"></canvas>
</div>
</div>
<script type="text/javascript" src ="js/main.js"></script>
</body>
</html>

还有main.js文件,把准备工作做好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var can1;
var can2;
var ctx1;
var ctx2;
var canWidth;
var canHeight;
var lastTime;
var deltaTime;//两帧之间的时间差
document.body.onload = game;
function game()
{
init();
lastTime = Date.now();
deltaTime = 0;
gameloop();
}
function init()
{
//获得canvas context
can1 = document.getElementById("canvas1");//前面的 fishes, dust, UI, circle
ctx1 = can1.getContext('2d');
can2 = document.getElementById("canvas2");//后面的 bg, ane, fruits
ctx2 = can2.getContext('2d');
}
function gameloop()
{
window.requestAnimFrame(gameloop);
var now = Date.now();
deltaTime = now - lastTime;
lastTime = now;
}

在html文件中添加js文件

1
<script type="text/javascript" src ="js/background.js"></script>


绘制

  1. 绘制背景
  2. 海葵绘制
  3. 果实绘制(静态果实)
  4. 果实绘制(果实上浮)
  5. 果实绘制(果实数量绘制)
  6. 大鱼绘制
  7. 大鱼随鼠标移动
  8. 大鱼和果实的碰撞检测
  9. 优化
  10. 小鱼绘制

1.绘制背景

编写background.js文件

1
2
3
4
function drawBackground()
{
ctx2.drawImage(bgPic, 0, 0, canWidth, canHeight);
}

把背景图片路径导入到main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var can1;
var can2;
var ctx1;
var ctx2;
var canWidth;
var canHeight;
var lastTime;
var deltaTime;//两帧之间的时间差
var bgPic = new Image();
document.body.onload = game;
function game()
{
init();
lastTime = Date.now();
deltaTime = 0;
gameloop();
}
function init()
{
//获得canvas context
can1 = document.getElementById("canvas1");//前面的 fishes, dust, UI, circle
ctx1 = can1.getContext('2d');
can2 = document.getElementById("canvas2");//后面的 bg, ane, fruits
ctx2 = can2.getContext('2d');
bgPic.src = "./src/background.jpg";
canWidth = can1.width;
canHeight = can1.height;
}
function gameloop()
{
window.requestAnimFrame(gameloop);
var now = Date.now();
deltaTime = now - lastTime;
lastTime = now;
drawBackground();
}

2.海葵绘制

建立ane.js文件定义海葵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var aneObj = function()
{
this.x = [];
this.len = [];
}
aneObj.prototype.num = 50;
//初始化海葵
aneObj.prototype.init = function()
{
for(var i = 0; i < this.num; i++)
{
this.x[i] = i * 16 + Math.random() * 20;
this.len[i] = 200 + Math.random() * 50;
}
}
aneObj.prototype.draw = function()
{
ctx2.save();
ctx2.globalAlpha = 0.6;
ctx2.lineWidth = 20;
ctx2.lineCap = "round";
ctx2.strokeStyle = "#3b154e"//先设置style
for(var i = 0; i < this.num; i++)
{
//beginPath, moveTo, lineTo, stroke, strokeStyle, lineWidth, lineCap
ctx2.beginPath();
ctx2.moveTo(this.x[i], canHeight);
ctx2.lineTo(this.x[i], canHeight - this.len[i]);
ctx2.stroke();
}
ctx2.restore();
}

同样的在main.js中绘制海葵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
var can1;
var can2;
var ctx1;
var ctx2;
var canWidth;
var canHeight;
var lastTime;
var deltaTime;//两帧之间的时间差
var bgPic = new Image();
var ane;
document.body.onload = game;
function game()
{
init();
lastTime = Date.now();
deltaTime = 0;
gameloop();
}
function init()
{
//获得canvas context
can1 = document.getElementById("canvas1");//前面的 fishes, dust, UI, circle
ctx1 = can1.getContext('2d');
can2 = document.getElementById("canvas2");//后面的 bg, ane, fruits
ctx2 = can2.getContext('2d');
bgPic.src = "./src/background.jpg";
canWidth = can1.width;
canHeight = can1.height;
ane = new aneObj();
ane.init();
}
function gameloop()
{
window.requestAnimFrame(gameloop);
var now = Date.now();
deltaTime = now - lastTime;
lastTime = now;
drawBackground();
ane.draw();
}

继续在html文件中添加js文件(接下来的步骤就省略这一步了,老是写这一步好麻烦,我好懒啊啊啊~)

最后画出来是酱紫的

3.果实绘制

  1. 果实绘制(静态果实)
  2. 果实绘制(果实上浮)
  3. 果实绘制(果实数量绘制)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
var fruitObj = function()
{
this.alive = [];//bool
this.x = [];
this.y = [];
this.aneID = [];
this.l = [];//果实的大小
this.spd =[];//果实自己的速度
this.fruitType = [];//果实类型
this.orange = new Image();
this.blue = new Image();
}
fruitObj.prototype.num = 30;
fruitObj.prototype.init = function()
{
for(var i = 0; i < this.num; i++)
{
this.alive[i] = false;
this.x[i] = 0;
this.y[i] = 0;
this.aneID[i] = 0;//哪一个海葵
this.spd[i] = Math.random() * 0.02 + 0.005;
//this.born(i);
this.fruitType[i] = "";
}
this.orange.src = "./src/fruit.png";
this.blue.src = "./src/blue.png";
}
fruitObj.prototype.draw = function()
{
for( var i = 0; i < this.num; i++)
{
var pic;
if(this.alive[i])
{
if(this.fruitType[i] == "blue") { //判断蓝色还是黄色果实
pic = this.blue;
}
else{
pic = this.orange;
}
//draw
//find an ane, grow, fly up
if(this.l[i] <= 14)
{
this.l[i] += this.spd[i] * deltaTime;
}
else{
//果实上升的速度变化
this.y[i] -= this.spd[i] * 8 * deltaTime;
ctx2.drawImage(pic, this.x[i]-this.l[i] / 2, this.y[i]-this.l[i] / 2, this.l[i], this.l[i]);
}
if(this.y[i] < 10)
{
this.alive[i] = false;
}
}
}
}
fruitObj.prototype.born = function(i)
{
var aneID = Math.floor(Math.random() * ane.num);
//this.aneID[i] = Math.floor(Math.random() * ane.num);
//果实出生点
this.x[i] = ane.x[aneID];
this.y[i] = canHeight - ane.len[aneID];
this.l[i] = 0;
this.alive[i] = true;
var fruRand = Math.random();
if(fruRand < 0.2)
{
this.fruitType[i] = "blue";
}
else{
this.fruitType[i] = "orange";
}
}
fruitObj.prototype.dead = function(i)
{
this.alive[i] = false;
}
function fruitMonitor()
{
var num = 0;
for(var i = 0; i < fruit.num; i++)
{
if(fruit.alive[i]) {
num++;
}
}
if(num < 15)
{
sendFruit();
return;
}
}
function sendFruit()
{
for(var i = 0; i < fruit.num; i++)
{
if(!fruit.alive[i])
{
fruit.born(i);
return;
}
}
}
// fruitObj.prototype.update = function()
// {
// var num = 0;
// for(var i = 0; i < this.alive[i]; i++)
// {
// if(this.alive[i]) num++;
// }
// }

6. 大鱼绘制

  1. 大鱼绘制
  2. 大鱼随鼠标移动
  3. 大鱼和果实的碰撞检测

大鱼的绘制及随鼠标移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var dadObj = function()
{
this.x;
this.y;
this.angle;
this.bigEye = new Image();
this.bigBody = new Image();
this.bigTail = new Image();
}
dadObj.prototype.init = function()
{
this.x = canWidth * 0.5;
this.y = canHeight * 0.5;
this.angle = 0;
this.bigEye.src = "./src/bigEye0.png";
this.bigBody.src = "./src/bigSwim0.png";
this.bigTail.src = "./src/bigTail0.png";
}
dadObj.prototype.draw = function()
{
//lerp x, y 趋向于一个目标值
this.x = lerpDistance(mx, this.x, 0.9);
this.y = lerpDistance(my, this.y, 0.9);
//delta angle
//Math.atan2(y, x)
var deltaY = my - this.y;
var deltaX = mx - this.x;
//大鱼运动时旋转角度
var beta = Math.atan2(deltaY, deltaX) + Math.PI;
//lerp angle
this.angle = lerpAngle(beta, this.angle, 0.7);
ctx1.save();//仅适用于大鱼
ctx1.translate(this.x, this.y);
ctx1.rotate(this.angle);
ctx1.drawImage(this.bigTail, -this.bigTail.width * 0.5 + 30, -this.bigTail.height * 0.5);
ctx1.drawImage(this.bigBody, -this.bigBody.width * 0.5, -this.bigBody.height * 0.5);
ctx1.drawImage(this.bigEye, -this.bigEye.width * 0.5, -this.bigEye.height * 0.5);
ctx1.restore();
}

碰撞检测

添加collision.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//判断大鱼和果实的距离
function dadFruitsCollision()
{
for(var i = 0; i < fruit.num; i++)
{
if(fruit.alive[i])
{
//calculate length
var l = calLength2(fruit.x[i], fruit.y[i], dad.x, dad.y);
if(l < 400)
{
//fruit eaten
fruit.dead(i);
}
}
}
}

10. 小鱼绘制

  1. 优化
  2. 小鱼绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var babyObj = function()
{
this.x;
this.y;
this.angle;
this.babyEye = new Image();
this.babyBody = new Image();
this.babyTail = new Image();
}
babyObj.prototype.init = function()
{
this.x = canWidth * 0.5 - 50;
this.y = canHeight * 0.5 + 50;
this.angle = 0;
this.babyEye.src = "./src/babyEye0.png";
this.babyBody.src = "./src/babyFade0.png";
this.babyTail.src = "./src/babyTail0.png";
}
babyObj.prototype.draw = function()
{
//lerp x, y
this.x = lerpDistance(dad.x, this.x, 0.98)
this.y = lerpDistance(dad.y, this.y, 0.98)
//lerp angle
var deltaY = dad.y - this.y;
var deltaX = dad.x - this.x;
//大鱼运动时旋转角度
var beta = Math.atan2(deltaY, deltaX) + Math.PI;// [-PI, PI]
//lerp angle
this.angle = lerpAngle(beta, this.angle, 0.7);
//ctx1
ctx1.save();
//translate() 转移原点API
ctx1.translate(this.x, this.y);
ctx1.rotate(this.angle);
ctx1.drawImage(this.babyTail, -this.babyTail.width * 0.5 + 23, -this.babyTail.height * 0.5);
ctx1.drawImage(this.babyBody, -this.babyBody.width * 0.5, -this.babyBody.height * 0.5);
ctx1.drawImage(this.babyEye, -this.babyEye.width * 0.5, -this.babyEye.height * 0.5);
ctx1.restore();
}

到这里,我们的游戏的上半部分都完成了,接下来我们要做的就是添加一些动态效果。

以上用到的很多函数都封装在了commonFunction.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
window.requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
return window.setTimeout(callback, 1000 / 60);
};
})();
function calLength2(x1, y1, x2, y2) {
return Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
}
function randomColor() {
var col = [0, 1, 2];
col[0] = Math.random() * 100 + 155;
col[0] = col[0].toFixed();
col[1] = Math.random() * 100 + 155;
col[1] = col[1].toFixed();
col[2] = Math.random() * 100 + 155;
col[2] = col[2].toFixed();
var num = Math.floor(Math.random() * 3);
col[num] = 0;
return "rgba(" + col[0] + "," + col[1] + "," + col[2] + ",";
}
function lerpAngle(a, b, t) {
var d = b - a;
if (d > Math.PI) d = d - 2 * Math.PI;
if (d < -Math.PI) d = d + 2 * Math.PI;
return a + d * t;
}
function lerpDistance(aim, cur, ratio) {
var delta = cur - aim;
return aim + delta * ratio;
}
function inOboundary(arrX, arrY, l, r, t, b) { //在l r t b范围内的检测
return arrX > l && arrX < r && arrY > t && arrY < b;
}
function rgbColor(r, g, b) {
r = Math.round(r * 256);
g = Math.round(g * 256);
b = Math.round(b * 256);
return "rgba(" + r + "," + g + "," + b + ",1)";
}
function rgbNum(r, g, b) {
r = Math.round(r * 256);
g = Math.round(g * 256);
b = Math.round(b * 256);
return "rgba(" + r + "," + g + "," + b;
}
function rnd(m) {
var n = m || 1;
return Math.random() * n;
}
function rateRandom(m, n) {
var sum = 0;
for (var i = 1; i < (n - m); i++) {
sum += i;
}
var ran = Math.random() * sum;
for (var i = 1; i < (n - m); i++) {
ran -= i;
if (ran < 0) {
return i - 1 + m;
}
}
}
function distance(x1, y1, x2, y2, l) {
var x = Math.abs(x1 - x2);
var y = Math.abs(y1 - y2);
if (x < l && y < l) {
return true;
}
return false;
}
function AABBbox(object1, w1, h1, object2, w2, h2, overlap) {
A1 = object1.x + overlap;
B1 = object1.x + w1 - overlap;
C1 = object1.y + overlap;
D1 = object1.y + h1 - overlap;
A2 = object2.x + overlap;
B2 = object2.x + w2 - overlap;
C2 = object2.y + overlap;
D2 = object2.y + h2 - overlap;
if (A1 > B2 || B1 < A2 || C1 > D2 || D1 < C2) return false;
else return true;
}
function dis2(x, y, x0, y0) {
var dx = x - x0;
var dy = y - y0;
return dx * dx + dy * dy;
}
function rndi2(m, n) {
var a = Math.random() * (n - m) + m;
return Math.floor(a);
}