Canvas编写五彩连珠

上节中讲了哪些寻路,在和爱大家钻探时都反应临时走的不太对,绕远路了,其实代码首假若大方向的推断 
比方指标在右上,那应该是先右依旧先上
那几个并未做管理,假诺这么些做了拍卖,效果会更加好有的,但也难免会走弯路。 
贪心正是那般,不是最优,临近最优。也可望其余的同校有观点的能够谈谈下。小编那也只是私人民居房主张。

在看了几篇Canvas相关的篇章后,开采前两节的代码达成依旧非凡,因为了解的少,所以只幸亏蚀身已知的学问上做完成。可是万幸,那是贰个意识的历程,也是四个纠错和全面包车型客车长河。笔者首先次尝试一边上学一回写博客,笔者想那也会有助作者的读书,可以把文化精通的钢铁GreatWall些,起码忘的慢一些吧:)。

上一节中,我们留下了贰个flyin的主意未有介绍,这里想单独写一篇html5的动画达成。
在其次节中我们兑现了画贰个泡沫,并且成功的擦除了泡泡,但立即也说了别把棋盘的线给擦掉了,所以做了偏移量。所以说html5
Canvas照旧初级, 未有图层的概念,擦掉再想补回来,怎么补? 
答案正是重绘。 
没有错,整个Canvas重绘,这样就能够不用怀恋补哪儿了。即使带来了品质的损失,但绝对裁减的编码难度。何况计算机的力量也不差这一点损失。那么重绘的话,大家在Canvas是上存有的内需绘制的靶子都应有有draw方法。那是必须的。别的,全数的因素都有个左右的概念,所以要先绘制上边包车型地铁,再绘制上面包车型大巴。
而这几个上下就得靠
子元素的概念,那样在父成分绘制完结后遍历其子成分绘制,就无须操心掩饰的标题。

  既然可以接触了,那就足以判定是或不是足以祛除一样颜色的行、列或斜线了。只要>=5个一样的色球,就免去他们,况兼能够继续移动。如果无法去掉,那就再追加3个球。

  前两节学习了多少个为主绘制的办法,lineTomoveTo和arc,也询问坐标的状态,但写的相比较傻,只是单纯的落到实处。
比方棋盘的原初坐标假使有偏移量,大家还要总括他的有血有肉开头坐标和竣事坐标,实际上Canvas有存活的措施提供偏移的成效。
他叫
translate,另外还应该有缩放scale、旋转rotate,他们都足以用transform取代。所以,在代码方面还大概会稍微调解。然则这几个的求学恰巧也让自个儿理解如何达成动画效果。如若三个因素在一个Canvas上,实现动画,必然会须要擦除重绘的情形,倘诺成分之间有覆盖的情事,擦除就要求多思考了。当然,轻易的主意就是把任何画布根据当然全体因素的职务再次绘制三遍。所以在代码设计方面,须求把不一致的因素独立出来,每一个成分都有友好的draw方法,何况要遵从程序绘制Canvas。

  假若想把ready区的3个泡“飞入”棋盘,就须要让Canvas在泡移动的时候举行重绘,泡泡不动时不须要重绘。泡泡的移动很轻便完结,只要退换他的x,y坐标就能够。即使想达到动画的功用,就得在改造坐标时期,定时重绘,能够应用setInterval来促成。
  别的,大家不光飞入的动作要求重绘,游戏开端后游戏发烧友还要点击移动二个泡泡到别的一个格子,所以这里也要重绘。那绘制的消息这么多,整个重绘职业都要付出game来拓展,game调整全部的父成分。

