• [Lua][Love] "图块集与地图" 加载显示功能 TileMap


    效果


    安装库

    安装两个库,分别用来读xml和csv,如果有luarocks,执行下列命令

    powershell
    luarocks install xml2lua
    luarocks install ftcsv
    

    manoelcampos/xml2lua (github.com)

    FourierTransformer/ftcsv


    整体结构 tilemap.lua

    引入要用的两个库,创建xml解析器,读取xml解析到的数据是从handler.root里读的,而不是parser

    lua
    local xml2lua = require 'xml2lua'
    local handler = require 'xmlhandler.tree'
    local ftcsv = require 'ftcsv'
    local parser = xml2lua.parser(handler)
    local map = {}
    local tileset = {}
    

    love.load执行两个回调函数加载图块集跟地图

    lua
    function map:load(...)
      map:loadTMX(love.filesystem.getWorkingDirectory() .. "/map/map.tmx")
    end
    
    function tileset:load(...)
      tileset:loadTSX(love.filesystem.getWorkingDirectory() .. "/map/tilemap_packed.tsx")
      tileset:loadTile()
    end
    

    在游戏窗口绘制各个图块,图层从map列表获得,图块从tileset列表获得

    lua
    function map:draw()
      for _, layer in ipairs(map) do
        for i = 1, map.height, 1 do
          for k = 1, map.width, 1 do
            if layer[i][k] ~= "0" then
              love.graphics.draw(
                tileset.image,
                tileset[tonumber(layer[i][k])],
                tileset.tilewidth * (k - 1),
                tileset.tileheight * (i - 1)
              )
            end
          end
        end
      end
    end
    

    加载文件用,找不到文件就报错

    lua
    function openFile(filename)
      local f = io.open(filename, 'r')
      if f then
        return f
      else
        error("no file exists", 2)
      end
    end
    

    加载地图文件 tmx,先用xml解析器解析,然后用ftcsv解析各个图层保存到map

    tmx各个图层csv的最后一行末尾没有 , 符号会导致 ftcsv 出错,手动添加一个 , 符号

    lua
    function map:loadTMX(filename)
      local f = openFile(filename)
      local tmx = f:read('a')
      parser:parse(tmx)
      local attr = handler.root.map._attr
      map.width = tonumber(attr.width)
      map.height = tonumber(attr.height)
      for _, value in pairs(handler.root.map.layer) do
        map[#map + 1] = ftcsv.parse(value.data[1] .. ',', ",", { headers = false, loadFromString = true })
      end
    end
    

    加载 TSX 图块集的信息,包括图块大小,图块数量,列数,行数需要自己算,记得转换成 number 类型

    lua
    function tileset:loadTSX(filename)
      local f = openFile(filename)
      local tsx = f:read('a')
      parser:parse(tsx)
      local attr = handler.root.tileset._attr
      local image = handler.root.tileset.image._attr
      tileset.tilewidth = tonumber(attr.tilewidth)
      tileset.tileheight = tonumber(attr.tileheight)
      tileset.tilecount = tonumber(attr.tilecount)
      tileset.columns = tonumber(attr.columns)
      tileset.rows = tileset.tilecount / tileset.columns
      tileset.source = image.source
      tileset.width = tonumber(image.width)
      tileset.width = tonumber(image.height)
    end
    

    加载 TSX 图块集信息后,已经拿到图块数量,图块大小,图片集文件地址等信息。通过这些信息,我们使用加载图片集文件,然后使用 newQuad 将图片集各个部分,即图块,保存到tileset列表中

    lua
    function tileset:loadTile()
      self.source = string.gsub(self.source, "%.%.", "")
      self.image = love.graphics.newImage(self.source)
      for i = 0, tileset.rows - 1, 1 do
        for j = 0, tileset.columns - 1, 1 do
          tileset[#tileset + 1] = love.graphics.newQuad(
            j * tileset.tilewidth, i * tileset.tileheight,
            tileset.tilewidth, tileset.tileheight, self.image
          )
        end
      end
    end
    

    返回maptileset

    lua
    return {
      map = map,
      tileset = tileset
    }
    

    鼠标位置显示坐标 mouse.lua

    根据鼠标在屏幕的x、y坐标绘制方块和文字

    lua
    local mouse = {}
    
    mouse.pos_x = 0
    mouse.pos_y = 0
    mouse.size = 32
    
    function mouse:update(t)
      mouse.pos_x, mouse.pos_y = love.mouse.getPosition()
    end
    
    function mouse:draw()
      local x = math.floor(self.pos_x / self.size)
      local y = math.floor(self.pos_y / self.size)
      love.graphics.rectangle("fill", x * self.size, y * self.size, self.size, self.size)
      love.graphics.print(
        string.format("(%d,%d)", x + 1, y + 1),
        x * self.size + self.size,
        y * self.size + math.floor(self.size / 2), 0, 2,
        2)
    end
    
    return mouse
    

    游戏循环 main.lua

    引入我们写的模块

    lua
    local tilemap = require 'tilemap'
    local map = tilemap.map
    local tileset = tilemap.tileset
    local mouse = require 'mouse'
    require 'debugger'
    

    原图块像素只有 16px,游戏画面需要放大几倍,如果按默认过滤模式,会出现“流血现象”(下图所示),全部改成nearest,单纯的像素放大。love 引擎加载时候加载了图块集与地图。

    lua
    love.load = function()
      love.graphics.setDefaultFilter("nearest", "nearest")
      tileset:load()
      map:load()
      player = love.graphics.newImage("assets/player.png")
    end
    

    更新鼠标位置

    lua
    love.update = function(t)
      mouse:update(t)
    end
    

    先保存原视角状态,修改视角整体放大两倍,绘制地图,之后还原视角,再画玩家(32*32)跟鼠标。

    lua
    love.draw = function()
      love.graphics.push()
      love.graphics.scale(2)
      map:draw()
      love.graphics.pop()
      love.graphics.draw(player, 16 * 16, 192)
      mouse:draw()
    end
    

    自己创建 TSX、TMX

    用的一款工具 Tiled Map Editor by Thorbjørn (itch.io)


    完整代码 tilemap.lua

    lua
    local xml2lua = require 'xml2lua'
    local handler = require 'xmlhandler.tree'
    local ftcsv = require 'ftcsv'
    local parser = xml2lua.parser(handler)
    local map = {}
    local tileset = {}
    
    function map:load(...)
      map:loadTMX(love.filesystem.getWorkingDirectory() .. "/map/map.tmx")
    end
    
    function tileset:load(...)
      tileset:loadTSX(love.filesystem.getWorkingDirectory() .. "/map/tilemap_packed.tsx")
      tileset:loadTile()
    end
    
    function map:draw()
      for _, layer in ipairs(map) do
        for i = 1, map.height, 1 do
          for k = 1, map.width, 1 do
            if layer[i][k] ~= "0" then
              love.graphics.draw(
                tileset.image,
                tileset[tonumber(layer[i][k])],
                tileset.tilewidth * (k - 1),
                tileset.tileheight * (i - 1)
              )
            end
          end
        end
      end
    end
    
    function openFile(filename)
      local f = io.open(filename, 'r')
      if f then
        return f
      else
        error("no file exists", 2)
      end
    end
    
    function map:loadTMX(filename)
      local f = openFile(filename)
      local tmx = f:read('a')
      parser:parse(tmx)
      local attr = handler.root.map._attr
      map.width = tonumber(attr.width)
      map.height = tonumber(attr.height)
      for _, value in pairs(handler.root.map.layer) do
        map[#map + 1] = ftcsv.parse(value.data[1] .. ',', ",", { headers = false, loadFromString = true })
      end
    end
    
    function tileset:loadTSX(filename)
      local f = openFile(filename)
      local tsx = f:read('a')
      parser:parse(tsx)
      local attr = handler.root.tileset._attr
      local image = handler.root.tileset.image._attr
      tileset.tilewidth = tonumber(attr.tilewidth)
      tileset.tileheight = tonumber(attr.tileheight)
      tileset.tilecount = tonumber(attr.tilecount)
      tileset.columns = tonumber(attr.columns)
      tileset.rows = tileset.tilecount / tileset.columns
      tileset.source = image.source
      tileset.width = tonumber(image.width)
      tileset.width = tonumber(image.height)
    end
    
    function tileset:loadTile()
      self.source = string.gsub(self.source, "%.%.", "")
      self.image = love.graphics.newImage(self.source)
      for i = 0, tileset.rows - 1, 1 do
        for j = 0, tileset.columns - 1, 1 do
          tileset[#tileset + 1] = love.graphics.newQuad(
            j * tileset.tilewidth, i * tileset.tileheight,
            tileset.tilewidth, tileset.tileheight, self.image
          )
        end
      end
    end
    
    return {
      map = map,
      tileset = tileset
    }
    

    完整代码 main.lua

    lua
    local tilemap = require 'tilemap'
    local map = tilemap.map
    local tileset = tilemap.tileset
    local mouse = require 'mouse'
    require 'debugger'
    
    
    love.load = function()
      love.graphics.setDefaultFilter("nearest", "nearest")
      tileset:load()
      map:load()
      player = love.graphics.newImage("assets/player.png")
    end
    
    love.update = function(t)
      mouse:update(t)
    end
    
    love.draw = function()
      love.graphics.push()
      love.graphics.scale(2)
      map:draw()
      love.graphics.pop()
      love.graphics.draw(player, 16 * 16, 192)
      mouse:draw()
    end
    

    完整项目

    https://linxiaoxu.oss-cn-hangzhou.aliyuncs.com/static/files/2023/tinytown.zip


    __EOF__

  • 本文作者: 小能正在往前冲
  • 本文链接: https://www.cnblogs.com/linxiaoxu/p/17655081.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    Windows Server服务器下的Linux子系统
    全面重构存储系统,释放AI时代全新数据价值
    FastDFS安装(含nginx)
    观测云产品更新|Pipeline 使用体验优化;支持写入用户的自定义事件;自定义查看器支持选择更多类型的数据等
    如何选择国产压力测试工具?
    模拟实现【哈希】
    AWS SAA-C03 #50
    [附源码]计算机毕业设计疫情背景下社区互助服务系统Springboot程序
    Excel查找函数的高级用法
    抖音小程序开发教学系列(4)- 抖音小程序组件开发
  • 原文地址:https://www.cnblogs.com/linxiaoxu/p/17655081.html