将游戏核心代码从ts转rust转wasm的闭坑记录(支持微信小游戏)

背景

平常都是用ts写代码,目前ts转wasm还不成熟。就只能中间转一次
【go方案】:
go转wasm,包比较大,放弃
【rust方案】:(今天的尝试的)
包体积小(已兼容微信小程序!!)。

本文主要是探索,ts转rust的一些实践总结

环境

rustc 1.76.0 (07dca489a 2024-02-04)
包版本:
wasm-bindgen = “=0.2.76” // 这个注意要用=,之前就是这个版本引起不兼容微信小程序
js-sys = “0.3”
打包:
wasm-pack 0.13.1

bun 1.2.8 (这个是为了ts测试用的)

倒水游戏为实验对象

选了一个最简单的倒水游戏逻辑作为实验
因为是核心玩法,所以是不需要cocos环境的

让ai帮忙无缝将ts转rust的几点总结

开始的时候,我还是用平常的ts写法写逻辑,但是很快发现转出来的rust代码和ts差异很大
为了ts能无缝的转rust,记录了以下几点

先给出瓶子为例子的代码

这个是最开始的ts代码

// 瓶子状态
export enum BottleState {
    Empty,
    Some,
    Full,
    Close,
}
// 瓶子
export interface IBottle {
    bodys: number[]
    index: number
    state: BottleState
}

这个是为了无缝转rust后的代码

export class Bottle {
    private bodys: number[]
    private index: number
    private state: BottleState
    constructor(index: number) {
        this.index = index
        this.state = BottleState.Empty
        this.bodys = []
    }
    get_index(): number {
        return this.index
    }
    set_index(index: number): void {
        this.index = index
    }
    get_state(): BottleState {
        return this.state
    }
    set_state(state: BottleState): void {
        this.state = state
    }
    get_bodys(): number[] {
        return this.bodys
    }
    set_bodys(bodys: number[]): void {
        this.bodys = bodys
    }
}

你会发现以下几点差异

  • 1、rust函数方法命名都变为了小写加下划线

比如:getBodys() 需要改为 get_bodys()
(好家伙,我好不容易养成了写驼峰习惯了,又要倒退吗,算了还好只是转核心代码,忍了)

  • 2、数据结构偏向类
    比如:
    bottles: IBottle[] = [] // 原先只需抽象的数据
    变成了
    bottles: Bottle[] = [] // 现在必须是个类

  • 3、类属性的访问不行了,因为转wasm后没法直接获取属性,只能全传方法
    比如:
    原先获取瓶子bottle的bodys,只需要 bottle.bodys
    现在
    bottle.getBodys()

  • 4、获取对象需要转换输出,直接输出会导出ts代码和rust转出来的有差异,不能直接用
    需要通过JSON.parse(JSON.stringify(bottle))来转换
    如:

        public get_cur_bottle(): Bottle | null {
            if (this.cur_index == -1) {
                return null
            }
            // return this.bottles[this.cur_index] // 这个wasm在js中没法直接用
            return JSON.parse(JSON.stringify(this.bottles[this.cur_index]))
        }
    
  • 5、因为对象经过rust类后转成json对象,所以属性的顺序排序都的按字母顺序来,不然单元测试时用JSON.stringify出来的顺序不一致。如下面的bodys,index,state顺序

    export class Bottle {
        private bodys: number[]
        private index: number
        private state: BottleState
        ....
    }
    
  • 6、微信小程序的兼容,因为微信小程序里的WebAssembly是老版本的。需要低版本,如wasm-bindgen = “=0.2.76” 并且 在demo.ts中代码 :face_with_monocle: 找到下面代码的位置进行按图修改**

转rust

这个当然是请ai帮忙了^ ^,有了上面无缝对接的经验,相信你写出的ts可以顺利生成,无缝衔接的rust版本

rust代码和ts代码的对比

获取瓶子列表,这个rust版本

    // 获取所有瓶子
    pub fn get_bottles(&self) -> JsValue {
        // 将 Vec<Bottle> 转换为 JavaScript 数组
        let js_array = js_sys::Array::new();
        for bottle in &self.bottles {
            js_array.push(&JsValue::from(bottle.clone()));
        }
        js_array.into()
    }

这个是ts版本

    public get_bottles() {
        return JSON.parse(JSON.stringify(this.bottles))
    }

rust打包wasm

 wasm-pack build --target web --release --no-typescript

这个时候会生成一个pkg的文件,里面有demo_bg.wasm demo.js

如果是ts项目,需要将demo.js 改为demo.ts

测试

目前准备了4个环境

1、nodejs/bunjs 测试
2、浏览器端测试
3、cocos环境测试
4、微信小程序端测试

1、nodejs/bunjs 测试

通过 bun xxxx.ts 进行原始ts的测试

2、浏览器端测试 测试

3、cocos环境测试

4、微信小程序端测试

这里如果直接用cocos的会错误需要修改,demo.ts中代码 :face_with_monocle: 找到下面代码的位置进行按图修改


通过降低wasm-bindgen版本以及修改部分demo.ts的代码,终于解决了微信兼容问题

源码

最后附上实验源码
cocos_ts_to_wasm_daoshui.zip (2.5 MB)

9赞

想问一下,ts把算法,比如rvo2,碰撞检测,四叉树这些,转为wasm。 性能会提升吗?

我可以回答没试过吗,真的 :sweat_smile:

大佬,怎么把ts代码转wasm的,有什么教程吗。

上面有源码可以参考一下。ts转rust,目前是靠ai的,而且是不一定全部成功的。转出来的是函数方法名或者一些方法ai无法实现的会被忽略掉。并不是所有现有的ts都无缝能转rust的。

上面的例子是,“无缝能转rust”的ts写法的一些避坑点

好吧 :rofl: :rofl: :rofl:

牛逼啊!!!

那为什么不直接用ai把ts转成wasm

搞个类似的, 性能是会有提升, 但幅度大不大取决于你wasm输出处理, 最大的性能开销是把wasm的数据转给js, 比如你要wasm输出数组类数据, 那麽js会有一层拷贝处理。

这么做的好处是什么

计算量大的时候,用wasm,速度会提升.
如果颈瓶不在计算量的时候,没必要这么做.

好像接触过计算量大的游戏就割草类

这个主要看你的计算量,简单的计算提升并不明显;之前项目中大量数据计算用过,测试结果是10倍

少次数,大算量

性能提升这么多。那看来这碰撞检测这些用wasm会好了。

目前主要是为了核心代码的“二进制化”当加密源码使用 :rofl:。作者当前只会ts,不会rust,不过通过ai转写也学习到不少。

├── Cargo.lock
├── Cargo.toml
├── DaoShuiGameBox.ts // 游戏核心源码ts版本
├── cocos_project // cocos使用wasm项目
│ ├── assets
│ ├── package.json
│ ├── settings
│ └── tsconfig.json
├── index.html // 浏览器使用wasm
├── old_ts_version
│ └── DaoShuiGameBox.ts // 第一个ts版本(这个版本没法无缝转rust)
├── pkg
│ ├── demo.js // 打包出来的wasm的js使用文件(如果项目是ts,请将后缀改为ts)
│ ├── demo_bg.wasm // 打包出来的wasm
│ └── package.json
├── src
│ └── lib.rs // DaoShuiGameBox.ts 通过ai转换后的rust源码
└── tests
├── package.json
├── test001.test.ts // ts版本的单元测试文件,请用 bun test001.test.ts 进行测试
└── tools

嗯,目前共识就是将代码尽可能转成 wasm 或者其它可加载的二进制资源进行防破解是吗?