[javascript]
clearLine: function (x1, y1, color, isClick) { if (this.isEmpty(x1,
y1))
{ if (isClick) game.ready.flyin();
return;
};
//给定三个坐标,看是或不是有满意的line能够被消除 //4根线 一 | / \
var current = this.getBubble(x1, y1);
if (!current.color) { console.log(current); }
 var arr1, arr2, arr3, arr4; arr1 = this.bubbles[y1];
//横线很轻松,正是数组的一项,现有的 arr2 = [];
for (var y = 0;
y < game.cellCount;
y++) arr2.push(this.getBubble(x1, y));
//竖线就是一列。
arr3 = [current];
arr4 = [current];
for (var i = 1;
i < game.cellCount ;
i++) { if (x1 – i >= 0 && y1 – i >= 0)
//\斜线的上半部分… arr3.unshift(this.getBubble(x1 – i, y1 – i));
if (x1 + i < game.cellCount && y1 + i < game.cellCount)
//\斜线的下半部分 arr3.push(this.getBubble(x1 + i, y1 + i));
if (x1 – i >= 0 && y1 + i < game.cellCount)
// /斜线的下半部分 arr4.push(this.getBubble
(x1 – i, y1 + i));
if (x1 + i < game.cellCount && y1 – i >= 0)
// /斜线的上班部分 arr4.unshift(this.getBubble(x1 + i, y1 – i));
}
var line1 = getLine(arr1);
var line2 = getLine(arr2);
var line3 = getLine(arr3);
var line4 = getLine(arr4);
var line = line1.concat(line2).concat(line3).concat(line4);
if (line.length < 5) {
if (isClick) game.ready.flyin();
return; }
else {
var me = this;
var i = 0;
game.play(“clearline”, function ()
{
if (i == line.length)
{ game.score.addScore(line.length); game.stop(“clearline”);
me.isMoving = false;
//game.ready.flyin();
return;
} me.isMoving = true;
var p = line[i];
me.setBubble(p.x, p.y, null);
i++; },
100);
} function getLine(bubbles) { var line = [];
for (var i = 0;
i < bubbles.length; i++) { var b = bubbles[i];
if (b.color == color)
{ line.push({ “x”: b.x, “y”: b.y });
} else {
if (line.length < 5) line = [];
else return line;
}
}
if (line.length < 5) return [];
return line;
}
}, 

  剖析一下嬉戏所需的要素:1、棋盘(地图)2、泡泡
3、等待区域(新的3个就要步入棋盘的泡沫)4、奖赏区域(白搭星、一级百搭星、炸弹)5、计算新闻6、按键
  所以在目的的希图方面起码要有几类
棋盘(map)、新泡泡区(ready)、奖赏区(awards)、泡泡(bubble)、星星1(star1)
、星星2(star2)
、炸弹(boom)、计算积分(score),还要富含游戏背后的多寡(data)。
OK,先那样设计,挨个的去贯彻。先把map和bubble重写了。
  此前把map写成了类,显著是不妥帖的,因为那个游戏不恐怕会有多少个map,所以直接定义为对象更方便人民群众。而泡沫显明要求多多,所以必要写成类相比较实惠。游戏里面全体指标需求拜会的全局变量和常量需求定义在三个game对象里,游戏初步则是调用game.start()的办法。所以先看下game的概念:

