service/scene/init.lua中新增的内容:
function update(frame)
food_update()
move_update()
eat_update()
--碰撞略
--分裂略
end
现在思考一个问题,怎样开启稳定的定时器?可以开启一个死循环协程,协程中调用update,最后用skynet.sleep让它等待一小段时间。定义服务初始化方法init:
s.init = function()
skynet.fork(function()
--保持帧率执行
local stime = skynet.now()
local frame = 0
while true do
frame = frame + 1
local isok, err = pcall(update, frame)
if not isok then
skynet.error(err)
end
local etime = skynet.now()
local waittime = frame*20 - (etime - stime)
if waittime <= 0 then
waittime = 2
end
skynet.sleep(waittime)
end
end)
end
function move_update()
for i, v in pairs(balls) do
v.x = v.x + v.speedx * 0.2
v.y = v.y + v.speedy * 0.2
if v.speedx ~= 0 or v.speedy ~= 0 then
local msg = {"move", v.playerid, v.x, v.y}
broadcast(msg)
end
end
end
由于主循环会每隔0.2秒调用一次move_update,因此它只需遍历场景中的所有球,根据“路程 = 速度 × 时间”计算出每个球的新位置,再广播move协议通知所有客户端即可。
function food_update()
if food_count > 50 then
return
end
if math.random( 1,100) < 98 then
return
end
food_maxid = food_maxid + 1
food_count = food_count + 1
local f = food()
f.id = food_maxid
foods[f.id] = f
local msg = {"addfood", f.id, f.x, f.y}
broadcast(msg)
end
function eat_update()
for pid, b in pairs(balls) do
for fid, f in pairs(foods) do
if (b.x-f.x)^2 + (b.y-f.y)^2 < b.size^2 then
b.size = b.size + 1
food_count = food_count - 1
local msg = {"eat", b.playerid, fid, b.size}
broadcast(msg)
foods[fid] = nil --warm
end
end
end
end
代码中变量名的含义如下:
说明:本篇的场景服务代码更多的是为了演示如何使用框架, 没有很多性能考究。比如在eat_update代码中,双重嵌套for循环的计算量较大。在实际项目中,往往会使用一些简化的计算方法(后面会有简单的描述)。
至此,完成了场景服务的所有代码。
--scene (sid->sceneid)
for _, sid in pairs(runconfig.scene[mynode] or {}) do
local srv = skynet.newservice("scene", "scene", sid)
skynet.name("scene"..sid, srv)
end
说明: 为简单起见,演示程序会开启固定数量的场景服务。在 实际项目中,可以仿照agent 动态开启场景服务。
完整项目地址:https://gitee.com/frank-yangyu/ball-server