Tiled Python 插件开发指南用 Python 3 为 Tiled 添加自定义地图与图块集格式支持【免费下载链接】tiledFlexible level editor项目地址: https://gitcode.com/gh_mirrors/ti/tiledTiled 自带一个基于 Python 3 的插件源码位于 src/plugins/python允许你以 Python 脚本为 Tiled 增加自定义地图格式、图块集格式的导入与导出能力并支持脚本目录热加载——修改脚本后无需重启编辑器。本文以官方文档 docs/manual/python.rst 为核心结合仓库内插件实现源码与示例脚本完整讲解环境准备、插件编写、调试方法与可用 API帮助你快速交付可运行的自定义格式插件。官方文档原文docs/manual/python.rst同时说明自 Tiled 1.3 起Tiled 已支持通过JavaScript扩展详见 docs/manual/scripting.rst。JavaScript API 的功能远不止添加自定义地图格式文档完善、开箱即用、全平台可用因此官方推荐优先使用 JavaScript仅在确有需要时使用 Python 插件。此外docs/manual/export-custom.rst 也提醒Python 脚本在 macOS 发行版与 Ubuntu snap 发行版中不受支持且插件对 Python 版本非常敏感因此其使用不获官方推荐。插件能做什么地图与图块集的自定义格式读写Python 插件的作用是把 Python 脚本注册为 Tiled 的文件格式插件使 Tiled 的File Export导出与File Import导入对话框中出现该格式的选项。每个脚本定义一个继承自tiled.Plugin的类即可实现地图格式自 Tiled 1.11 起继承tiled.TilesetPlugin即可实现图块集格式。以官方文档中的导出示例为例假设你希望把地图导出为如下纯文本格式每行是一行瓷砖的 ID 列表-1表示空格最后一行以分号结尾29,29,29,29,29,29,32,-1,34,29,29,29,29,29,29, 29,29,29,29,29,29,32,-1,34,29,29,29,29,29,29, 29,29,29,29,29,29,32,-1,34,29,29,29,29,29,29, 29,29,29,29,29,29,32,-1,34,29,29,29,29,29,29, 25,25,25,25,25,25,44,-1,34,29,29,29,29,29,29, -1,-1,-1,-1,-1,-1,-1,-1,34,29,29,29,29,29,29, 41,41,41,41,41,41,41,41,42,29,29,24,25,25,25, 29,29,29,29,29,29,29,29,29,29,29,32,-1,-1,-1, 29,29,29,29,29,29,39,29,29,29,29,32,-1,35,41, 29,29,29,29,29,29,29,29,29,29,29,32,-1,34,29, 29,29,29,29,29,29,29,29,37,29,29,32,-1,34,29;这一需求只需一个脚本即可完成详见下文“编写你的第一个导出插件”。环境准备Python 版本、平台支持与启用插件必须安装兼容版本的 Python插件通过 C 嵌入 CPython 解释器工作因此系统必须安装与构建版本匹配的 Pythonsrc/plugins/python/pythonplugin.cpp 中通过Py_InitializeFromConfig初始化解释器。不同平台的官方发行版对版本要求如下以 Tiled 1.11 为例详见 docs/manual/python.rst平台 / 发行版Python 版本要求Windows 10 构建Python 3.12Windows 7-8 构建Python 3.8Linux AppImage基于 Ubuntu 22.04 构建Python 3.10Ubuntu 安装libpython3.10Fedora 安装python3.10-libsmacOS 发行版不提供 Python 插件Ubuntu snap不提供 Python 插件在 Windows 上安装 Python 时务必在安装器中勾选Add python.exe to PATH否则 Tiled 无法定位解释器插件默认禁用需在偏好设置中启用自 Tiled 1.2.4 起Python 插件默认禁用——因为根据系统安装的 Python 版本不同加载该插件可能引起崩溃对应官方 issue #2091。插件元数据文件 src/plugins/python/plugin.json 中的{ defaultEnable: false }即此默认策略的体现。要使用 Python 插件请打开 Tiled 的Preferences偏好设置在插件列表中启用 Python 插件。脚本放置目录~/.tiled为了让脚本被加载需要把.py文件放进用户主目录下的~/.tiled文件夹Windows打开命令提示符cmd.exe默认即从主目录启动输入mkdir .tiled创建文件夹Linux以点开头的文件夹默认隐藏多数文件管理器中可用CtrlH切换显示隐藏文件。从源码看src/plugins/python/pythonplugin.cpp 第 50 行脚本目录被硬编码为QDir::homePath() /.tiled并在初始化时将该路径插入sys.path随后扫描该目录下所有*.py文件逐一加载。热加载无需重启 TiledTiled 会监视~/.tiled目录及其中的脚本文件变化因此添加或修改脚本后无需重启 Tiled但目录必须在你启动 Tiled 时已存在。底层实现位于 src/plugins/python/pythonplugin.cpp插件构造函数中创建了QFileSystemWatcher监听目录变化directoryChanged与文件变化fileChanged变化信号触发一个1 秒的QTimermReloadTimer单次触发定时器超时后调用reloadModules()reloadModules()重新遍历脚本目录对已加载模块调用PyImport_ReloadModule对新文件调用PyImport_ImportModule并重新注册其中的插件类。这种“变更后延迟 1 秒批量重载”的设计避免了保存脚本过程中因文件写入未完成而加载到半截代码的问题。编写你的第一个导出插件将下面的脚本保存为~/.tiled/example.pyfrom tiled import * class Example(Plugin): classmethod def nameFilter(cls): return Example files (*.example) classmethod def shortName(cls): return example classmethod def write(cls, tileMap, fileName): with open(fileName, w) as fileHandle: for i in range(tileMap.layerCount()): if isTileLayerAt(tileMap, i): tileLayer tileLayerAt(tileMap, i) for y in range(tileLayer.height()): tiles [] for x in range(tileLayer.width()): if tileLayer.cellAt(x, y).tile() ! None: tiles.append(str(tileLayer.cellAt(x, y).tile().id())) else: tiles.append(str(-1)) line ,.join(tiles) if y tileLayer.height() - 1: line ; else: line , print(line, filefileHandle) return True脚本中每个类方法的含义如下nameFilter(cls)返回文件类型过滤字符串形如Example files (*.example)将显示在File Export的类型下拉框中shortName(cls)返回格式的短名称用于内部标识write(cls, tileMap, fileName)接收 Tiled 传入的地图对象与目标文件名执行实际的导出逻辑。方法返回True表示导出成功。导出逻辑要点先通过tileMap.layerCount()遍历所有图层用isTileLayerAt(tileMap, i)判断第i层是否为瓷砖层再用tileLayerAt(tileMap, i)取得该层随后双层循环遍历每个格子tileLayer.cellAt(x, y).tile()返回该格的Tile对象空则为None取tile().id()得到瓷砖 ID空格输出-1行与行之间用逗号分隔最后一行以分号结尾。保存脚本并如果插件未自动重载稍等片刻打开File Export类型下拉框中即会出现Example files条目选择它即可把当前地图导出为上述格式。注意该示例不支持组图层group layers——脚本只遍历了layerCount()层级的直接子层若地图包含组图层组内图层不会被导出。编写图块集插件Tiled 1.11若要为自定义的图块集格式导入/导出.tsx之外的图块集文件编写插件只需把基类从tiled.Plugin换成tiled.TilesetPlugin。仓库中的官方示例脚本 src/plugins/python/scripts/tileset.py 展示了最简写法from tiled import * class TExample(TilesetPlugin): classmethod def nameFilter(cls): return TExample files (*.texample) classmethod def shortName(cls): return texample classmethod def write(cls, tileset, fileName): with open(fileName, w) as f: f.write({}\n.format(tileset.tileCount())) for idx in range(tileset.tileCount()): tile tileset.tileAt(idx) f.write(\t{}. {}: {} {}x{}\n.format(idx, tile.id(), tile.type(), tile.width(), tile.height())) return True这里write(cls, tileset, fileName)接收的参数是一个Tileset对象示例用tileset.tileCount()与tileset.tileAt(idx)遍历全部瓷砖并输出每个瓷砖的id()、type()、width()、height()。仓库 src/plugins/python/scripts 目录还提供了多个可直接参考的完整示例脚本脚本说明fotf.pyFury of the Furries 关卡加载器展示了readsupportsFile导入流程、Tileset.create、TileLayer、Cell、addTileset/addLayer等 API 的完整用法mappy.pyMappy 地图格式支持pk2.py、zst.py其他游戏地图格式的导入/导出tileset.py最简图块集导出插件上文示例调试你的脚本脚本解析或运行过程中的任何错误都会打印到Console控制台。打开方式菜单View Views and Toolbars Console。从源码实现看错误的可见性是有保障的插件在初始化时src/plugins/python/pythonplugin.cpp 的initialize()把 Python 的sys.stdout与sys.stderr重定向到一个_Catcher类该类按行缓冲并调用sys._tiledplugin.log(...)最终经由Tiled::LoggingInterface转发到 Tiled 的 Console。同时解析/运行异常会调用PyErr_Print()输出 traceback。因此模块无法导入语法错误等时会输出** Parse exception **及 Python traceback脚本未定义任何tiled.Plugin/tiled.TilesetPlugin子类时会输出No extension of tiled.Plugin or tiled.TilesetPlugin defined in script: namenameFilter等类方法缺失时会输出Plugin extension doesnt define nameFilter之类的提示。API 参考绑定层提供了哪些类与方法官方文档指出完整的 API 参考以绑定源文件为准。该绑定文件位于 src/plugins/python/tiledbinding.py它使用 pybindgen 把 C 侧libtiled中的Map、Tileset、TileLayer等逐类绑定为 Python 可用的tiled模块。下面按绑定文件的声明逐类整理可作为速查表。模块级辅助函数由 tiledbinding.py 中的mod.add_function声明可直接从tiled导入函数签名说明isTileLayerAt(map, index) - bool第index层是否为瓷砖层isImageLayerAt(map, index) - bool第index层是否为图像层isObjectGroupAt(map, index) - bool第index层是否为对象组tileLayerAt(map, index) - TileLayer取第index层并转为TileLayerimageLayerAt(map, index) - ImageLayer取第index层并转为ImageLayerobjectGroupAt(map, index) - ObjectGroup取第index层并转为ObjectGrouploadTilesetFromFile(tileset, file) - bool从图片文件加载图块集图像loadTileset(file) - SharedTileset经由TilesetManager从文件加载图块集插件基类类说明tiled.Plugin地图格式插件基类C 侧对应PythonScript见 src/plugins/python/pythonplugin.h可重写nameFilter、shortName、write(cls, map, fileName)、read(cls, fileName)、supportsFile(cls, fileName)tiled.TilesetPlugin图块集格式插件基类C 侧对应PythonTilesetScript可重写nameFilter、shortName、write(cls, tileset, fileName)、read(cls, fileName)、supportsFile(cls, fileName)tiled.Tiled命名空间下的核心类脚本中常写import tiled as T随后通过T.Tiled.Map(...)等访问。绑定文件 src/plugins/python/tiledbinding.py 为每个类声明了如下方法Object所有对象的基类className()/setClassName(n)、properties()、propertyAsString(prop)、setProperty(prop, val)重载支持字符串、int、bool三种值、propertyType(prop)。Tile继承Objectid()、image()/setImage(pixmap)、width()、height()、size()、type()、tileset()构造函数Tile(pixmap, id, tileset)。Tileset继承Object静态工厂Tileset.create(name, tileWidth, tileHeight, tileSpacing, margin)name()/setName()、fileName()/setFileName()、isExternal()、tileWidth()、tileHeight()、tileSpacing()、margin()、tileOffset()/setTileOffset()、gridSize()/setGridSize()、loadFromImage(img, file)、loadImage()、findTile(id)、tileAt(id)、tileCount()、columnCount()、rowCount()、imageWidth()、imageHeight()、setTransparentColor(color)/transparentColor()、imageSourceString()/setImageSource(source)、isCollection()、sharedPointer()。SharedTilesetdata()返回底层Tileset指针脚本中常用于t.data().loadFromImage(...)参见fotf.py。Map继承Object枚举OrientationUnknown/Orthogonal/Isometric/Staggered/Hexagonal、LayerDataFormatXML/Base64/Base64Gzip/Base64Zlib/CSV、RenderOrderRightDown/RightUp/LeftDown/LeftUp、StaggerAxisStaggerX/StaggerY、StaggerIndexStaggerOdd/StaggerEven构造Map(orientation, width, height, tileWidth, tileHeight)属性与查询orientation()/setOrientation()、renderOrder()/setRenderOrder()、width()/setWidth()、height()/setHeight()、tileWidth()、tileHeight()、tileSize()、infinite()/setInfinite()、hexSideLength()/setHexSideLength()、staggerAxis()/setStaggerAxis()、staggerIndex()/setStaggerIndex()、backgroundColor()/setBackgroundColor()、nextLayerId()、nextObjectId()图层计数layerCount()、tileLayerCount()、objectGroupCount()、imageLayerCount()、groupLayerCount()图块集管理addTileset(sharedTileset)、insertTileset(pos, sharedTileset)、indexOfTileset(tileset)、removeTilesetAt(pos)、replaceTileset(oldTs, newTs)、tilesetAt(index)、tilesetCount()、isTilesetUsed(tileset)图层管理addLayer(layer)重载支持TileLayer/ObjectGroup/ImageLayer/GroupLayer、layerAt(index)。Cell构造函数Cell(tile)isEmpty()、tile()/setTile(tile)、tileset()实例属性flippedHorizontally、flippedVertically、flippedAntiDiagonally、rotatedHexagonal120、checked均带 getter/setter支持/!比较。Layer继承Object各类图层的公共基类name()/setName()、opacity()/setOpacity()、isVisible()/setVisible()、isLocked()/setLocked()、isUnlocked()、isHidden()、map()、x()/setX()、y()/setY()、setPosition(x, y)、offset()/setOffset()类型判断与转换isTileLayer()、isObjectGroup()、isImageLayer()、isGroupLayer()、asTileLayer()、asObjectGroup()、asImageLayer()、asGroupLayer()。TileLayer继承Layer构造函数TileLayer(name, x, y, width, height)width()、height()、cellAt(x, y)、setCell(x, y, cell)、referencesTileset(tileset)、isEmpty()。ImageLayer继承Layer构造函数ImageLayer(name, x, y)loadFromImage(img, file)、image()/setImage(pixmap)。GroupLayer继承Layer构造函数GroupLayer(name, x, y)layerCount()、layerAt(index)。ObjectGroup继承Layer构造函数ObjectGroup(name, x, y)addObject(mapObject)、insertObject(index, mapObject)、removeObject(mapObject)、objectAt(index)、objectCount()、referencesTileset(tileset)。MapObject继承ObjectShape枚举Rectangle/Polygon/Polyline/Ellipse/Capsule/Text/PointsetPosition(pos)、x()/setX()、y()/setY()、setSize(size)、width()/setWidth()、height()/setHeight()、setShape(shape)/shape()、setCell(cell)/cell()、objectGroup()、rotation()/setRotation()、opacity()/setOpacity()、isVisible()/setVisible()、name()/setName()、type()/setType()、effectiveType()。LoggingInterfaceOutputType枚举INFO/WARNING/ERRORlog(type, msg)用于向 Tiled 的消息系统写日志。tiled.qt子模块Qt 类型绑定除tiled主体外绑定还通过 src/plugins/python/qtbinding.py 生成tiled.qt子模块暴露少量 Qt 类型如QImage、QFileDialog等。例如fotf.py中即用T.qt.QImage(w, h, T.qt.QImage.Format_Indexed8)构造索引色图像再加载进图块集T.qt.QImage也常配合Tileset.loadFromImage使用。源码级原理插件加载与读写调用的完整链路加载与注册流程Tiled 启动时加载插件src/plugins/python/pythonplugin.cpp 的initialize()若解释器未初始化则用PyConfigPEP 587初始化 CPython并关闭site_import与user_site_directoryPEP 370保证不加载用户环境中可能不兼容的 site-packages通过PyImport_AppendInittab注册内建模块tiled绑定由PyInit_tiled导出随后导入tiled模块并取得Plugin、TilesetPlugin两个基类把~/.tiled插入sys.path调用reloadModules()扫描该目录所有*.py对每个模块findPluginSubclass()遍历模块属性找出第一个继承tiled.Plugin或tiled.TilesetPlugin的类PyObject_IsSubclass判定非类对象引发的TypeError会被清除忽略找到地图插件类则创建PythonMapFormat找到图块集插件类则创建PythonTilesetFormat并通过addObject()注册进 Tiled 的格式插件系统。能力Capabilities自动检测插件能做什么是由脚本定义的方法自动推断的src/plugins/python/pythonplugin.h 的setPythonClass定义了nameFilter且write→ 具备**写导出**能力定义了nameFilter且read且supportsFile→ 具备**读导入**能力。因此只导出不需要实现read/supportsFile若要支持导入则三者缺一不可。supportsFile(fileName)用于 Tiled 判断某文件能否由该脚本打开fotf.py中以文件魔数bbyt4判断。读写调用的双向转换导出writePythonMapFormat::write先把 CMap指针包装成 Python 对象_wrap_convert_c2py__Tiled__Map_const___star__再调用脚本的write(cls, map, fileName)脚本返回True视为成功返回False或抛出异常则视为失败错误信息会写入errorString()并显示在 Tiled 界面“Script returned false. Please check console.” 等导入readPythonMapFormat::read调用脚本的read(cls, fileName)把脚本返回的 PythonMap对象转换回 C 指针_wrap_convert_py2c__Tiled__Map___star__后交给 Tiled 渲染。这也解释了为什么read中必须构造并返回一个完整的Tiled.Map对象——它会被直接转回 C 侧成为 Tiled 打开的地图可参考 src/plugins/python/scripts/fotf.py 的完整导入实现。已知限制与使用建议不支持组图层官方示例导出插件只处理直接子层若地图含组图层需自行递归处理GroupLayer.layerAt(index)可逐层下钻平台与版本敏感macOS 与 Ubuntu snap 不提供 Python 插件不同构建的 Python 版本要求不同见上文表格版本不匹配可能导致加载崩溃issue #2091因此插件自 1.2.4 起默认关闭需在偏好设置中手动启用与 JavaScript 相比能力有限Python 插件专注于自定义地图/图块集格式的读写而自 Tiled 1.3 起推荐的 JavaScript 扩展docs/manual/scripting.rst提供更完整、全平台可用且文档齐全的 API。若你的需求不止于格式插件或希望获得更好的兼容性建议优先评估 JavaScript 方案绑定 API 覆盖范围绑定层由 src/plugins/python/tiledbinding.py 手工声明未绑定所有 C API例如多边形编辑相关方法在绑定中被注释掉。如需确认某个方法是否可用直接查阅该文件即可。总而言之Python 插件是 Tiled 自定义格式支持中一条轻量、可热加载的技术路径写好脚本放进~/.tiled启用插件后即可在导入/导出对话框中看到你的格式。无论是为老游戏格式编写导入器如仓库中的fotf.py、mappy.py还是为自家引擎输出定制文本格式如本文的 CSV 风格示例掌握Plugin/TilesetPlugin两个基类与上文的 API 速查表即可快速上手。【免费下载链接】tiledFlexible level editor项目地址: https://gitcode.com/gh_mirrors/ti/tiled创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