[javascript]
start: function () { this.map.init(); this.ready.init(); this.draw();
this.canvas.onclick = this.onclick; }, over: function () { alert(“GAME
OVER”); }, draw: function () { this.ctx.clearRect(0, 0, 400, 600);
this.ctx.save(); this.map.draw(); this.ready.draw(); this.ctx.restore();
}, play: function (action, interval) { var me = this; play =
setInterval(function () { action(); me.draw(); }, interval || 1); },
stop: function () { clearInterval(play); this.draw();
//console.log(this.ready.bubbles.length); }, 

 clearLine: function (x1, y1, color, isClick) {
if (this.isEmpty(x1, y1)) {
if (isClick) game.ready.flyin();
return;
};
//给定一个坐标,看是还是不是有满意的line可以被解除
//4根线 一 | / \
var current = this.getBubble(x1, y1);
if (!current.color) { console.log(current);
} var arr1, arr2, arr3, arr4; arr1 = this.bubbles[y1];
//横线极粗略,便是数组的一项,现有的 arr2 = [];
for (var y = 0;
 y < game.cellCount;
y++) arr2.push(this.getBubble(x1, y));
//竖线正是一列。
arr3 = [current];
arr4 = [current];
for (var i = 1;
i < game.cellCount ;
i++) { if (x1 – i >= 0 && y1 – i >= 0)
//\斜线的上半部分… arr3.unshift(this.getBubble(x1 – i, y1 – i));
if (x1 + i < game.cellCount && y1 + i < game.cellCount)
//\斜线的下半部分 arr3.push(this.getBubble(x1 + i, y1 + i));
if (x1 – i >= 0 && y1 + i < game.cellCount)
// /斜线的下半部分 arr4.push(this.getBubble(x1 – i, y1 + i));
 if (x1 + i < game.cellCount && y1 – i >= 0)
// /斜线的上班部分 arr4.unshift(this.getBubble(x1 + i, y1 – i));
 }
var line1 = getLine(arr1);
var line2 = getLine(arr2);
var line3 = getLine(arr3);
var line4 = getLine(arr4);
var line = line1.concat(line2).concat(line3).concat(line4);
if (line.length < 5) { if (isClick) game.ready.flyin();
return;
} else {
var me = this;
var i = 0; game.play(“clearline”, function () {
 if (i == line.length) { game.score.addScore(line.length);
game.stop(“clearline”);
me.isMoving = false;
//game.ready.flyin();
return;
} me.isMoving = true;
var p = line[i];
me.setBubble(p.x, p.y, null);
i++;
}, 100);
} function getLine(bubbles)
{ var line = [];
for (var i = 0;
i < bubbles.length;
i++) { var b = bubbles[i];
 if (b.color == color) {
line.push({ “x”: b.x, “y”: b.y });
} else { if (line.length < 5) line = [];
else return line;
}
}
 if (line.length < 5) return [];
 return line;
}
},
我们能够看看代码,主要有两点 1、可能须要管理的 横竖斜线
4个数组,然后看那4个数组是或不是有连日的。判别是或不是接二连三只要求三个动静数组来保障就足以了。
是因为考虑到而且免去多行的图景,所以要把三回九转的line
连接在联合,并在付钱得分时思索奖赏。
isClick参数重要用来剖断是不是是用户点击进行消行的要么新泡泡飞入发生的。 
假诺达不到消行条件还假使飞入的,那就不能够再调用飞入了,否则死循环了。

[javascript]
var game = { canvas: document.getElementById(“canvas”), ctx:
this.canvas.getContext(“2d”), cellCount: 9, cellWidth: 30, lineCount: 5,
mode: 7, colors: [“red”, “#039518”, “#ff00dc”, “#ff6a00”, “gray”,
“#0094ff”, “#d2ce00”], over: function () { alert(“GAME OVER”); },
getRandom: function (max) { return parseInt(Math.random() * 1000000 %
(max)); }, }; 

start: function () { this.map.init(); this.ready.init(); this.draw();
this.canvas.onclick = this.onclick; }, over: function () { alert(“GAME
OVER”); }, draw: function () { this.ctx.clearRect(0, 0, 400, 600);
this.ctx.save(); this.map.draw(); this.ready.draw(); this.ctx.restore();
}, play: function (action, interval) { var me = this; play =
setInterval(function () { action(); me.draw(); }, interval || 1); },
stop: function () { clearInterval(play); this.draw();
//console.log(this.ready.bubbles.length); },
game.start正是初阶化全体的父元素,
game.over自然不必说,只是这里未有写现实代码,甘休时应当不能持续操作泡泡。
game.draw 绘制全体的父元素
game.play
就是重绘方法,须要重绘时掉用此办法。接收2个参数,第贰个是重绘时需求做的动作,interval是绘制的间隔时间。不一致的动作恐怕间隔不一样样。
想必这种实现是野门路,真正的重绘应该是游玩初始后就不听的调用重绘方法,并不是具体哪个地方调用,只是在切切实实的灵巧(每种成分)Update自个儿意况就疑似自家那边的action。 
这里大家姑且这样实现,前面假如达不到供给再重构那几个重绘的代码,毕竟主旨的代码不改变,只是改改机制
不是大主题素材。
game.stop 甘休重绘,又调用了一回draw,是为着确定保障最后的绘图没难点。

满意的消行代码也相比轻便,便是广播三个消行动画,其实这几个动画真心没劲,要想设计好些能够做个渐变偏移消除。 
以后小编意识苗头未有陈设叁个数组来爱惜状态稍微不太明智,因为还会有奖赏的一定量和炸弹要绘制,挺麻烦的。。
那么,把积分展现出来啊。 那样就足以先试玩起来了:)

var game = { canvas: document.getElementById(“canvas”), ctx:
this.canvas.getContext(“2d”), cellCount: 9, cellWidth: 30, lineCount: 5,
mode: 7, colors: [“red”, “#039518”, “#ff00dc”, “#ff6a00”, “gray”,
“#0094ff”, “#d2ce00”], over: function () { alert(“GAME OVER”); },
getRandom: function (max) { return parseInt(Math.random() * 1000000 %
(max)); }, };
 cellCount正是格子的总的数量,cellwidth是每个格子的升幅,因为不但map里必要那一个,所以就定义在了此处,mode
是玩玩情势 5是简轻易单 7是困难。
 再看下map的代码:

  接下去思量下flyin飞入的完结。知道先导和了结的xy坐标,飞入的路线不成难点,无非是x
y的加加减减,那么动画方面,我们的game.play的action便是来加减ready.bubbles的xy坐标。飞入的逻辑实际不是这么轻松,首先供给3个没染色空格,倘使不足3个,那就GameOver了,所以map必要提供一个回到3个空格子的章程,其他,飞入之后ready区要双重生成新的泡泡,而那多少个被飞的泡沫需求删除,何况map要对3个空格子举办染色。
那就完了了一切飞入效果。
事实上还大概有二个逻辑正是飞入实现后检查是还是不是有何样泡泡能够被清除,那些大家前边再讲。
  先看收获3个空格的情势:

[javascript]
game.score = { basic: 0, operate: 0, star1: 0, star2: 0, boom: 0, draw:
function ()
{ var startX = game.cellWidth * 10 + game.map.startX;
var startY = game.map.startY;
var ctx = game.ctx; ctx.save();
ctx.translate(startX, startY);
ctx.clearRect(0, 0, 150, 400);
ctx.strokeStyle = “#456”;
//ctx.strokeRect(0, 0, 150, 200);
 ctx.font = “24px 微软雅黑”;
 ctx.fillStyle = “#fefefe”;
ctx.fillText(“score:” + (this.basic * 5 + this.star1 * 8 + this.star2
* 10 + this.boom * 20), 0, 30); ctx.stroke();
ctx.restore();
}, addScore: function (length) { switch (length) { case 5: this.basic++;
break;
case 6: this.star1++;
 break;
 case 7: this.star2++;
break;
default: this.boom++;
break;
} this.draw();
console.log(this.score);
},
}; 

[javascript]
game.map = { startX: 40.5, startY: 60.5, width: game.cellCount *
game.cellWidth, height: game.cellCount * game.cellWidth, bubbles: [],
init: function () { for (var i = 0; i < game.cellCount; i++) { var
row = []; for (var j = 0; j < game.cellCount; j++) { row.push(new
Bubble(i, j, null)); } this.bubbles.push(row); } }, draw: function () {
var ctx = game.ctx; ctx.save(); ctx.translate(this.startX, this.startY);
ctx.beginPath(); for (var i = 0; i <= 9; i++) { var p1 = i *
game.cellWidth;; ctx.moveTo(p1, 0); ctx.lineTo(p1, this.height); var p2
= i * game.cellWidth; ctx.moveTo(0, p2); ctx.lineTo(this.width, p2); }
ctx.strokeStyle = “#555”; ctx.stroke();
//绘制子元素(全数在棋盘上的泡) this.bubbles.forEach(function (row) {
row.forEach(function (bubble) { bubble.draw(); }); }); ctx.restore(); },
addBubble: function (bubble) { var thisBubble =
this.bubbles[bubble.x][bubble.y]; thisBubble.color = bubble.color;
}, getBubble: function (x, y) { var thisBubble = this.bubbles[x][y];
if (!thisBubble.color) { return null; } else { return thisBubble; } }
}; 

[javascript]
getEmptyBubbles: function () { var emptyBubbles = [];
this.bubbles.forEach(function (row) { row.forEach(function (bubble) { if
(!bubble.color) { emptyBubbles.push(new Bubble(bubble.x, bubble.y)); }
}); }); if (emptyBubbles.length <= 3) { game.over(); return []; }
var result = []; var usedRandom = []; for (var i = 0; i <
emptyBubbles.length; i++) { if (result.length == 3) { break; } var ra =
game.getRandom(emptyBubbles.length); var isUsed = false;
usedRandom.forEach(function (r1) { if (r1 === ra) { isUsed = true; }
else { usedRandom.push(ra); } }); if (!isUsed) {
result.push(emptyBubbles[ra]); } } return result; }, 

 game.score = { basic: 0, operate: 0, star1: 0, star2: 0, boom: 0, draw:
function () {
 var startX = game.cellWidth * 10 + game.map.startX;
var startY = game.map.startY;
var ctx = game.ctx; ctx.save();
ctx.translate(startX, startY);
ctx.clearRect(0, 0, 150, 400);
ctx.strokeStyle = “#456”;
//ctx.strokeRect(0, 0, 150, 200); ctx.font = “24px 微软雅黑”;
ctx.fillStyle = “#fefefe”;
ctx.fillText(“score:” + (this.basic * 5 + this.star1 * 8 + this.star2
* 10 + this.boom * 20), 0, 30); ctx.stroke();
ctx.restore();
}, addScore: function (length)
{ switch (length)
{ case 5: this.basic++;
break;
case 6: this.star1++;
break;
case 7: this.star2++;
break; default: this.boom++;
 break;
} this.draw();
console.log(this.score);
},
};
 

game.map = { startX: 40.5, startY: 60.5, width: game.cellCount *
game.cellWidth, height: game.cellCount * game.cellWidth, bubbles: [],
init: function () { for (var i = 0; i < game.cellCount; i++) { var
row = []; for (var j = 0; j < game.cellCount; j++) { row.push(new
Bubble(i, j, null)); } this.bubbles.push(row); } }, draw: function () {
var ctx = game.ctx; ctx.save(); ctx.translate(this.startX, this.startY);
ctx.beginPath(); for (var i = 0; i <= 9; i++) { var p1 = i *
game.cellWidth;; ctx.moveTo(p1, 0); ctx.lineTo(p1, this.height); var p2
= i * game.cellWidth; ctx.moveTo(0, p2); ctx.lineTo(this.width, p2); }
ctx.strokeStyle = “#555”; ctx.stroke();
//绘制子成分(全部在棋盘上的泡) this.bubbles.forEach(function (row) {
row.forEach(function (bubble) { bubble.draw(); }); }); ctx.restore(); },
addBubble: function (bubble) { var thisBubble =
this.bubbles[bubble.x][bubble.y]; thisBubble.color = bubble.color;
}, getBubble: function (x, y) { var thisBubble = this.bubbles[x][y];
if (!thisBubble.color) { return null; } else { return thisBubble; } }
};
map的init开始化方法里本人先把具备的泡泡安插好了,可是都未曾染色,作者并从未在ui的背后维护贰个数组,因为自身以为泡泡有未有颜色就代表
0,1了,所以就好像此也行。
draw方法不再想在此之前那么把初阶坐标总计进去了,而是使用了translate方法,这样就很实惠写代码了。
addBubble其实正是染色而已,接收参数是三个泡沫对象,这几个目标来自ready区域的泡泡。

