本帖最后由 脑的研发记录 于 2023-8-16 20:01 编辑
getUserId接口的排行榜翻新,附加隐藏显示按钮——用Replicated制作排行榜
所用编辑器版本0.25.0.4
工程附件在文末
先上图
总览:
零,修改背景
壹,一点思考想法
贰,排行榜代码
叁,相关推荐链接
零,修改背景
之前用的这位论坛师傅整的排行榜。
【RPC和Replicated】—— 用Replicated制作排行榜 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
然后发现getPlayerID()会被维护掉会出现以下问题:
击落广播功能——单脚本(含附件) 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
后来进行翻新,想法子解决问题,免得游戏更新了人还在上学没时间收拾,然后做了一点适配广播系统的修改,
把客户端发送到服务端的通信改成了服务端到服务端的通信
Events.dispatchToServer("reqAddScore", value)
变成了
Events.dispatchLocal("reqAddScore", killer, value)
壹,一点思考想法
1.把所有的number类型的playerID变成string类型,
2.解决getplayer()需要number类型的playerID,
于是就用map,不过由于这个排行榜和广播系统绑定了,
map就不增加了,而是随用随从广播系统里Events.dispatchLocal("reqAddScore", killer, value)发送到这里
不多说,上代码:
贰,排行榜代码
PS.老师傅的代码注释没有删去,鄙人用于自学理解的代码注释也混入其中,所以只讲与原排行榜不同的部分
即进行修改的部分。
附件的移植说明在最后有统一总结
1.在新增了玩家名称变量:playerName
ScoreSyncScript 脚本的代码改动:
新增变量playerName,用@core装饰器,不启用回调onchanged
import { RankMgr } from "./RankMgr";
@Core.Class
export default class ScoreSyncScript extends Core.Script {
@Core.Property({ replicated: true, onChanged: "onPlayerChange" })
public playerID: string = "";
@Core.Property({ replicated: true, onChanged: "onScoreChange" })
public score: number = -1;
@Core.Property({ replicated: true })
public playerName: string = "";
//新增部分playerName,不onChanged回调函数,
// /**初始化属性数据(如果同一帧触发了多个不同的onChanged函数,是不能保证其时序性的,所以我这里根据不同字段做了延迟操作,保证时序性) */
public serverInit(playerID: string, playerName: string, score: number) {
this.playerID = playerID
this.playerName = playerName
setTimeout(() => {
// 同步分数(因为同步分数的时候,会触发刷新逻辑,需要使用到UI和角色对象,所以要稍微慢一秒,等待角色和UI初始化完毕)
this.score = score
}, 1000);
}
2.由于Wts My Name 更新玩家名称需要两秒,于是通过获取玩家名称来赋值的脚本就得延时,这里延时为3秒
RankMgr脚本部分的初始化代码改动:
1.player.getPlayerID()统一替换为play.getUserId()
2.script.serverInit()函数增加参数string,即存储player.character.characterName用
PS.在上传的文件当中,demo额外加了几行,单独初始化了一个同步脚本,注释“这是针对本次点击按钮试验而额外添加的代码.......”,
如果实际应用,去掉这四行代码,就可以在排行榜中不显示“游戏姬”及其相关分数。
private registJoinedAndLeftListenerEvent() {
// 服务端监听玩家进入/离开房间
if (SystemUtil.isServer()) {
// 玩家上线,创建一个分数同步脚本
Core.Script.spawnScript(ScoreSyncScript).then((script) => {
script.serverInit("游戏姬", "游戏姬", 0)
this.scoreSyncScriptList.push(script);
})
// 这是针对本次点击按钮试验而额外添加的代码,在真实使用中,这上面四行代码注释掉即可。
Events.addPlayerJoinedListener(async (player: Gameplay.Player) => {
await Core.Script.spawnScript(ScoreSyncScript).then((script) => {
// 初始化同步脚本,同时进行一次上线同步
setTimeout(() => {
script.serverInit(player.getUserId(), player.character.characterName, 0)
// 必须延迟在命名之后
this.scoreSyncScriptList.push(script);
}, 3000);
// 将同步脚本添加到列表中,方便管理
})
})
// 玩家下线,将该玩家分数同步脚本移除
Events.addPlayerLeftListener((player: Gameplay.Player) => {
// 删除服务器的分数同步脚本
this.deleteScriptByPlayerID(player.getUserId())
// 告诉所有客户端,这个玩家下线了,需要删除他在客户端的分数同步脚本
Events.dispatchToAllClient("DeleteScriptByPlayerID", player.getUserId())
})
}
}
3.替换RPC,变成addLocalListner,同时内部参数也变成如图所示的killer,value,这里是完成给攻击来源加分的功能。
注意其中一行注释“通过遍历来找到伤害来源的同步脚本”这就是说,killer 实际上是UserID。所以使用的时候,传进去的是UserID而不是玩家昵称。
/**注册RPC事件 */
private registRPCEvent() {
if (SystemUtil.isServer()) {
// 监听客户端发来的增加分数请求
Events.addLocalListener("reqAddScore", (killer: string, value: number) => {
// 原位置是接受客户端的消息,现在变成接受服务器其他脚本的消息。
for (let script of this.scoreSyncScriptList) {
if (script.playerID == killer) {
// 通过遍历id来找到伤害来源的同步脚本
script.serverAddScore(value)
}
// console.log("manager" + this.scoreSyncScriptList.length)
}
})
}
if (SystemUtil.isClient()) {
// 监听服务端发来的删除同步脚本请求
Events.addServerListener("DeleteScriptByPlayerID", (playerID: string) => {
this.deleteScriptByPlayerID(playerID)
})
}
}
RPC得改一对儿,把接收端处理了,再解决发送端,外部函数名不变,内部改成如图所示。这个函数会在“击落广播”的贰,战况收集代码块的最后部分出现,
这个函数也基本上是服务器处理完其他系统之后使用,把处理得到的赋分结果给到排行榜系统进行加分。
这个AddScore的killer实际上就是UserID,这个函数安插到服务器的其他系统的时候,记得第一个参数是UserID,第二个参数是加的分数,可以自己设定加几分。
/**向服务端内部文件模块间请求增加分数 */
public AddScore(killer: string, value: number) {
Events.dispatchLocal("reqAddScore", killer, value)
// 把客户端与服务器端通信变成服务器内部的文件之间的通信。
}
4.RankMgr的dataList中的PlayerID由number变成string
而且dataList.push,放进去的不是PlayerID,而是之前scoreSyncScript新增的PlayName.
public tellUIFresh() {
// 申明一个列表,存储玩家的分数数据
let dataList: { "playerID": string, "score": number }[] = []
// 从分数同步脚本上获取数据(这个脚本是在本地的,所以可以随取随用,不用发RPC)
for (let script of this.scoreSyncScriptList) {
if (script) {
dataList.push({ "playerID": script.playerName, "score": script.score })
// console.log("mgr"+script.playerName)
}
}
// 排序
dataList.sort((a, b) => { return b.score - a.score })
// 通知UI刷新
Events.dispatchLocal("FreshByData", dataList)
}
3.RankItemUI的修改
PS.Author是别人,鄙人只是适配修改,如对该文案有修改问题还请先联系在下,暂时缘此不打扰老师傅了
由于dataList,的字符串成员存的是玩家名称,所以直接在RankItemUI脚本里更新mRank_txt,这个就是排行榜的每条玩家的名字
/*
* @Author: ao001.wu ao001.wu@appshahe.com
* @Date: 2023-03-19 18:24:47
* @LastEditors: ao001.wu ao001.wu@appshahe.com
* @LastEditTime: 2023-03-20 11:32:47
* @FilePath: \ReplicatedDemo\JavaScripts\UI\RankItemUI.ts
* @Description:
*/
import RankItemUI_Generate from "../../ui-generate/RankItemUI_generate"
export class RankItemUI extends RankItemUI_Generate {
/**根据数据刷新显示 */
public async freshByData(index: number, playerID: string, score: number) {
this.mRank_txt.text = (index + 1).toString()
if (playerID) {
this.mName_txt.text = playerID
}
this.mScore_txt.text = score.toString()
}
}
4.显示隐藏按钮
这个是做跑酷游戏的时候觉得还是有隐藏按钮视野开阔,打算在踩影子PVP中给加上显示隐藏按钮
/**
* AUTHOR: 脑的研发记录
* TIME: 2023.08.12-18.31.34*/
import rankClose_Generate from "../ui-generate/rankClose_generate";
import { RankUI } from "./UI/RankUI";
export default class rankbtn extends rankClose_Generate {
/**
* 构造UI文件成功后,在合适的时机最先初始化一次
*/
protected onStart() {
//设置能否每帧触发onUpdate
this.canUpdate = false;
this.layer = UI.UILayerMiddle;
this.btn.text = "隐藏"
let show :boolean = true;
this.btn.onPressed.add(()=>{
if(show){
UI.UIManager.instance.hide(RankUI)
show = false
this.btn.text = "显示"
}else {
UI.UIManager.instance.show(RankUI)
show = true;
this.btn.text = "隐藏"
}
})
}
}
附件与附件说明:
广播与排行榜组合.zip
(123.67 KB, 下载次数: 88)
|