#28 tiled 数据解析与渲染

This commit is contained in:
yhh
2020-08-13 17:39:24 +08:00
parent e5805960e0
commit b0511db001
23 changed files with 2586 additions and 638 deletions

View File

@@ -1,15 +1,17 @@
module es {
export class TiledMapRenderer extends RenderableComponent{
export class TiledMapRenderer extends RenderableComponent {
public tiledMap: TmxMap;
public physicsLayer: number = 1 << 0;
/**
* 如果空,所有层将被渲染
*/
public layerIndicesToRender: number[];
public get width(){
public get width() {
return this.tiledMap.width * this.tiledMap.tileWidth;
}
public get height(){
public get height() {
return this.tiledMap.height * this.tiledMap.tileHeight;
}
@@ -17,13 +19,14 @@ module es {
public _shouldCreateColliders: boolean;
public _colliders: Collider[];
constructor(tiledMap: TmxMap, collisionLayerName: string = null, shouldCreateColliders: boolean = true){
constructor(tiledMap: TmxMap, collisionLayerName: string = null, shouldCreateColliders: boolean = true) {
super();
this.tiledMap = tiledMap;
this._shouldCreateColliders = shouldCreateColliders;
this.displayObject = new egret.DisplayObjectContainer();
if (collisionLayerName){
this.collisionLayer = tiledMap.tileLayers.get(collisionLayerName);
if (collisionLayerName) {
this.collisionLayer = tiledMap.tileLayers[collisionLayerName];
}
}
@@ -31,7 +34,7 @@ module es {
* 将此组件设置为只渲染单层
* @param layerName
*/
public setLayerToRender(layerName: string){
public setLayerToRender(layerName: string) {
this.layerIndicesToRender = [];
this.layerIndicesToRender[0] = this.getLayerIndex(layerName);
}
@@ -40,18 +43,18 @@ module es {
* 设置该组件应该按名称呈现哪些层。如果你知道索引你可以直接设置layerIndicesToRender
* @param layerNames
*/
public setLayersToRender(...layerNames: string[]){
public setLayersToRender(...layerNames: string[]) {
this.layerIndicesToRender = [];
for (let i = 0; i < layerNames.length; i ++)
for (let i = 0; i < layerNames.length; i++)
this.layerIndicesToRender[i] = this.getLayerIndex(layerNames[i]);
}
private getLayerIndex(layerName: string){
private getLayerIndex(layerName: string) {
let index = 0;
let layerType = this.tiledMap.getLayer(layerName);
for (let layer in this.tiledMap.layers){
for (let layer in this.tiledMap.layers) {
if (this.tiledMap.layers.hasOwnProperty(layer) &&
this.tiledMap.layers.get(layer) == layerType){
this.tiledMap.layers[layer] == layerType) {
return index;
}
}
@@ -64,14 +67,14 @@ module es {
return this.tiledMap.worldToTilePositionY(yPos);
}
public getColumnAtWorldPosition(xPos: number): number{
public getColumnAtWorldPosition(xPos: number): number {
xPos -= this.entity.transform.position.x + this._localOffset.x;
return this.tiledMap.worldToTilePositionX(xPos);
}
public onEntityTransformChanged(comp: transform.Component) {
// 这里我们只处理位置变化。平铺地图不能缩放
if (this._shouldCreateColliders && comp == transform.Component.position){
if (this._shouldCreateColliders && comp == transform.Component.position) {
this.removeColliders();
this.addColliders();
}
@@ -90,19 +93,19 @@ module es {
}
public render(camera: es.Camera) {
if (!this.layerIndicesToRender){
TiledRendering.renderMap(this.tiledMap, Vector2.add(this.entity.transform.position, this._localOffset),
if (!this.layerIndicesToRender) {
TiledRendering.renderMap(this.tiledMap, this.displayObject as egret.DisplayObjectContainer, Vector2.add(this.entity.transform.position, this._localOffset),
this.transform.scale, this.renderLayer);
}else{
for (let i = 0; i < this.tiledMap.layers.size; i ++){
if (this.tiledMap.layers.get(i.toString()).visible && this.layerIndicesToRender.contains(i))
TiledRendering.renderLayer(this.tiledMap.layers.get(i.toString()), Vector2.add(this.entity.transform.position, this._localOffset),
} else {
for (let i = 0; i < this.tiledMap.layers.length; i++) {
if (this.tiledMap.layers[i].visible && this.layerIndicesToRender.contains(i))
TiledRendering.renderLayer(this.tiledMap.layers[i] as TmxLayer, this.displayObject as egret.DisplayObjectContainer, Vector2.add(this.entity.transform.position, this._localOffset),
this.transform.scale, this.renderLayer);
}
}
}
public addColliders(){
public addColliders() {
if (!this.collisionLayer || !this._shouldCreateColliders)
return;
@@ -111,7 +114,7 @@ module es {
// 为我们收到的矩形创建碰撞器
this._colliders = [];
for (let i = 0; i < collisionRects.length; i ++){
for (let i = 0; i < collisionRects.length; i++) {
let collider = new BoxCollider().createBoxRect(collisionRects[i].x + this._localOffset.x,
collisionRects[i].y + this._localOffset.y, collisionRects[i].width, collisionRects[i].height);
collider.physicsLayer = this.physicsLayer;
@@ -122,11 +125,11 @@ module es {
}
}
public removeColliders(){
public removeColliders() {
if (this._colliders == null)
return;
for (let collider of this._colliders){
for (let collider of this._colliders) {
Physics.removeCollider(collider);
}
this._colliders = null;

View File

@@ -7,6 +7,10 @@ module es {
* 核心发射器。只发出核心级别的事件
*/
public static emitter: Emitter<CoreEvents>;
/**
* 是否启用调试渲染
*/
public static debugRenderEndabled = false;
/**
* 全局访问图形设备
*/

View File

@@ -7,10 +7,10 @@ module es {
public properties: Map<string, string>;
public visible: boolean;
public name: string;
public layers: TmxList<any>;
public tileLayers: TmxList<TmxLayer>;
public objectGroups: TmxList<TmxObjectGroup>;
public imageLayers: TmxList<TmxImageLayer>;
public groups: TmxList<TmxGroup>;
public layers: ITmxLayer[];
public tileLayers: TmxLayer[];
public objectGroups: TmxObjectGroup[];
public imageLayers: TmxImageLayer[];
public groups: TmxGroup[];
}
}

View File

@@ -26,12 +26,12 @@ module es {
* 包含所有的ITmxLayers不管它们的具体类型是什么。
* 注意TmxGroup中的层将不在此列表中。TmxGroup管理自己的层列表。
*/
public layers: TmxList<any>;
public tilesets: TmxList<TmxTileset>;
public tileLayers: TmxList<TmxLayer>;
public objectGroups: TmxList<TmxObjectGroup>;
public imageLayers: TmxList<TmxImageLayer>;
public groups: TmxList<TmxGroup>;
public layers: ITmxLayer[];
public tilesets: TmxTileset[];
public tileLayers: TmxLayer[];
public objectGroups: TmxLayer[];
public imageLayers: TmxImageLayer[];
public groups: TmxGroup[];
public properties: Map<string, string>;
/**
@@ -59,9 +59,9 @@ module es {
if (gid == 0)
return null;
for (let i = this.tilesets.size - 1; i >= 0; i --){
if (this.tilesets.get(i.toString()).firstGid <= gid)
return this.tilesets.get(i.toString());
for (let i = this.tilesets.length - 1; i >= 0; i --){
if (this.tilesets[i].firstGid <= gid)
return this.tilesets[i];
}
console.error(`tile gid${gid}未在任何tileset中找到`);
@@ -96,7 +96,7 @@ module es {
* @param name
*/
public getLayer(name: string): ITmxLayer {
return this.layers.get(name);
return this.layers[name];
}
/**

View File

@@ -8,13 +8,15 @@ module es {
public offsetY: number;
public color: number;
public drawOrder: DrawOrderType;
public objects: TmxList<TmxObject>;
public objects: TmxObject[];
public properties: Map<string, string>;
}
export class TmxObject implements ITmxElement {
public id: number;
public name: string;
public shape: egret.Shape;
public textField: egret.TextField;
public objectType: TmxObjectType;
public type: string;
public x: number;
@@ -27,6 +29,11 @@ module es {
public text: TmxText;
public points: Vector2[];
public properties: Map<string, string>;
constructor(){
this.shape = new egret.Shape();
this.textField = new egret.TextField();
}
}
export class TmxText {

View File

@@ -1,8 +1,8 @@
module es {
export class TmxDocument {
public TmxDirectory: string;
public tmxDirectory: string;
constructor(){
this.TmxDirectory = "";
this.tmxDirectory = "";
}
}
@@ -10,37 +10,40 @@ module es {
name: string;
}
export class TmxList<T extends ITmxElement> extends Map<string, T>{
public _nameCount: Map<string, number> = new Map<string, number>();
public add(t: T){
let tName = t.name;
// 通过附加数字重命名重复的条目
if (this.has(tName))
this._nameCount.set(tName, this._nameCount.get(tName) + 1);
else
this._nameCount.set(tName, 0);
}
protected getKeyForItem(item: T): string {
let name = item.name;
let count = this._nameCount.get(name);
let dupes = 0;
// 对于重复的键,附加一个计数器。对于病理情况,插入下划线以确保唯一性
while (this.has(name)){
name = name + Enumerable.repeat("_", dupes).toString() + count.toString();
dupes ++;
}
return name;
}
}
// export class TmxList<T extends ITmxElement> extends Map<string, T>{
// public _nameCount: Map<string, number> = new Map<string, number>();
//
// public add(t: T){
// let tName = t.name;
//
// // 通过附加数字重命名重复的条目
// if (this.has(tName))
// this._nameCount.set(tName, this._nameCount.get(tName) + 1);
// else
// this._nameCount.set(tName, 0);
// }
//
// protected getKeyForItem(item: T): string {
// let name = item.name;
// let count = this._nameCount.get(name);
//
// let dupes = 0;
//
// // 对于重复的键,附加一个计数器。对于病理情况,插入下划线以确保唯一性
// while (this.has(name)){
// name = name + Enumerable.repeat("_", dupes).toString() + count.toString();
// dupes ++;
// }
//
// return name;
// }
// }
export class TmxImage {
public texture: egret.Texture;
public bitmap: egret.Bitmap;
public get texture(): egret.Texture{
return this.bitmap.texture;
}
public source: string;
public format: string;
public data: any;
@@ -49,9 +52,9 @@ module es {
public height: number;
public dispose(){
if (this.texture){
if (this.bitmap){
this.texture.dispose();
this.texture = null;
this.bitmap = null;
}
}
}

View File

@@ -0,0 +1,406 @@
module es {
import Bitmap = egret.Bitmap;
export class TiledMapLoader {
public static loadTmxMap(map: TmxMap, filePath: string){
let xMap = RES.getRes(filePath);
return this.loadTmxMapData(map, xMap);
}
public static async loadTmxMapData(map: TmxMap, xMap: any){
map.version = xMap["version"];
map.tiledVersion = xMap["tiledversion"];
map.width = xMap["width"];
map.height = xMap["height"];
map.tileWidth = xMap["tilewidth"];
map.tileHeight = xMap["tileheight"];
map.hexSideLength = xMap["hexsidelength"];
map.orientation = this.parseOrientationType(xMap["orientation"]);
map.staggerAxis = this.parseStaggerAxisType(xMap["staggeraxis"]);
map.staggerIndex = this.parseStaggerIndexType(xMap["staggerindex"]);
map.renderOrder = this.parseRenderOrderType(xMap["renderorder"]);
map.nextObjectID = xMap["nextobjectid"];
map.backgroundColor = TmxUtils.color16ToUnit(xMap["color"]);
map.properties = this.parsePropertyDict(xMap["properties"]);
// 我们保持记录的最大瓷砖大小的情况下图像tileset随机大小
map.maxTileWidth = map.tileWidth;
map.maxTileHeight = map.tileHeight;
map.tilesets = [];
for (let e of xMap["tilesets"]){
let tileset = await this.parseTmxTileset(map, e);
map.tilesets.push(tileset);
this.updateMaxTileSizes(tileset);
}
map.layers = [];
map.tileLayers = [];
map.objectGroups = [];
map.imageLayers = [];
map.groups = [];
this.parseLayers(map, xMap, map, map.width, map.height, map.tmxDirectory);
return map;
}
/**
* 解析xEle中的所有层将它们放入容器中
* @param container
* @param xEle
* @param map
* @param width
* @param height
* @param tmxDirectory
*/
public static parseLayers(container: any, xEle: any, map: TmxMap, width: number, height: number, tmxDirectory: string){
}
private static updateMaxTileSizes(tileset: TmxTileset){
// 必须迭代字典因为tile.gid可以是任意顺序的的任何数字
tileset.tiles.forEach(tile => {
if (tile.image){
if (tile.image.width > tileset.map.maxTileWidth)
tileset.map.maxTileWidth = tile.image.width;
if (tile.image.height > tileset.map.maxTileHeight)
tileset.map.maxTileHeight = tile.image.height;
}
});
tileset.tileRegions.forEach(region => {
let width = region.width;
let height = region.height;
if (width > tileset.map.maxTileWidth) tileset.map.maxTileWidth = width;
if (width > tileset.map.maxTileHeight) tileset.map.maxTileHeight = height;
});
}
public static parseOrientationType(type: string){
if (type == "unknown")
return OrientationType.unknown;
if (type == "orthogonal")
return OrientationType.orthogonal;
if (type == "isometric")
return OrientationType.isometric;
if (type == "staggered")
return OrientationType.staggered;
if (type == "hexagonal")
return OrientationType.hexagonal;
return OrientationType.unknown;
}
public static parseStaggerAxisType(type: string){
if (type == "y")
return StaggerAxisType.y;
return StaggerAxisType.x;
}
public static parseStaggerIndexType(type: string){
if (type == "even")
return StaggerIndexType.even;
return StaggerIndexType.odd;
}
public static parseRenderOrderType(type: string){
if (type == "right-up")
return RenderOrderType.rightUp;
if (type == "left-down")
return RenderOrderType.leftDown;
if (type == "left-up")
return RenderOrderType.leftUp;
return RenderOrderType.rightDown;
}
public static parsePropertyDict(prop) {
if (!prop)
return null;
let dict = new Map<string, string>();
for (let p of prop["property"]){
let pname = p["name"];
let valueAttr = p["value"];
let pval = valueAttr ? valueAttr : p;
dict.set(pname, pval);
}
return dict;
}
public static async parseTmxTileset(map: TmxMap, xTileset: any){
// firstgid总是在TMX中而不是在TSX中
let xFirstGid = xTileset["firstgid"];
let firstGid = xFirstGid;
let source = xTileset["image"];
// 如果是嵌入式TmxTileset即不是外部的source将为null
if (!source){
source = "resource/assets/" + source;
// 其他所有内容都在TSX文件中
let xDocTileset = await RES.getResByUrl(source, null, this, RES.ResourceItem.TYPE_IMAGE);
let tileset = this.loadTmxTileset(new TmxTileset(), map, xDocTileset["tileset"], firstGid);
return tileset;
}
return this.loadTmxTileset(new TmxTileset(), map, xTileset, firstGid);
}
public static async loadTmxTileset(tileset: TmxTileset, map: TmxMap, xTileset: any,
firstGid: number){
tileset.map = map;
tileset.firstGid = firstGid;
tileset.name = xTileset["name"];
tileset.tileWidth = xTileset["tilewidth"];
tileset.tileHeight = xTileset["tileheight"];
tileset.spacing = xTileset["spacing"] != undefined ? xTileset["spacing"] : 0;
tileset.margin = xTileset["margin"] != undefined ? xTileset["margin"] : 0;
tileset.columns = xTileset["columns"];
tileset.tileCount = xTileset["tilecount"];
tileset.tileOffset = this.parseTmxTileOffset(xTileset["tileoffset"]);
let xImage = xTileset["image"];
if (xImage)
tileset.image = await this.loadTmxImage(new TmxImage(), xTileset);
let xTerrainType = xTileset["terraintypes"];
if (xTerrainType){
tileset.terrains = [];
for (let e of xTerrainType["terrains"])
tileset.terrains.push(this.parseTmxTerrain(e));
}
tileset.tiles = new Map<number, TmxTilesetTile>();
for (let xTile of xTileset["tiles"]){
let tile = await this.loadTmxTilesetTile(new TmxTilesetTile(), tileset, xTile, tileset.terrains);
tileset.tiles[tile.id] = tile;
}
tileset.properties = this.parsePropertyDict(xTileset["properties"]);
// 缓存我们的源矩形为每个瓷砖,所以我们不必每次我们渲染计算他们。
// 如果我们有一个image这是一个普通的tileset否则它是一个image tileset
tileset.tileRegions = new Map<number, Rectangle>();
if (tileset.image){
let id = firstGid;
for (let y = tileset.margin; y < tileset.image.height - tileset.margin; y += tileset.tileHeight + tileset.spacing){
let column = 0;
for (let x = tileset.margin; x < tileset.image.width - tileset.margin; x += tileset.tileWidth + tileset.spacing){
tileset.tileRegions.set(id++, new Rectangle(x, y, tileset.tileWidth, tileset.tileHeight));
if (++column >= tileset.columns)
break;
}
}
}else{
tileset.tiles.forEach(tile => {
tileset.tileRegions.set(firstGid + tile.id, new Rectangle(0, 0, tile.image.width, tile.image.height));
});
}
return tileset;
}
public static async loadTmxTilesetTile(tile: TmxTilesetTile, tileset: TmxTileset, xTile: any, terrains: TmxTerrain[]){
tile.tileset = tileset;
tile.id = xTile["id"];
tile.terrainEdges = xTile["terrain"];
tile.probability = xTile["probability"] != undefined ? xTile["probability"] : 1;
tile.type = xTile["type"];
let xImage = xTile["image"];
if (xImage){
tile.image = await this.loadTmxImage(new TmxImage(), xImage);
}
tile.objectGroups = [];
if (xTile["objectgroup"])
for (let e of xTile["objectgroup"])
tile.objectGroups.push(this.loadTmxObjectGroup(new TmxObjectGroup(), tileset.map, e));
tile.animationFrames = [];
if (xTile["animation"]){
for (let e of xTile["animation"]["frame"])
tile.animationFrames.push(this.loadTmxAnimationFrame(new TmxAnimationFrame(), e));
}
tile.properties = this.parsePropertyDict(xTile["properties"]);
if (tile.properties)
tile.processProperties();
return tile;
}
public static loadTmxAnimationFrame(frame: TmxAnimationFrame, xFrame: any){
frame.gid = xFrame["tileid"];
frame.duration = xFrame["duration"] / 1000;
return frame;
}
public static loadTmxObjectGroup(group: TmxObjectGroup, map: TmxMap, xObjectGroup: any) {
group.map = map;
group.name = xObjectGroup["name"] != undefined ? xObjectGroup["name"] : "";
group.color = TmxUtils.color16ToUnit(xObjectGroup["color"]);
group.opacity = xObjectGroup["opacity"] != undefined ? xObjectGroup["opacity"] : 1;
group.visible = xObjectGroup["visible"] != undefined ? xObjectGroup["visible"] : true;
group.offsetX = xObjectGroup["offsetx"] != undefined ? xObjectGroup["offsetx"] : 0;
group.offsetY = xObjectGroup["offsety"] != undefined ? xObjectGroup["offsety"] : 0;
let drawOrderDict = new Map<string, DrawOrderType>();
drawOrderDict.set("unknown", DrawOrderType.unkownOrder);
drawOrderDict.set("topdown", DrawOrderType.IndexOrder);
drawOrderDict.set("index", DrawOrderType.TopDown);
let drawOrderValue = xObjectGroup["draworder"];
if (drawOrderValue)
group.drawOrder = drawOrderDict[drawOrderValue];
group.objects = [];
for (let e of xObjectGroup["object"])
group.objects.push(this.loadTmxObject(new TmxObject(), map, e));
group.properties = this.parsePropertyDict(xObjectGroup["properties"]);
return group;
}
public static loadTmxObject(obj: TmxObject, map: TmxMap, xObject: any){
obj.id = xObject["id"] != undefined ? xObject["id"] : 0;
obj.name = xObject["name"] != undefined ? xObject["name"] : "";
obj.x = xObject["x"];
obj.y = xObject["y"];
obj.width = xObject["width"] != undefined ? xObject["width"] : 0;
obj.height = xObject["height"] != undefined ? xObject["height"] : 0;
obj.type = xObject["type"] != undefined ? xObject["type"] : "";
obj.visible = xObject["visible"] != undefined ? xObject["visible"] : true;
obj.rotation = xObject["rotation"] != undefined ? xObject["rotation"] : 0;
// 评估对象类型并分配适当的内容
let xGid = xObject["gid"];
let xEllipse = xObject["ellipse"];
let xPolygon = xObject["polygon"];
let xPolyline = xObject["polyline"];
let xText = xObject["text"];
let xPoint = xObject["point"];
if (xGid){
obj.tile = new TmxLayerTile(map, xGid, Math.round(obj.x), Math.round(obj.y));
obj.objectType = TmxObjectType.tile;
}else if(xEllipse){
obj.objectType = TmxObjectType.ellipse;
} else if(xPolygon){
obj.points = this.parsePoints(xPolygon);
obj.objectType = TmxObjectType.polygon;
}else if(xPolyline){
obj.points = this.parsePoints(xPolyline);
obj.objectType = TmxObjectType.polyline;
}else if(xText){
obj.text = this.loadTmxText(new TmxText(), xText);
obj.objectType = TmxObjectType.text;
}else if(xPoint){
obj.objectType = TmxObjectType.point;
}else{
obj.objectType = TmxObjectType.basic;
}
obj.properties = this.parsePropertyDict(xObject["properties"]);
return obj;
}
public static loadTmxText(text: TmxText, xText: any){
text.fontFamily = xText["fontfamily"] != undefined ? xText["fontfamily"] : "sans-serif";
text.pixelSize = xText["pixelsize"] != undefined ? xText["pixelsize"] : 16;
text.wrap = xText["wrap"] != undefined ? xText["wrap"] : false;
text.color = TmxUtils.color16ToUnit(xText["color"]);
text.bold = xText["bold"] ? xText["bold"] : false;
text.italic = xText["italic"] ? xText["italic"] : false;
text.underline = xText["underline"] ? xText["underline"] : false;
text.strikeout = xText["strikeout"] ? xText["strikeout"] : false;
text.kerning = xText["kerning"] ? xText["kerning"] : true;
text.alignment = this.loadTmxAlignment(new TmxAlignment(), xText);
text.value = xText;
return text;
}
public static loadTmxAlignment(alignment: TmxAlignment, xText: any){
function firstLetterToUpperCase(str: string) {
if (!str || str == "")
return str;
return str[0].toString().toUpperCase() + str.substr(1);
}
let xHorizontal = xText["halign"] != undefined ? xText["halign"] : "left";
alignment.horizontal = TmxHorizontalAlignment[firstLetterToUpperCase(xHorizontal)];
let xVertical = xText["valign"] != undefined ? xText["valign"] : "top";
alignment.vertical = TmxVerticalAlignment[firstLetterToUpperCase((xVertical))];
return alignment;
}
public static parsePoints(xPoints: any){
let pointString: string = xPoints["points"];
let pointStringPair = pointString.split(' ');
let points = [];
let index = 0;
for (let s of pointStringPair)
points[index ++] = this.parsePoint(s);
return points;
}
public static parsePoint(s: string){
let pt = s.split(',');
let x = Number(pt[0]);
let y = Number(pt[1]);
return new Vector2(x, y);
}
public static parseTmxTerrain(xTerrain: any){
let terrain = new TmxTerrain();
terrain.name = xTerrain["name"];
terrain.tile = xTerrain["tile"];
terrain.properties = this.parsePropertyDict(xTerrain["properties"]);
return terrain;
}
public static parseTmxTileOffset(xTileOffset: any){
let tmxTileOffset = new TmxTileOffset();
if (!xTileOffset){
tmxTileOffset.x = 0;
tmxTileOffset.y = 0;
return tmxTileOffset;
}
tmxTileOffset.x = xTileOffset["x"];
tmxTileOffset.y = xTileOffset["y"];
return tmxTileOffset;
}
public static async loadTmxImage(image: TmxImage, xImage: any){
let xSource = xImage["image"];
if (xSource) {
image.source = "resource/assets/" + xSource;
image.bitmap = new Bitmap(await RES.getResByUrl(image.source, null, this, RES.ResourceItem.TYPE_IMAGE));
}else {
image.format = xImage["format"];
let xData = xImage["data"];
image.data = TmxUtils.decode(xData, xData["encoding"], xData["compression"]);
}
image.trans = TmxUtils.color16ToUnit(xImage["trans"]);
image.width = xImage["width"] != undefined ? xImage["width"] : 0;
image.height = xImage["height"] != undefined ? xImage["height"] : 0;
return image;
}
}
}

View File

@@ -1,119 +1,154 @@
module es {
export class TiledRendering {
public static renderMap(map: TmxMap, position: Vector2, scale: Vector2, layerDepth: number) {
public static renderMap(map: TmxMap, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, layerDepth: number) {
map.layers.forEach(layer => {
if (layer instanceof TmxLayer && layer.visible) {
this.renderLayer(layer, position, scale, layerDepth);
this.renderLayer(layer, container, position, scale, layerDepth);
} else if (layer instanceof TmxImageLayer && layer.visible) {
this.renderImageLayer(layer, position, scale, layerDepth);
this.renderImageLayer(layer, container, position, scale, layerDepth);
} else if (layer instanceof TmxGroup && layer.visible) {
this.renderGroup(layer, position, scale, layerDepth);
this.renderGroup(layer, container, position, scale, layerDepth);
} else if (layer instanceof TmxObjectGroup && layer.visible) {
this.renderObjectGroup(layer, position, scale, layerDepth);
this.renderObjectGroup(layer, container, position, scale, layerDepth);
}
});
}
public static renderLayer(layer: TmxLayer, position: Vector2, scale: Vector2, layerDepth: number) {
public static renderLayer(layer: TmxLayer, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, layerDepth: number) {
if (!layer.visible)
return;
let tileWidth = layer.map.tileWidth * scale.x;
let tileHeight = layer.map.tileHeight * scale.y;
let color = new Color(0, 0, 0, layer.opacity * 255);
for (let i = 0; i < layer.tiles.length; i ++){
let color = DrawUtils.getColorMatrix(0x000000);
for (let i = 0; i < layer.tiles.length; i++) {
let tile = layer.tiles[i];
if (!tile)
continue;
this.renderTile(tile, position, scale, tileWidth, tileHeight, color, layerDepth);
this.renderTile(tile, container, position, scale, tileWidth, tileHeight, color, layerDepth);
}
}
public static renderImageLayer(layer: TmxImageLayer, position: Vector2, scale: Vector2, layerDepth: number) {
public static renderImageLayer(layer: TmxImageLayer, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, layerDepth: number) {
if (!layer.visible)
return;
let color = new Color(0, 0, 0, layer.opacity * 255);
let color = DrawUtils.getColorMatrix(0x000000);
let pos = Vector2.add(position, new Vector2(layer.offsetX, layer.offsetY).multiply(scale));
// TODO: draw
if (!layer.image.bitmap.parent)
container.addChild(layer.image.bitmap);
layer.image.bitmap.x = pos.x;
layer.image.bitmap.y = pos.y;
layer.image.bitmap.scaleX = scale.x;
layer.image.bitmap.scaleY = scale.y;
layer.image.bitmap.filters = [color];
}
public static renderObjectGroup(objGroup: TmxObjectGroup, position: Vector2, scale: Vector2, layerDepth: number) {
public static renderObjectGroup(objGroup: TmxObjectGroup, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, layerDepth: number) {
if (!objGroup.visible)
return;
for (let object in objGroup.objects) {
let obj = objGroup.objects.get(object);
let obj = objGroup.objects[object];
if (!obj.visible)
continue;
// TODO: debug draw
// 如果我们不调试渲染,我们只渲染平铺块和文本类型
if (!Core.debugRenderEndabled){
if (obj.objectType != TmxObjectType.tile && obj.objectType != TmxObjectType.text)
continue;
}
let pos = Vector2.add(position, new Vector2(obj.x, obj.y).multiply(scale));
switch (obj.objectType) {
case TmxObjectType.basic:
// TODO: draw
if (!obj.shape.parent)
container.addChild(obj.shape);
let rect = new Rectangle(pos.x, pos.y, obj.width * scale.x, obj.height * scale.y);
DrawUtils.drawHollowRect(obj.shape, rect, objGroup.color);
break;
case TmxObjectType.point:
let size = objGroup.map.tileWidth * 0.5;
pos.x -= size * 0.5;
pos.y -= size * 0.5;
// TODO: draw
if (!obj.shape.parent)
container.addChild(obj.shape);
DrawUtils.drawPixel(obj.shape, pos, objGroup.color, size);
break;
case TmxObjectType.tile:
let tileset = objGroup.map.getTilesetForTileGid(obj.tile.gid);
let sourceRect = tileset.tileRegions[obj.tile.gid];
pos.y -= obj.tile.tilesetTile.image.height;
// TODO: draw
if (!obj.tile.tilesetTile.image.bitmap)
container.addChild(obj.tile.tilesetTile.image.bitmap);
obj.tile.tilesetTile.image.bitmap.x = pos.x;
obj.tile.tilesetTile.image.bitmap.y = pos.y;
obj.tile.tilesetTile.image.bitmap.filters = [];
obj.tile.tilesetTile.image.bitmap.rotation = 0;
obj.tile.tilesetTile.image.bitmap.scaleX = scale.x;
obj.tile.tilesetTile.image.bitmap.scaleY = scale.y;
break;
case TmxObjectType.ellipse:
pos = new Vector2(obj.x + obj.width * 0.5, obj.y + obj.height * 0.5).multiply(scale);
// TODO: draw
if (!obj.shape.parent)
container.addChild(obj.shape);
DrawUtils.drawCircle(obj.shape, pos, obj.width * 0.5, objGroup.color);
break;
case TmxObjectType.polygon:
case TmxObjectType.polyline:
let points = [];
for (let i = 0; i < obj.points.length; i++)
points[i] = Vector2.multiply(obj.points[i], scale);
// TODO: draw
DrawUtils.drawPoints(obj.shape, pos, points, objGroup.color, obj.objectType == TmxObjectType.polygon);
break;
case TmxObjectType.text:
// TODO: draw
if (!obj.textField.parent)
container.addChild(obj.textField);
DrawUtils.drawString(obj.textField, obj.text.value, pos, obj.text.color, MathHelper.toRadians(obj.rotation),
Vector2.zero, 1);
break;
default:
// TODO: debug draw
if (Core.debugRenderEndabled){
if (!obj.textField.parent)
container.addChild(obj.textField);
DrawUtils.drawString(obj.textField, `${obj.name}(${obj.type})`, Vector2.subtract(pos, new Vector2(0, 15)), 0xffffff, 0, Vector2.zero, 1);
}
break;
}
}
}
public static renderGroup(group: TmxGroup, position: Vector2, scale: Vector2, layerDepth: number) {
public static renderGroup(group: TmxGroup, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, layerDepth: number) {
if (!group.visible)
return;
group.layers.forEach(layer => {
if (layer instanceof TmxGroup) {
this.renderGroup(layer, position, scale, layerDepth);
this.renderGroup(layer, container, position, scale, layerDepth);
}
if (layer instanceof TmxObjectGroup) {
this.renderObjectGroup(layer, position, scale,layerDepth);
this.renderObjectGroup(layer, container, position, scale, layerDepth);
}
if (layer instanceof TmxLayer){
this.renderLayer(layer, position, scale, layerDepth);
if (layer instanceof TmxLayer) {
this.renderLayer(layer, container, position, scale, layerDepth);
}
if (layer instanceof TmxImageLayer){
this.renderImageLayer(layer, position, scale, layerDepth);
if (layer instanceof TmxImageLayer) {
this.renderImageLayer(layer, container, position, scale, layerDepth);
}
});
}
public static renderTile(tile: TmxLayerTile, position: Vector2, scale: Vector2, tileWidth: number, tileHeight: number, color: Color, layerDepth: number) {
public static renderTile(tile: TmxLayerTile, container: egret.DisplayObjectContainer, position: Vector2, scale: Vector2, tileWidth: number, tileHeight: number, color: egret.ColorMatrixFilter, layerDepth: number) {
let gid = tile.gid;
// 动画tiles(以及来自图像tile的tiles)将位于Tileset本身内位于单独的TmxTilesetTile对象中不要与我们在此循环中处理的TmxLayerTiles混淆
@@ -153,9 +188,23 @@ module es {
let pos = new Vector2(tx, ty).add(position);
if (tile.tileset.image) {
// TODO: draw
if (!tile.tilesetTile.image.bitmap.parent)
container.addChild(tile.tilesetTile.image.bitmap);
tile.tilesetTile.image.bitmap.x = pos.x;
tile.tilesetTile.image.bitmap.y = pos.y;
tile.tilesetTile.image.bitmap.scaleX = scale.x;
tile.tilesetTile.image.bitmap.scaleY = scale.y;
tile.tilesetTile.image.bitmap.rotation = rotation;
tile.tilesetTile.image.bitmap.filters = [color];
} else {
// TODO: draw
if (!tilesetTile.image.bitmap)
container.addChild(tilesetTile.image.bitmap);
tilesetTile.image.bitmap.x = pos.x;
tilesetTile.image.bitmap.y = pos.y;
tilesetTile.image.bitmap.scaleX = scale.x;
tilesetTile.image.bitmap.scaleY = scale.y;
tilesetTile.image.bitmap.rotation = rotation;
tilesetTile.image.bitmap.filters = [color];
}
}
}

View File

@@ -13,7 +13,7 @@ module es {
public tileOffset: TmxTileOffset;
public properties: Map<string, string>;
public image: TmxImage;
public terrains: TmxList<TmxTerrain>;
public terrains: TmxTerrain[];
/**
* 为每个块缓存源矩形
*/

View File

@@ -7,7 +7,7 @@ module es {
public type: string;
public properties: Map<string, string>;
public image: TmxImage;
public objectGroups: TmxList<TmxObjectGroup>;
public objectGroups: TmxObjectGroup[];
public animationFrames: TmxAnimationFrame[];
// TODO: 为什么动画瓷砖需要添加firstGid

View File

@@ -0,0 +1,50 @@
module es {
export class TmxUtils {
/**
* 解码
* @param data 数据
* @param encoding 编码方式 目前暂时只支持XML、base64(无压缩)、csv解析
* @param compression 压缩方式
* @returns 返回解析后的数据列表
*
* @version Egret 3.0.3
*/
static decode(data: any, encoding: any, compression: string): Array<number> {
compression = compression || "none";
encoding = encoding || "none";
var text:string=data.children[0].text;
switch (encoding) {
case "base64":
var decoded = Base64Utils.decodeBase64AsArray(text, 4);
return (compression === "none") ? decoded : Base64Utils.decompress(text, decoded, compression);
case "csv":
return Base64Utils.decodeCSV(text);
case "none":
var datas: Array<number> = [];
for (var i: number = 0; i < data.children.length; i++) {
datas[i] = +data.children[i].attributes.gid;
}
return datas;
default:
throw new Error("未定义的编码:" + encoding);
}
}
/**
* 将带"#"号的颜色字符串转换为16进制的颜色,例如:可将"#ff0000"转换为"0xff0000"
* @param $color 要转换的颜色字符串
* @returns 返回16进制的颜色值
* @version Egret 3.0.3
*/
static color16ToUnit($color:string): number {
if (!$color)
return 0x000000;
var colorStr: string = "0x" + $color.slice(1);
return parseInt(colorStr, 16);
}
}
}

View File

@@ -1,124 +1,134 @@
class Base64Utils {
private static _keyNum = "0123456789+/";
private static _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static _keyAll = Base64Utils._keyNum + Base64Utils._keyStr;
/**
* 加密
* @param input
*/
public static encode = function (input) {
let output = "";
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
let i = 0;
input = this._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyAll.charAt(enc1) + this._keyAll.charAt(enc2) +
this._keyAll.charAt(enc3) + this._keyAll.charAt(enc4);
}
return this._keyStr.charAt(Math.floor((Math.random() * this._keyStr.length))) + output;
};
module es{
export class Base64Utils {
private static _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/**
* 解码
* @param input
* @param isNotStr
*/
public static decode(input, isNotStr: boolean = true) {
let output = "";
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = this.getConfKey(input);
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyAll.indexOf(input.charAt(i++));
enc2 = this._keyAll.indexOf(input.charAt(i++));
enc3 = this._keyAll.indexOf(input.charAt(i++));
enc4 = this._keyAll.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
if (chr2 == 0) {
if (isNotStr) output = output + String.fromCharCode(chr2);
} else {
output = output + String.fromCharCode(chr2);
}
}
if (enc4 != 64) {
if (chr3 == 0) {
if (isNotStr) output = output + String.fromCharCode(chr3);
} else {
output = output + String.fromCharCode(chr3);
/**
* 判断是否原生支持Base64位解析
*/
static get nativeBase64() {
return (typeof (window.atob) === "function");
}
/**
* 解码
* @param input
*/
static decode(input:string): string {
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
if (this.nativeBase64) {
return window.atob(input);
} else {
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(String.fromCharCode(chr1));
if (enc3 !== 64) {
output.push(String.fromCharCode(chr2));
}
if (enc4 !== 64) {
output.push(String.fromCharCode(chr3));
}
}
output = output.join("");
return output;
}
}
/**
* 编码
* @param input
*/
static encode(input:string): string {
input = input.replace(/\r\n/g, "\n");
if (this.nativeBase64) {
window.btoa(input);
} else {
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output.push(this._keyStr.charAt(enc1));
output.push(this._keyStr.charAt(enc2));
output.push(this._keyStr.charAt(enc3));
output.push(this._keyStr.charAt(enc4));
}
output = output.join("");
return output;
}
}
/**
* 解析Base64格式数据
* @param input
* @param bytes
*/
static decodeBase64AsArray(input: string, bytes: number): Uint32Array {
bytes = bytes || 1;
var dec = Base64Utils.decode(input), i, j, len;
var ar: Uint32Array = new Uint32Array(dec.length / bytes);
for (i = 0, len = dec.length / bytes; i < len; i++) {
ar[i] = 0;
for (j = bytes - 1; j >= 0; --j) {
ar[i] += dec.charCodeAt((i * bytes) + j) << (j << 3);
}
}
return ar;
}
output = this._utf8_decode(output);
return output;
}
private static _utf8_encode(string) {
string = string.replace(/\r\n/g, "\n");
let utftext = "";
for (let n = 0; n < string.length; n++) {
let c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
/**
* 暂时不支持
* @param data
* @param decoded
* @param compression
* @private
*/
static decompress(data: string, decoded: any, compression: string): any {
throw new Error("GZIP/ZLIB compressed TMX Tile Map not supported!");
}
return utftext;
}
private static _utf8_decode(utftext) {
let string = "";
let i = 0;
let c = 0;
let c1 = 0;
let c2 = 0;
let c3 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
/**
* 解析csv数据
* @param input
*/
static decodeCSV(input: string): Array<number> {
var entries: Array<any> = input.replace("\n", "").trim().split(",");
var result:Array<number> = [];
for (var i:number = 0; i < entries.length; i++) {
result.push(+entries[i]);
}
return result;
}
return string;
}
private static getConfKey(key): string {
return key.slice(1, key.length);
}
}
}

View File

@@ -5,6 +5,29 @@ module es {
this.drawLineAngle(shape, start, MathHelper.angleBetweenVectors(start, end), Vector2.distance(start, end), color, thickness);
}
public static drawCircle(shape: egret.Shape, position: Vector2, radius: number, color: number){
shape.graphics.beginFill(color);
shape.graphics.drawCircle(position.x, position.y, radius);
shape.graphics.endFill();
}
public static drawPoints(shape: egret.Shape, position: Vector2, points: Vector2[], color: number,
closePoly: boolean = true, thickness: number = 1){
if (points.length < 2)
return;
for (let i = 1; i < points.length; i ++)
this.drawLine(shape, Vector2.add(position, points[i - 1]), Vector2.add(position, points[i]), color, thickness);
if (closePoly)
this.drawLine(shape, Vector2.add(position, points[points.length - 1]), Vector2.add(position, points[0]), color, thickness);
}
public static drawString(textField: egret.TextField, text: string, position: Vector2, color: number, rotation: number,
origin: Vector2, scale: number){
}
public static drawLineAngle(shape: egret.Shape, start: Vector2, radians: number, length: number, color: number, thickness = 1) {
shape.graphics.beginFill(color);
shape.graphics.drawRect(start.x, start.y, 1, 1);