> 文章列表 > 【从零开始学Skynet】实战篇《球球大作战》(十二):场景代码设计(上)

【从零开始学Skynet】实战篇《球球大作战》(十二):场景代码设计(上)

【从零开始学Skynet】实战篇《球球大作战》(十二):场景代码设计(上)

        场景服务会处理绝大部分的游戏逻辑。新建service/scene/init.lua,开始编写相关代码。

1、Ball类

   场景中包含小球和食物这两种对象,先看看小球的实现。代码如下所示:
--球
local balls = {} --[playerid] = ballfunction 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
  • 首先定义ballsballballs会以玩家id为索引,保存战场中各个小球的信息;
  • 小球与玩家关联,它会记录玩家id(playerid、 代理服务(agentid、代理服务所在的节点node
  • 每个球都包含x坐标、y坐标和尺寸这三种属性x, y, size,以及移动速度speedxspeedy;
  • 玩家进入战场会新建ball对象,并为其赋予随机的坐标

 下图展示了ball一些属性的含义

 

   定义辅助方法balllist_msg,代码如下所示:
--球列表
local function balllist_msg()local msg = {"balllist"}for i, v in pairs(balls) dotable.insert( msg, v.playerid )table.insert( msg, v.x )table.insert( msg, v.y )table.insert( msg, v.size )endreturn 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包含idx坐标y坐标这三种属性;
  • foods会以食物id为索引,保存战场中各食物的信息;
  • 为给食物赋予唯一id,定义变量food_maxid,其初始值为0,每创建一个食物,给food_maxid1
  • 变量food_count用于记录战场中食物数量,以限制食物总量。

  定义辅助方法foodlist_msg,代码如下所示:

--食物列表
local function foodlist_msg()local msg = {"foodlist"}for i, v in pairs(foods) dotable.insert( msg, v.id )table.insert( msg, v.x )table.insert( msg, v.y )endreturn msg
end

   它会收集战场中的所有食物,并构建foodlist协议

 3、进入战斗

       下图展示了进入战斗的流程,agent收到enter协议(开始比赛, 图中阶段①)后,随机选择一个scene服务,给它发送enter消息(稍后实现,见图中阶段②)。scene和客户端的所有交互,都以agent作为中介。

 

 现在看看scene服务的内容,定义的enter远程调用。代码如下所示:

--进入
s.resp.enter = function(source, playerid, node, agent)if balls[playerid] thenreturn falseendlocal b = ball()b.playerid = playeridb.node = nodeb.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
  • 参数agentnode指玩家对应的代理服务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) dos.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] thenreturn falseendballs[playerid] = nillocal leavemsg = {"leave", playerid}broadcast(leavemsg)
end

 它会删除与玩家对应的小球(设置balls列表对应id为空),并广播leave协议。

 5、操作移动

        当玩家要改变移动方向时,客户端会发送shift协议,经由agent转发(稍后实现),调用sceneshift方法。实现shift远程调用方法,代码如下所示:
--改变速度
s.resp.shift = function(source, playerid, x, y)local b = balls[playerid]if not b thenreturn falseendb.speedx = xb.speedy = y
end

 它根据参数playerid找到与玩家对应的小球,并设置它的速度。

完整代码下载:https://gitee.com/frank-yangyu/ball-server