js 插件随机地牢,迷宫地图生成
插件git https://github.com/ondras/rot.js/tree/master/dist
使用:
1.我们的游戏是在网页内进行的;一个基本的 HTML 文件就足够了
<!doctype html>
<html>
<head>
<title>Ananas aus Caracas: rot.js tutorial game</title>
<script src="https://cdn.jsdelivr.net/npm/rot-js@2/dist/rot.js"></script>
<script src="/path/to/the/game.js"></script>
</head>
<body onload="Game.init()">
<h1>Ananas aus Caracas</h1>
</body>
</html>
2.cocoscreator,Laya引擎使用去上面git下载,然后把里面的rot.js 或者 rot.min.js 文件放到项目里面即可
3.下面是一些使用的方法
地图数据生成
我们将使用 rot.js 的内置地图生成器之一来创建游戏关卡。Rot的设计范式之一.是人们不应该被迫使用一些预定义的数据结构;这就是生成器基于回调的原因。我们将自定义函数传递给生成器;在此过程中,它将被反复调用。
这可能是查看rot.js手册的好时机,其中包含有用的代码示例和使用概述。
我们应该如何存储生成的地图数据?我们将使用一种非常基本的存储方法:一个普通的JS对象(“hashmap”),由字符串索引(格式为“x,y”),值表示地砖。我们不会存储墙壁/固体细胞。
注意:我们正在通过,而不仅仅是挖掘机。这对于确保在正确的上下文(ECMA 术语中的激活对象)中调用我们的回调是必要的
var digger = new ROT.Map.Digger();
var digCallback = function(x, y, value) {
if (value) { return; } /* do not store walls */
var key = x+","+y;
this.map[key] = ".";
for (var key in this.map) {
var parts = key.split(",");
var x = parseInt(parts[0]);
var y = parseInt(parts[1]);
this.display.draw(x, y, this.map[key]);
}
}
digger.create(digCallback.bind(this));
随机生成的框
最后,让我们创建一些盒子 - 潜在的阿纳纳斯存储。我们将在本教程的后面部分中将 ananas 隐藏在其中一个中。要放置 10 个随机框,我们将利用 rot.js 的随机数生成器。
ROT.RNG可以做很多事情,但我们需要一些简单的东西:一个介于零(包括)和一(不包括)之间的随机、均匀分布的数字,就像它一样。执行此操作的正确方法是调用 。Math.randomROT.RNG.getUniform()
我们将空单元格存储在一个数组中;对于放置的每个框,我们将选择一个随机的空单元格,将其从列表中删除,并在存储结构中将该位置标记为框(星号)。
_generateMap = function() {
var digger = new ROT.Map.Digger();
var freeCells = [];
var digCallback = function(x, y, value) {
if (value) { return; } /* do not store walls */
var key = x+","+y;
freeCells.push(key);
this.map[key] = ".";
}
digger.create(digCallback.bind(this));
this._generateBoxes(freeCells);
this._drawWholeMap();
}
_generateBoxes = function(freeCells) {
for (var i=0;i<10;i++) {
var index = Math.floor(ROT.RNG.getUniform() * freeCells.length);
var key = freeCells.splice(index, 1)[0];
this.map[key] = "*";
}
}
上面其实只是地图的生成,Rot.js其实还有很多功能,他其实是一个游戏插件
玩家角色
是时候制作一些有趣的互动闪亮了!首先,玩家需要一个体面的代表。使用普通 JS 对象来表示玩家就足够了,但通常通过其构造函数定义播放器并将其实例化会更健壮。
此时,您可能已经习惯了某些变量名称以下划线开头的事实。这是一种相对常见的将它们标记为私有的技术。JavaScript 不提供真正的私有变量,所以这种基于下划线的命名法只是我们将东西标记为“内部”的有用方法。
我们想将玩家放在一些备用地砖上:让我们使用与本教程第 1 部分完全相同的技术来放置盒子:只需从列表中选择一个空闲位置即可。
var Player = function(x, y) {
this._x = x;
this._y = y;
this._draw();
}
Player.prototype._draw = function() {
Game.display.draw(this._x, this._y, "@", "#ff0");
}
Game.player = null;
Game._generateMap = function() {
/* ...previous stuff... */
this._createPlayer(freeCells);
};
Game._createPlayer = function(freeCells) {
var index = Math.floor(ROT.RNG.getUniform() * freeCells.length);
var key = freeCells.splice(index, 1)[0];
var parts = key.split(",");
var x = parseInt(parts[0]);
var y = parseInt(parts[1]);
this.player = new Player(x, y);
};
准备游戏回合引擎
在我们的游戏中将有两个实体轮流:玩家角色和佩德罗(敌人)。为了简单起见,让我们均匀地交替轮次。但即使在这种简单的情况下,我们也可以利用时间框架来发挥我们的优势。ROT.Engine
这是如何工作的?首先,将提供所有可用的参与者 - 该组件将负责公平回合调度。然后,将使用此调度程序在循环中自动调用相关参与者。(注意:我们作为第二个参数传递给 - 这意味着我们的 actor 不是一个一次性事件,而是一个重复的项目。ROT.Scheduler.SimpleROT.Enginetruescheduler.add
接受客户端 JavaScript 世界中一切都是异步的事实非常重要:基本上没有阻塞调用。这消除了将简单的 while 循环作为我们的主要计时/调度工具的可能性。幸运的是,为此做好了充分的准备。ROT.Engine
创建调度程序和引擎只需在我们的代码中添加几行:
Game.engine = null;
Game.init = function() {
var scheduler = new ROT.Scheduler.Simple();
scheduler.add(this.player, true);
this.engine = new ROT.Engine(scheduler);
this.engine.start();
}
演员和引擎之间的交互
引擎与其参与者之间存在紧密的共生关系。运行时,引擎会反复选择适当的参与者(使用调度程序)并调用参与者的方法。允许参与者通过调用并恢复此循环来中断此循环(例如,异步等待时)。act()ROT.Engine::lockROT.Engine::unlock
可以有多个锁级别(锁是递归的);这允许异步调用的复杂链接。幸运的是,这在我们的简单游戏中是不需要的。
那么,什么是演员呢?任何带有 act 方法的 JS 对象。
Player.prototype.act = function() {
Game.engine.lock();
/* wait for user input; do stuff when user hits a key */
window.addEventListener("keydown", this);
}
Player.prototype.handleEvent = function(e) {
/* process user input */
}
我们使用一些不常见(但非常有用!)的技术来分配事件处理程序:我们将一个JS对象作为第二个参数传递给调用。这样的对象(在本例中)必须具有方法,一旦事件发生(“keydown”)就会调用该方法。addEventListenerthishandleEvent
查看盒子内部
游戏生成了几个盒子,但到目前为止,它们都没有包含珍贵的阿那纳斯。让我们将 ananaas 存储在生成的第一个框中:
Game.ananas = null;
Game._generateBoxes = function(freeCells) {
for (var i=0;i<10;i++) {
/* ...previous stuff... */
if (!i) { this.ananas = key; } /* first box contains an ananas */
}
}
玩家还必须执行一项交互:查看盒子。我们将允许输入执行此操作:
打开一个盒子来验证其内容就像将玩家的当前位置与我们的盒子列表和存储的 ananas 位置进行比较一样简单:
Player.prototype._checkBox = function() {
var key = this._x + "," + this._y;
if (Game.map[key] != "*") {
alert("There is no box here!");
} else if (key == Game.ananas) {
alert("Hooray! You found an ananas and won this game.");
Game.engine.lock();
window.removeEventListener("keydown", this);
} else {
alert("This box is empty :-(");
}
}
基于寻路的人工智能
到目前为止,佩德罗缺少它的方法。我们将使用 rot.js 的寻路函数之一来实现 Pedro 的行为:(A* 算法)。一些基本的脚手架是必要的:act()ROT.Path.AStar
玩家必须有公共方法来读取其位置,
我们需要一个 passableCallback 函数,它告诉路径查找器哪些区域是可通过的,
我们需要一个pathCallback函数,它将从路径查找器中调用(以通知我们找到的最短路径)。
此外,为了使佩德罗比玩家弱一些,我们将仅在 4 拓扑中使用探路者。
Player.prototype.getX = function() { return this._x; }
Player.prototype.getY = function() { return this._y; }
Pedro.prototype.act = function() {
var x = Game.player.getX();
var y = Game.player.getY();
var passableCallback = function(x, y) {
return (x+","+y in Game.map);
}
var astar = new ROT.Path.AStar(x, y, passableCallback, {topology:4});
var path = [];
var pathCallback = function(x, y) {
path.push([x, y]);
}
astar.compute(this._x, this._y, pathCallback);
}
我们现在有佩德罗和球员之间的最短路径,存储在变量中。请注意,佩德罗目前的位置也是路径的一部分;这就是为什么我们首先放弃我们路径的第一项。如果生成的路径只有一个单元格长,则佩德罗站在玩家附近,游戏结束(玩家丢失)。否则,我们将应用与本教程第 2 部分中用于玩家的相同移动逻辑。path
Pedro.prototype.act = function() {
/* ...previous stuff... */
path.shift(); /* remove Pedro's position */
if (path.length == 1) {
Game.engine.lock();
alert("Game over - you were captured by Pedro!");
} else {
x = path[0][0];
y = path[0][1];
Game.display.draw(this._x, this._y, Game.map[this._x+","+this._y]);
this._x = x;
this._y = y;
this._draw();
}
}
心得:确定这个插件刚开始接触,只是能使用,但是确实封装的可以,有很多学习的地方。有什么使用上的心得想要交流的可以加我qq 2053163664。