跨服1v1PK匹配逻辑该怎么写
🤖 回答引擎: 后端Lua |
🕒 时间: 2026-05-20 19:17:28 |
👁️ 阅读: 0
我是lua后端老师!
小仙,想实现跨服1v1的PK匹配功能是吧?这个想法很酷,但需要咱们仔细规划一下。根据跨服的特点,我们需要把逻辑拆分成**本服部分**和**跨服部分**来写。
**核心思路:**
1. **本服**负责收集报名玩家,并尝试匹配。
2. 匹配成功后,**本服**通过 `bfbackcall` 通知**跨服**创建比赛房间和传送玩家。
3. **跨服**负责具体的比赛地图管理、胜负判定和奖励发放。
下面我给你一个基础的逻辑框架和关键代码示例:
### 第一步:本服匹配逻辑 (写在本服的脚本里,比如NPC或QF)
这个部分负责收集玩家,并尝试将两个玩家配对。
```lua
-- 假设我们用一个全局表来存储等待匹配的玩家
-- 键:玩家UserID, 值:{角色名, 报名时间等}
local waitingQueue = {}
-- 玩家点击报名按钮触发的函数
function signUpForPK(actor)
local userID = getbaseinfo(actor, 2) -- 获取玩家UserID
local roleName = getbaseinfo(actor, 1) -- 获取角色名
-- 检查是否已在队列中
if waitingQueue[userID] then
sendmsg(actor, "您已经在匹配队列中,请耐心等待!", 2)
return
end
-- 将玩家加入等待队列
waitingQueue[userID] = {
name = roleName,
signTime = os.time()
}
sendmsg(actor, "已加入1v1跨服PK匹配队列!", 2)
-- 触发匹配尝试
tryMatchPlayers()
end
-- 尝试匹配玩家的函数
function tryMatchPlayers()
local queueList = {}
-- 将等待队列转换成数组方便操作
for uid, info in pairs(waitingQueue) do
table.insert(queueList, {uid = uid, info = info})
end
-- 如果至少有2个人,则进行匹配
if #queueList >= 2 then
-- 这里用简单的先到先得规则,取前两个人
local player1 = queueList[1]
local player2 = queueList[2]
-- 从等待队列中移除这两位玩家
waitingQueue[player1.uid] = nil
waitingQueue[player2.uid] = nil
release_print("匹配成功:", player1.info.name, "vs", player2.info.name)
-- **关键步骤:通知跨服服务器准备比赛**
-- 使用bfbackcall,id设为1(代表匹配成功消息)
-- parama 传递玩家1的UserID,paramb传递玩家2的UserID
bfbackcall(1, "0", player1.uid, player2.uid) -- 用系统对象"0"发送
-- 继续检查是否还能匹配
tryMatchPlayers()
end
end
-- 一个简单的定时器,定期清理等待时间过长的玩家(可选)
function cleanTimeoutQueue()
local now = os.time()
local timeout = 300 -- 5分钟超时
for uid, info in pairs(waitingQueue) do
if now - info.signTime > timeout then
waitingQueue[uid] = nil
local player = getplayerbyid(uid)
if player then
sendmsg(player, "匹配超时,已退出队列。", 2)
end
end
end
end
```
### 第二步:跨服比赛逻辑 (写在跨服的 `QFunction-0.lua` 的 `kflogin` 或相关触发里)
跨服端接收到本服的匹配通知后,负责创建比赛实例。
```lua
-- 在跨服QF中,接收本服的匹配通知
-- 对应 bfbackcall 的 id=1
function bfsyscall1(actor, player1_uid, player2_uid)
-- actor 在这里是系统对象,player1_uid和player2_uid是字符串
release_print("跨服收到匹配请求:", player1_uid, "vs", player2_uid)
-- 1. 为这场比赛生成一个唯一ID(例如使用时间戳)
local matchId = tostring(os.time())
-- 2. 将两个玩家传送到跨服的PK地图
-- 假设我们有一个专门用于1v1的跨服地图,地图参数带 Kuafu
local pkMapName = "K001"
local x1, y1 = 10, 10 -- 玩家1出生点
local x2, y2 = 90, 90 -- 玩家2出生点
-- 通知本服将玩家传送到跨服地图(这里需要再发消息回本服,或者由本服在发送bfbackcall后自行传送)
-- 更常见的做法是:本服在发送bfbackcall后,立即将玩家传送到跨服入口地图(带Kuafu参数的地图),
-- 然后跨服在kflogin触发里根据规则再将玩家分配到具体的比赛房间。
-- 3. 设置比赛状态,记录到跨服的变量中(例如使用G变量)
local matchInfoKey = "PKMatch_" .. matchId
setsysvar(matchInfoKey .. "_Player1", player1_uid)
setsysvar(matchInfoKey .. "_Player2", player2_uid)
setsysvar(matchInfoKey .. "_StartTime", os.time())
setsysvar(matchInfoKey .. "_Status", "fighting") -- 状态:fighting, finished
-- 4. 可以在这里给玩家发送开始提示
sendmsgnew(player1_uid, "比赛开始!您的对手是" .. player2_uid, 2) -- 需要本服转发或跨服有玩家对象
sendmsgnew(player2_uid, "比赛开始!您的对手是" .. player1_uid, 2)
release_print("比赛", matchId, "已创建。")
end
-- 在跨服QF中,处理玩家击杀(判断胜负)
function killplay(actor, killedPlayer)
-- actor: 击杀者, killedPlayer: 被击杀者
local killerUid = getbaseinfo(actor, 2)
local killedUid = getbaseinfo(killedPlayer, 2)
release_print("跨服击杀事件:", killerUid, "击杀了", killedUid)
-- 遍历查找正在进行中的比赛,判断这两人是否在同一场比赛中
-- 这里简化处理,实际需要更严谨的映射关系管理(比如用表记录玩家所在比赛ID)
local matchId = findMatchByPlayers(killerUid, killedUid)
if matchId then
-- 比赛结束,killerUid 获胜
endMatch(matchId, killerUid, killedUid)
end
end
-- 辅助函数:根据玩家查找比赛ID (这里需要你根据存储的比赛信息来实现)
function findMatchByPlayers(uid1, uid2)
-- 示例:遍历所有G变量键名以"PKMatch_"开头的比赛信息
-- 实际项目中,建议用更高效的数据结构管理
for i = 1, 100 do -- 假设最多100场同时比赛
local key = "PKMatch_" .. i
local p1 = getsysvar(key .. "_Player1")
local p2 = getsysvar(key .. "_Player2")
local status = getsysvar(key .. "_Status")
if p1 and p2 and status == "fighting" then
if (p1 == uid1 and p2 == uid2) or (p1 == uid2 and p2 == uid1) then
return i
end
end
end
return nil
end
-- 结束比赛,发放奖励
function endMatch(matchId, winnerUid, loserUid)
local key = "PKMatch_" .. matchId
setsysvar(key .. "_Status", "finished")
setsysvar(key .. "_Winner", winnerUid)
setsysvar(key .. "_EndTime", os.time())
-- 发放奖励(跨服只能给予物品,不能修改人物属性)
local winnerPlayer = getplayerbyid(winnerUid)
if winnerPlayer then
giveitem(winnerPlayer, "金币", 1000000) -- 奖励金币
sendmsg(winnerPlayer, "恭喜你赢得1v1跨服PK赛!", 2)
end
local loserPlayer = getplayerbyid(loserUid)
if loserPlayer then
sendmsg(loserPlayer, "很遗憾,你输掉了比赛。", 2)
end
-- 比赛结束一段时间后,将玩家传送回本服(可以在跨服设置一个定时器)
-- 或者由本服在收到比赛结束消息后,使用 kfbackcall 通知跨服将玩家踢回
release_print("比赛", matchId, "结束。胜者:", winnerUid)
end
```
### 第三步:本服与跨服通信完善
你需要在本服的 `QFunction-0.lua` 中处理跨服发回的消息,比如比赛结束后将玩家接回。
```lua
-- 在本服QF中,接收跨服发回的玩家比赛结束消息
-- 假设跨服用 kfbackcall, id=2 发送比赛结束通知
function kfsyscall2(actor, matchId, winnerUid)
release_print("本服收到比赛结束通知:", matchId, "胜者:", winnerUid)
-- 1. 将还在跨服地图的玩家传送回本服安全区
-- 2. 可以在这里发放本服独有的奖励(如称号、积分等)
local winnerPlayer = getplayerbyid(winnerUid)
if winnerPlayer then
-- 检查玩家是否在跨服地图,是则传回
if checkkuafu(winnerPlayer) then
mapmove(winnerPlayer, "0", 330, 330) -- 传回土城
end
-- 给予本服奖励
changeexp(winnerPlayer, 1000000) -- 奖励经验
sendmsg(winnerPlayer, "欢迎回到本服,这是你获胜的额外奖励!", 2)
end
end
```
### 总结与建议
1. **状态管理**:这是最复杂的部分。你需要清晰地管理“等待匹配”、“比赛中”、“已结束”等状态,并在本服和跨服之间同步关键信息。
2. **玩家映射**:需要建立 `玩家 <-> 比赛场次` 的映射关系,以便快速查找。
3. **超时与异常处理**:考虑玩家掉线、匹配超时、比赛过程中掉线等情况。
4. **数据存储**:使用 `setsysvar`/`getsysvar` (G变量) 或自定义的全局表来存储匹配队列和比赛信息。注意,跨服不保存自定义全局变量,所以跨服的信息要用G/A变量或文件存储。
5. **通信协议**:定义好 `bfbackcall` 和 `kfbackcall` 的 `id` 和参数含义,形成一套简单的通信协议。
6. **测试**:先在单服测试匹配逻辑,再搭建跨服环境测试完整流程。
小仙,这个框架给你搭好了,但每个游戏的规则(如匹配算法、地图、奖励)都不同,你需要根据实际情况填充细节。先从简单的“两人匹配即开始”做起,慢慢增加功能。过程中有不懂的,随时来问我或者请教浮生梦尊师!
← 返回问答库