一、示例:
二、一分钟上手:
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 的间隔,检查拾取距离和是否每帧更新。前端拾取到物体后,会通过事件通知到后端。前端生成的物体会用对象池保存。
四、实现方式:
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)
|