本帖最后由 脑的研发记录 于 2023-8-16 19:46 编辑
击落广播功能——单脚本(含附件)
受论坛师傅的指点写的代码,现在分享。
总览:
第一部分:故事背景,文案初衷
第二部分:代码总思路
第三部分:代码与代码详解
第四部分:可搭载进广播系统的拓展模块链接(不定期更新)
有附件,编辑时编辑器版本:0.25.0.4
第一部分:故事背景
原来是做很简单的踩影子PVP小游戏,然后遇着了不会写击落广播的问题,全网浏览,求索未获,幸得论坛老师傅们出手相救,指点迷津,在下感激不尽。念无以为报,又见论坛暂无此类文案,便出此拙作,不似师傅们一语中的,多有啰啰嗦嗦,偏向新手,还请诸位师傅见谅。
原来也是想着是做一点有意思的东西,然后解答一下小时候的问题,就算是多年之后的回音吧。
念念不忘,必有回响!
我来兑现承诺哩,每做一个游戏,就公开设计思路和关键代码!
第二部分:代码总思路
老师傅的原话:
“1.服务器维护 map<playerid(就是唯一标识符),昵称>
2.玩家进游戏的时候客户端过几秒获取自己的昵称 然后rpc给服务器,服务器设置好map
3.连杀就是维护连杀信息组map,这个不用讲哈,反正每次要报连杀的时候,直接从服务器广播给所有玩家就可以了”
这也是基本思路,
不过遇着一点实际情况就是getPlayer()的追溯要求限制,需要另想出路:
把这几行代码粘贴到游戏脚本里,就能发现有报错,还有废弃函数标记,鼠标悬停几秒即可详情信息
let a = player.getPlayerID()
//这个是说是要废弃的接口,但是具体失效时间不明,
let newplayerA = getPlayer(a)
//这个是根据上面的playid来查找玩家,
//但是
let b = player.getUserId()
let newplayerB = getPlayer(b)
//这个userID的类型还是string,不是number,这样用getPlayer()代码会报错参数类型不符合而不能运行
于是我就找一点新的素材,当时也恰好是稍微接触一点数据结构,playerID和玩家可以组成一个map,
虽然map明确不支持Player类型,但是我只要玩家id和玩家昵称,玩家id和玩家连击,只要拿map做出这俩的查询表就行了。
然后茅塞顿开,再回去一看原来第一句就已经这样写了。
但觉得要rpc,写dispatchtoServer()还是觉得应该有新的办法,带宽还能再节省,代码还能再少写一点,playerID也更新一下变成UserID
正巧那个时候在该排行榜的代码,就有了Events.addPlayerJoinedListener(),来初始化map。
于是捯饬下来,就有了下面的思路:
1.map1<userID,玩家名称>,map2<userID,玩家连击数>,都在服务器模块声明并维护更新。
2.Events.addPlayerJoinedListener(),监听玩家进入,初始化map,
Events.addPlayerLeftListener()监听玩家离开,释放map中对应模块内存
就不用RPC了。
3.报连击的时候就Events.dispatchToAllClient()广播括号里面自定义的参数就行。
第三部分:代码与代码详解
完整代码&啰嗦的注释
interface deads {
name: string
killBy: string
killNum: number
}
// 这个声明类似c语言的结构体,
// 但是interface关键词下声明的变量还有其他特性。
// 但是我们这里只用到了它其中的那个类似于c结构体的特性
// import { RankMgr } from "../ranks/RankMgr"
// 这是加上排行榜系统用于同步排行榜信息,
// 但是目前在这一文案里我们不会用到这个模块下的函数
// RankMgr.Instance.AddScore(killer,1)
// 在第66行处的代码就是如上模块里的函数,注释掉,不影响广播
// 与之适配的广播系统在文案末尾推荐链接里会出现
@Core.Class
export default class killerData extends Core.Script {
protected onStart(): void {
if (SystemUtil.isServer()) {
let playerName: Map<string, string> = new Map()
// 新建玩家名称“playerName”,声明为Map的string与string对应类型,然后实例化一个 Map对象。
// 没有 new Map() 那一句编译器不会报错,但是存不住东西
// 这个playerNAame是用于玩家名字溯源,采集到被击落的玩家的userID然后在该map里查找name
let killsNum: Map<string, number> = new Map()
// 连杀记录,用于查找玩家名下的连杀记录
let onePersonsHave: Array<deads> = []
// 队列,push进,shift出,监听端push,死亡名单数组shift后转端口的格式发送
playerName.set("游戏姬","游戏姬")
killsNum.set("游戏姬",0)
// 这里给了一个样例,在GameUI里,点按钮会发送key,
// 然后在这里就手动添加了一个键key“游戏姬”举一个例子,
// 、实际使用时,key其实就发送所需的key就行,这个key的来源在后续更新帖子时加上链接补上说明
// 壹,初始化map:
// 1.Map获取玩家ID与名称然后存档
Events.addPlayerJoinedListener((player: Gameplay.Player) => {
setTimeout(() => {
playerName.set(player.getUserId(), player.character.characterName)
// set是存储,鼠标悬停,可以看到英文注释,
// 第一个空是类似于箱子名称,player.character.characterName存进箱子里,箱子用Userid标记
// 下面是日志检查能否输出那个存储了后缀名称为ook的玩家id
// playerName.set(player.getUserId(), player.getUserId() + "ook")
// let have = playerName.get(player.getUserId())
// console.log(have)
killsNum.set(player.getUserId(), 0)
}, 3000);
// console.log(killsNum.get(player.getUserId()))
// 这一处初始化无问题
})
// 2.map删除离开玩家的数据,节约服务器内存,也防止内存泄露,减轻服务器压力
Events.addPlayerLeftListener((player: Gameplay.Player) => {
playerName.delete(player.getUserId())
killsNum.delete(player.getUserId())
})
// 贰,战况收集:
Events.addClientListener("zhanKuang", (player: Gameplay.Player, killer: string) => {
// killsNum.set(player.getUserId(), 0)
// 被击落者的连击数清零
let a = killsNum.get(killer)
killsNum.set(killer, a + 1)
// console.log(killsNum.get(killer))
// 伤害来源连杀+1
onePersonsHave.push({
// 存储每一个被击落的客户端发来的被击落消息
name: player.character.characterName,
killBy: playerName.get(killer),
// 第一个空是类似于箱子名称,player.character.characterName存进箱子里,箱子用Userid标记
// 现在是在get里输入Userid,然后函数运行,函数本身变成player.character.characterName,存进变量killBy里
// 用于:存储查找到的伤害来源昵称
killNum: killsNum.get(killer)
// 存储查找到的伤害来源的连击数
})
// 目前不用管,先注释掉,这是拓展时接入其他模块再用的
// RankMgr.Instance.AddScore(killer, 1)
// 功能:向排行榜系统发送加分者,与加分,由于这部分代码在服务器服务器运行,
// 使用背景:rankMgr也是服务器运行的代码,所以这是在服务器内部的通信,尝试采用dispatchlocal函数,验证发现可行
})
// 叁,战况发送
let timer = setInterval(() => {
if (onePersonsHave.length > 0) {
let sendOneDeath: deads = onePersonsHave.shift()
// 队列中取出的一个元素存进sendOneDead里
Events.dispatchToAllClient("deadCast", sendOneDeath.name, sendOneDeath.killBy, sendOneDeath.killNum)
// 对所有客户端发送,被击落者,击落来源,连续击落数,广播战况,到各客户端的UI脚本里接收这几个参数。
}
}, 1000)
// 借助计时器,每秒都检查array数组,不为空时,广播一次战况
}
}
}
附件,解压粘贴到编辑器项目目录下,
运行点击按钮即可看到广播
测试文件.zip
(87.18 KB, 下载次数: 85)
|