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

1、主循环

        《球球大作战》是一款服务端运算的游戏,一般会使用主循环程序结构,让服务端处理战斗逻辑。如下图所示,图中的 balls foods代表服务端的状态,在循环中执行 食物生成”“位置更新碰撞检 测” 等功能,从而改变服务端的状态。 scene 启动后,会开启定时器,每隔一段时间(0.2 秒)执行一次循环,在循环中会处理食物生成、位置更新等功能。

 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
  • 它会调用skynet.fork开启一个协程,协程的代码位于匿名函数中。
  • pcall是为安全调用update而引入的,它的功能可以参照前面的xpcall
  • waittime 代表每次循环后需等待的时间。
  • 由于程序有可能卡住,我们很难保证“ 每隔 0.2 秒调用一次 update” 是精确的。
  • update 方法也需要一定的执行时间,等待时间waittime 的实际值应为 0.2减去执行时间,如下左图所示,图中 update 前的竖直黑线代表 update 的执行时间。
  • 若某次执行时间超过间隔(如下右图 0.2 秒执行的 update ),则程序需要加快执行,只能给很短的间隔时间。使得运行较长时间后,最终会在第N 秒执行 N×5 update

2、移动逻辑 

   服务端要处理的第一项业务功能是球的移动,现在实现 move_update 方法:
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协议通知所有客户端即可。

3、生成食物 

服务端会每隔一小段时间放置一个新食物,定义 food_update 方法来实现该功能:
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
这段代码做了如下几件事情:
  • 判断食物总量:场景中最多能有50个食物,多了就不再生成;
  • 控制生成时间:计算一个 0 100 的随机数,只有大于等于 98 才往下执行,即往下执行的概率是1/50 。由于主循环每 0.2 秒调用一次 food_update,因此平均下来每 10 秒会生成一个食物。
  • 生成食物:创建 food 类型对象 f ,把它添加到 foods 列表中,并广播addfood 协议。生成食物时,会更新食物总量 food_count 和食物最大标识food_maxid

 4、吞下食物

编写吃食物的 eat_update 方法:
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 协议,并让食物消失(设置 foods 对应值为 nil )。

 代码中变量名的含义如下:

  • pid:即playerid,指遍历到的小球对应的玩家id
  • b:遍历到的ball对象。
  • fid:遍历到的食物id
  • f :遍历到的 food 对象。

 说明:本篇的场景服务代码更多的是为了演示如何使用框架, 没有很多性能考究。比如在eat_update代码中,双重嵌套for循环的计算量较大。在实际项目中,往往会使用一些简化的计算方法(后面会有简单的描述)。

 至此,完成了场景服务的所有代码。

5、主服务修改

        假设服务端启动时就开启了多个战场。 现在修改主服务,让它开启scene服务。 新增的内容
如下代码 示:
--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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/11923.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

商城系统开发方案分析

互联网的不断发展&#xff0c;电商行业已经成为了当前最重要的商业形式之一。商城系统的开发也因此而备受关注。商城系统的开发是针对B2C、B2B2C等多种商业模式&#xff0c;如用户熟知的SHOP、商派等一系列商城系统&#xff0c;将商品和服务进行在线销售的一个综合性平台。那么…

【C语言进阶:动态内存管理】常见的动态内存错误

本节重点内容&#xff1a; 对NULL指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同一块动态内存多次释放动态开辟内存忘记释放&#xff08;内存泄漏&#xff09;经典的笔试题⚡对NULL指针的解引用操作 ⚡对动态…

Linux基础命令-seq打印数字序列

Linux基础命令-sed流编辑器 前言 seq命令通常是用来打印一串有规律的数字&#xff0c;常与其他命令搭配使用&#xff0c;一起来看下它的用法。 一. 命令介绍 在doc文档中查看seq命令的含义 NAMEseq - print a sequence of numbers DESCRIPTIONPrint numbers from FIRST to…

李宏毅教程系列——增强学习

目录 0. 强化学习wiki 1. 介绍 2. Exploration vs Exploitation 探索与开发 3. 各类最优化方法 3.1 Brute force猛兽蛮力法&#xff08;暴力搜索&#xff09; 3.2 Value function estimation&#xff08;价值函数估计&#xff09; 3.2.1 Monte Carlo methods 蒙特卡洛方…

3年经验,面试测试岗只会功能测试开口要求18K,令我陷入沉思。

由于朋友临时有事&#xff0c; 所以今天我代替朋友进行一次面试&#xff0c;公司需要招聘一位自动化测试工程师&#xff0c;我以很认真负责的态度完成这个过程&#xff0c; 大概近30分钟。 主要是技术面试&#xff0c; 在近30分钟内&#xff0c; 我与被面试者是以交流学习的方式…

【Linux】通过网络版计算器来认识协议

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;再谈协议&…

腾讯云4核8G12M轻量服务器配置性能评测

腾讯云轻量4核8G12M服务器&#xff0c;之前是4核8G10M配置&#xff0c;现在公网带宽和月流量包整体升级&#xff0c;12M公网带宽下载速度可达1536KB/秒&#xff0c;系统盘为180GB SSD盘&#xff0c;每月2000GB免费流量&#xff0c;腾讯云百科来详细说下4核8G12M轻量应用服务器配…

