[开发者心得] getUserId接口的排行榜翻新,附加隐藏显示按钮——用Replicated制作排行榜

  [复制链接]
1027 |2
脑的研发记录 发表于 2023-8-16 13:26:24 | 显示全部楼层 |阅读模式
本帖最后由 脑的研发记录 于 2023-8-16 20:01 编辑

getUserId接口的排行榜翻新,附加隐藏显示按钮——用Replicated制作排行榜

所用编辑器版本0.25.0.4
工程附件在文末
先上图
image.png
总览:

零,修改背景
壹,一点思考想法
贰,排行榜代码
叁,相关推荐链接

零,修改背景
之前用的这位论坛师傅整的排行榜。
【RPC和Replicated】—— 用Replicated制作排行榜 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)


然后发现getPlayerID()会被维护掉会出现以下问题:
击落广播功能——单脚本(含附件) 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)


image.png


后来进行翻新,想法子解决问题,免得游戏更新了人还在上学没时间收拾,然后做了一点适配广播系统的修改,
把客户端发送到服务端的通信改成了服务端到服务端的通信
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)
回复

使用道具 举报

kk 发表于 2023-8-16 17:09:27 | 显示全部楼层
牛逼
回复

使用道具 举报

脑的研发记录楼主 发表于 2023-8-16 19:55:24 | 显示全部楼层
本帖最后由 脑的研发记录 于 2023-8-16 19:59 编辑

是靠谱同志哩,工程文件已更新
回复

使用道具 举报

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