使用 javascript 渲染瓦片地图

2024-01-31

我正在寻找对获取瓷砖地图的示例实现想法的逻辑理解,如下所示:

http://thorsummoner.github.io/old-html-tabletop-test/pallete/tilesets/fullmap/scbw_tiles.png http://thorsummoner.github.io/old-html-tabletop-test/pallete/tilesets/fullmap/scbw_tiles.png

并以逻辑方式渲染,如下所示:

http://thorsummoner.github.io/old-html-tabletop-test/ 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);     
}   

Gets me:

但这意味着我必须手动找出每个图块在数组中的位置,以便形成逻辑形状(岩层、草丛等)

显然,制作上面 github 代码的人使用了不同的方法。任何有关理解的指导logic(使用简单的伪代码)会非常有帮助


那里没有任何逻辑。

如果您检查页面的源代码,您将看到正文中的最后一个脚本标记具有大量的图块坐标。

该示例并没有什么神奇之处,它演示了一个用于弄清楚如何形成形状的“智能”系统。

话虽如此,确实有这样的事情……但它们并不简单。

What is更简单、更易于管理的是地图编辑器。


平铺编辑器

盒子外面:

有很多方法可以做到这一点...有一些免费或便宜的程序可以让您绘制瓷砖,然后输出 XML 或 JSON 或 CSV 或给定程序支持/导出的任何内容。

Tiled ( http://mapeditor.org http://mapeditor.org)就是这样的一个例子。
还有其他的,但 Tiled 是我能想到的第一个,它是免费的,而且实际上相当不错。

pros:
直接的好处是您可以获得一个应用程序,可以让您加载图像图块并将其绘制到地图中。
这些应用程序甚至可能支持添加碰撞层和实体层(将敌人置于 [2,1],将能量提升置于 [3,5],并在熔岩上方放置“伤害玩家”触发器)。

cons:...缺点是您需要确切地知道这些文件的格式,以便您可以将它们读入游戏引擎。
现在,这些系统的输出相对标准化......因此您可以将地图数据插入不同的游戏引擎(否则有什么意义?),虽然游戏引擎并不都使用完全正确的图块文件同样,大多数优秀的图块编辑器都允许导出为多种格式(有些可以让您定义自己的格式)。

...也就是说,替代方案(或者实际上是相同的解决方案,只是手工制作)是创建您自己的图块编辑器。

DIY
您可以在 Canvas 中创建它,就像创建绘制图块的引擎一样简单。
关键区别在于您拥有图块地图(例如来自 StarCr...erm...示例中的“found-art”的图块地图 .png)。
您不需要循环遍历数组,找到图块的坐标并在与该索引匹配的世界坐标处绘制它们,而是从地图中选择一个图块(就像在 MS Paint 中选择颜色),然后在任何地方您单击(或拖动),找出与之相关的数组点,并将该索引设置为等于该图块。

pros:
天空才是极限;你可以制作任何你想要的东西,让它适合你想要使用的任何文件格式,并让它处理你想要扔给它的任何疯狂的东西......
cons:
...当然,这意味着您必须自己制作它,并定义要使用的文件格式,并编写逻辑来处理所有这些滑稽的想法...

基本实现
虽然我通常会尝试使其变得整洁且 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);
    };

现在您有了一个简单的系统来确定单击了哪个图块。
再说一遍,构建这个的方法有很多,你可以让它更加面向对象,
并制作一个适当的“平铺”数据结构,您希望在整个引擎中读取和使用它。

现在,我只是返回图块的从零开始的数字,从左到右、从上到下读取。
如果每行有 5 个图块,并且有人选择了第二行的第一个图块,那就是图块 #5。

然后,对于“绘画”,您只需要听一下画布的点击声,找出 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,将其转换为另一个比例/集合)。

那么,绘制图块的过程几乎完全相反。
给定世界索引和图块编号,反向工作即可找到世界 x/y 和图块地图 x/y。
您也可以在示例代码中看到该部分。

这种瓷砖绘画是制作 2D 地图的传统方式,无论我们谈论的是《星际争霸》、《塞尔达传说》还是《马里奥兄弟》。
并非所有人都拥有“用瓷砖绘制”编辑器(有些是在文本文件甚至电子表格中手动编写的,以获得正确的间距),但如果您加载《星际争霸》甚至《魔兽争霸 III》(这是3D),然后进入他们的编辑器,瓷砖画家就是你所得到的,这正是暴雪制作这些地图的方式。

