【从零开始学Skynet】实战篇《球球大作战》(十三):场景代码设计(下)
1、主循环
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 = 0while true doframe = frame + 1local isok, err = pcall(update, frame)if not isok thenskynet.error(err)endlocal etime = skynet.now()local waittime = frame*20 - (etime - stime)if waittime <= 0 thenwaittime = 2endskynet.sleep(waittime)endend)
end
- 它会调用skynet.fork开启一个协程,协程的代码位于匿名函数中。
- pcall是为安全调用update而引入的,它的功能可以参照前面的xpcall。
- waittime 代表每次循环后需等待的时间。
- 由于程序有可能卡住,我们很难保证“ 每隔 0.2 秒调用一次 update” 是精确的。
- update 方法也需要一定的执行时间,等待时间waittime 的实际值应为 0.2减去执行时间,如下左图所示,图中 update 前的竖直黑线代表 update 的执行时间。
- 若某次执行时间超过间隔(如下右图 第 0.2 秒执行的 update ),则程序需要加快执行,只能给很短的间隔时间。使得运行较长时间后,最终会在第N 秒执行 N×5 次 update 。
2、移动逻辑
function move_update()for i, v in pairs(balls) dov.x = v.x + v.speedx * 0.2v.y = v.y + v.speedy * 0.2if v.speedx ~= 0 or v.speedy ~= 0 thenlocal msg = {"move", v.playerid, v.x, v.y}broadcast(msg)endend
end
由于主循环会每隔0.2秒调用一次move_update,因此它只需遍历场景中的所有球,根据“路程 = 速度 × 时间”计算出每个球的新位置,再广播move协议通知所有客户端即可。
3、生成食物
function food_update()if food_count > 50 thenreturnendif math.random( 1,100) < 98 thenreturnendfood_maxid = food_maxid + 1food_count = food_count + 1local f = food()f.id = food_maxidfoods[f.id] = flocal 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、吞下食物
function eat_update()for pid, b in pairs(balls) dofor fid, f in pairs(foods) doif (b.x-f.x)^2 + (b.y-f.y)^2 < b.size^2 thenb.size = b.size + 1food_count = food_count - 1local msg = {"eat", b.playerid, fid, b.size}broadcast(msg)foods[fid] = nil --warmendendend
end
代码中变量名的含义如下:
- pid:即playerid,指遍历到的小球对应的玩家id。
- b:遍历到的ball对象。
- fid:遍历到的食物id。
- f :遍历到的 food 对象。
说明:本篇的场景服务代码更多的是为了演示如何使用框架, 没有很多性能考究。比如在eat_update代码中,双重嵌套for循环的计算量较大。在实际项目中,往往会使用一些简化的计算方法(后面会有简单的描述)。
至此,完成了场景服务的所有代码。
5、主服务修改
--scene (sid->sceneid)
for _, sid in pairs(runconfig.scene[mynode] or {}) dolocal srv = skynet.newservice("scene", "scene", sid)skynet.name("scene"..sid, srv)
end 说明: 为简单起见,演示程序会开启固定数量的场景服务。在 实际项目中,可以仿照agent 动态开启场景服务。
完整项目地址:https://gitee.com/frank-yangyu/ball-server
相关文章:
【从零开始学Skynet】实战篇《球球大作战》(十三):场景代码设计(下)
1、主循环 《球球大作战》是一款服务端运算的游戏,一般会使用主循环程序结构,让服务端处理战斗逻辑。如下图所示,图中的 balls 和 foods代表服务端的状态,在循环中执行“ 食物生成”“位置更新”和“碰撞检 测” 等功能࿰…...
2023年虚拟数字人行业研究报告
第一章 行业概况 虚拟数字人指存在于非物理世界中,由计算机图形学、图形渲染、动作捕捉、深度学习、语音合成等计算机手段创造及使用,并具有多种人类特征(外貌特征、人类表演能力、人类交互能力等)的综合产物。虚拟人可分为服务型…...
Oracle 之表的连接类型——舞蹈跳出
嵌套循环(Nested Loops Join) Oracle 中最基本的连接方法,用于处理数据表之间的连接操作。 嵌套循环是通过对其中一个表(外部表)进行全循环操作,然后针对每条记录在另一张表(内部表)…...
深入浅出JS定时器:从setTimeout到setInterval
前言 当谈到 JavaScript 编程语言最基本的概念时,定时器就是一个必须掌握的知识点。在编写网站时,你经常会遇到需要在一定时间间隔内执行一些代码的情况。这时候,JavaScript 定时器就可以派上用场了。 什么是定时器? JS 定时器是…...
CountDownLatch、CyclicBarrier、Semaphore 的原理以及实例总结
文章目录 CountDownLatch、CyclicBarrier、Semaphore 的原理以及实例总结一、CountDownLatch二、CyclicBarrier三、Semaphore总结 CountDownLatch、CyclicBarrier、Semaphore 的原理以及实例总结 在Java多线程编程中,有三种常见的同步工具类:CountDownL…...
企业电子招投标系统源码之了解电子招标投标全流程
随着各级政府部门的大力推进,以及国内互联网的建设,电子招投标已经逐渐成为国内主流的招标投标方式,但是依然有很多人对电子招投标的流程不够了解,在具体操作上存在困难。虽然各个交易平台的招标投标在线操作会略有不同࿰…...
SpringCloud之Gateway组件简介
网关的理解 网关类似于海关或者大门,出入都需要经过这个网关。别人不经过这个网关,永远也看不到里面的东西。可以在网关进行条件过滤,比如大门只有对应的钥匙才能入内。网关和大门一样,永远暴露在最外面 不使用网关 前端需要记住每…...
GoNote第三章 主流框架加对比
GoNote第三章 主流框架加对比 Golang主流框架介绍 自从面市以来,Golang成为了程序员在编写API和开发Web服务时的首选之一。近90%的受访者表示会在自己下一组项目中持续使用Golang。与我们熟悉的C和C类似,Go语言也是现有Golang的“灵魂”。而Golang则是…...
Quartz框架详解分析
文章目录 1 Quartz框架1.1 入门demo1.2 Job 讲解1.2.1 Job简介1.2.2 Job 并发1.2.3 Job 异常1.2.4 Job 中断 1.3 Trigger 触发器1.3.1 SimpleTrigger1.3.2 CornTrigger 1.4 Listener监听器1.5 Jdbc store1.5.1 简介1.5.2 添加pom依赖1.5.3 建表SQL1.5.4 配置文件quartz.propert…...
Nginx专题-基于多网卡的主机配置
文章目录 Nginx 基于多网卡的主机实现一、虚拟机前置环境准备ifcfg-ens32配置文件的内容参考ifcfg-ens33配置文件的内容 二、案例演示修改nginx.conf配置文件解决中文乱码 Nginx 基于多网卡的主机实现 一、虚拟机前置环境准备 点击虚拟机右下角的 红色标框按钮,然后…...
4.2和4.3、MAC地址、IP地址、端口
计算机网络等相关知识可以去小林coding进行巩固(点击前往) 4.2和4.3、MAC地址、IP地址、端口 1.MAC地址的简介2.IP地址①IP地址简介②IP地址编址方式③A类IP地址④B类IP地址⑤C类IP地址⑥D类IP地址⑧子网掩码 3.端口①简介②端口类型 1.MAC地址的简介 …...
放弃 console.log 吧!用 Debugger 你能读懂各种源码
很多同学不知道为什么要用 debugger 来调试,console.log 不行么? 还有,会用 debugger 了,还是有很多代码看不懂,如何调试复杂源码呢? 这篇文章就来讲一下为什么要用这些调试工具: console.lo…...
epoll机制解析
一、epoll实现原理 1、实现原理 epoll通过3个方法来实现对句柄的监控操作,要深刻理解epoll,首先得了解epoll的三大关键要素:mmap、红黑树、链表。下面是epoll的框架图,如下: mmap epoll是通过内核与用户空间mmap同一块…...
基于 SpringBoot + Vue 实现的可视化拖拽编辑的大屏项目
今天给小伙伴们分享一个基于 SpringBoot Vue 实现的可视化拖拽编辑的大屏项目; 一、简介 这个是一个开源的一个BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。 多数据源支持,内置mysql、el…...
我们为什么要写作?
为什么要写书是一个很难回答的问题,因为从不同的角度,会有不同的答案。 最近ChatGPT很火!诸事不决,先问问ChatGPT,看看它是怎么回答的。 ChatGPT给出的答案还是比较全,虽然没有“一本正经的胡说八道”&…...
设计模式:创建者模式 - 建造者模式
文章目录 1.概述2.结构3.实例4.优缺点5.使用场景6.模式扩展 1.概述 将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。 分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某…...
String a = new String(“abc“); 创建了几个对象?String a = “abc“; 呢?
String a new String(“abc”); 创建了几个对象?String a “abc”; 呢? 答案:String a new String(“abc”); 创建了1个或2个对象;String a “abc”; 创建了0个或1个都对象 String a new String(“abc”); 创建过程 首先在…...
keepalived+nginx安装
欢迎使用ShowDoc! 1、安装基础包: yum -y install libnl libnl-devel 2、上传包: tar -zxvf keepalived-2.0.20.tar.gz -C /data/imas/base_soft mkdir -p /data/imas/base_soft/keepalived cd /data/imas/base_soft/keepalived-2.0.20 .…...
硬盘格式化工具,强烈推荐这个!
案例:硬盘格式化工具推荐 【我的电脑已经用了好几年了,硬盘存储容量严重不够了,最近想把它格式化,但却不知道怎么操作,大家有什么比较好的硬盘格式化工具可以推荐吗?】 硬盘作为存储设备,我们…...
Python的异常捕获和处理
程序在运行过程当中,不可避免的会出现一些错误,比如:使用了没有赋值过的变量,使用了不存在的索引,一个数字除以0 …… 这些错误在程序中,我们称其为异常。 程序运行过程中,一旦出现异常将会导致…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
vue3 手动封装城市三级联动
要做的功能 示意图是这样的,因为后端给的数据结构 不足以使用ant-design组件 的联动查询组件 所以只能自己分装 组件 当然 这个数据后端给的不一样的情况下 可能组件内对应的 逻辑方式就不一样 毕竟是 三个 数组 省份 城市 区域 我直接粘贴组件代码了 <temp…...
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
Nginx 是什么:高性能的HTTP和反向代理Web服务器。怎么用:通过配置文件定义代理规则、负载均衡、静态资源服务等。为什么用:提升Web服务性能、高并发处理、负载均衡和反向代理。优缺点:轻量高效,但动态处理能力较弱&am…...