AJAX | 拦截器、文件上传和下载

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; AJAX Ajax即Asynchronous Javascript And XML&#xff08;异步JavaScript和XML&#xff09;&#xff1b;Ajax技术网页应用能够快速地将增量更新呈现在用户界面上&…

锁子甲 bulid+sim

链接: youtube 分析&#xff1a;洒一堆点——copy 模型——点和模型符合一定规律 点和点的距离符合上述图中的关系 &#xff08;横纵&#xff09; 横向 但是我们要横向10个点够了&#xff1a; 用modulo 除余 纵向 这里用除法向上取整 /10 eg &#xff1a; 0-9 得0 10-19 得1…

redis哨兵模式配置(配置文件等)

Redis-Sentinel机制主要用三个功能&#xff1a; (1)监控&#xff1a;不停监控Redis主从节点是否安装预期运行 (2)提醒&#xff1a;如果Redis运行出现问题可以 按照配置文件中的配置项 通知客户端或者集群管理员 (3)自动故障转移&#xff1a;当主节点下线之后&#xff0c;哨兵…

【版本控制】Github同步Gitee镜像仓库自动化脚本

文章目录Github同步Gitee镜像仓库自动化脚本前言什么是Hub Mirror Action&#xff1f;1.介绍2.用法配置步骤1.生成密钥对2.GitHub私钥配置3.Gitee公钥配置4.Gitee生成私人令牌5.Github绑定Gitee令牌6.编写CI脚本7.多仓库同步推送8.定时运行脚本总结Github同步Gitee镜像仓库自动…

【MyBatis Plus】002 -- 通用CRUD(插入、更新、删除、查询)

目录 3、通用CRUD 3.1 插入操作 3.1.1 方法定义 3.1.2 测试用例 3.1.3 测试 3.1.4 TableField 3.2 更新操作 3.2.1 根据id更新 3.2.2 根据条件更新 3.3 删除操作 3.3.1 根据id删除&#xff08;deleteById&#xff09; 3.3.2 根据Map删除数据&#xff08;deleteByMap&#xff09…

Level_2(2)题目整理

文章目录L2-022 重排链表&#xff08;模拟❗&#xff09;L2-023 图着色问题L2-024 部落(并查集)L2-025 分而治之&#xff08;与 L2-023差不多&#xff0c;邻接表遍历&#xff09;L2-026 小字辈&#xff08;求树的深度&#xff09;L2-027 名人堂与代金券(&#x1f4a1;处理&…

得物 API一站式协作平台的一些思考

1.背景 Mooncake是得物API一站式协作平台。从2022年3月份开始负责Mooncake&#xff0c;到现在已经一年了&#xff0c;回顾这一年&#xff0c;Mooncake大的阶段上&#xff0c;总共经历过两个版本: 1、Mooncake 1.0: 面向前端和客户端的mock平台&#xff0c;主要解决接口调用者…

C++实现前缀树

文章目录1. 什么是前缀树2. 前缀树的实现2.1 前缀树的基本结构2.2 插入2.3 word出现了几次2.3 word作为前缀出现几次2.4 删除1. 什么是前缀树 假设这里有一个字符串数组&#xff0c;和一个树的根结点&#xff1a; 这个结点的pass意思是&#xff1a;有几个字符通过了这个结点。…

ubuntu下Thrift安装

thrift是一种常用rpc框架&#xff0c;工作中经常会用到&#xff0c;本文记录一下其安装过程。 目录 1.下载软件包 1.1thrift下载 1.2libevent下载 1.3boost下载 2.安装&#xff08;注意步骤&#xff09; 2.1安装libevent 2.2安装boost 2.3安装与Python2.7版本对应的py…

【工作感悟】老程序员总结的四条工作经验教训

文章目录前言1. 不要做小需求2. 要做大需求3. 定期同步工作进度4. 项目结束&#xff0c;主动复盘总结前言 想来从事互联网工作已经很多年了&#xff0c;已经从当初的懵懂少年逐渐退化成老油条。刚毕业的时候&#xff0c;真是个愣头青&#xff0c;什么都不懂&#xff0c;也什么…

UE4 回放系统升级到UE5之后的代码报错问题解决

关键词&#xff1a; UE4 回放系统 升级 UE5 报错 DemoNetDriver GetDemoCurrentTime GetDemoTotalTime 背景 照着网上教的UE4的回放系统&#xff0c;也叫重播系统&#xff0c;英文Replay。做完了&#xff0c;测试运行正常&#xff0c;可升级到UE5却报了一堆 WorldSetting 和 …

计算机组成原理——第五章中央处理器

半生风雨半生伤&#xff0c;半醉半醒半心凉 文章目录前言5.1 CPU的功能和基本结构5.2 指令周期的数据流5.3.1 单总线结构5.3.2 专用通路结构前言 之前我们就说过CPU主要包括两个部分&#xff0c;运算器和控制器&#xff0c;运算器主要是实现算数运算.逻辑运算&#xff0c; 运算…

亲测:腾讯云轻量应用服务器性能如何?

腾讯云轻量应用服务器性能评测&#xff0c;轻量服务器CPU主频、处理器型号、公网带宽、月流量、Ping值测速、磁盘IO读写及使用限制&#xff0c;轻量应用服务器CPU内存性能和标准型云服务器CVM处于同一水准&#xff0c;所以大家不要担心轻量应用服务器的性能&#xff0c;腾讯云百…