Flash/Flex/AIR:ActionScript2扩展图形接口

    本例将为ActionScript2 MovieClip 添加坐标系统和网格系统,这将方便Flash 游戏的开发。下面结合代码详细介绍:

1.接口

interface wargrey.identification.IExtensible {

/**

* @description: public function extend(Object):Void;

*/

}

IExtensive接口是一个标识接口,用于表示实现此接口的类是“可扩展的”。不过可能有一点比较奇怪,为什么其方法要注释掉?其实这也是因为AS2在面向对象方面的不足,因为AS2本身是一门动态语言,这类语言的特点之一便是类型弱化的,而这一特征比面向对象的方法重载更灵活,可以因为这个原因,AS一直不支持方法重载,而对于面向对象,又是强类型的,因此实现接口时必须严格实现匹配的接口方法。本来这没有矛盾,可以保留public function extend(Object):Void;方法,但很明显本例的扩展性是针对可视对象的,因此参数Object是比较宽的类型,为了避免不要必要的麻烦,因此设计成让开发者自我约束的方式:与其让开发者关注被扩展的对象类型,还不如原本就不知道有更多的方法可选,况且这样更利于定制系统所需要的接口。这是针对动态语言的解决方案,不适合带到其他静态面向对象语言。例外本例并非没有其他更规范的解决方案,只是这样的设计体现的一种思想,因为本人认为,软件设计在很多时候都需要反规范。本例实现此接口的类实现了public function extend(MovieClip):Void;方法。

2.实用工具类

a.网格系统Grids

class wargrey.util.Grids {

private var _currentGrid:Object; //当前网格副本

public function get currentGrid():Object{

return this._currentGrid;

}

public function set currentGrid(pos:Object):Void{

var x=(pos.x==undefined)?pos._x:pos.x;

var y=(pos.y==undefined)?pos._y:pos.y;

if ((x!=undefined)&&(y!=undefined)){

var w=(pos.width==undefined)?pos._width:pos.width;

var h=(pos.height==undefined)?pos._height:pos.height;

if (w!=undefined)x=x+w;

if (h!=undefined)y=y+h;

this._currentGrid.x=Math.floor(x/this._width)*this._width+1;

this._currentGrid.y=Math.floor(y/this._height)*this._height+1;

}

}

private var _width:Number = 25; //网格宽

public function get width():Number{

return this._width;

}

private var _height:Number = 25; //网格高

public function get height():Number{

return this._height;

}

public function Grids(w:Number,h:Number){

if((w!=undefined)&&(w>=1))this._width=Math.round(w);

if((h!=undefined)&&(h>=1))this._height=Math.round(h);

this._currentGrid=new Object();

this._currentGrid.x=1;

this._currentGrid.y=1;

this._currentGrid.width=this._width;

this._currentGrid.height=this._height;

}

public function getCurrentGrid(pos:Object):Object{

if (pos!=undefined)currentGrid=pos;

return this._currentGrid;

}

public function getNextRowGrid(pos:Object):Object{

if (pos!=undefined)currentGrid=pos;

this._currentGrid.x+=this._width;

return this._currentGrid;

}

public function getNextLineGrid(pos:Object):Object{

if (pos!=undefined)currentGrid=pos;

this._currentGrid.y+=this._height;

this._currentGrid.x=1;

return this._currentGrid;

}

}

网格系定义了网格的一系列属性,作用是为任一MovieClip划分行和列,以方便该MovieClip上其他MovieClip的位置布局,并且不受该MovieClip上可能存在的坐标系统的影响。

网格系统共三个属性,分别为:

width(Number):只读属性网格宽。初始大小在构造网格系时传递,默认为25,单位像素。

height(Number):只读属性网格高。初始大小在构造网格系时传递,默认为25,单位像素。

currentGrid(Object):当前网格位置。Object是一个包含位置属性的对像,无所谓其是否可视,实际上若该对象可视,会忽略其的这一特性。位置被定义为ObjectMovieClip上所处的网格的左上顶点的MovieClip坐标。当currentGrid=Object发生时,object的大小或位置不会正好匹配网格大小或顶点,因此传入参数将被视为该Object右下顶点。

网格系还有三个方法getCurrentGrid(Object),getNextRowGrid(Object)getNextLineGrid(Object),这三个方法的作用依次是获取相对Object的当前网格、下一列网格和下一行网格对象,并且在该方法发生后,currentGrid都会被设置成相对Object的相应网格。关于Object的说明同上。

b.坐标系统Coordinates


