AS3,即ActionScript3.0,flash页游时代的遗产。随着各大浏览器围剿flash,以及H5的兴起,AS3以及过时了。不过,AS3还是很重要的,毕竟flash页游时代的所有游戏项目都是用AS3开发的,现在主流的H5游戏引擎,比如Laya,其早期版本也是以AS3语言为准的。但AS3开发H5毕竟只是一种过渡方案,新版的Laya已经全面转向TS。这就带来一个问题,如何便捷高效地把AS3项目转换为TS版本?
网上的工具用起来扎心啊
AS3和TS的语法是很相似的,这意味着想把AS3代码“粗糙”地转换为TS代码并不困难。此前网上也有很多工具,但这些工具都存在很多痛点,包括某Egret自带的转换工具。比如:
-
仅仅是简单的语法格式替换,比如去掉AS3的
var
和function
关键字,修改package
为module
或namespace
等等。这种机械式地翻译会有一堆bug,正如这款工具的“善意提示”:
image.png
-
AS3的
this
指针是可以缺省的,而绝大多数工具只是简单的机械翻译,导致转换后的TS代码有无数的error。对于一个大型项目来说,几千个error令人崩溃。
比如:
TsScripts/System/scene/SceneModule.ts:623:13 - error TS2663: Cannot find name 'continueQuestOrPinstance'. Did you mean the instance member 'this.continueQuestOrPinstance'?
- AS3相同文件夹下的类是不需要显示
import
的,于是转换后的TS代码会有无数以下error:
TsScripts/System/scene/SceneModule.ts:967:43 - error TS2304: Cannot find name 'ProtocolUtil'.
庆幸的是,github上有一款工具完美地解决了以上痛点!这款工具叫as2ts-smart。
as2ts-smart
使用环境
Node.js
安装
npm i as2ts-smart -g
用法
简单模式
as2ts-smart -s E:\\asproj\\src\\ --dist E:\\tsproj\\src\\
高级模式
as2ts-smart -s E:\\asproj\\src\\ --dist E:\\tsproj\\src\\ -r E:\\rule.json
as2ts-smart是一款通用的工具,高级模式的具体使用方法参考github上的说明:https://github.com/Halliwood/as2ts/
其最强大的功能是会把所有缺省的this
指针一个不落地加上,不管是自身的成员变量和方法,还是父类的变量和方法,而且会把所有缺省的import
语句补充完整。
先来看看as2ts-smart官方例子的转换效果,以下AS例子在https://github.com/Halliwood/as2ts/tree/master/example/test/asproj/src
AS原代码
// example\test\asproj\src\human\EnumGender.as
package human {
public class EnumGender {
public static const Boy: int = 1;
public static const Girl: int = 2;
}
}
// example\test\asproj\src\human\Human.as
package human {
public class Human {
// 变量类型将被替换
protected var _name: String;
protected var _age: int;
protected var _gender: int;
public function Human() {
super();
}
public function set age(value: int): void {
// 这里缺省了this指针
if(value < _age) {
// trace将被替换为console.log
trace("I become younger");
} else if(value > _age) {
trace("I become oldder");
}
_age = value;
}
public function hello(): void {
trace("I'm " + _name + ".");
}
public function howOldAreYou(): void {
trace("I'm " + _age);
}
public function passToDo(callback: Function): void {
callback();
}
}
}
// example\test\asproj\src\human\Male.as
package human {
// 同目录下的Human的import缺省了,as2ts-smart将智能import
public class Male extends Human {
public function Male() {
super();
// as2ts-smart将识别出_gender是父类的属性并智能添加this指针
_gender = EnumGender.Boy;
}
}
}
// example\test\asproj\src\human\Female.as
package human {
public class Female extends Human {
public function Female() {
super();
// as2ts-smart将识别出_gender是父类的属性并智能添加this指针
_gender = EnumGender.Girl;
}
}
}
// example\test\asproj\src\Main.as
package {
import human.Female;
import human.Human;
import human.Male;
public class Main {
private var mike: Male;
private var lily: Female;
// Vector类型翻译
private var people: Vector.<Human> = new Vector.<Human>();
public function Main() {
// as2ts-smart将智能添加this指针
mike = new Male();
lily = new Female();
people.push(mike, lily);
doSomething();
mike.passToDo(function(lily: Male):void {
// 此处的lily是匿名函数的参数,不会添加this指针
lily.howOldAreYou();
})
}
private function doSomething(): void {
// for each语句翻译
for each(var hm in people) {
hm.hello();
}
}
}
}
转换后的TS代码
// example\test\asproj\src\human\EnumGender.ts
export class EnumGender {
public static Boy: number = 1;
public static Girl: number = 2;
}
// example\test\asproj\src\human\Human.ts
export class Human {
protected _name: string;
protected _age: number;
protected _gender: number;
public constructor() {
}
public set age(value: number) {
if(value < this._age ) {
console.log("I become younger");
} else if(value > this._age ) {
console.log("I become oldder");
}
this._age = value;
}
public hello(): void {
console.log("I'm " + this._name + ".");
}
public howOldAreYou(): void {
console.log("I'm " + this._age);
}
public passToDo(callback: Function): void {
callback();
}
}
// example\test\asproj\src\human\Male.ts
import {Human} from "./Human";
import {EnumGender} from "./EnumGender";
export class Male extends Human {
public constructor() {
super();
this._gender = EnumGender.Boy;
}
}
// example\test\asproj\src\human\Female.ts
import {Human} from "./Human";
import {EnumGender} from "./EnumGender";
export class Female extends Human {
public constructor() {
super();
this._gender = EnumGender.Girl;
}
}
// example\test\asproj\src\Main.ts
import {Female} from "./human/Female";
import {Human} from "./human/Human";
import {Male} from "./human/Male";
export class Main {
private mike: Male;
private lily: Female;
private people: Array<Human> = [];
public constructor() {
this.mike = new Male();
this.lily = new Female();
this.people.push(this.mike, this.lily);
this.doSomething();
this.mike.passToDo(function(lily: Male): void {
lily.howOldAreYou();
});
}
private doSomething(): void {
for(let hm of this.people) {
hm.hello();
}
}
}
从上述例子可以看到,as2ts-smart能智能判断是否成员变量、函数并准确无误地添加this
指针和import
,并且可以完美翻译foreach
/for...in
/Vector.<xxx>
等语句。转换后的代码可以直接使用,无须手动修改。
使用as2ts-smart将Laya1.0+AS3项目改造为Laya2.0+TS项目
很简单,安装as2ts-smart后只需一句命令即可。先从github上下载一份配置(https://github.com/Halliwood/as2ts/blob/master/example/laya1upto2.json
),存放到任意位置,比如E:/laya1upto2.json
假设AS3代码目录为laya1-as3/src
,把生成的TS代码放到laya2-ts/src
,则使用以下命令:
as2ts-smart -s laya1-as3/src --dist laya2-ts/src -r E:/laya1upto2.json
这里对laya1upto2.json的配置稍作解释:
{
"skipRule": { // 配置哪些目录可以跳过翻译
"dirs": ["^\\bui\\b"] // laya1.0生成的ui类不需要翻译,跳过ui目录
},
"idReplacement": { // 配置类名替换,除此还可以配置变量名、函数名等替换
"Laya.Component": "Laya.UIComponent", // laya2.0没有Component类了
"laya.ui.Component": "Laya.UIComponent",
"Laya.RaycastHit": "Laya.HitResult", // laya2.0没有HitResult类了
"Laya.StandardMaterial": "Laya.BlinnPhongMaterial" // laya2.0没有StandardMaterial类了
},
"importRule": { // 配置哪些import语句换成module.xxx的形式
"fromModule": [
{"module": "Laya", "regular": "^laya/" }, // 把laya1.0的laya.xxx import语句去掉,换成Laya.xxx
{"module": "ui", "regular": "^ui/", "import": "ui/layaMaxUI" } // 把laya1.0的ui.xxx换成2.0的写法,2.0需要import "ui/layaMaxUI"
]
},
// 把2.0用到的引擎声明.d.ts文件放到tsLibs里,as2ts-smart根据这些信息智能添加this指针
"tsLibs": ["E:/qhgame/tsproj/libs/LayaAir.d.ts", "E:/qhgame/tsproj/libs/layaAir.minigame.d.ts", "E:/qhgame/tsproj/src/ui/layaMaxUI.ts"],
"module": false, // 不生成模块
"errorDetail": true, // 翻译异常时输出详细信息
"terminateWhenError": true, // 翻译异常时终止翻译,建议处理完异常后继续
"continueLast": false, // 从上次中断翻译处继续翻译
"tmpRoot": "tmp/" // 临时文件目录
}
as2ts-smart是根据分析不同类之间的关系来确定是否需要添加this
指针的,如果使用了第三方库,需要把库声明文件加入到tsLibs
的配置项中,以免遗漏this
指针。
网友评论