我正在寻找关于采用如下瓷砖地图的示例实施思路的逻辑理解:
http://thorsummoner.github.io/old-html-tabletop-test/pallete/tilesets/fullmap/scbw_tiles.png
以合理的方式渲染,例如:
http://thorsummoner.github.io/old-html-tabletop-test/
我看到所有的瓷砖都在那里,但我不明白它们是如何以形成形状的方式放置的。
到目前为止,我对渲染图块的理解很简单,而且非常手动。循环遍历地图数组,其中有数字(1,2,3,无论如何),渲染指定的图块。
var mapArray = [
[0, 0, 0, 0 ,0],
[0, 1, 0, 0 ,0],
[0, 0, 0, 0 ,0],
[0, 0, 0, 0 ,0],
[0, 0, 1, 1 ,0]
];
function drawMap() {
background = new createjs.Container();
for (var y = 0; y < mapArray.length; y++) {
for (var x = 0; x < mapArray[y].length; x++) {
if (parseInt(mapArray[y][x]) == 0) {
var tile = new createjs.Bitmap('images/tile.png');
}
if (parseInt(mapArray[y][x]) == 1) {
var tile = new createjs.Bitmap('images/tile2.png');
}
tile.x = x * 28;
tile.y = y * 28;
background.addChild(tile);
}
}
stage.addChild(background);
}
得到我:
但这意味着我必须手动确定每个瓷砖在阵列中的位置,以便形成逻辑形状(岩层,草地等)
显然,上面制作github代码的人使用了不同的方法。理解逻辑(简单的伪代码)的任何指导都会非常有用
答案 0 :(得分:31)
那里没有任何逻辑。
如果您检查页面的来源,您会看到正文中的最后一个脚本标记有一个巨大的图块坐标数组。
在这个例子中没有任何魔术可以证明一个聪明的&#34;用于确定如何形成形状的系统。
现在,那就是说,有这样的事情......但他们并不简单。
更简单,更易于管理,是地图编辑器。
开箱即用:
有很多方法可以做到这一点...有免费或廉价的程序可以让你绘制瓷砖,然后吐出XML或JSON或CSV或任何给定的程序支持/导出。
Tiled (http://mapeditor.org)就是这样一个例子 还有其他人,但是Tiled是我能想到的第一个,是免费的,而且实际上相当不错。
<强>的优点:强>
直接的好处是,您可以获得一个应用程序,可以加载图像切片,并将其绘制到地图中
这些应用程序甚至可能支持添加碰撞层和实体层(放置[2,1]的敌人,[3,5]的加电和#34;伤害玩家&#34;触发器,岩浆)。
<强>缺点:强> ...缺点是您需要确切知道这些文件的格式,以便您可以将它们读入您的游戏引擎 现在,这些系统的输出是相对标准化的...这样你就可以将地图数据插入到不同的游戏引擎中(不管怎么说?),而游戏引擎并不是全部使用完全相同的tile文件,大多数优秀的tile-editors允许导出为多种格式(有些格式可以让你定义自己的格式)。
...所以说,替代(或者真的,相同的解决方案,只是手工制作),将是创建自己的拼贴编辑器。
<强> DIY 强>
您可以在Canvas中创建它,就像创建引擎来绘制图块一样容易
关键的区别在于你有你的瓷砖地图(比如StarCr中的tilemap .png ......呃...&#34;发现艺术&#34;来自示例,那里)。
找到瓷砖的坐标并在与该指数匹配的世界坐标上绘制它们,而不是循环遍历数组,你要做的是从地图中选择一个瓷砖(比如在MS Paint中选择颜色),然后在哪里单击(或拖动),找出与之相关的数组点,并将该索引设置为等于该图块。
<强>的优点:强>
天空才是极限;你可以做任何你想要的东西,使它适合你想要使用的任何文件格式,并让它处理你想要扔的任何疯狂的东西......
的缺点:强>
......当然,这意味着你必须自己制作它,并定义你想要使用的文件格式,并编写逻辑来处理所有这些想法......
基本实施
虽然我通常会尝试使这个整洁,并且JS范例友好,但这会产生大量代码。
因此,我将尝试表示它应该分解为单独的模块。
// assuming images are already loaded properly
// and have fired onload events, which you've listened for
// so that there are no surprises, when your engine tries to
// paint something that isn't there, yet
// this should all be wrapped in a module that deals with
// loading tile-maps, selecting the tile to "paint" with,
// and generating the data-format for the tile, for you to put into the array
// (or accepting plug-in data-formatters, to do so)
var selected_tile = null,
selected_tile_map = get_tile_map(), // this would be an image with your tiles
tile_width = 64, // in image-pixels, not canvas/screen-pixels
tile_height = 64, // in image-pixels, not canvas/screen-pixels
num_tiles_x = selected_tile_map.width / tile_width,
num_tiles_y = selected_tile_map.height / tile_height,
select_tile_num_from_map = function (map_px_X, map_px_Y) {
// there are *lots* of ways to do this, but keeping it simple
var tile_y = Math.floor(map_px_Y / tile_height), // 4 = floor(280/64)
tile_x = Math.floor(map_px_X / tile_width ),
tile_num = tile_y * num_tiles_x + tile_x;
// 23 = 4 down * 5 per row + 3 over
return tile_num;
};
// won't go into event-handling and coordinate-normalization
selected_tile_map.onclick = function (evt) {
// these are the coordinates of the click,
//as they relate to the actual image at full scale
map_x, map_y;
selected_tile = select_tile_num_from_map(map_x, map_y);
};
现在你有一个简单的系统来确定点击了哪个瓷砖
同样,有很多方法可以建立这个,你可以使它更多OO,
并做一个适当的&#34;瓷砖&#34;数据结构,您希望在整个引擎中读取和使用。
现在,我只是返回从零到零的数字,从左到右,从上到下阅读。
如果每行有5个图块,并且有人选择第二行的第一个图块,那就是#3图块。
然后,对于&#34;绘画&#34;,你只需要听一下画布点击,弄清楚X和Y是什么,
找出世界上的位置,以及等于的阵列点
从那里,您只需转储selected_tile
的价值,以及它的相关信息。
// this might be one long array, like I did with the tile-map and the number of the tile
// or it might be an array of arrays: each inner-array would be a "row",
// and the outer array would keep track of how many rows down you are,
// from the top of the world
var world_map = [],
selected_coordinate = 0,
world_tile_width = 64, // these might be in *canvas* pixels, or "world" pixels
world_tile_height = 64, // this is so you can scale the size of tiles,
// or zoom in and out of the map, etc
world_width = 320,
world_height = 320,
num_world_tiles_x = world_width / world_tile_width,
num_world_tiles_y = world_height / world_tile_height,
get_map_coordinates_from_click = function (world_x, world_y) {
var coord_x = Math.floor(world_px_x / num_world_tiles_x),
coord_y = Math.floor(world_px_y / num_world_tiles_y),
array_coord = coord_y * num_world_tiles_x + coord_x;
return array_coord;
},
set_map_tile = function (index, tile) {
world_map[index] = tile;
};
canvas.onclick = function (evt) {
// convert screen x/y to canvas, and canvas to world
world_px_x, world_px_y;
selected_coordinate = get_map_coordinates_from_click(world_px_x, world_px_y);
set_map_tile(selected_coordinate, selected_tile);
};
正如您所看到的,执行另一个的过程与执行另一个的过程非常相似(因为它是 - 在一个坐标集中给定x和y,将其转换为另一个比例/集)
然后,绘制瓷砖的过程几乎完全相反 给定world-index和tile-number,反向查找world-x / y和tilemap-x / y。 您也可以在示例代码中看到该部分。
这张瓷砖画是制作2D地图的传统方式,无论我们是在谈论星际争霸,塞尔达还是马里奥兄弟。 并非所有人都拥有瓷砖涂料的奢侈品和#34;编辑器(有些是手工制作文本文件,甚至是电子表格,以获得正确的间距),但是如果加载星际争霸甚至是WarCraft III(这是3D),并进入他们的编辑,那么瓷砖画家就是你得到了什么,这正是暴雪制作这些地图的方式。
在基本前提下,您现在可以使用其他地图&#34;地图&#34;这也是必需的: 你需要一张碰撞图来知道你可以/不能走的那些瓷砖,一个实体图,以显示有门的地方,或者电源或矿物,或敌人产卵,或过场动画的事件触发器......
并非所有这些都需要在与世界地图相同的坐标空间中运行,但它可能有所帮助。
此外,您可能想要一个更聪明的世界&#34;
能够在一个级别中使用多个平铺贴图,例如...
并在tile-editor中下拉以交换tile-maps。
...一种方法可以保存两个图块信息(不仅仅是X / Y,还有其他关于图块的信息),并保存完成的&#34;地图&#34;数组,填充瓷砖。
即使只是复制JSON,并将其粘贴到自己的文件中......
另一种方式,你之前建议的方式(&#34;知道如何连接岩石,草等等#34;)被称为程序生成。
这是一个更难,更多涉及
像暗黑破坏神这样的游戏使用它,这样你每次玩游戏时都会处于不同的随机生成环境中。 Warframe是一个使用程序生成来做同样事情的FPS。
<强>前提:强>
基本上,你从瓷砖开始,而不是只是一个瓷砖是一个图像,瓷砖必须是一个有图像和位置的对象,但也有一个可能在它周围的东西列表。
当你放下一片草时,那草就有可能在它旁边产生更多的草
草可能会说,在它周围的四个方向中的任何一个方向,水的概率为10%,岩石几率为20%,污垢几率为30%,草的几率为40%。
当然,它真的不是那么简单(如果你错了,也可能是这样)。
虽然这是一个想法,但程序生成的棘手部分实际上是确保一切正常而不会破坏。
的约束强>
你不可能,例如在这个例子中,悬崖墙出现在高地的内侧。它只能出现在上方和右方的高地,低地和左下方的地方(星际争霸编辑器会自动执行此操作,如您所绘)。斜坡只能连接有意义的瓷砖。你可以不在门外,或将世界包裹在河流/湖泊中,阻止你移动(或者更糟糕的是,阻止你完成一个级别)。
<强>优点强>
对于长寿来说真的很棒,如果你可以让你的所有寻路和约束工作 - 不仅是伪随机生成地形和布局,还有敌人放置,战利品放置等等。
近14年后,人们仍在玩暗黑破坏神II。
<强>缺点强>
当你是一个单人团队(在业余时间他们没有碰巧成为数学家/数据科学家)时,真的很难做对。
确保地图有趣/平衡/竞争真的很糟糕...
星际争霸永远不会使用100%随机生成公平的游戏玩法
程序生成可以用作种子&#34;
你可以点击&#34;随机化&#34;按钮,看看你得到了什么,然后从那里进行调整和修复,但是为了约束传播而编写的平衡&#34;或许多游戏规则,你和# 39;最终花费更多的时间修理发电机,而不仅仅是自己画地图。
有一些教程,学习遗传算法,寻路等等,都是很好的技能......为了学习制作2D自上而下的瓷砖游戏,buuuut是在你获得一两个游戏/引擎之后,你需要考虑一下这个问题。