class wargrey.util.Coordinates{

private var coordinateX:Number; //坐标系X轴原点所在的影片坐标系坐标

private var coordinateY:Number; //坐标系Y轴原点所在的影片坐标系坐标

private var scaleX:Number; //坐标系X轴缩放比,值为 影片尺寸:实际尺寸

private var scaleY:Number; //坐标系Y轴缩放比,值为 影片尺寸:实际尺寸

private var _scale:Number; //坐标系缩放比,值由scaleXscaleY决定

public function get scale():Number{

return _scale;

}

public function Coordinates(coordinateX:Number,coordinateY:Number,scaleX:Number,scaleY:Number){

this.coordinateX=(coordinateX==undefined)?0:coordinateX;

this.coordinateY=(coordinateY==undefined)?0:coordinateY;

this.scaleX=((scaleX!=undefined)&&(scaleX>=0))?scaleX:1;

this.scaleY=((scaleY!=undefined)&&(scaleY>=0))?scaleY:1;

var radian=Math.atan2(this.scaleY,this.scaleX);

this._scale=scaleY/Math.sin(radian);

}

public function mapping(target:Object):Object{

if (target instanceof Number){

return target*_scale;

}else{

var x:Number=(target.x==undefined)?target._x:target.x;

var y:Number=(target.y==undefined)?target._y:target.y;

var w:Number=(target.width==undefined)?target._width:target.width;

var h:Number=(target.height==undefined)?target._height:target.height;

var result:Object=new Object();

if(x!=undefined)result.x=coordinateX+x*scaleX;

if(y!=undefined)result.y=coordinateY-y*scaleY;

if(w!=undefined)result.width=w*scaleX;

if(h!=undefined)result.height=h*scaleY;

return result;

}

}

public function converse(target:Object):Object{

if (target instanceof Number){

return target/_scale;

}else{

var x:Number=(target.x==undefined)?target._x:target.x;

var y:Number=(target.y==undefined)?target._y:target.y;

var w:Number=(target.width==undefined)?target._width:target.width;

var h:Number=(target.height==undefined)?target._height:target.height;

var result:Object=new Object();

if(x!=undefined)result.x=(x-coordinateX)/scaleX;

if(y!=undefined)result.y=-(y-coordinateY)/scaleY;

if(w!=undefined)result.width=w/scaleX;

if(h!=undefined)result.height=h/scaleY;

return result;

}

}

}

坐标系统使得开发人员可以为MovieClip添加自定义坐标系的能力,这样一来对矢量的控制将更为方便。与网格系相比,坐标系更加强调与MovieClip的绑定。

坐标系只有一个只读属性scale(Number),表示坐标缩放比(影片尺寸:实际尺寸,以对角线为准)。其大小由构造时传入的scaleXscaleY决定,默认为21/2

坐标系有两个核心方法mapping(Object)converse(Object)Object是一个具有位置和大小属性的对象,同样也无所谓是否可视。这两个方法分别用于“将实际对象按其位置和大小映射为MovieClip对象”及“MovieClip对象按其位置和大小还原为实际对象”。

补充说明:

  • 坐标系的(0,0)点对应于MovieClip坐标上的一个点,该点再构造时传入,默认为(0,0)

  • 一旦为MovieClip绑定了坐标系,则无论是否指定坐标参数,MovieClip都具有了一个正常的数学直角坐标系。

  • mappingconverse两个方法的参数也可以就是Number,这样就根据scale值只作长度变换。不过转化后的值受scaleXscaleY的夹角的影响。

3.核心类

import mx.core.UIObject;

import wargrey.util.*;

import wargrey.identification.IExtensible;


