[开发者心得] 单脚本检测rpc次数,为你的游戏保驾护航

[复制链接]
184 |1
山山山 发表于 2024-3-22 15:00:50 | 显示全部楼层 |阅读模式
本帖最后由 山山山 于 2024-3-22 15:00 编辑

1. 什么是基础性能接口

不会吧不会吧,不会还有人不知道口袋方舟在0.29的版本就推出了基础性能接口,可以通过这个接口查看游戏相关性能了吧


/**
     * @description 游戏性能数据,辅助 Debug 和性能优化
     * @groups UTILITY
     * @networkStatus usage:双端
     */
    class DebugUtil {
        /**
         * @description 帧时间是生成一帧游戏内容所花费的总时间。由于游戏线程和渲染线程在完成一帧之前保持同步,帧时往往接近其中一个线程中显示的时间。单位ms
         */
        static get frameTime(): number;
        /**
         * @description 对象在游戏线程中消耗的时间,包括脚本,动画,游戏逻辑等。单位ms。如果帧时接近游戏线程中显示的时间,则游戏的性能很可能会受到游戏线程的阻碍。单位ms。
         */
        static get gameThreadTime(): number;
        /**
         * @description 在渲染线程中处理这些对象的时间,受粒子,特效,网格等影响。单位ms。
         * @precautions 如果帧时接近Draw线程中显示的时间,则游戏的性能很可能会受到渲染线程的阻碍。单位ms。服务端该值为0。
         */
        static get renderThreadTime(): number;
        /**
         * @description 当前使用的总内存大小,单位MB。
         */
        static get usedMemory(): number;
        /**
         * @description 一秒内发出的网络包的总大小。单位 Byte
         */
        static get sentBytes(): number;
        /**
         * @description 一秒内收到的网络包的总大小。单位 Byte。
         */
        static get receivedBytes(): number;
        static tsEventRpcFuncs: string[];
        /**
         * @description 当前帧收到的RPC消息。
         */
        static get receivedRPCs(): string[];
        /**
         * @description 当前帧发送的RPC消息。
         */
        static get sentRPCs(): string[];
        /**
         * @description 当前帧缓存的RPC消息。
         */
        static get cachedRPCs(): string[];
    }



2. 单脚本统计rpc数量

有了这个性能接口,就可以统计游戏中的rpc数量了,要知道rpc如果使用不当,有可能会导致rpc栈溢出,玩家断线的情况。
下面是一个统计游戏中rpc数量的单脚本,可以统计固定时间内的rpc数量,并以MarkDown的格式打印出来,可以更直观的看到哪些rpc过多,从而进行优化。

@Component
export default class RpcStatistic extends Script{
    sentRpcs: Map<number,  { [key: string]: number } > = new Map();

    receivedRpcs: Map<number, {[key:string]: number}> = new Map();


    @Property({displayName: "检测时间段(s)"})
    checkTime: number = 30;

    timer: number = 0;

    curTimeRegion: number = 0;


    protected onStart(): void {
        this.useUpdate = true;
        if(SystemUtil.isClient()){
            InputUtil.onKeyDown(Keys.O,()=>{
                this.printRpcs();
            })
        }
    }

    protected onUpdate(dt: number): void {
        this.checkRpcs();
        this.timer += dt;
        if(this.timer >= this.checkTime){
            this.timer = 0;
            this.curTimeRegion+=this.checkTime;
        }
    }

    checkRpcs(){
        if(!this.sentRpcs.has(this.curTimeRegion)){
            this.sentRpcs.set(this.curTimeRegion, {});
        }
        if(!this.receivedRpcs.has(this.curTimeRegion)){
            this.receivedRpcs.set(this.curTimeRegion, {});
        }
        const curSends = this.sentRpcs.get(this.curTimeRegion);
        const curReceives = this.receivedRpcs.get(this.curTimeRegion);
        const sends = DebugUtil.sentRPCs;
        const received = DebugUtil.receivedRPCs;
        DebugUtil.cachedRPCs;
        for (let i = 0; i < sends.length; i++) {
            const rpc = sends[i];
            if(rpc.startsWith("Type:RPC")){return};
            curSends[rpc] = (curSends[rpc]?curSends[rpc]:0) + 1;

        }
        for (let i = 0; i < received.length; i++) {
            const rpc = received[i];
            if(rpc.startsWith("Type:RPC")){return};
            curReceives[rpc] = (curReceives[rpc]?curReceives[rpc]:0) + 1;
        }
    }

    @RemoteFunction(Client, Server)
    printRpcs() {
        let output = "# RPC数量图\n\n";
        this.sentRpcs.forEach((sentRpcs, timeRegion) => {
            output += `## 时间段:${timeRegion}s - ${timeRegion + this.checkTime}s\n\n`;
            const sentRpcsSorted = Object.entries(sentRpcs).sort((a, b) => b[1] - a[1]);
            output += "| 方法 | 发送的RPC数量 |\n";
            output += "| --- | --- |\n";
            sentRpcsSorted.forEach(([method, count]) => {
                output += `| ${method} | ${count}次 |\n`;
            });
   
            const receivedRpcs = this.receivedRpcs.get(timeRegion);
            if (receivedRpcs) {
                const receivedRpcsSorted = Object.entries(receivedRpcs).sort((a, b) => b[1] - a[1]);
   
                output += "\n| 方法 | 接收的RPC数量 |\n";
                output += "| --- | --- |\n";
                receivedRpcsSorted.forEach(([method, count]) => {
                    output += `| ${method} | ${count}次 |\n`;
                });
            }
            output += "\n";
        });
        console.log(output);
    }
}


3. 使用方法
a. 将这个脚本放到代码中,并在编辑器中拖到场景上;
b. 设置脚本检测时间段的大小,默认为30s;
c. 运行游戏,并对关注的内容进行游玩测试,待感觉测试的差不多的时候,点击O键,输出日志。该脚本记录了服务端和客户端的rpc,因此会在服务端及客户端都会输出。
d. 复制日志信息,保存为.md格式的文件,并在VScode中以markDown格式进行预览。
ed6724e6-6552-4025-8c57-5def0e4aa9f5.jpeg b9fdd790-f618-4555-8611-3adf67cd00ed.jpeg



回复

使用道具 举报

马桶人3.5 发表于 2024-3-25 22:43:48 | 显示全部楼层
非常好单脚本,使我的rpc旋转~
回复

使用道具 举报

热门版块
快速回复 返回顶部 返回列表