前语
很早之前,我就关注到了Github上一款叫做shapez的高星游戏,首要玩法就和他的姓名相同,是个需求利用挖掘器,传送带等各种东西来完结每个关卡所需求搜集的挖掘物的游戏(游戏链接:shapez.io/ , 原创作者:github.com/tobspr-game… )。边玩的时分一边感叹,怎样能用JS做出功用这么优异的高难度逻辑的游戏。那时分的我对JS的运用只能说是停留在能操控DOM元素。做点简略逻辑游戏的份上。也幻想尝试着做这个游戏,可是直接就在创立地图的阶段就暴毙了。
往后的时间里,我一向都在不断磨炼自己的JS运用技术,特意去参阅学习了许多大佬解析的一些游戏源代码。相对的在这方面的提高也是肉眼可见的。
到了今天的10月中旬渐渐终于闲下来的时分,我终所以想起了shapez这款游戏,决计一定要应战一下哪怕仅仅抽象的能完结多少就完结多少。
剖析
原版游戏剖析
先对原版游戏进行一下剖析,他的地图是能够不断动态创立的,且进行缩小到最小地图之后大约显现50万个网格,可是经过研讨了一段时间后发现,在不放置任何东西的时分他永远只会不断更新一块4块16*16巨细的网格高亮区域。其他当地是不会以高刷新率从头更新全部画布的,但假设在其他区域放置一个东西的话,会在那个放置点的16 * 16的方向上形成一个新的高亮刷新区域。
那么对于地图上那些蓝的绿的红的挖掘区是怎样生成的我后边也渐渐想到一个叫做柏林噪点的东西,尽管不能百分百确定,但至少我觉得生成的东西就和这游戏地图上的板块是非常类似的
其他的东西放置之类的功用相对就比较简略这里就不做更多讲解了
制造流程
这是我对自己要所要完结的功用做的一个最基本的规划,规划内容如下:
graph TD
canvas网格地图制造 --> 地图缩放拖动动态创立显现 --> 地图板块填充噪点等 --> 地图东西放置 --> 地图挖掘运行/中心接纳/进入下一关
制造
那么剖析完了就能够开端动手了,可是为了便利以后的制造,我对自己的代码规范,以及预留一些添加后边功用板块的方位也是很有必要的。
canvas网格地图制造
先要想想这个网格怎样创立,依据刚刚的剖析假设光是创立2级数组作为地图存储的话会导致后边要完结放置东西变成高亮刷新区域后期就很难添加了,所以我的主意是这样的。 地图X Y坐标别离代表别离是二维数组的maps[y][x],在数组值里保存一个Class类,里边再存一个二维数组:如下面的。(ps:应该是还有其他办法的可是我觉得最简略的便是这种)
[
[
{grids: [[]]},{},{},{}
],
[{},{},{},{}],
[{},{},{},{}],
]
然后便是封装类开端完结,先封装好canvas相关操作的类
// 制造canvas地图
class Map {
constructor() {
this.canvas = document.querySelector('canvas');
this.ctx = this.canvas.getContext('2d');
// 屏幕巨细共同的canvas
window.onresize = () => {
this.canvas.width = document.documentElement.clientWidth;
this.canvas.height = document.documentElement.clientHeight;
}
// 保存加载好的图片
this.loadImg = {}
this.runTimer = null;
}
// 绘画开端
begin(){
this.ctx.beginPath();
this.ctx.save();
}
// 绘画结束
close(){
this.ctx.closePath();
this.ctx.restore();
}
// 从头制造地图
one(){
this.ctx.clearRect(0,0, document.documentElement.clientWidth, document.documentElement.clientHeight)
app.maps.flat().map(item => item.update());
}
// 旋转
rotate(x, y, rotate){
this.ctx.translate(x + 3.25, y + 3.25);
this.ctx.rotate(rotate * Math.PI / 180);
this.ctx.translate(-x - 3.25, -y - 3.25);
}
// 绘画图片
drawImage(x, y, name, w = 20, h, cx, cy, cw, ch){
if(this.loadImg[name]){
if(cw && ch){
console.log(cx, cy, cw, ch, x, y, w, h || w);
this.ctx.drawImage(this.loadImg[name], cx, cy, cw, ch, x, y, w, h || w);
}else{
this.ctx.drawImage(this.loadImg[name], x, y, w, h || w);
}
}else{
let img = new Image();
img.src = `${name}`;
img.onload = () => this.loadImg[name] = img;
}
}
}
再去封装一个存入地图数据的Grid类,由于是用Vue写的里边有带app.??? 一般都是之前封装的函数
// 地图里的网格方块
class Item {
constructor(i, j) {
this.i = i;
this.j = j;
this.x = i * app.game.w;
this.y = j * app.game.h;
this.img = '';
this.w = (app.game.w / 16);
}
}
class Grid extends Item{
constructor(i, j, color, prop) {
super(i, j)
// 随机的挖掘区的颜色
this.color = color;
// 假设是灰色的添加图形
this.prop = prop;
this.main = null;
// 依据show来判别是否随机在二维数组中生成存入一个噪点
this.show = ~~(Math.random() * 3);
this.first = false;
// 存入一个16*16的二维数组
this.grids = new Array(16).fill(0).map((item, j) => {
return new Array(16).fill(0).map((item, i) => {
return new Block(i, j, false, false)
})
})
if(this.show > 0) return;
this.getRandomHP(7, 7, 20)
}
update(){
if(this.show > 0) return;
this.add();
app.map.begin();
this.grids.map((item, j) => {
item.map((item, i) => {
if(app.line){
app.map.ctx.strokeStyle = '#E3E7EA';
app.map.ctx.lineWidth = '0.2'
app.map.ctx.strokeRect(this.x + (i * 6.5), this.y + (j * 6.5), Math.abs(app.game.w) / 16, Math.abs(app.game.h) / 16);
}
})
})
app.map.close();
}
}
初步就制造出以下的区块
地图缩放拖动
这个拖动功用基本便是计算个偏移量的工作,所以不必多做解释啦。但对于这个定点缩放的确让我想了好一阵子。首先我的主意是经过直接添加制造方块时分canvas增减制造 x y 的偏移位和 w h 的巨细来完结缩放作用发现并不抱负。所以我直接选用了 canvas的 scale 和 translate 办法来进行偏移和缩放。
大约便是这样的办法
let ctx = document.getElementById('canvas').getContext('2d');
let obj = {
fontX: 0,
fontY: 0,
fontZoom: 1,
curZoom: 1,
translateX: 0,
translateY: 0,
draw() {
ctx.fillRect(150, 150, 50, 50)
},
zoom(offsetX, offsetY, z) {
ctx.save()
ctx.clearRect(0, 0, 300, 300);
this.curZoom = this.fontZoom + z
this.translateX = offsetX - (offsetX - this.translateX) * this.curZoom / this.fontZoom
this.translateY = offsetY - (offsetY - this.translateY) * this.curZoom / this.fontZoom
ctx.translate(this.translateX, this.translateY);
ctx.scale(this.curZoom, this.curZoom);
this.draw()
ctx.restore()
this.fontY = offsetY
this.fontX = offsetX
this.fontZoom = this.curZoom
}
}
obj.draw()
document.getElementById('canvas').addEventListener('mousewheel', (e) => {
let z = e.deltaY > 0 ? -0.1 : 0.1
obj.zoom(e.offsetX, e.offsetY, z)
})
地图板块填充
噪点生成
那种真的像我的世界地图相同的噪点生成代码理解和写起来实在是麻烦了,所以我只要自创一个符合我要求的也不知道是不是“噪点”的demo。原理很简略,这里我直接用exl表来做一个简略的模型:简略来说便是假设中心点的方位是7,他的四周都是7的以内的随机数。当然为了防止7直接随机到1的尴尬状况,所以我是选用 7/2+(7*Math.random()) 就能够保证得到的数字至少是在一半以上,接下来经过图片就很好理解了
(留意:这些代码仅仅便利看我单独做的demo,在实践游戏中运用请看在线代码)
class Grid{
constructor() {
this.canvas = document.querySelector('canvas');
this.ctx = this.canvas.getContext('2d');
this.grids = new Array(15).fill(0).map((item, j) => {
return new Array(15).fill(0).map((item, i) => {
return {if: false, rp: false};
})
})
this.getRandomHP(7, 7, 15)
}
//寻找邻近的方块
getNearby(i, j, rp){
return [
[i - 1, j],
[i + 1, j],
[i, j - 1],
[i, j + 1],
].map(item => {
// console.log((this.grids[item[1]][item[0]].rp <= 1 && this.grids[item[1]][item[0]].rp !== false), this.grids[item[1]][item[0]].rp)
if(!(this.grids[item[1]] && this.grids[item[1]][item[0]]) || rp <= 1 || this.grids[item[1]][item[0]].if === true) return false;
this.grids[item[1]][item[0]].if = true;
let rand = ~~(Math.random() * rp / 2) + ~~(rp / 2);
return {
i: item[0],
j: item[1],
rp: rand
}
})
}
// 随机生成一个噪点
getRandomHP(i, j, rp){
this.getNearby(i, j, rp).filter(item => {
if(!item) return;
this.getRandomHP(item.i, item.j, item.rp)
})
}
// 依据噪点填充方格
createMap(){
this.grids.map((item, j) => {
item.map((items, i) => {
this.stroke(i, j)
if(items.if) this.draw(i, j)
})
})
}
// 填充方格
draw(i, j){
this.ctx.beginPath();
this.ctx.save()
this.ctx.fillStyle = 'red';
this.ctx.fillRect(i * 30, j * 30, 30, 30);
this.ctx.closePath();
this.ctx.restore()
}
// 划线
stroke(i, j){
this.ctx.beginPath();
this.ctx.save()
this.ctx.strokeStyle = 'red';
this.ctx.strokeRect(i * 30, j * 30, 30, 30);
this.ctx.closePath();
this.ctx.restore()
}
}
let grid = new Grid();
let btn = document.querySelector('button');
btn.onclick = function (){
grid.createMap();
}
完结出来的作用和原版比照
看看 看看,比照一下还是很像的嘛!
各种类型的挖掘区
挖掘区就比较简略啦,无非便是把噪点里填充的赤色板块填充些其他什么。封装一个通用的挖掘区的Class类,然后再经过承继来别离创立不同类型的挖掘区。现在看起来或许有点剩余了,但后边是有这几种挖掘区都需求添加不同的功用的。
// 一切图片网址保存成的目标
let urlImg = {
red: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bcdf1d95fb2f4203899f56ac4e0d5177~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
blue: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1a8496861a64eb8b72ca6e77a969edb~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
green: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e03f33112ab486b8fbca50797782a78~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
circular: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/da017de4947847acb217eb51e3a778a5~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
square: 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ccdd6b9183b745f8914cea5797b677e2~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
cut: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f6336eefefa94937adfe7e0ade7d25b1~tplv-k3u1fbpfcp-watermark.image?',
main: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/971af565f1b54f9494614722e50fc6da~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
weizi: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e1b5fdb97a54668914529840c037a6e~tplv-k3u1fbpfcp-zoom-mark-crop-v2:240:240:0:0.awebp?',
miner: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df7bc18ef96c40af8e8ff74fbed9c5bb~tplv-k3u1fbpfcp-watermark.image?',
remove: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9934ebad4852405dab42d5e956c4d70f~tplv-k3u1fbpfcp-zoom-mark-crop-v2:460:460:0:0.awebp?',
path_top_bottom: 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9f5ba39cbf6a470b9091164eb1e469f0~tplv-k3u1fbpfcp-watermark.image?',
path_bottom_top: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89a739cc9118468cbb9d2a70a22bffc2~tplv-k3u1fbpfcp-watermark.image?',
path_left_right: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e1cbdfa5844d4deb87ac23624e6898e2~tplv-k3u1fbpfcp-watermark.image?',
path_right_left: 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/83c6bac94f3b4837a4c5bbdc29fd8979~tplv-k3u1fbpfcp-watermark.image?',
path_left_bottom: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5416f686ed1a4f80ac940e6ced5459f5~tplv-k3u1fbpfcp-watermark.image?',
path_top_left: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6274f62a2ffc458f9851e679b96a8134~tplv-k3u1fbpfcp-watermark.image?',
path_right_top: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/24490d7fc5de4c9ea0694bdea8e90f77~tplv-k3u1fbpfcp-watermark.image?',
path_bottom_right: 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/148fbcef46e341f19e444fbceabca65c~tplv-k3u1fbpfcp-watermark.image?',
path_right_bottom: 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/84bbd233b4d6415f8da8781b91fee3b1~tplv-k3u1fbpfcp-watermark.image?',
path_top_right: 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d5f15016ac940a28b599e38e43d3636~tplv-k3u1fbpfcp-watermark.image?',
path_left_top: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f22d1be13e6424d8274d304a6082ea7~tplv-k3u1fbpfcp-watermark.image?',
path_bottom_left: 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4461257ffd7b4f9fb66f761ef16f0d56~tplv-k3u1fbpfcp-watermark.image?',
}
// 每个方块里的矿藏承继Class
class Color extends Item{
constructor(i, j, color, prop = null) {
super(i, j);
this.color = color;
this.prop = prop;
}
update(){
if(app.line){
app.map.ctx.globalAlpha = .5;
app.map.drawImage(this.i + 1.5, this.j + 1.5, urlImg[this.prop || Object.keys(this.color)[0]], Math.abs(app.game.w) / 32);
}
app.map.ctx.fillStyle = Object.values(this.color)[0];
app.map.ctx.fillRect(this.i, this.j, Math.abs(app.game.w) / 16, Math.abs(app.game.h) / 16)
}
}
// 绿色染料块
class Green extends Color{
constructor(i, j, color) {
super(i, j, color);
}
}
// 蓝色染料块
class Blue extends Color{
constructor(i, j, color) {
super(i, j, color);
}
}
// 赤色染料块
class Red extends Color{
constructor(i, j, color) {
super(i, j, color);
}
}
// 灰色染料块,一起里边的正方形或者圆形
class Grey extends Color{
constructor(i, j, color, prop) {
super(i, j, color, prop);
}
}
// 地图中心点
class Main extends Item{
constructor(i, j, grids) {
super(i, j);
this.grids = grids
this.img = 'main';
this.w = 26;
this.init();
}
init(){
this.type = app.levels[app.level].type;
this.num = app.levels[app.level].num;
this.getNum = 0;
}
update(){
if(app.line){
app.map.drawImage(this.x + 39, this.y + 39, urlImg['main'], 26);
this.info(this.x, this.y)
}else{
new Array(4).fill(0).map((item, j) => {
new Array(4).fill(0).map((item, i) => {
this.grids[6 + j][6 + i].tool = 'main';
app.map.ctx.fillStyle = 'red';
app.map.ctx.fillRect(this.x + ((i + 6) * 6.5), this.y + ((j + 6) * 6.5), Math.abs(app.game.w) / 16, Math.abs(app.game.h) / 16)
})
})
app.map.drawImage((this.x + 26), this.y, urlImg['weizi'], 52);
}
}
info(x, y){
app.map.ctx.font = 'bold 1.5px Arial';
app.map.ctx.textAlign = 'left';
app.map.ctx.fillStyle = '#ffffff';
app.map.ctx.fillText('关卡', x + (6 * 6.5) + 3.5, y + (7 * 6.5) - 2.5);
app.map.ctx.fillText(app.level + 1, x + (6 * 6.5) + 4.5, y + (7 * 6.5) - 0.5);
app.map.drawImage((x + (6 * 6.5) + 4.5), y + (7 * 6.5) + 1, urlImg[this.type], 7.5);
// app.map.drawImage(
// x + (6 * 6.5) + 4.5,
// y + (7 * 6.5) + 1,
// urlImg[this.type],
// 7.5,
// 7.5,
// -30,
// 0,
// 60,
// 60
// )
app.map.ctx.font = 'bold 3px Arial';
app.map.ctx.textAlign = 'left';
app.map.ctx.fillStyle = '#555';
app.map.ctx.fillText('关卡', x + (8 * 6.5) - 1.8, y + (7 * 6.5));
app.map.ctx.fillText('解锁', x + (8 * 6.5) - 1.8, y + (9 * 6.5) - 2);
app.map.ctx.fillStyle = '#FD0752';
app.map.ctx.fillText(app.levels[app.level].info, x + (8 * 6.5) - 1.8, y + (9 * 6.5) + 1.5);
app.map.ctx.font = 'bold 5px Arial';
app.map.ctx.textAlign = 'left';
app.map.ctx.fillStyle = '#555';
app.map.ctx.fillText(this.getNum, x + (8 * 6.5) + 1.5, y + (8 * 6.5) - 2);
app.map.ctx.font = 'bold 3px Arial';
app.map.ctx.fillStyle = '#b1b1b1';
app.map.ctx.fillText("/" + this.num, x + (8 * 6.5) + 1.5, y + (8 * 6.5) + 1);
}
}
Grid类里新增一个 add办法 放入的生成的噪点里
// 添加噪点
add(){
if(this.show > 0 && !app.line) return;
if(this.first) return;
this.grids = this.grids.map((item, j) => {
return item.map((item, i) => {
if(item.if) {
let color = new cont[Object.keys(this.color)[0]](this.x + (i * 6.5), this.y + (j * 6.5), this.color, this.prop)
color.update();
item.color = color;
return item;
}
return item
})
})
this.first = true;
}
// canvas网格地图制造里的 Grid 类里的 update
update(){
if(this.show > 0 && !app.line) return;
// 调用把不同类型的挖掘区添加进噪点
this.add();
app.map.begin();
this.grids.map((item, j) => {
item.map((item, i) => {
if(app.line){
app.map.ctx.strokeStyle = '#E3E7EA';
app.map.ctx.lineWidth = '0.2'
app.map.ctx.strokeRect(this.x + (i * 6.5), this.y + (j * 6.5), Math.abs(app.game.w) / 16, Math.abs(app.game.h) / 16);
}
if(item.if) {
item.color.update();
}
})
})
app.map.close();
}
let cont = {red: Red, green: Green, blue: Blue, grey: Grey};
别眨眼!看看完结出来的作用(图一原版,图二完结的作用),是不是有一种以假乱真甚至更美观的感觉!
地图东西放置
由于只写了第一关的原因,现在我只搞的东西也便是第一关和第二关的首先创立一个tools的大数组,里边保存的每个目标都代表一个东西,我来讲讲目标里边的每个属性都代表什么:
属性 | 代表的东西 |
---|---|
type | 东西类型 |
img | 东西的图片 |
level | 解锁这个东西的关卡 |
// 一切东西
let tools: [
{type: 'path', img: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89a739cc9118468cbb9d2a70a22bffc2~tplv-k3u1fbpfcp-watermark.image?', level: 0},
{type: '', img: '', level: 9},
{type: '', img: '', level: 9},
{type: 'miner', img: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df7bc18ef96c40af8e8ff74fbed9c5bb~tplv-k3u1fbpfcp-watermark.image?', level: 0},
{type: 'cut', img: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f6336eefefa94937adfe7e0ade7d25b1~tplv-k3u1fbpfcp-watermark.image?', level: 1},
{type: '', img: '', level: 9},
{type: '', img: '', level: 9},
{type: '', img: '', level: 9},
{type: '', img: '', level: 9},
{type: 'remove', img: 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9934ebad4852405dab42d5e956c4d70f~tplv-k3u1fbpfcp-zoom-mark-crop-v2:460:460:0:0.awebp?', level: 1},
];
// 正在运用的东西保存
let tool = [];
// 方向数组
let dirs = [ 'top', 'right', 'bottom', 'left',];
// 现在的方向
let dir = 0;
然后便是和挖掘区的创立相同,一个主类,然后分出每个东西的承继类
// 东西通用承继Class
class Tool{
constructor(i, j) {
this.i = i;
this.j = j;
this.w = app.game.w / 16;
this.h = app.game.h / 16;
this.x = this.i * this.w;
this.y = this.j * this.h;
this.img = '';
this.pre = false;
this.timer = false;
this.goods = []
}
update(fn){
if(this.img === '') return;
app.map.begin();
fn && fn();
app.map.drawImage(this.x, this.y, urlImg[this.img], this.w);
app.map.close();
}
getNearby() {
return [
[this.i - 1, this.j, 'left'],
[this.i + 1, this.j, 'right'],
[this.i, this.j - 1, 'top'],
[this.i, this.j + 1, 'bottom'],
].map(item => {
return {
grid: app.game.getGrid(...item),
dir: item[2]
}
}).filter(item => item.grid.tool)
}
getNearbyGrid(){
return [
[this.i - 1, this.j, 'left'],
[this.i + 1, this.j, 'right'],
[this.i, this.j - 1, 'top'],
[this.i, this.j + 1, 'bottom'],
].map(item => {
return {
grid: app.game.getGrid(...item),
dir: item[2]
}
}).filter(item => item.grid)
}
}
// 挖掘器东西
class Miner extends Tool{
constructor(i, j, dir) {
super(i, j, dir);
this.img = 'miner';
this.dir = dir;
this.pre = false;
}
update(){
super.update(() => {
if(this.dir === 'right'){
this.rotate = 90;
}else if(this.dir === 'bottom'){
this.rotate = 180;
}else if(this.dir === 'left'){
this.rotate = 270
}else if(this.dir === 'top'){
this.rotate = 0;
}
app.map.ctx.globalAlpha = this.pre ? .4 : 1;
app.map.rotate(this.x, this.y, this.rotate);
})
}
}
// 剪切东西
class Cut extends Tool{
constructor(i, j, dir) {
super(i, j, dir);
this.img = 'cut'
this.dir = dir;
this.pre = false;
}
update(){
if(this.dir === 'right'){
this.rotate = 90;
}else if(this.dir === 'bottom'){
this.rotate = 180;
}else if(this.dir === 'left'){
this.rotate = 270
}else if(this.dir === 'top'){
this.rotate = 0;
}
app.map.begin();
app.map.ctx.globalAlpha = this.pre ? .4 : 1;
app.map.rotate(this.x, this.y, this.rotate);
console.log(this.w * this.wd, this.h * this.hd)
app.map.drawImage(this.x, this.y, urlImg[this.img], this.w * 2, this.h);
app.map.close();
}
}
// 画路东西
class Path extends Tool{
constructor(i, j, dir, nextDir) {
super(i, j, dir);
this.dir = dir;
this.img = '';
if (nextDir) {
this.nextDir = nextDir;
this.img = `path_${this.dir}_${this.nextDir}`;
return;
}
this.nextDir = app.reversePos(this.dir)
this.img = `path_${this.dir}_${this.nextDir}`;
}
update(){
super.update(() => {
app.map.ctx.globalAlpha = this.pre ? .4 : 1;
})
}
}
地图挖掘与运行
逻辑很浅显,便是挖掘器的放置方位是可挖掘的区域里,而且挖掘器的朝向周围的路开始方向是共同的就能进行挖掘,那么这层逻辑有了,需求考虑的便是怎样生成挖掘出的东西,还有中心搜集点的正确搜集判别了
判别是否达到生成的条件
class Tool{
// 生成挖掘物判别
isCorrect(){
if(this instanceof Miner) {
if(app.game.getGrid(this.i, this.j).color){
let nearby = this.getNearbyGrid().filter(item => {
if(item.grid.tool && (app.reversePos(item.grid.tool.dir) === this.dir)){
return true;
}else{
return false;
}
})
if(nearby.length < 1) return false;
return true
}
}
}
}
生成挖掘物
// 每个方块挖掘出来的东西
class Goods{
constructor(i ,j, type, dir) {
this.i = i;
this.j = j;
this.x = this.i * 6.5;
this.y = this.j * 6.5;
this.type = type;
this.dir = dir;
}
// 判别下一步该往哪走
update(){
this.i = ((parseFloat(this.x.toFixed(1)) + 6.5) / 6.5);
this.j = ((parseFloat(this.y).toFixed(1)) / 6.5);
let grid = app.game.getGrid(this.i, this.j);
if(((parseFloat(this.x.toFixed(1)) + 6.5) % 6.5 === 0) && ((parseFloat(this.y).toFixed(1)) % 6.5 === 0)){
if(grid.tool) this.dir = grid.tool.nextDir || grid.tool.dir;
else this.dir = '';
let nearby = [];
if(grid.tool !== 'main') nearby = grid.tool.getNearby().filter(item => {
return item.dir === this.dir && item.grid.tool
})
this.getMain(grid)
if(nearby.length < 1) this.dir = '';
}
app.map.drawImage(this.x + (8), this.y + (1.5), urlImg[this.type], 104 / 32);
// app.map.drawImage(this.x + (8), this.y + (1.5), urlImg[this.type], 104 / 32, 104 / 32, this.x + (8), this.y + (1.5), 104 / 32, 104 / 32);
}
// 是否传输进入中心点,并判别是否符合中心点需求
getMain(grid){
if(!grid.tool || grid.tool === 'main') return;
let nearby = grid.tool.getNearby().filter(item => {
return grid.tool && (grid.tool.nextDir === item.dir) && item.grid.tool === 'main';
})
if(nearby.length >= 1) {
if(this.type === app.game.get(40, 39).main.type){
setTimeout(() => {
app.game.get(40, 39).main.getNum += 1;
app.game.get(40, 39).main.info();
if(app.game.get(40, 39).main.getNum === app.game.get(40, 39).main.num){
app.level += 1
app.game.get(40, 39).main.init();
$('.rank').show();
app.game.get(40, 39).main.info();
}
}, 1000)
}
}
}
}
终究作用.
在线代码链接
code./pen/7157617…
结束语
现在截止到我预备发布这篇文章的时分,这个游戏其实也才做到第二关。但接下来关卡的制造无疑是飞速的,由于剩余需求考虑的常识变成了每个关卡解锁的东西运用上的思路编写了,假设这个星期的时间充裕的话我会抓经更新完剩余的关卡~ 当然上面的代码解析也仅仅讲了一些比较模糊的概念,假设掘友们有需求我进行具体解析的功用板块能够在谈论区留言我会抓经和下几关的内容一起更新出一篇相关文章的!!也希望掘友们能为我的文章和在线代码 动动手指 点个赞!爱你们~