class wargrey.extension.GraphicalInterface extends UIObject implements IExtensible{

private var grids:Grids; //网格系统

private var coordinates:Coordinates; //坐标系统

private var currentX:Number; //光标坐标x

private var currentY:Number; //光标坐标y

private var currentZ:Number; //光标坐标z,即影片深度

private var textFields:Number; //用于构建display生成的文本域名称序号

public function GraphicalInterface(cs:Coordinates,gs:Grids){

setGraphicals(cs,gs);

var loc=coordinates.converse({x:0,y:0});

currentX=loc.x;

currentY=loc.y;

currentZ=1;

textFields=1;

}

public function setGraphicals(cs:Coordinates,gs:Grids):Void{

if (cs!=undefined)coordinates=cs;

if (coordinates==undefined)coordinates=new Coordinates();

if (gs!=undefined)grids=gs;

if (grids==undefined)grids=new Grids();

}

public function createVisualObject(target:Object, id:Object, init:Object):MovieClip{

var von=(id==undefined)?"viusalobjectatdepth"+currentZ.toString():id.toString();

if (target instanceof Function){

var obj:UIObject=this.createClassObject(Function(target),von,getNextDepth(),init);

}else{

var obj:MovieClip=this.attachMovie(target.toString(),von,getNextDepth(),init);

}

var rect:Object=new Object();

rect.x=(init.x==undefined)?init._x:init.x;

rect.y=(init.y==undefined)?init._y:init.y;

if (rect.x==undefined)rect.x=currentX;

if (rect.y==undefined)rect.y=currentY;

rect.width=(init.width==undefined)?init._width:init.width;

rect.height=(init.height==undefined)?init._height:init.height;

if (rect.width==undefined)rect.width=obj._width;

if (rect.height==undefined)rect.height=obj._height;

var w=rect.width;

var h=rect.height;

rect=coordinates.mapping(rect);

obj._x=rect.x;

obj._y=rect.y;

if (!init.scale){

rect.width=w;

rect.height=h;

}

obj.width=rect.width;

obj.height=rect.height;

newRow(obj);

return obj;

}

public function display(msg:Object, size:Number, textColor:Number, backColor:Number, text:TextField, format:TextFormat):TextField{

if (!((size==undefined)&&(textColor==undefined)&&(format==undefined))){

var ft:TextFormat=(format==undefined)?new TextFormat():format;

if (size!=undefined)ft.size=size;

if (textColor!=undefined)ft.color=textColor;

}

var tfn:String="display"+(textFields++).toString();

var pos=coordinates.mapping({x:currentX,y:currentY});

createTextField(tfn,getNextDepth(),pos.x,pos.y,0,0);

var temp:TextField=TextField(this[tfn]);

if (text!=undefined)temp=text;

if (backColor!=undefined){

temp.background=true;

temp.backgroundColor=backColor;

}

temp.type="dynamic";

if (temp.antiAliasType!="normal")temp.antiAliasType="advanced";

temp.autoSize=true;

temp.html=true;

temp.htmlText=msg.toString();

if (ft!=undefined)temp.setTextFormat(ft);

newRow(temp);

return temp;

}

public function newLine(obj:Object):Void{

var cr=coordinates.converse(grids.getNextLineGrid(obj));

currentX=cr.x;

currentY=cr.y;

}

public function newRow(obj:Object):Void{

var cr=coordinates.converse(grids.getNextRowGrid(obj));

currentX=cr.x;

}

public function locate(x:Number,y:Number):Void{

currentX=x;

currentY=y;

var p:Object=new Object();

p.x=x;

p.y=y;

p=coordinates.mapping(p);

grids.currentGrid=p;

}

public function horizontalAlign(ui:Array,align:String):Void{

align=align.toLowerCase();

var min=ui[0]._x;

var max=ui[0]._x+ui[0]._width;

for (var i=1;i<ui.length;i++){

var l=ui[i]._x;

var r=ui[i]._width+l;

if (min>l)min=l;

if (max<r)max=r;

}

if (align=="left"){

for(var i=0;i<ui.length;i++)ui[i]._x=min;

}else if(align=="right"){

for(var i=0;i<ui.length;i++)ui[i]._x=max-ui[i]._width;

}else if ((align=="centre")||(align=="center")||(align=="middle")){

for(var i=0;i<ui.length;i++)ui[i]._x=Math.round((min+max-ui[i]._width)/2);

}

}

public function verticalAlign(ui:Array,align:String):Void{

align=align.toLowerCase();

var min=ui[0]._y;

var max=ui[0]._y+ui[0]._height;

for (var i=1;i<ui.length;i++){

var t=ui[i]._y;

var b=ui[i]._height+t;

if (min>t)min=t;

if (max<b)max=b;

}

if (align=="top"){

for(var i=0;i<ui.length;i++)ui[i]._y=min;

}else if(align=="bottom"){

for(var i=0;i<ui.length;i++)ui[i]._y=max-ui[i]._height;

}else if ((align=="centre")||(align=="center")||(align=="middle")){

for(var i=0;i<ui.length;i++)ui[i]._y=Math.round((min+max-ui[i]._height)/2);

}

}

public function gridAlign(ui:Array):Void{

var cg=grids.currentGrid;

for (var i=0;i<ui.length;i++){

var loc:Object=new Object();

loc.x=ui[i]._x;

loc.y=ui[i]._y;

loc=grids.getCurrentGrid(loc);

ui[i]._y=loc.y;

}

grids.currentGrid=cg;

}

public function extend(target:MovieClip):Void{

var loc=coordinates.converse({x:0,y:0});

target["coordinates"]=this.coordinates;

target["grids"]=this.grids;

target["currentX"]=loc.x;

target["currentY"]=loc.y;

target["currentZ"]=1;

target["textFields"]=1;

target["setGraphicals"]=this.setGraphicals;

target["createVisualObject"]=this.createVisualObject;

target["display"]=this.display;

target["newLine"]=this.newLine;

target["newRow"]=this.newRow;

target["locate"]=this.locate;

target["horizontalAlign"]=this.horizontalAlign;

target["verticalAlign"]=this.verticalAlign;

target["verticalAlign"]=this.verticalAlign;

target["gridAlign"]=this.gridAlign;

target["getNextDepth"]=this.getNextDepth;

target["destroyObject"]=this.destroyObject;

if (target instanceof IExtensible){

var oldExtend:Function=target.extend;

var thisExtend:Function=this.extend;

var newExtend:Function=function(target:Object){

oldExtend(target);

thisExtend(target);

};

target.extend=newExtend;

}

}

private function getNextDepth():Number{

if (currentZ<1048574)currentZ++;

var depth=currentZ;

return depth;

}

}

