写一个狼吃羊的小游戏
其实在2016年,我就准备写这个小游戏了。但是奈何当时没文化😌,也还没有转行成前端。既没有面向对象的思想,也不懂什么寻径算法,更不了解模块化,只是掌握了canvas的基本知识。所以以当时的水平,就搞了一段时间后,把羊,狼,砖块画了出来,具体怎么运动?搞不下去,就搁置下来了。时间飞逝,这个小游戏就这么静静地烂尾了两年多,直到前几天,在codepen上看到了一个寻径的demo,才又重新捡起来。
现在再捡起来,感觉得心应手应手了许多,尽管中间也碰到了一些坑,但不至于无从下手。尽管还有许多可以改进的地方,代码写的也不怎么规范,断断续续搞了一周多,好歹搞出来一个能用的版本。下面说说开发过程中遇到的一些问题,和学到的知识。
这里是试玩地址:https://js13kgames.com/games/wolf-and-sheep/index.html
github: https://github.com/imgss/wAs
一 devicePixelRatio
在手机上打开这个小游戏时,游戏画面会变得比较模糊,这是由于手机屏幕的css尺寸和真实的物理像素数量是不一样的。这也是window.devicePixelRatio
这个属性存在的意义。同时canvas的css宽度和canvas本身的宽度也是这个区别。举个例子:
一个手机的 devicePixelRatio 是3,canvas的css宽度是500px,要想使canvas绘制出的图片不模糊,需要给canvas的宽度设置成多少?答案是500 * 3 = 1500。写成代码是:
var ratio = window.devicePixelRatio;
var oldWidth = canvas.width;
var oldHeight = canvas.height;
canvas.width = oldWidth * ratio; //根据radio设置新的width,解决图片模糊问题
canvas.height = oldHeight * ratio;
canvas.style.width = oldWidth + "px";
canvas.style.height = oldHeight + "px";//使canvas在屏幕上的显示和之前保持一致
二 寻径算法
这也是整个游戏最难的地方,游戏中羊是在不断运动的,狼需要根据羊当前的位置计算出行动路径。在实现中,我参考了codepen上的这个demo,具体的思路是,用一个二维数组来表示整张地图,从目标节点(羊的位置)开始,向上下左右遍历,如果碰到墙或者边界就停止,并标记为访问过。再根据这些节点的兄弟节点进行遍历,直到找到狼所在的位置,或者整个地图全部搜索了一遍。
当在遍历中找到狼的位置后,进行一次回溯,就拿到了狼的运动路径。
整个算法在search.js中,是根据上面的那个demo修改的,实现了一个pathFinder类;
export default class Pathfinder{
constructor(gridData, targetPosition, foundCallback) {
this.gridData = gridData;
this.targetPosition = targetPosition;
this.foundCallback = foundCallback;
this.width = this.gridData[0].length;
this.height = this.gridData.length;
}
// 将二维地图转换成节点数据
parseGridData() {
...
}
了解更多的寻径算法知识可以看这篇文章: https://www.redblobgames.com/pathfinding/a-star/introduction.html
三 一个有用的库
在开发过程中发现手机端的操作不是很流畅,找到了一个叫nipplejs的虚拟摇杆的库。使用方法也很简单,虚拟摇杆可以是固定在屏幕的某个地方,也可以是显示在屏幕的任何一个地方。但是在实际中使用发现,用摇杆操作,很容易导致羊多跑或者少跑一两格,对于这个游戏来说,不如箭头控制精度好,最终还是放弃了。
游戏里还用到一个sweetalert库,用来做弹出框的,没有依赖,用起来很方便(其实是做不出酷炫的弹窗特效)。
后记
整个过程中,加深了自己对算法重要性的认识,虽然前端很少涉及高深的算法,但是多懂一点算法知识绝对大有裨益。小游戏中还有许多值得优化的地方,也还有几个bug,这些以后再慢慢优化吧。(完)