[开发者心得] 可以动态异步创建更换预制件的对象

[复制链接]
779 |2
夏季凉风 发表于 2023-4-19 10:29:31 | 显示全部楼层 |阅读模式
有时我们会碰到一个物体频繁更换模型的情况,比如:一块花田里的作物经常改变,自己的某个装饰,如车位上的汽车经常动态设置。如果异步创建管理不好,可能出现后创建的物体被先创建的物体覆盖的情况。我分享一个类,可以频繁创建更换预制件 ^_^

1. 定义一个异步物体类 AsyncObject
2. 在类里定义一个 公有属性 public go: Core.GameObject;。这个属性就是现在这个对象所创建出来的 物体。
3. 定义一个公有函数  public async setGuid(guid?: string): Promise<Core.GameObject>。用于外部每次设置需要异步加载的预制体的 guid,并异步返回创建的物体。

/**
     * 设置物体的 guid
     * @param guid 设置的 guid
     * @param initCall 生成完成的回调
     * @param initCallTag 生成完成的回调的调用对象
     * @returns
     */
    public async setGuid(guid?: string): Promise<Core.GameObject> {

        // 校验是否需要生成
        if (this._goGuid == guid) {
            return this.getGo();
        }

        this._goGuid = guid;
        if (this.go) {
            this.go.destroy();
            this.go = null;
        }
        if (!guid) {
            this.onCreateGo(null);
            return null;
        }

        // 生成物体
        let go = await Core.GameObject.asyncSpawnGameObject(guid);
        await go.ready();
        if (guid != this._goGuid) {
            go.destroy();
            return this.getGo();
        }

        // 校验物体是否需要释放
        if (guid != this._goGuid) {
            go.destroy();
            return;
        }
        if (this.go) {
            this.go.destroy();
        }

        // 将物体赋值给类成员
        this.go = go;
        this.onCreateGo(go);

        return go;
    }



4. 我们再考虑这个函数多次调用的情况:
5. 每次调用 setGuid,如果当前 guid没变,且是相同创建。哪我们就需要保存当前的 promise,等待这次创建结束后再调用。
6. 相同 guid的物体不能多次初始化,所以我们将初始化函数传入,就只有在创建的时候执行一次初始化。



    /**
     * 设置物体的 guid
     * @param guid 设置的 guid
     * @param initCall 生成完成的回调
     * @param initCallTag 生成完成的回调的调用对象
     * @returns
     */
    public async setGuid(guid?: string, initCall?: (go: Core.GameObject) => Promise<void>, initCallTag?: any): Promise<Core.GameObject> {

        // 校验是否需要生成
        if (this._goGuid == guid) {
            return this.getGo();
        }

        this._goGuid = guid;
        if (this.go) {
            this.go.destroy();
            this.go = null;
        }
        if (!guid) {
            this.onCreateGo(null);
            return null;
        }

        // 生成物体
        let go = await Core.GameObject.asyncSpawnGameObject(guid);
        await go.ready();
        if (guid != this._goGuid) {
            go.destroy();
            return this.getGo();
        }

        // 初始化物体
        if (initCall) {
            await initCall.call(initCallTag, go);
        }

        // 校验物体是否需要释放
        if (guid != this._goGuid) {
            go.destroy();
            return;
        }
        if (this.go) {
            this.go.destroy();
        }

        // 将物体赋值给类成员
        this.go = go;
        this.onCreateGo(go);

        return go;
    }


7. 我们再考虑加入设置显示隐藏的功能,这个功能将显示的数据和物体的具体显示分开。




    /**
     * 设置显隐
     */
    private _show: boolean = true;
    public get show() {
        return this._show;
    }
    public set show(isShow: boolean) {
        this._show = isShow;
        if (this.go) {
            this.go.setVisibility(isShow ? Type.PropertyStatus.On : Type.PropertyStatus.Off);
        }
    }


8. 创建出来了再设置一次显隐

image.png
9. 剩下的 onCreateGo 等函数 见楼下详细代码 Q.Q


回复

使用道具 举报

夏季凉风楼主 发表于 2023-4-19 10:31:44 | 显示全部楼层


export class AsyncObject {

    /** 物体 */
    public go: Core.GameObject;

    /** 物体的 guid */
    private _goGuid: string;
    /** 等待回调 */
    private _waitCall: ((go: Core.GameObject) => void)[] = [];


    /**
     * 设置显隐
     */
    private _show: boolean = true;
    public get show() {
        return this._show;
    }
    public set show(isShow: boolean) {
        this._show = isShow;
        if (this.go) {
            this.go.setVisibility(isShow ? Type.PropertyStatus.On : Type.PropertyStatus.Off);
        }
    }

    /**
     * 设置物体的 guid
     * @param guid 设置的 guid
     * @param initCall 生成完成的回调
     * @param initCallTag 生成完成的回调的调用对象
     * @returns
     */
    public async setGuid(guid?: string, initCall?: (go: Core.GameObject) => Promise<void>, initCallTag?: any): Promise<Core.GameObject> {

        // 校验是否需要生成
        if (this._goGuid == guid) {
            return this.getGo();
        }

        this._goGuid = guid;
        if (this.go) {
            this.go.destroy();
            this.go = null;
        }
        if (!guid) {
            this.onCreateGo(null);
            return null;
        }

        // 生成物体
        let go = await Core.GameObject.asyncSpawnGameObject(guid);
        await go.ready();
        if (guid != this._goGuid) {
            go.destroy();
            return this.getGo();
        }

        // 初始化物体
        if (initCall) {
            await initCall.call(initCallTag, go);
        }

        // 校验物体是否需要释放
        if (guid != this._goGuid) {
            go.destroy();
            return;
        }
        if (this.go) {
            this.go.destroy();
        }

        // 将物体赋值给类成员
        go.setVisibility(this._show ? Type.PropertyStatus.On : Type.PropertyStatus.Off);
        this.go = go;
        this.onCreateGo(go);

        return go;
    }

    /**
     * 获取当前的物体
     */
    public async getGo() {
        if (this.go || !this._goGuid) {
            return this.go
        } else {
            return new Promise<Core.GameObject>((resolve, reject) => {
                this._waitCall.push(resolve);
            });
        }
    }

    /**
     * 当前的物体初始化完成
     * @param go 设置的物体
     */
    protected async onCreateGo(go: Core.GameObject) {
        this._waitCall.forEach((v) => {
            v(go);
        });
        this._waitCall.length = 0;
    }

}



回复

使用道具 举报

kk 发表于 2023-4-19 13:03:29 | 显示全部楼层
6
回复

使用道具 举报

72小时热榜
2
【反馈】翻译异常
求助与反馈
11人已阅读
3
编辑器无法正常启动
求助与反馈
27人已阅读
5
邮件系统
游戏开发
67人已阅读
热门版块
快速回复 返回顶部 返回列表