GraphicalInterface类实现了IExtensible接口,并借助网格系和坐标系来扩展MovieClip实例。所谓的扩展其实跟混入用了同样的手法,下面详细介绍:

setGraphicals(Coordinate,Grids):指定被扩展对象所绑定的坐标系和网格系,省略则创建默认实例。构造函数的参数与此相同。

createVisualObject(target:Object,id:Object,init:Object):MovieClip:创建一个有init初始化的可视对象targettarget可以是一个UIObject类或库中的MovieClip实例,其名称由id.toString()决定。idinit可选,若id省略,则target的名称由影片深度决定;若init省略,则在当前位置创建默认实例,init有个隐含属性scale,指示对象的大小是否也被映射,init的位置和大小属性都针对实际坐标而言。返回被创建的对象。

display(msg:Object,sz:Number,tc:Number,bc:Number,tt:TextField,tf:TextFormat):TextField:将tf格式化后的msg.toString()显示在tt上,或者也可以简单一点省略tttfmsg的大小有sz决定,前景和背景色分别有tcbc决定。如果省略tt,将在当前位置新建一个TextField对象。返回显示了msgTextField对象,该对象支持html

locate(Number,Number)newRow(Object)newLine(Object):这三个方法用于定位。有关参数Object的说明见Gridslocate方法的参数是实际坐标。

horizontalAlign(Array,String)verticalAlign(Array,String)gridAlign(Array):这三个方法用于将Array里的可视对象分别进行水平对齐,竖直对齐和网格对齐。对于水平和竖直对齐,效果与Flashalign工具相同,String的取值有Top(Left)Bottom(Right)Center三种情况。

extend(MovieClip):这是扩展MovieClip的方法,从这里我们看到了动态语言的优势。因此建议被扩展的对象是被声明为dynamic的,不然混入的方法就只能通过数组访问操作符来引用了。

4.测试用例

下面给出一个完整的测试用例来演示该框架。

// TestArea.as

import mx.controls.Button;

import wargrey.extension.GraphicalInterface;