补充

基本前提完成后,您现在还需要其他“地图”: 你需要一个碰撞地图来知道你可以/不能在哪些瓷砖上行走,一个实体地图来显示哪里有门,或能量提升或矿物,或敌人生成,或事件-过场动画的触发器...

并非所有这些都需要在与世界地图相同的坐标空间中操作,但它可能会有所帮助。

此外,您可能想要一个更智能的“世界”。
例如,能够在一个关卡中使用多个图块地图……
以及图块编辑器中用于交换图块地图的下拉菜单。

...一种保存图块信息(不仅是 X/Y,还包括有关图块的其他信息)并保存已完成的“地图”数组(填充有图块)的方法。

即使只是复制 JSON,并将其粘贴到自己的文件中......


程序生成

另一种方法,即您之前建议的方法(“知道如何连接岩石、草等”)称为程序生成.
这要困难得多,而且涉及得多。
像《暗黑破坏神》这样的游戏就使用了这一点,这样你每次玩时都会处于不同的随机生成的环境中。 Warframe 是一款 FPS,它使用程序生成来完成同样的事情。

premise:
基本上,您从图块开始,图块不仅仅是图像,图块必须是具有图像和位置的对象,而且还具有可能在其周围的事物的列表。
当你放下一块草时,该草旁边就有可能生成更多草。
草可能会说,在它周围的四个方向中的任何一个方向上,有 10% 的可能性有水,20% 的可能性有岩石,30% 的可能性有泥土,40% 的可能性有更多的草。

当然,事情确实没那么简单(或者可能很简单,如果你错了)。

虽然这是这个想法,但程序生成的棘手部分实际上是确保一切正常运行而不会中断。
限制条件
例如,在该示例中,你不能让悬崖墙出现在高地的内部。它只能出现在上方和右侧有高地、下方和左侧有低地的地方(星际争霸编辑器会自动执行此操作,如您所画)。坡道只能连接有意义的瓷砖。你不能用墙隔开门,或者将世界包裹在河流/湖泊中,从而阻止你移动(或更糟糕的是,阻止你完成关卡)。

pros
如果你能让所有的寻路和约束发挥作用,这对长寿来说真的很棒——不仅可以伪随机生成地形和布局,还可以放置敌人、放置战利品等等。
近 14 年后,人们仍在玩《暗黑破坏神 II》。

cons
当你是一个单人团队时(业余时间碰巧不是数学家/数据科学家),真的很难做到正确。
对于保证地图的乐趣/平衡/竞争来说真的很糟糕......
《星际争霸》永远不可能使用 100% 随机生成来实现公平的游戏玩法。
程序生成可以用作“种子”。
你可以点击“随机化”按钮,看看你得到了什么,然后从那里进行调整和修复,但是会有太多的“平衡”修复,或者写了太多的游戏规则来限制传播,你'最终你会花更多的时间来修理发电机,而不是自己画地图。

有一些教程,学习遗传算法、寻路等等,都是很棒的技能……但是,对于学习制作 2D 自上而下的瓷砖游戏来说,这有点过分了,更确切地说,是在你掌握一两个游戏/引擎之后需要研究的东西。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 javascript 渲染瓦片地图 的相关文章

