LayaFlash新增方法

发布时间:2015-11-04

  为更好的解决Flash与JavaScript的差异问题,让Flash程序员直接用AS3语言开发HTML5,除了支持Flash的原生API,LayaFlash引擎还提供了一些新增的API方法。

1、函数作用域绑定:bind()
  由于JS的函数不自动绑定this作用域,这会导致同一个的函数在不同的上下文调用时,this会指向不同的对象,而在AS3中this一直指向函数的父对象。bind()的作用就是保证JS和AS的运行效果保持一致。

  1.1 bind()的使用示例:

  以下代码,当test.testMethod在TestBindCaller对象中调用时会报错的用法:

private function layaFlashBindTest():void
{
    var test:TestForBind = new TestForBind("layaBind");
    var caller:TestBindCaller = new TestBindCaller(test.testMethod);
}

  若要在AS3和JS中都运行正常,正确的代码如下所示:

private function layaFlashBindTest():void
{
    var test:TestForBind = new TestForBind("layaBind");
    //使用bind后,JS与AS3的运行效果就可以达到一致
    var caller:TestBindCaller = new TestBindCaller(bind(test, test.testMethod));          
}


  1.2 bind()的使用规范

  当函数被当作参数使用的时候,必须使用bind()以保障AS3与JS运行效果一致。bind方法的第一个参数传递的是需要绑定作用域的函数父对象,第二个参数是需要绑定作用域的函数的引用。比如1.1的bind()示例中,第一个参数test是第二个参数test.testMethod的父对象。

  1.3 什么情况下可以不用增加bind()

  在新项目开发中,开发者要严格遵循bind()使用规范,避免后期再产生调整。但是对于已有的旧项目中,为减少开发者的手动修改量,当函数的作用域和定义它的父对象一致的时候,LayaFlash编译时会自动修改as3源码添加bind()。(如果开发者更愿意手动去逐一修改,本条可无视)

下例代码是可以被LayaFlash自动修改,无需人工调整的:

private function helloBind():void
{
    trace("this value is main: " + (this is Main));
}
private function layaFlashBindTest():void
{
    var test:TestForBind = new TestForBind("layaBind");
    //传递自身的方法时,可以不用人工增加bind()
    var caller2:TestBindCaller = new TestBindCaller(this.helloBind);
    
}

  1.4 bind()的使用示例详解:

  bind示例源码下载:layaFlashBind.rar

  先创建一个新项目,然后分别创建两个新类:TestForBind类和TestBindCaller类,把TestForBind内的一个方法作为参数传递到TestBindCaller的对象内,并在TestBindCaller的对象内调用这个方法。
  TestForBind.as的代码:

package bindTest
{
    import flash.display.Sprite;
    
    public class TestForBind extends Sprite
    {
        
        private var _name:String; //对象里的私有数据
        
        public function TestForBind(name:String)
        {
            super();
            this._name = name; //初始化这个数据
        }
        
        /**
         * 要在不同的作用域里调用的方法
         *
         */
        public function testMethod():void
        {
            trace("bindTest: the name is " + this._name);
        }
    }
}

  TestBindCaller.as的代码:

package bindTest
{
    import flash.display.Sprite;
    
    public class TestBindCaller extends Sprite
    {
        
        public function TestBindCaller(method:Function)
        {
            super();
            (method) && method(); //调用外部传入的的函数
        }
    }
}

  主文件Main.as里的代码:

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    
    import bindTest.TestBindCaller;
    import bindTest.TestForBind;
    
    import iflash.method.bind;
    
    public class Main extends Sprite
    {
        
        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;
            
            layaFlashBindTest();
        }
        
        private function helloBind():void
        {
            trace("this value is main: " + (this is Main));
        }
        
        private function layaFlashBindTest():void
        {
            var test:TestForBind = new TestForBind("layaBind");
            var caller:TestBindCaller = new TestBindCaller(bind(test, 
            test.testMethod));
            var caller2:TestBindCaller = new TestBindCaller(this.helloBind);
        }
    }
}

  以上代码在AS3环境中运行的输出结果为:bindTest: the name is layaBind
  编译后的代码在LayaFlash IDE中的输出结果为bindTest: the name is undefined2.png  产生这样的结果是由于在BindCall中调用被传入的BindTest对象的testMethod方法,this的的指向已经改变,尝试和AS3一样获取this._name的值是无法得到的,因为此时JS中的this的值是windows全局对象,而非函数被定义时的父对象。要解决这个问题,需要使用LayaFlash新增的bind()方法。

private function layaFlashBindTest():void
{
    var test:TestForBind = new TestForBind("layaBind");
    var caller:TestBindCaller = new TestBindCaller(bind(test, test.testMethod)); //绑定函数作用域,这里进行了修改。
}

  按上面的方式修改代码后,再次编译,LayaFlash IDE中输出的结果就正常了。如下图所示