class wargreytest.extension.graphicalinterface.TestArea extends MovieClip {

private var c:Button;

private var ha:Button;

private var va:Button;

private var hua:Array;

private var vua:Array;

public function set creatable(b:Boolean):Void{

c.enabled=b;

}

public function set VAable(b:Boolean):Void{

va.enabled=b;

}

public function set HAable(b:Boolean):Void{

ha.enabled=b;

}

private function click(evt:Object):Void{

switch(evt.target){

case ha:

var t=Math.round(Math.random()*4+1);

var type:String;

if (t==1){

type="Left";

ha.label="左端对齐";

}else if (t==2){

type="Right";

ha.label="右端对齐";

}else if (t==3){

type="Center";

ha.label="居中对齐";

}

if (t==4){

this["gridAlign"](hua);

ha.label="网格对齐";

}else{

this["horizontalAlign"](hua,type);

}

break;

case va:

var t=Math.round(Math.random()*4+1);

var type:String;

if (t==1){

type="Top";

va.label="顶端对齐";

}else if (t==2){

type="Bottom";

va.label="底端对齐";

}else if (t==3){

type="Center";

va.label="居中对齐";

}

if (t==4){

this["gridAlign"](vua);

va.label="网格对齐";

}else{

this["verticalAlign"](vua,type);

}

break;

case c:

ha.label="水平对齐";

va.label="竖直对齐";

if (this["h1"]!=undefined){

//this["destroyObject"]("h1");

//this["destroyObject"]("h2");

//this["destroyObject"]("h3");

//this["destroyObject"]("v1");

//this["destroyObject"]("v2");

//this["destroyObject"]("v3");

//for (var i=1;i<this["textFields"];i++)this["display"+i.toString()].removeTextField();

var loc=this["coordinates"].converse({x:0,y:0});

this["currentX"]=loc.x;

this["currentY"]=loc.y;

this["currentZ"]=1;

}

this["display"]("下列组件用于演示水平对齐",20,0x00ff00,0x0000ff);

for(var i=1;i<=3;i++){

var p:Object=new Object();

p.x=Math.round(Math.random()*100+1);

p.y=i*30;

p=this["coordinates"].converse(p);

var w=Math.round(Math.random()*100+100);

this["locate"](p.x,p.y);

this["h"+i.toString()]=this["createVisualObject"](Button,"h"+i.toString(),{_width:w});

this["h"+i.toString()].label="HATest"+i.toString();

hua[i-1]=this["h"+i.toString()];

}

this["newLine"]();

this["display"]("下列元件用于演示竖直对齐",20,0x0000ff,0x00ff00);

for(var i=1;i<=3;i++){

var x=[5,100,200];

var p:Object=new Object();

p.x=x[i-1];

p.y=Math.round(Math.random()*50+150);

var w=Math.round(Math.random()*40+50);

p=this["coordinates"].converse(p);

this["locate"](p.x,p.y);

this["v"+i.toString()]=this["createVisualObject"]("VA"+i.toString(),"v"+i.toString(),{_width:w});

vua[i-1]=this["v"+i.toString()];

}

VAable=true;

HAable=true;

break;

}

}

private function onLoad():Void{

hua=new Array(3);

vua=new Array(3);

c.addEventListener("click",this);

ha.addEventListener("click",this);

va.addEventListener("click",this);

}

}



// GraphicalInterfaceTest.as

import mx.controls.Label;

import mx.controls.TextInput;

import mx.controls.Button;


import wargreytest.extension.graphicalinterface.TestArea;

import wargrey.extension.GraphicalInterface;

import wargrey.util.Coordinates;

import wargrey.util.Grids;


class wargreytest.extension.GraphicalInterfaceTest extends MovieClip {

private var lblmc:Label;

private var lblac:Label;

private var lblop:Label;

private var lbldh:Label;

private var lblkh:Label;

private var lblwg:Label;

private var lblh:Label;

private var lbls:Label;

private var lbly:Label;

private var txtmc:Label;

private var txtac:Label;

private var txtgc:Label;

private var x:TextInput;

private var y:TextInput;

private var sx:TextInput;

private var sy:TextInput;

private var width:TextInput;

private var height:TextInput;

private var area:TestArea;

private var initialize:Button;

function GraphicalInterfaceTest() {

super();

}

private function onLoad():Void{

initialize.addEventListener("click",this);

initialize.label="Begin";

x.text="150";

y.text="150";

sx.text="0.5";

sy.text="0.5";

width.text="12";

height.text="24";

area.creatable=false;

area.VAable=false;

area.HAable=false;

}

private function click():Void{

initialize.label="Reset";

var c:Coordinates=new Coordinates(Number(x.text),Number(y.text),Number(sx.text),Number(sy.text));

var g:Grids=new Grids(Number(width.text),Number(height.text));

var gi=new GraphicalInterface(c,g);

gi.extend(area);

area.creatable=true;

var self=this;

var mouse:Object=new Object();

mouse.onMouseMove=function(){

if ((self._xmouse>self.area._x)&&(self._xmouse<(self.area._x+self.area._width))&&(self._ymouse>self.area._y)&&(self._ymouse<(self.area._y+self.area._height))){

var p:Object=new Object();

p.x=self.area._xmouse;

p.y=self.area._ymouse;

self.txtmc.text="("+Math.round(p.x)+","+Math.round(p.y)+")";

p=self.area["coordinates"].converse(p);

self.txtac.text="("+Math.round(p.x)+","+Math.round(p.y)+")";

p=self.area["grids"].getCurrentGrid(self.area["coordinates"].mapping(p));

self.txtgc.text="("+Math.round(p.x)+","+Math.round(p.y)+")";

}

};

Mouse.addListener(mouse);

}

}


 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值