场景服务会处理绝大部分的游戏逻辑。新建service/scene/init.lua,开始编写相关代码。
1、Ball类
场景中包含小球和食物这两种对象,先看看小球的实现。代码如下所示:
--球
local balls = {} --[playerid] = ball
function ball()
local m = {
playerid = nil,
node = nil,
agent = nil,
x = math.random( 0, 100),
y = math.random( 0, 100),
size = 2,
speedx = 0,
speedy = 0,
}
return m
end
- 首先定义balls表和ball类,balls表会以玩家id为索引,保存战场中各个小球的信息;
- 小球与玩家关联,它会记录玩家id(playerid)、 代理服务(agent)的id、代理服务所在的节点(node);
- 每个球都包含x坐标、y坐标和尺寸这三种属性(x, y, size),以及移动速度speedx和speedy;
- 玩家进入战场会新建ball对象,并为其赋予随机的坐标。
下图展示了ball类一些属性的含义
定义辅助方法balllist_msg,代码如下所示:
--球列表
local function balllist_msg()
local msg = {"balllist"}
for i, v in pairs(balls) do
table.insert( msg, v.playerid )
table.insert( msg, v.x )
table.insert( msg, v.y )
table.insert( msg, v.size )
end
return msg
end
它会收集战场中的所有小球,并构建balllist协议。
2、Food类
完成了小球类,再看看食物类。定义如下代码
所示的
foods
表和 food类。
local foods = {} --[id] = food
local food_maxid = 0
local food_count = 0
--食物
function food()
local m = {
id = nil,
x = math.random( 0, 100),
y = math.random( 0, 100),
}
return m
end
- 食物类food包含id、x坐标、y坐标这三种属性;
- 表foods会以食物id为索引,保存战场中各食物的信息;
- 为给食物赋予唯一id,定义变量food_maxid,其初始值为0,每创建一个食物,给food_maxid加1;
- 变量food_count用于记录战场中食物数量,以限制食物总量。
定义辅助方法foodlist_msg,代码如下所示:
--食物列表
local function foodlist_msg()
local msg = {"foodlist"}
for i, v in pairs(foods) do
table.insert( msg, v.id )
table.insert( msg, v.x )
table.insert( msg, v.y )
end
return msg
end
它会收集战场中的所有食物,并构建foodlist协议
3、进入战斗
下图
展示了进入战斗的流程,
agent
收到
enter
协议(开始比赛, 图中阶段①
)后,随机选择一个
scene
服务,给它发送
enter
消息(稍后实现,见图中阶段②
)。
scene
和客户端的所有交互,都以
agent
作为中介。
现在看看scene服务的内容,定义的enter远程调用。代码如下所示:
--进入
s.resp.enter = function(source, playerid, node, agent)
if balls[playerid] then
return false
end
local b = ball()
b.playerid = playerid
b.node = node
b.agent = agent
--广播
local entermsg = {"enter", playerid, b.x, b.y, b.size}
broadcast(entermsg)
--记录
balls[playerid] = b
--回应
local ret_msg = {"enter",0,"进入成功"}
s.send(b.node, b.agent, "send", ret_msg)
--发战场信息
s.send(b.node, b.agent, "send", balllist_msg())
s.send(b.node, b.agent, "send", foodlist_msg())
return true
end
- 参数playerid指玩家id;
- 参数agent和node指玩家对应的代理服务id及其所在的节点;
- 参数source是消息的发送方,它等同于agent。
实现了如下几项功能:
(1)判定能否进入战斗场景:如果玩家已在战场内,不可再次进入,返回失败信息(false)。
(2)创建ball对象:创建玩家对应的ball对象,并给各个属性赋值。
(3)向战场内的其他玩家广播enter协议,说明新的玩家到来 (broadcast方法稍后实现)。
(4)将ball对象存入balls表。
(5)向玩家回应成功进入的信息(enter协议),此处使 用“s.send(....,"send"....,”向agent发送消息,agent相关处理会稍后实现。
(6)向玩家发送战场信息(涉及balllist协议和foodlist协议)。
定义
辅助方法
broadcast
,代码如下:
--广播
function broadcast(msg)
for i, v in pairs(balls) do
s.send(v.node, v.agent, "send", msg)
end
end
用于广播协议。它会遍历balls表,把消息发送给每个玩家。
4、退出战斗
当玩家掉线时,agent
会远程调用
scene
服务的
leave
方法(稍后实现)。leave
远程调用方法,代码如下所示:
--退出
s.resp.leave = function(source, playerid)
if not balls[playerid] then
return false
end
balls[playerid] = nil
local leavemsg = {"leave", playerid}
broadcast(leavemsg)
end
它会删除与玩家对应的小球(设置balls列表对应id为空),并广播leave协议。
5、操作移动
当玩家要改变移动方向时,客户端会发送shift
协议,经由
agent
转发(稍后实现),调用scene
的
shift
方法。实现
shift
远程调用方法,代码如下所示:
--改变速度
s.resp.shift = function(source, playerid, x, y)
local b = balls[playerid]
if not b then
return false
end
b.speedx = x
b.speedy = y
end
它根据参数playerid找到与玩家对应的小球,并设置它的速度。