Demo工程介绍

Unity部分

打开HotFixDemoScene1.scene场景,就能看到示例工程。本示例工程介绍了怎样使用ActionScript3执行逻辑。
总体而言,要在Unity项目中执行ActionScript3,需要以下几个步骤:

  1. 创建ActionScript3运行时。
  2. 加载ActionScript3字节码。
  3. 创建ActionScript3的入口对象。
  4. 调用 或者每帧都调用一次刚才创建的ActionScript3对象的某个方法。

创建ActionScript3运行时

首先需要创建一个ActionScript3的虚拟机。这个虚拟机可以实际执行ActionScript3代码。一般来说全局只需要一个,在任意需要执行ActionScrip3的地方都调用这个虚拟机来执行。
有两种方案,一种是将虚拟机保存在一个全局静态变量中,另一种是创建一个GameObject,使它一直存在于场景中。在这个GameObject上附加一个MonoBehaviour,让这个MonoBehaviour承载虚拟机。 可随时用GameObject.Find()来访问。
本Demo工程选用第二种方案。
在场景中有一个名叫AS3Player的GameObject。在下面挂载了ActionScriptStartUp.cs。ActionScriptStartUp是一个Monobehaviour。如前所述,此Monobehaviour承载了运行时,并且每帧都会调用ActionScript3的方法,以驱动逻辑更新。

以下代码可创建AS3运行时。

1
var flashplayer = new ASRuntime.Player();

以下代码用以从二进制字节数组中加载as3代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#if UNITY_ANDROID

WWW www = new WWW(Application.streamingAssetsPath + "/hotfix.cswc");
#else
WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/hotfix.cswc");
#endif
while (!www.isDone)
yield return null;
if (!string.IsNullOrEmpty(www.error))
UnityEngine.Debug.LogError(www.error);

//加载as3编译器生成的字节码。
//Loads the byte code generated by the compiler.
ASBinCode.CSWC swc = ASBinCode.CSWC.loadFromBytes(www.bytes);
www.Dispose();

ASRuntime.nativefuncs.BuildInFunctionLoader.loadBuildInFunctions(swc);

字节码可以保存在任意地方,您也可以从网络下载字节码。

以下代码开始加载API代码。由于可能导出了大量的API函数,因此加载时可以试用进度条。
使用协程功能,可以避免卡顿,和监测加载进度。

1
2
3
4
5
6
7
8
9
10
11
12
13
//**注册本地代码有可能非常之多。所以提供了一个进度条**
//API code can be very much. So a progress bar is provided
int functioncount = 0;
while (regenumerator.MoveNext())
{
functioncount++;
if (functioncount % 50 == 0)
{
progress.value = extfunctions.progress;
progressValue.text = "loading:" + extfunctions.progress * 100 + "%";
yield return null;
}
}

最后,需要让运行时加载二进制字节码,用以下代码进行加载:

1
flashplayer.loadCode(swc);

当运行时加载完成字节码后,即可调用运行时的一些方法,来对ActionScript3代码进行操作。
例如,createInstance()可用来创建一个ActionScript3对象,getMethod则可以访问对象的某个方法,等等。
我们创建 Main.as 的一个对象实例,然后找到它的update方法,并保存起来。每帧都执行一次此实例的update方法,即可推动逻辑更新。

1
2
main = flashplayer.createInstance("Main");
updatemethod= flashplayer.getMethod(main, "update");

在MonoBehaviour的Update()方法中,调用运行时驱动ActionScript3代码。

1
2
3
4
5
6
void Update () {
if (player != null)
{
player.invokeMethod(main, updatemethod);
}
}

ActionScript3部分

现在我们来看ActionScript3部分的代码。
本Demo的逻辑功能都定义在Main类中。当上面创建Main的实例时,构造函数将会被调用。
构造函数中,创建了100个立方体到场景中。
然后,给场景中的按钮追加事件处理函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var cube:UObject = GameObject.find("Cube");
for (var i:int = 0; i < 100; i++)
{
var c2:GameObject = GameObject.createPrimitive( PrimitiveType.Cube); //UObject.instantiate__(cube) as GameObject;
MeshRenderer( c2.getComponent(MeshRenderer)).material = MeshRenderer( GameObject( cube).getComponent(MeshRenderer)).material;

c2.transform.position = new Vector3( Random.range(-5,5),Random.range(0,5),Random.range(-5,5) );

cubes.push(c2);

mvs.push( new Vector3(Random.range( -5, 5), Random.range(-5, 5), Random.range( -5, 5)) );
mvs[mvs.length - 1].normalize();

//if (i > 10)
//{
//c2.setActive(false);
//
//}

}

var btn:Button = Button( GameObject.find("Button").getComponent(Button));

btn.onClick.addListener(
onclick
);

Main类有实例方法update。在Unity工程中,每帧都会驱动此方法,这个方法更新立方体们的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public function update():void
{
if (isstop)
return;

for (var i:int = 0; i < 100; i++)
{

var cube:GameObject = cubes[i];
var v:Vector3 = mvs[i];

var vv:Vector3 = new Vector3(4, 5, 6);

cube.transform.localPosition += v * Time.deltaTime;

var p:Vector3 = cube.transform.localPosition;
if (p.x <-5 || p.y < -5 || p.z < -5 || p.x > 5 || p.y > 5 || p.z > 5)
{
mvs[i] =-mvs[i];
}

//var k:NavMesh;
//k = cube;
}

}

您可以点击场景中的按钮,观察事件的处理。

update方法中,可以看到使用了大量的new Vector3操作。由于Vector3是一个结构体。在C#中,方法内部new结构体不会导致GC。
Apple Juice 脚本引擎同样实现了这个效果,因此,update代码是不会导致内存分配的。

nogc