getEmptyBubbles: function () { var emptyBubbles = [];
this.bubbles.forEach(function (row) { row.forEach(function (bubble) { if
(!bubble.color) { emptyBubbles.push(new Bubble(bubble.x, bubble.y)); }
}); }); if (emptyBubbles.length <= 3) { game.over(); return []; }
var result = []; var usedRandom = []; for (var i = 0; i <
emptyBubbles.length; i++) { if (result.length == 3) { break; } var ra =
game.getRandom(emptyBubbles.length); var isUsed = false;
usedRandom.forEach(function (r1) { if (r1 === ra) { isUsed = true; }
else { usedRandom.push(ra); } }); if (!isUsed) {
result.push(emptyBubbles[ra]); } } return result; },
获得3个随机的空格照旧挺多代码的。。。然后正是flyin的兑现了。

其三节在统一希图里聊起了
百搭星和炸弹,小编太懒了,不准备讲了,因为这么些管理也没有啥样复杂的,HTML5
Canvas的的基本功学习也算能够了。后边应该学一些更深刻的事物,举例怎么样管理品质、物体碰撞、运动学神马的。。。

Ready区域实际仿佛俄罗丝四方那样,有八个备选的泡沫就要步入map区域。

第一定三个一个status,来存飞入的场合。3个都飞完结本领做前边的逻辑。Bubble对象也为此扩张了px和py俩个分子(即Bubble的其实坐标),这样才能垄断各个像素的位移。
其实际总括飞入路径时自己写了十分久的代码,别看今朝就好像此几行,开垦进度中或许颇费劲。种种光怪陆离的宇航。。。开头是按x++
y++递增飞行的,那样正是45°角飞行,但猛烈飞行路径(开首到停止的线)的倾斜度不是45°,那就能够晤世先飞完x或y,再走直线,很傻的。所以要用斜率来估测计算当前的y坐标。而x的坐标能够固定常熟移动。小编画了贰个斜率的公式,忘记的同窗能够看看下。遵照长宽的比值,就能够揣摸当前的y值。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图