随机推荐

  • 将背景颜色设置为 HighChart xAxis 标签

    如何为 HighChart xAxis 标签设置背景颜色 我尝试了以下但没有运气 x轴 类别 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 标签 旋转 90 风格 颜色 89A54E 填写 000 背景颜色 F
  • 导航到“url”,等待“load” - Python Playwright Issue

    嘿 我在 python playwright 中有用于获取页面源代码的代码 import json import sys import bs4 import urllib parse from bs4 import BeautifulSou
  • 悬停后保持下拉菜单打开(CSS)

    我创建了一个水平菜单 当您将鼠标悬停在某个项目上时 会出现一个下拉菜单 这一切都很好 但是 当您离开菜单项 使用下拉菜单 时 下拉菜单就会消失 我知道这是因为你不再悬停它 但我该如何解决这个问题 注意 我不希望下拉菜单直接位于其下方 我希望
  • 无法在 Angularjs 中使用 templateUrl 加载模板

    我刚刚学习 Angularjs 以及如何使用 templateUrl 加载模板 我有一个简单的指令 var mainApp angular module mainApp mainApp directive request function
  • 如何将文本区域的标签对齐到顶部?

    我确实有一个表格数据 如下所示 td td
  • 空字符串而不是不匹配的组错误

    我有这段代码 for n in range 1 10 new re sub r regex group regex regex r something str n r 1 old count 1 它会抛出不匹配的组错误 但如果它不匹配 我想
  • 启用 Vuforia 时 Unity 崩溃

    在 XR 设置中启用 Vuforia 增强现实支持 时 我的编辑器崩溃 以下是我遵循的步骤 重现步骤 1 创建一个新项目 2 文件 gt 构建设置 gt 播放器设置 gt 播放器 3 从 XR 设置中启用 Vuforia 增强现实 复选框
  • 使用 matplotlib 从图中获取数据

    我在 python 中使用 matplotlib 来构建散点图 假设我有以下 2 个数据列表 X 1 2 3 4 5 Y 6 7 8 9 10 然后我使用X作为X轴值 Y作为Y轴值来绘制散点图 所以我会有一张上面有 5 个散射点的图片 对吧
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • 通过网络桥接串行端口

    对于使用串行端口进行通信 确切地说是 Modbus RTU 的应用程序 我需要通过网络桥接串行端口以进行测试 所以我想要以下设置 Device Network PC SerialPort SerialPort Map Serialport
  • 如何将节点标题放置在 d3 Sankey 图中节点的左侧或右侧?

    这是我指的例子 http bl ocks org d3noob c9b90689c1438f57d649 http bl ocks org d3noob c9b90689c1438f57d649 第二级节点的标题位于其右侧 如何将它们放置在
  • 函数导入(存储过程)需要 SaveChanges() 吗?

    Is 保存更改 函数导入 存储过程 有必要吗 Example void foo Product product AddProduct is a function import of a stored procedure entities A
  • WebAPI 中的 DTO 和投影

    WebAPI支持OData 因此API使用者可以指定他需要的字段 而且效果很好 但是有没有办法在 WebAPI 中使用 DTO 对象和投影呢 例如我有 public class WebSite public string Url get s
  • iOS 中的 Google Analytics SDK 3.0 _sqlite3 链接器错误

    我正在整合谷歌分析 SDK 3 0在我的项目中 但我越来越链接器错误当尝试构建我的项目时 正如在文档 https developers google com analytics devguides collection ios v3 hea
  • 如何按键对字典进行排序,其中值是 Swift 4 中的对象数组?

    我有字典 其中包含String键和数组Objects作为价值 使用以下方法将这些值从已排序的对象数组添加到字典中append方法 根据对象属性的首字母将值分类为键 但返回未排序的字典 字典声明为 var namesDic String Na
  • Java 泛型问题:类“不在类型变量的范围内”错误。

    我正在开发一个涉及泛型的类项目 public interface Keyable
  • 耳机是否已插入? IOS 7

    为 iPhone 开发一个应用程序 其中包含也需要通过耳机收听的音频文件 如何检查耳机是否未插入 以便我可以告诉用户插入耳机 我有来自另一个线程的以下代码 但不推荐使用 audioSessionGetProperty 方法 任何人都知道如何
  • IntelliJ IDEA - 将 Finder 中的 Reveal 更改为新的文件浏览器

    我已经设置了路径查找器作为我的 OSX Yosemite 上的默认文件浏览器 管理器 因此 大多数应用程序在显示文件时会打开路径查找器窗口 但不会打开 IDEA 当我选择Reveal in Finder对文件执行操作时 它总是会打开一个新的
  • 如何在sqlite数据库中手动插入数据?

    我想在 sqlite 数据库中插入数据手册 使用cmd插入数据 我在android项目中使用了数据 如果您想从开发机器手动插入数据 那么您应该首先下载SQLite管理器从这里 http code google com p sqlite ma
  • 使用 javascript 渲染瓦片地图

    我正在寻找对获取瓷砖地图的示例实现想法的逻辑理解 如下所示 http thorsummoner github io old html tabletop test pallete tilesets fullmap scbw tiles png