IsoUitls類,實現空間座標與平面座標之間的轉換。具體原理可參考《Flash actionScript 3.0高級動畫教程》第三章 等角投影
IsoUitls源代碼
package com.hope.isometric
{
import flash.geom.Point;
public class IsoUtils
{
public static const Y_CORRECT: Number = Math.cos( -Math.PI / 6 )*Math.SQRT2
public function IsoUtils()
{
}
/**
* 把等角座標中的3D座標轉換成屏幕上的2D座標
* @param pos Point3D
* @return
*
*/
public static function isoToScreen( pos: Point3D ): Point
{
var screenX:Number= pos.x - pos.z;
var screenY:Number= pos.y * Y_CORRECT + ( pos.x + pos.z ) * 0.5;
//var screenZ:Number = ( pos.x + pos.z ) * 0.800 - pos.y * 0.707;
return new Point( screenX, screenY );
}
public static function screenToIso( point: Point ): Point3D
{
var xpos:Number = point.y + point.x * .5;
var ypos:Number = 0;
var zpos:Number = point.y - point.x * .5;
return new Point3D( xpos, ypos, zpos );
}
}
}
Point3D類,三維空間座標
package com.hope.isometric
{
public class Point3D
{
public var x: Number;
public var y: Number;
public var z: Number;
public function Point3D( x: Number = 0, y: Number = 0, z: Number = 0 )
{
this.x = x;
this.y = y;
this.z = z;
}
}
}
IsoObject類
package com.hope.isometric
{
import flash.display.Sprite;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.getDefinitionByName;
public class IsoObject extends Sprite
{
protected var _position: Point3D;
protected var _size: Number;
protected var _walkable: Boolean = false;
protected var _color: uint;
protected var _height: Number;
public static const TILE: String = "Tile" ;
public static const BOX: String = "Box";
public function IsoObject()
{
_position = new Point3D();
updateScreenPosition();
}
static public function create( type: Object): IsoObject
{
switch( type ){
case IsoObject.TILE:
return new Tile();
break;
case IsoObject.BOX:
return new Box();
break;
default:
return new IsoObject();
}
}
protected function updateScreenPosition(): void
{
var screenPos: Point = IsoUtils.isoToScreen( _position );
super.x = screenPos.x;
super.y = screenPos.y;
}
public function get rect(): Rectangle
{
return new Rectangle( x - size / 2, z - size / 2, size, size );
}
public function draw( size: Number, color: uint, height: Number = 0 ): void
{
this.size = size;
this.color = color;
this.height = height;
}
public function dispose(): void
{
graphics.clear();
parent.removeChild( this );
}
override public function toString():String
{
return "[IsoObject( x:" + _position.x + " y:" + _position.y + " z:" + _position.z + ")]";
}
/**
* 讀取/設置3D空間中的x座標
*/
override public function set x(value:Number):void
{
_position.x = value;
updateScreenPosition();
}
override public function get x():Number
{
return _position.x;
}
/**
* 讀取/設置3D空間中的y座標
*/
override public function set y(value:Number):void
{
_position.y = value;
updateScreenPosition();
}
override public function get y():Number
{
return _position.y;
}
/**
* 讀取/設置3D空間中的z座標
*/
override public function set z(value:Number):void
{
_position.z = value;
updateScreenPosition();
}
override public function get z():Number
{
return _position.z;
}
public function set position( position: Point3D ): void
{
this._position = position;
updateScreenPosition();
}
public function get position(): Point3D
{
return _position;
}
public function get depth(): Number
{
return ( _position.x + _position.z ) * 0.866 - _position.y * 0.707;
}
/**
* 指定其他對對象是否可以通過所佔的位置
* @param value
*
*/
public function set walkable( value: Boolean ): void
{
_walkable = value;
}
public function get walkable(): Boolean
{
return _walkable;
}
/**
* 返回尺寸
* @return
*
*/
public function get size(): Number
{
return _size;
}
public function set size( value: Number ): void
{
_size = value;
}
/**
* set/get the height of this tile
* @param value
*
*/
override public function set height(value:Number):void
{
_height = value;
}
override public function get height():Number
{
return _height;
}
/**
* Set/get the color of this tile
* @return
*
*/
public function set color( value: uint ): void
{
_color = value;
}
public function get color(): uint
{
return _color;
}
}
}
IsoObject類,這個類所實現的是 對象的3D狀態到屏幕狀態的轉換,繼承之sprite。與書上的相比,添加了create工廠方法,根據參數(type )返回相對應的子類實例對象。
這個類有兩個子類tile, box。IsoObject不包函任何圖形,圖形由子類通過draw方法完成,稍後介紹。
Title類
package com.hope.isometric
{
import flash.display.Sprite;
import flash.geom.Point;
public class Tile extends IsoObject
{
public function Tile()
{
super();
}
/**
*
* 繪製圖形
*/
override public function draw( size: Number, color: uint, height: Number = 0 ): void
{
super.draw( size, color, height );
graphics.clear();
graphics.lineStyle(0, 0, .5);
graphics.beginFill( _color );
graphics.moveTo(-size, 0);
graphics.lineTo(0, -size * .5);
graphics.lineTo(size, 0);
graphics.lineTo(0, size * .5);
graphics.lineTo( -size, 0 );
}
}
}
Tile繼承IsoObject, 重寫了draw函數,繪製一個自定義大小,顏色,高度,長寬比爲2:1的菱形。
Box類
package com.hope.isometric
{
public class Box extends IsoObject
{
public function Box()
{
super();
}
override public function draw( size: Number, color: uint, height: Number = 0 ):void
{
super.draw( size, color, height );
var red: uint = color >> 16;
var green: uint = color >> 8 && 0xff;
var blue: uint = color && 0xff;
var leftShadow:uint = (red * .5) << 16 |(green * .5) << 8 |(blue * .5);
var rightShadow:uint = (red * .75) << 16 |(green * .75) << 8 | (blue * .75);
var h:Number = height * IsoUtils.Y_CORRECT;
graphics.clear();
drawTop( color, h );
drawLeft( leftShadow, h );
drawRight( rightShadow, h );
}
private function drawTop( color: uint, h: Number ): void
{
graphics.beginFill( color );
graphics.lineStyle( 0, 0, 0.5 );
graphics.moveTo(-size, -h);
graphics.lineTo(0, -size * .5 - h);
graphics.lineTo(size, -h);
graphics.lineTo(0, size * .5 - h);
graphics.lineTo(-size, -h);
graphics.endFill();
}
private function drawLeft( color: uint, h: Number ): void
{
graphics.beginFill(color );
graphics.lineStyle(0, 0, .5);
graphics.moveTo(-size, -h);
graphics.lineTo(0, size * .5 - h);
graphics.lineTo(0, size * .5);
graphics.lineTo( -size, 0 );
graphics.lineTo( -size, -h );
graphics.endFill();
}
private function drawRight( color: uint, h: Number ): void
{
graphics.beginFill( color );
graphics.lineStyle(0, 0, .5);
graphics.moveTo(size, -h);
graphics.lineTo(0, size * .5 - h);
graphics.lineTo(0, size * .5);
graphics.lineTo(size, 0);
graphics.lineTo(size, -h);
graphics.endFill();
}
}
}
Box類與Tile類一樣,同樣是繼類IsoObject類,重寫draw函數,繪製一個方盒子。
IsoWorld類
package com.hope.isometric
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.utils.Dictionary;
// http://www.java3z.com/cwbwebhome/article/article2/2825.html
public class IsoWorld extends Sprite
{
private var grid: Grid;
private var cellSize: Number;
private var floor: Sprite;
private var world: Sprite;
[ArrayElementType("IsoObject")]
private var objectList: Array;
private var numCols: int = 10;
private var numRows: int = 10;
public function IsoWorld()
{
}
static public function create(): IsoWorld
{
return new IsoWorld();
}
/**
*
* @param size The tile size to user when making the world
* @return A fully populated IsoWorld instance
*
*/
public function makeWorld( size: Number, mapGrid: Array, tileTypes: Dictionary ): void
{
//cellSize = size;
numCols = mapGrid.length;
numRows = mapGrid[0].length;
//createGrid( size );
for( var i:int = 0; i < mapGrid.length; i++ )
{
for( var j:int = 0; j < mapGrid[i].length; j++ )
{
var cellType: String = mapGrid[i][j] as String;
var cell: Object = tileTypes[ cellType ];
var tile: IsoObject = IsoObject.create( cell.type );
tile.draw(size, parseInt(cell.color), parseInt(cell.height));
tile.walkable = cell.walkable == "true" ? true:false;
tile.position = new Point3D( j * 20, 0, i * 20 );
//grid.setNodeObject( j, i, tile );
addChild( tile );
//trace( "tile type:" + cell.type + " x:" + i + " y:" + j + " walkable:" + tile.walkable + " " + grid.getNode(j,i).walkable );
}
}
}
public function setPosition( x: Number, y: Number ): void
{
this.x = x;
this.y = y;
}
}
}
IsoWord類根據Map信息通過makeWorld創建地圖信息。
Map 地圖信息 ( map.txt 文件) //行爲註釋, # 行爲定義, 其他的地圖信息 0表示Tile, 1 表示Box。 如果添加新的圖形,可定義2, 3, 4等等.
//this is a comment
# 0 type:Tile walkable:true height:0 color:0xcccccc
# 1 type:Box walkable:false height:20 color:0xff6666
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MapLoder類
package com.hope.isometric
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Dictionary;
public class MapLoader extends EventDispatcher
{
private var _tileTypes: Dictionary;
private var _mapGrid: Array;
private var _loader: URLLoader;
public function MapLoader()
{
init();
}
static public function create(): MapLoader
{
return new MapLoader();
}
private function init(): void
{
_tileTypes = new Dictionary();
}
/**
* Loads a text file from the specified url
* @param url The location of the text file to load
*
*/
public function loadMap( url: String ): void
{
_loader = new URLLoader();
_loader.addEventListener( Event.COMPLETE, onLoad );
_loader.load( new URLRequest( url ));
}
/**
* Parse text file int tile definitions and map;
* @param event
*
*/
private function onLoad( event: Event ): void
{
_mapGrid = new Array();
var data: String = _loader.data;
trace( "mapData:" + data );
//first get each line of the file
var lines: Array = data.split( "\r\n" );
for( var i: int = 0; i < lines.length; i++ )
{
var line: String = lines[i];
//if line is tile definition
if( isDefinition( line ) )
{
parseDefinition( line );
}
else if( !lineIsEmpty(line) && !isComment( line ) )
{
var cells: Array = line.split(" ");
_mapGrid.push( cells );
}
}
dispatchEvent( new Event( Event.COMPLETE ) );
}
/**
*
* @param line
*
*/
private function parseDefinition( line: String ): void
{
var tokens: Array = line.split(" ");
//shift #
tokens.shift();
var symbol: String = tokens.shift() as String;
var definition: Dictionary = new Dictionary();
for( var i:int = 0; i < tokens.length; i++ )
{
var key:String = tokens[i].split(":")[0];
var val:String = tokens[i].split(":")[1];
definition[ key ] = val;
}
//register the type and definition
setTileType( symbol, definition );
}
/**
* Links a symbol with a definition object
* @param symbol The charactor to user the definition
* @param definition A generic object with definition properties;
*
*/
public function setTileType( symbol: String, definition: Object ): void
{
_tileTypes[ symbol ] = definition;
}
/**
* Return true if line only contains space, false if any other characters;
* @param line
* @return
*
*/
private function lineIsEmpty( line: String ): Boolean
{
for( var i: int = 0; i < line.length; i++ )
{
if( line.charAt(i) != " " ) return false;
}
return true;
}
/**
* Retrun true if line is a comment( start with "//" )
* @param line The string to test
* @return
*
*/
private function isComment( line: String ): Boolean
{
return line.indexOf( "//" ) == 0 ? true : false;
}
/**
* Return true if line is a definition ( start width "#" )
* @param line The string to test
* @return
*
*/
private function isDefinition( line: String ): Boolean
{
return line.indexOf( "#" ) == 0 ? true : false;
}
//////////////
///setter / getter
/////////////
public function get tileTypes(): Dictionary
{
return _tileTypes;
}
public function get mapGrid(): Array
{
return _mapGrid;
}
}
}
MapLoader類用於下載map.txt文件,並記取文件信息
IsoWorldTest測試類
package com.hope.isometric
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.net.URLRequest;
public class IsoWorldTest extends Sprite
{
private var world: IsoWorld;
private var mapLoader: MapLoader;
private var astar: AStar;
public function IsoWorldTest()
{
if( stage )
init();
else
addEventListener( Event.ADDED_TO_STAGE, init );
}
private function init( event: Event = null ): void
{
removeEventListener( Event.ADDED_TO_STAGE, init );
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
loadMap();
}
private function loadMap(): void
{
mapLoader = new MapLoader();
mapLoader.addEventListener( Event.COMPLETE, onMapComplete );
mapLoader.loadMap( "map.txt" );
}
private function onMapComplete( event: Event ): void
{
trace( "load complete" );
world = IsoWorld.create();
world.makeWorld( 20, mapLoader.mapGrid, mapLoader.tileTypes );
addChild( world );
world.setPosition( 500, 200 );
}
}
}
首先加載地圖信息文件,完成後能過IsoWorld類的makeWorld函數創建地圖。
最後運行結果