以下是实用型代码,仅供参考。
import { _decorator, Component, Node, TiledMap, Vec2, Input, input, EventTouch, resources, error, log } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('TiledMapFullTool')
export class TiledMapFullTool extends Component {
@property({tooltip: "resources下地图路径,无.tmx后缀(例:map/gameMap)"})
mapPath: string = "";
@property({tooltip: "地图节点名称"})
mapNodeName: string = "GameTiledMap";
private tiledMap: TiledMap = null; // 地图组件缓存
start() {
// 一键执行:加载→校验→绑定坐标功能
this.loadAndInitMap();
}
// 1. 地图加载核心(动态创建+挂载)
async loadAndInitMap() {
if(!this.mapPath) {error("请填写resources下地图路径!"); return;}
log("开始加载Tiled地图...");
try {
// 动态创建地图节点+组件
let mapNode = this.node.getChildByName(this.mapNodeName);
if(!mapNode) {
mapNode = new Node(this.mapNodeName);
this.node.addChild(mapNode);
}
mapNode.setAnchorPoint(0, 0); // 必设,对齐Tiled原点
this.tiledMap = mapNode.getComponent(TiledMap) || mapNode.addComponent(TiledMap);
// 加载地图资源
const tmxRes = await resources.load<TiledMap>(this.mapPath);
this.tiledMap.tmxAsset = tmxRes;
log("✅ 地图加载成功");
// 加载后自动校验+绑定坐标功能
this.checkMapCompatibility();
this.bindClickGetTile();
} catch (e) {
error(`❌ 地图加载失败:${e}`);
log("💡 排查:路径错误/资源不在resources/格式非XML");
}
}
// 2. 兼容性自动校验(4大核心项)
checkMapCompatibility() {
if(!this.tiledMap) return;
log("开始地图兼容性校验...");
// 校验1:地图格式(必须XML)
if(this.tiledMap.tmxAsset?.rawXml) log("✅ 地图格式为XML,符合要求");
else error("❌ 地图格式错误!Tiled需导出XML格式,禁用二进制/CSV");
// 校验2:图层(无中文/空格)
const invalidLayers = this.tiledMap.getLayers().filter(l => l.name.includes(" ") || /[\u4e00-\u9fa5]/.test(l.name));
invalidLayers.length > 0 ? error(`❌ 非法图层名:${invalidLayers.map(l=>l.name)}`) : log("✅ 图层校验通过");
// 校验3:瓦片集(无缺失)
this.tiledMap.getTileSets().length > 0 ? log("✅ 瓦片集加载正常") : error("❌ 瓦片集缺失!tmx/tsx/png需同目录");
// 校验4:路径(无中文)
/[\u4e00-\u9fa5]/.test(this.mapPath) ? error("❌ 地图路径含中文") : log("✅ 地图路径合法");
this.tiledMap.getLayers().length>0 ? log("🎉 所有校验通过,地图可用") : error("❌ 地图无有效图层");
}
// 3. 坐标互转核心(世界↔瓦片)
// 世界坐标转瓦片坐标
worldToTile(worldPos: Vec2): Vec2 {
if(!this.tiledMap) return Vec2.ZERO;
const tileSize = this.tiledMap.tileSize;
const mapWorldPos = this.tiledMap.node.worldPosition;
const x = Math.floor((worldPos.x - mapWorldPos.x) / tileSize.width);
const y = Math.floor((mapWorldPos.y - worldPos.y) / tileSize.height);
return new Vec2(x, y);
}
// 瓦片坐标转世界坐标(默认返回瓦片中心)
tileToWorld(tilePos: Vec2): Vec2 {
if(!this.tiledMap) return Vec2.ZERO;
const tileSize = this.tiledMap.tileSize;
const mapWorldPos = this.tiledMap.node.worldPosition;
const worldX = mapWorldPos.x + tilePos.x * tileSize.width + tileSize.width/2;
const worldY = mapWorldPos.y - tilePos.y * tileSize.height - tileSize.height/2;
return new Vec2(worldX, worldY);
}
// 实用功能:点击地图打印瓦片坐标
bindClickGetTile() {
input.on(Input.EventTouch.END, (event: EventTouch) => {
if(!this.tiledMap) return;
const worldPos = event.getUILocation();
const tilePos = this.worldToTile(worldPos);
log("当前点击瓦片坐标:X="+tilePos.x+",Y="+tilePos.y);
}, this);
}
}
✅ 3步零踩坑使用(直接上线)
-
脚本挂载到Canvas节点,无需额外节点
-
属性面板填2个参数:①mapPath(resources下地图路径,无后缀)②地图节点名(默认即可)
-
运行项目,自动完成加载→校验→坐标可用,控制台看结果
🚨 终极避坑(必看)
-
地图资源:tmx+tsx+瓦片png 必须同目录,且全放resources文件夹
-
Tiled导出:XML格式+外部tsx+兼容1.4格式,禁用高版本特性
-
坐标系:地图锚点已自动适配(0,0),无需手动改