[开发者心得] 随机生成系统

[复制链接]
854 |0
夏季凉风 发表于 2023-8-4 16:42:57 | 显示全部楼层 |阅读模式

一、示例:
20230804-144822.gif


二、一分钟上手:
1. 在服务端 使用配置和刷新时间 初始化 FindItemBaseS:
new FindItemBaseS<FindItemType>(FindInfo, 3000);


2. 在客户端 使用,分别用旋转对象,资源 guid,拾取距离,看到距离,和获取位置的方法初始化 FindItemBaseC:
new FindItemBaseC<RotationFind>(RotationFind, "162052", 100, 5000, this.syncKey, (id: number) => {
        return FindInfo.find((item) => { return item.id === id; })?.location;
});



三、原理:
       后端根据配置的 id,按一定刷新间隔,随机生成一个物体,后端只产生数据 id,不产生对象。然后通过事件通知到前端,前端按 id 获取对应id的位置,生成单端物体。并按300 的间隔,检查拾取距离和是否每帧更新。前端拾取到物体后,会通过事件通知到后端。前端生成的物体会用对象池保存。


四、实现方式:

20230804-155417.jpg


1. 后端配置初始化:因为一个 id 位置只有一个物体,所以我们将配置初始化成一个数组,作为隐藏列表。显示时就删除,并添加到显示数组中。隐藏就删除显示列表,添加到隐藏列表中。

public setList(list: T[], isClear = false) {
        isClear && this.clear();
        this._list = list || [];
        this.showList.length = 0;
        this.hideList.length = 0;
        this._list.forEach(e => {
            this.hideList.push(e.id);
        })
    }


2. 生成一个 find 元素:后端生成和删除物体都通过事件通知到前端。可能会有多个 find 对象,之间使用唯一 key 即 syncEventKey 区分。

private createItem() {
        if (this.hideList.length <= 0) {
            return;
        }
        const index = Math.floor(Math.random() * this.hideList.length);
        const id = this.hideList[index];
        this.hideList.splice(index, 1);
        this.showList.push(id);
        this.onCreate && this.onCreate(id);
        Events.dispatchToAllClient(this.syncEventKey, [id]);
    }


3. 前端生成元素:前端将所有生成的对象,放到网格中,只更新距离自己近的网格,节省性能,同时用对象池管理生成物体。

Events.addServerListener(this.syncEventKey, async (ids: number[]) => {
            await this.ready.ready();
            ids.forEach(id => {
                if (this._itemBook.has(id)) {
                    return;
                }

                // 生成物体
                let posTemplate = this.getPos(id);
                if (posTemplate) {
                    const go = this._itemPool.get();
                    const pos = go.worldLocation = posTemplate.clone();
                    const meshItem = new this._itemCls(id, go, this.checkFind);
                    this._mesh.addItem(meshItem, pos.x, pos.y, this.lookFar, this.lookFar);
                    this._itemBook.set(id, meshItem);
                    const show = this._bEnable ? Type.PropertyStatus.On : Type.PropertyStatus.Off;
                    meshItem.go.setVisibility(show, true)
                    this.onShow && this.onShow(id, go);
                }
            });
        })


4. 前端分了两个update,一个是按游戏帧率每帧更新,用于出来动画,如旋转等。一个是按时间更新,用于检查距离计算。
     游戏逻辑帧更新旋转
逻辑帧做旋转动画
public frameUpdate(dt: number) {
        this.nowRotation.z += dt * 100;
        this.go.worldRotation = this.nowRotation;
    }



定时帧做距离检测
private checkFind = (dt: number, tag: T, tagX: number, tagY: number, srcX: number, srcY: number) => {
        if (!this._bEnable) {
            return;
        }
        if (this._far >= (tagX - srcX) * (tagX - srcX) + (tagY - srcY) * (tagY - srcY)) {
            if (!this.canPick || this.canPick(tag.id)) {
                this._itemPool.hide(tag.go);
                this.onHide && this.onHide(tag.id, tag.go);
                this._itemBook.delete(tag.id);
                this._mesh.removeItem(tag);
                Events.dispatchToServer(this.syncEventKey, tag.id);
            }
        }
    }


5. 拾取后通知到后端,并由后端通知所有客户端

private findItem(player: Gameplay.Player, id: number) {
        const idx = this.showList.indexOf(id);
        if (idx < 0) {
            return;
        }
        this.showList.splice(idx, 1);
        this.hideList.push(id);
        this.onFind && this.onFind(player, id);

        Events.dispatchToClient(player, this.syncEventKey + "_find", id);
        Gameplay.getAllPlayers().forEach(p => {
            if (p != player) {
                Events.dispatchToClient(p, this.syncEventKey + "_clear", [id]);
            }
        });
    }


五、demo
demoFind.zip (91.88 KB, 下载次数: 51)
回复

使用道具 举报

72小时热榜
热门版块
快速回复 返回顶部 返回列表