3.png

 

2、文件引入:importJS
  importJS方法用于在AS3项目中引用JS文件,该方法主要的用途是引入编译后的分包JS文件。禁止引入游戏入口文件。通过importJS引入的JS文件,在AS3项目中不被运行生效,仅在HTML5运行环境中生效。

  分包用法示例代码如下:

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    
    import iflash.method.importJS;
    
    /**
     * 本代码示例包含项目分包及引入JS文件
     *
     *
     */
    public class Main extends Sprite
    {
        
        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            importJS("swfobject.js", importComplete); //引入其他JS文件
        
        }
        
        private function importComplete():void
        {
            trace("swfobject.js is imported.");
        }
    }
}

  以Flash Builder项目生成的swfobject.js文件为例,项目开发时这个引入的JS文件必须存放在h5根目录或子目录内。(上线后的根目录名可以自行定义,放在可以找到的路径中即可)

4c.png

  importJS()方法的使用说明

  importJS方法第一个参数是引入目标JS的完整路径。第二个参数是JS文件引入完成后的回调函数。通常在回调函数中编写JS文件引入后的执行逻辑。关于该方法在项目分包的应用,可以参考《项目分包》教程。

  需要顺带一提的是,zlib.min.js是H5下压缩和解压缩文件的JS代码库,项目中如果采用压缩或解压缩文件相关的功能,必须要保障在使用这些功能之前必须保证zlib.min.js文件已引入。引入zlib.min.js文件除了使用importJS方法之外,也可以通过修改项目入口HTML文件的代码实现,详细方法参见《LayaFlash编译项目》教程的“3.在H5项目入口HTML文件中引入其他JS文件”章节内容。

 

3、资源预加载:IFlash.preSwfAssets
  由于LayaFlash实现的MovieClip并非在初始化的时候将资源全部加载,而是在需要的时候才加载,这会导致使用MovieClip对象时,有可能会因为首次播放动画,网速慢或图片较大等原因造成加载过程不连贯的不友好体验,开发者可以采用LayaFlash提供的IFlash.preSwfAssets方法,通过预加载方式解决这个问题。
  具体用法如下:

package
{
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.net.URLRequest;
    
    public class Main extends Sprite
    {
        public function Main():void
        {

            IFlash.setSize(1000, 600); //2D项目中设置场景尺寸
            IFlash.setOrientationEx(1); //是否为横屏模式
            IFlash.setBgcolor("#000000"); //背景色
            IFlash.showInfo(false); //是否显示帧率

            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;
            
            
            
            preSwfAssetsTest();
        }
        
        /**
         * 测试预加载
         *
         */
        private function preSwfAssetsTest():void
        {
            var path:String = "assets/layaAsset.swf";
            var  path1:String = "assets/layaAsset1.swf";
            IFlash.preSwfAssets([path,path1]);//这里开始调用预加载
            
            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, layaAssetLoadComplete);
            loader.load(new URLRequest(path));
         }
         private function layaAssetLoadComplete(event:Event):void
         {
            var loaderInfo:LoaderInfo = event.target as LoaderInfo;
            loaderInfo.removeEventListener(Event.COMPLETE, layaAssetLoadComplete);
        
            //code here.
         }
    }
}


   按如上示例即可完成资源预加载。这里特别提醒两点,一是预加载操作必须放在资源执行加载的代码之前;二是预加载要谨慎使用,不能滥用。当预加载的资源过大的时候,也会造成加载等待时间长的问题,哪怕是多等待几秒,都会造成游戏用户的流失。因此,是否采用预加载,预加载的使用尺度,要根据游戏的实际情况来把握。


4、直译代码方法:__JS__
  __JS__是LayaFlash提供的直译方法,这里核心在于直译,顾名思义,所谓直译就是把这个方法中传入的字符串,不经任何改变,直接编译到JS文件中,因此,不要被__JS__的命名迷惑,__JS__方法内可以编写任何你想编写的代码。当然,你要考虑清楚后果,编写的代码运行环境会不会支持。所以对于HTML5项目开发来讲,还是以引用JS代码为主要目的。

  通过__JS__这个方法 ,开发者可以直接在AS3项目中写JavaScript代码,从而实现JS代码和AS3代码混合编写的目的。由于__JS__()中引用的代码在AS3项目中并不生效,仅是编译成JS代码时在浏览器或LayaPlayer运行器中被运行,对于想同时发布Flash和HTML5版本等多版本的开发者,请谨慎使用。

  下面通过代码示例,看一下具体用法:

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    
    public class Main extends Sprite
    {
        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;
            
            __JS__Test();
        }
        
        private function __JS__Test():void
        {
            __JS__("window.alert('__JS__语句把我召唤出来了')");
        }
    }
}

  上面的代码AS3中不会有任何效果,编译成JS后的执行效果如下:

