运行环境
Win7,Win8,Win10 win64
Reworld版本 体验版
针对零基础读者的补充
下载安装 Reworld对应版本
Reworld官网链接:http://www.reworlder.com/
下载后安装后注册账号打开空地图
思路分析
时间戳实现思路分为三个部分:首先,获取当前时间和要计时为止的时间,然后转化为时间戳;其次,在当前时间与到达时间做差值,求得CD时间的时间戳差;最后,把求得的时间差转换为时间日期格式,在Update函数中进行计算与时间文本字符的赋值。其中有两个难点:第一点,时间戳与日期的转换,时间戳转换为具体某一天,某时的换算。第二点,要把时间戳的差值转为日期格式,明白os.time()与os.date()的用法。
功能效果展示
引入用例
相信大家通过教学视频和一些项目,都已经了解过Lua里面的协程(coroutine),简单的等待触发是用wait()来控制一段时间后再继续执行的。下面是用协程的例子:
function TestFun()
print("我是重小启")
end
Players.PlayerAdded:Connect(function(Uid)
coroutine.start(function()
print("角色生成了")
wait(3)
TestFun()
end)
end)
上面的Players里面有一个PlayerAdded事件,是角色生成的时候调用执行,整体逻辑就是在角色生成后输出“角色生成了”,3秒钟以后,执行TestFun方法,输出:我是重小启。
但是当程序中出现多个携程的时候,停止协程可能会导致程序错误。所以上面的计时器适用于时间较短,周期短的倒计时。那么我就引入了时间戳倒计时。
一、时间戳介绍
时间戳是什么呢?在写制作步骤之前,先简短介绍一下时间戳:百度给的定义是:“一个能表示一份数据在某个特定时间之前已经存在的、 完整的、 可验证的数据,通常是一个字符序列,唯一地标识某一刻的时间。”
那么时间戳是一个字符序列,下面我们一起探究时间戳!首先我打印一下时间戳:
Players.PlayerAdded:Connect(function(Uid)
player = Players:GetPlayerByUserId(Uid)
print(os.time()) --本地系统的时间戳
end)
下面是编辑器运行的结果:
没接触过时间戳的人会有疑问,(1560501534)这串数字是什么?代表了什么时间?其实我们可以通过百度搜索“时间戳在线转换工具”进行转换(打开转换工具——>输入时间戳——>得到具体时间日期)。
上面就是时间戳转换为具体时间的流程。1560501534 = 2019-06-14 16:38:54
如此一来,大家都明白了吧!但是每次都通过浏览器获得时间,那也太麻烦了!我告诉大家通过代码获取时间戳里面的日期:
Players.PlayerAdded:Connect(function(Uid)
player = Players:GetPlayerByUserId(Uid)
print("这是年: " .. os.date("%y",os.time()) ..
"这是月: " .. os.date("%m",os.time())..
"这是日: " .. os.date("%d",os.time()))
print("这是时: " .. os.date("%H",os.time()) ..
"这是分: " .. os.date("%M",os.time())..
"这是秒: " .. os.date("%S",os.time()))
end)
上面代码里,os.date()函数是时间戳转换日期方法,下面是运行结果:
将详细的日期打印了下来,那么知道了现在的时间戳如何获得明天的时间戳呢?想必大家已经知道了,就是通过将日期+1,得到的就是明天的时间戳。下面进行测试:
Players.PlayerAdded:Connect(function(Uid)
player = Players:GetPlayerByUserId(Uid)
print(os.date("%d",os.time()))
local CurrentTimer = os.time({day=os.date("%d",os.time())+1, --日期+1操作
month=os.date("%m",os.time()), year=os.date("%Y",os.time()),
hour=os.date("%H",os.time()), min=os.date("%M",os.time()),
sec=os.date("%S",os.time())});
print(os.date("%d",CurrentTimer))
end)
下面是运行结果
可以看到,两个日期相差一天。所以终于给倒计时提供了一个思路:用时间戳的差值得到相差的时间戳,再转换为具体时间。
好了时间戳介绍完了,下面是时间戳倒计时的具体步骤:
二、创建服务器脚本并定义变量
首先在服务器逻辑里面创建服务器脚本:
然后打开脚本,创建倒计时所需要的变量:
local workMin = 0 --分钟
local workSec = 0 --秒钟
local CurrentTimer = 0 --未来时间的倒计时
local TotalTime = 5 --CD总时间
local StartTimeCount = 0 --开始计时的时间戳
local Hero --角色
PlayerInfo = {}
PlayerInfo.PlayerID = 0 --角色Id
Players.PlayerAdded:Connect(function(Uid)
PlayerInfo.PlayerID = Uid
Hero = Players:GetPlayerByUserId(PlayerInfo.PlayerID)
三、编写倒计时逻辑
倒计时是需要实时去执行的,所以写在update里面,下面是代码:
GameRun.Update:Connect(function()
--判断时间戳是否已经赋值
if StartTimeCount ~= 0 then
--设置将来的时间
CurrentTimer = os.time({day=os.date("%d",StartTimeCount), month=os.date("%m",StartTimeCount), year=os.date("%Y",StartTimeCount), hour=os.date("%H",StartTimeCount), min=os.date("%M",StartTimeCount), sec=os.date("%S",StartTimeCount)+TotalTime});
local PeriordTimer = CurrentTimer - StartTimeCount
workMin = os.date("%M",CurrentTimer - os.time())
workSec = os.date("%S",CurrentTimer - os.time())
--做差值,得到要倒计时的时间与现在时间戳的差
local alltime = PeriordTimer - ( os.time()-StartTimeCount)
if alltime <= 0 then
alltime = 0
a= a+1
if a == 1 then
PlayerInfo.MatchState = false
PlayerInfo.HideState = true
end
if a == 2 then
PlayerInfo.HideState = false
PlayerInfo.StartGame = true
end
if a == 3 then
PlayerInfo.StartGame = false
end
if a == 4 then
print("----end----")
end
end
--将时间戳格式化为时间格式
local second = tonumber(workSec)
local mint = tonumber(workMin)
local hour = math.floor(alltime / 60 /60);
if alltime <= 0 then
hour = 0;
mint = 0;
end
if mint < 10 then
mint = '0'..mint
end
if hour < 10 then
hour = '0'..hour
end
if second < 10 then
second = '0'..second
end
--得到时间格式的倒计时(我们要显示的倒计时文字)
local temptime = tostring(hour).. ":" ..tostring(mint).. ":" ..tostring(second)
if PlayerInfo.MatchState then
--发送消息给客户端进行显示
MessageEvent.FireClient(PlayerInfo.PlayerID,"Timer",temptime)
end
end
end)
--匹配设置开始时间
MessageEvent.ServerEventCallBack("StartMatch"):Connect(function()
StartTimeCount = os.time()
PlayerInfo.MatchState = true
end)
上面的时间戳倒计时中 temptime就是一串字符:00:00:05 ,可以直接赋值到Text文本中。当然如果想让倒计时更加的精准,不受本地时间的影响就要获取到服务器的时间戳,这样就能保证客户端与服务器的计时是一致的。
四、创建客户端脚本,给UI赋值
我用到的UI是编辑器上给定的UI,下面是我的UI层级逻辑和客户端脚本创建位置:
UI的“具体时间”文本框就是我们要显示的倒计时文字,刚刚在服务器脚本已经得到倒计时的文字了,只要在客户端脚本中接收然后给文本复制就可以了,下面是代码:
local MoonPic = GameUI.时间显示.时间.月亮
local SunPic = GameUI.时间显示.时间.太阳
--匹配倒计时
MessageEvent.ClientEventCallBack("Timer"):Connect(function(time)
GameUI.时间显示.IsVisable = true
if tonumber(os.date("%H",os.time())) >= 17 then
MoonPic.IsVisable = true
SunPic.IsVisable = false
else
MoonPic.IsVisable = false
SunPic.IsVisable = true
end
GameUI.时间显示.时间.时间文字.具体时间.Text = time
GameUI.时间显示.时间.时间文字.文本控件.Text = "匹配倒计时"
end)
所以,只要把temptime赋值给具体时间的文本控件即可。
五、设置开始倒计时的时间
我们是点击匹配按钮,开始倒计时,所以只要在点击按钮的时候,把StartTimeCount赋值为当前时间的时间戳即可,下面是匹配按钮UI层级和客户端脚本的代码:
local MatchBtn = GameUI.MatchingPanel.MatchBtn
--点击匹配
MatchBtn.OnClick:Connect(function()
--发送给服务器消息
MessageEvent.FireServer("StartMatch")
MatchBtn.IsVisable = false
GameUI.跳跃.IsVisable = false
end)
大功告成,运行后显示结果如下:
上面的月亮图片是根据时间的小时来判断的,如果当前时间的小时>17,也就是晚于晚上5点,我就判断为晚上,显示月亮反之则显示太阳。“00:00:04”就是我们要得到的倒计时,很精准。
如果想重置计时时间可以把StartTimeCount重新赋值为os.time(),这样就能重新去CD了。改动CD时间的话就改动TotalTime。
以上便是我的分享,希望时间戳倒计时可以帮到各位朋友,喜欢的童鞋记得关注哦~
六、补充说明
1.什么是客户端脚本?
只会在客户端执行的脚本,执行的逻辑和表现也只会在本地客户端展现;可在以下几个文件目录下自动执行,客户端脚本在工作区下不会自动执行,需要放在以下对象里面:
1. 客户端最先加载 。
2. 工作区中的角色模型。玩家初始化中的角色初始化脚本,在运行后会自动移动到角色模型下。
3. 玩家列表中的玩家。玩家初始化中的玩家初始化脚本,在运行后会自动移动到玩家下
4. 玩家的玩家界面。界面初始化的脚本,在运行后会自动移动到玩家界面下。
5. 玩家的揹包,例如工具里面的
2.为什么要使用客户端脚本?
2D平面UI是只在客户端存在并加载的,在服务器是没有2D平面UI的实体对象的,所以只能客户端脚本来对2D平面UI的信息进行更新修改。其他这种只在客户端存在的实体对象还有摄像机、鼠标、键盘等。
3.什么是服务器逻辑?
服务对象。
此服务下的脚本会在服务器上运行,用于放置服务器端游戏逻辑 。
不可创建。
不能用RWObject.Create()函数创建此对象。
不可删除。
不能用Destroy()函数删除此对象。
不可复制。
不能用Clone()函数复制此对象。
4.什么是服务器脚本?
只会在服务器运行的Lua脚本代码,用于编写服务器逻辑。
5.为什么要使用服务器脚本?
计时功能适用於单人和多人等不同环境,所以采用通用的服务器脚本。
- 服务器脚本与客户端脚本不同,客户端执行的操作只有本地客户端,也就是玩家自己有效。而服务器执行的操作不仅针对单人有效,还针对与服务器相连的所有客户端同步生效。
- 在多人游戏中,如果这个对象的变化是针对一个人的,必须在客户端脚本进行编写;如果这个对象的变化是针对所有人的,那就必须在服务器脚本进行编写。
- 对于只能在客户端脚本修改的对象,如何让服务器知晓变化结果是很重要的。这里采用传统游戏的制作流程,也就是在客户端进行修改,把修改后的结果通过与服务器通信的方式发送到服务器,再通过服务器进行逻辑运算,把执行结果再同步给所有客户端。
6.什么是文本控件?
图像控件显示非交互图像,经常用于装饰或者图标使用。
7.什么是按钮控件?
按钮控件用于响应来自用户的事件,经常用于启动或者确认某项操作使用。