在工作中,我们虽然有Flascc可以将C++代码编译为AS3,但是实际在工作中,该方法有诸多不便,比如调试非常麻烦,基于GDB的命令行调试对于不熟悉的人来说简直是噩梦,扩展开发、编译配置等也存在非常高的门槛,不利于整体一致性开发和部署,这样就存在需要将C++的算法进行AS3的移植,这里记录了一些坑和注意事项,和大家进行分享。
1.对应基础类型

2.内存操作

3.数组操作
1 2 3 4 5 6
| unsigned char* pa = &cont.verts[(cont.nverts-2)*4]; unsigned char* pb = &cont.verts[(cont.nverts-1)*4]; if ((int)pb[3] == r) { 。。。。。。 }
|
正确的翻译为
1 2 3 4 5 6
| Var pa : int = (cont.nverts-2)*4; Var pb : int = (cont.nverts-1)*4; if (int(cont.verts[pb+3] == r)) { 。。。。。。 }
|
4.类型转换
1
| pb[1] = (unsigned char)y => pb[1] = uint(y)
|
5.函数回值
函数的返回值对于基础类型,在调用函数中可能被改变,例如
1 2 3 4 5 6 7 8 9 10 11
| static void projectPoly(const float* axis, const float* poly, const int npoly, float& rmin, float& rmax) { rmin = rmax = dtVdot2D(axis, &poly[0]); for (int i = 1; i < npoly; ++i) { const float d = dtVdot2D(axis, &poly[i*3]); rmin = dtMin(rmin, d); rmax = dtMax(rmax, d); } }
|
正确的翻译为:
1 2 3 4 5 6 7 8 9 10 11
| public static function projectPoly(axis : Vector.<Number>, axisIndex : int, poly : Vector.<Number>, polyIndex : int , npoly : int, rmin : Number, rmax : Number):Object { rmin = rmax = dtVdot2D(axis, axisIndex, poly, polyIndex); for (var i : int = 1; i < npoly; ++i) { var d : Number = dtVdot2D(axis, 0, poly, i*3); rmin = dtMin(rmin, d); rmax = dtMax(rmax, d); } return {"rmin":rmin,"rmax":rmax}; }
|
6.序列化
C++的序列化可以直接将对象的内存导出和导入,但是AS3不可以,所以只能自己写fromBytes和toBytes的方式,并且自己手工拼流。
1 2 3 4 5 6 7 8 9
| dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize); float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize); dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize); d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load. dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize); float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize); unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize); dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize); dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
|
其中dtGetThenAdvanceBufferPointer是获取指向该数据的指针,这里翻译需要改造该函数
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public static function dtGetThenAdvanceBufferPointer(bindingMemoryDatas : Vector.<MemoryBinding>, __className : String, __bytes : ByteArray, __index : int, __size : int):Object { var __result : Object; var i : int; switch(__className) { case "float": { __result = new Vector.<Number>(__size/4); for(i = 0; i < __size / 4; i++) { __result[i] = 0; } break; } case "UnsignedChar": { __result = new Vector.<uint>(__size); for(i = 0; i < __size; i++) { __result[i] = 0; } break; } case "dtMeshHeader": { __result = new dtMeshHeader(); break; } case "dtPoly": { __result = new Vector.<dtPoly>(__size/dtPoly.bytesSize); for(i = 0; i < __size/dtPoly.bytesSize; i++) { __result[i] = new dtPoly(); } break; } case "dtPolyDetail": { __result = new Vector.<dtPolyDetail>(__size/dtPolyDetail.bytesSize); for(i = 0; i < __size/dtPolyDetail.bytesSize; i++) { __result[i] = new dtPolyDetail(); } break; } case "dtBVNode": { __result = new Vector.<dtBVNode>(__size/dtBVNode.bytesSize); for(i = 0; i < __size/dtBVNode.bytesSize; i++) { __result[i] = new dtBVNode(); } break; } case "dtOffMeshConnection": { __result = new Vector.<dtOffMeshConnection>(__size/dtOffMeshConnection.bytesSize); for(i = 0; i < __size/dtOffMeshConnection.bytesSize; i++) { __result[i] = new dtOffMeshConnection(); } break; } } bindingMemoryDatas.push(new MemoryBinding(__className, __bytes, __index, __size, __result)); return __result; }
|
PS:原作者表达的含义将这一堆的数据顺序写入内存一个连续区域,这个内存块其实是个配置文件,可以方便导出整个内存块,注意这其中使用了一个很特殊的MemoryBinding的AS3对象,当AS3对象改变的时候,对应序列化的结果也会发生改变,这时候如果后面有需要读取这个内存块的情况下,需要手动再次调用序列化的方式来更新连续内存。
7.模板
AS3中除了Vector均不可支持模板操作,这里需要使用泛型,如果需要确定类型,需要手工进行识别。不过好在大多模板都和数组连用,基本可以使用Vector翻译,但是Map之类就需要自己判断类型了。
8.重载操作符
使用function的方式实现,将操作符改成函数方式,但是需要注意左右项
9.多态
多函数名自己手工改变调用。
10.自定义数组
这次移植过程中遇到一个作者自己封装的IntArray数组,看字面意思是个int数组,改变数据长度的时候不改变数据本身,可以做到快速回收,但是遇到了一个不知道为啥这么写的坑
1 2 3 4
| IntArray aaa = [1, 1, 2]; aaa.length = 0; aaa.length = 3; function(aaa[0])……
|
原本使用Vector.来替代
1 2 3 4
| var aaa : Vector.<int> = Vector.<int>([1,1,2]); aaa.length = 0; aaa.length = 3; function(aaa[0])……
|
结果最后aaa[0]的值为0,而原始代码的值为1,这里就需要特别谨慎,之后改用类封装后解决该问题。