6.png


5.Dictionary类的替代类LayaDictionary

     为了彻底解决编译成JS后Dictionary的性能问题,LayaFlash提供了一个新的类LayaDictionary来替代Flash原生下的Dictionary,通过set()、get()、remove()、clear()、contains()方法的辅助,实现了Dictionary在编译后保持原有的功能效果。

  (注意:LayaFlash 2.1.0版本以及更高版本支持LayaDictionary类的使用

5.1. 添加enableDic=false参数

     针对Dictionary其实LayaFlsah引擎有两套编译方案,旧的方案是早期为了让转换产品能尽早运行起来的临时方案,会存在性能问题。为了兼容一些已使用旧方案的项目还能正常运行起来,LayaFlash启用了两套方案,为了让编译器识别区分两套方案,需要用户去设置编译参数enableDic=false。

  注意:enableDic=true,则使用的是原生Dictionary,会存在性能问题

第一种方案:通过自变量添加

操作步骤如下:(以Flash builder为例)

a、打开外部工具配置

2.bmp

b、在自变量中添加enableDic=false参数

3.bmp

c、点击应用按钮即可完成此操作。


第二种方案:通过LayaFlash IDE编译器参数设置

a、下载LayaFlash 2.4.0版本引擎或更高级版本

b、打开LayaFlash 编译器环境,点击帮助按钮选择编译器参数设置

123.png

打开器参数配置面板

234.png

在输入栏中输入enableDic=true参数,点击确认,配置完成

345.png


5.2. set()、get()、remove()、clear()方法的使用

  示例如下:

public function layaDictionaryTest()
{
	var data:LayaDictionary = new LayaDictionary();
	data.set(1, "a"); //根据字典的键取出键值
	data.set(2, "b");
	data.set(3, "a string value.");
	var key:Object = {"my": 100};
	data.set(key, "any data");
	trace(data.get(key).toString()); //读取字典中的键所对应的键值
	
	data.remove(1); //移除字典中的某个键所对应的键值对象
	trace(data.elements.toString());
	
	data.clear(); //清空字典中所有的键值对象
	trace(data.elements.toString());
}

5.2.1. set(key:*,value:*)

    set()方法用于LayaDictionary在编译成JS后实现键值的存储和赋值功能。

5.2.2. get(key:*)

     get()方法用于读取通过set()方法存储的键值数据。

5.2.3. remove(key:*)

      remove()方法用于移除某个键的键值对象。

5.2.4. clear()

     clear()方法用于清除LayaDictionary所有存储的键值对象,使用此方法,LayaDictionary的elements和keys所对应的Array为0。

5.3. contains(key:*)

     contains()方法用于表示对象是否已经定义了指定的属性。已经定义,则此方法返回 true;否则返回 false。此方法替代了原生hasOwnProperty()方法。 

      示例如下:

var data:LayaDictionary = new LayaDictionary(); 
    data.set(1,"a"); 
    data.set(2,"b"); 
    data.set(3,"a string value."); 
    data.set(4,[]);
    var key:Array = []; 
    data.set(key,"any data");
    //输出结果为true
    trace("Is data has aListKey: " + data.contains(key));

 

6、读取子节点长度方法:xmlLength()

    由于XML的用法非常多样,编译器很难完全识别,因此LayaFlash提供xmlLength()方法取代原AS3语言中的lenth(),以保障编译器正确识别编译。

    使用示例如下:

private function layaFlashXMLLenghtTest():void {
	var xml:XML = 
		<data>
			<items>
				<item>0</item>
				<item>1</item>
				<item>2</item>
				<item>3</item>
			</items>
		</data>;
	var items:XMLList = xml.items.children();
	var len:int;
	//len = items.length();//AS3原length()读取长度方法,
	len = xmlLength(items);//使用LayaFlash提供的xmlLength()替换lenth()
	trace("xml sub item is: " + len);
}


7读写指定位上数据:byteAt()和byteSet()

    LayaFlash在取二进制对象指定位上的数据时不能使用方括号语法(“[]”)语法获取和设置指定位上的数据,必须改用 byteAt()和byteSet()做对应的“读取” 和“写人”操作。

private function byteArrayGetAndSet_Test():void
{
    var byteArray:ByteArray = new ByteArray();
    //byteArray[100] = 20;//原生写法,该写法LayaFlash引擎中禁用
    byteSet(byteArray, 100, 20); //LayaFlash允许的写法
    trace("AS3/LayaFlash byeArray possition 100 is: " + byteAt(byteArray, 100));//采用byteAt读取
}