Project1

标题: 新手教程——计算累计时间Window_(2) [打印本页]

作者: 一箭烂YiJL    时间: 2010-12-6 21:53
标题: 新手教程——计算累计时间Window_(2)
本帖最后由 一箭烂YiJL 于 2011-1-3 17:21 编辑

课序号:2
作者:一箭烂YiJL
出版社:VX教程出版社
售价:完全免费
VX教程出版社的其它教程

上一课:
新手教程——基础


注意:这时教程!请不要讨论它的用途方面,讨论技术即可,
这教程的方法不能非常精确的计算秒数!
(记住!这时只是教程,不是实用脚本!!!)

终于有人能看完和参透整个教程的流程.....


序:
曾经有人提问过怎么计算累计游戏时间,一般那些人都只知道计算总秒数。
虽然这股小小的风波已经解决,但还是教一教吧!
高手请指点,新手可以学习。


我们这次的目标是学习:
1.super让父窗口接收方法。
2.%的符号用法
3.sprintf(格式化字符串)
4.self.contents.draw_text用法(文字显示法)


这个是效果:


先建立一个类(class),在建立两个函数。(建立的类的父窗口是Window_Base)
类的名字定为Window_PlayTime吧!(这个可以自定),
建立两个分别叫做initialize(初始化),update。然后就成为:

  1. class WindowPlayTime < Window_Base
  2.   def initialize
  3.   end
  4.   def update
  5.   end
  6. end
复制代码
不懂这部看上一课。


然后将initializen那函数变成:

  1. #这里经67提点后改正了是w和h
  2. def initialize(x, y, w = 160, h = 90)
  3.   super(x, y, w, h)
  4. end
复制代码
def initialize(x, y, w = 160, h = 90) 这表示了:
建立x、y、w、h四个变数(这种变数只限用于当前的函数),并且w默认是160,h默认是50.
(关于默认参数,之后有机会再说吧~,想知道什么是默认参数看32楼)
而super(x, y, w, h)就是把刚才定义的x, y, w, h返回父窗口Window_Base的initialize。
你会看到Window_Base有:def initialize(x, y, width, height)
这四个变数会根据位置代入父窗口的(x, y, width, height)这四个变数
这是一个简单的表示:
super(x, y, w, h)  =>  initialize(x, y, width, height)
x, y, 160, 90  =>  x, y, width, height
如果不明白的话请看下图:

若还是不懂就一句话,(这里的)super是控制窗口的xy坐标还有高宽。
还是不动就帮不到你了。(懂就最好)


很多人都知道总秒数的计法是:Graphics.frame_count / Graphics.frame_rate # 总帧数/设定的帧频率
@total_sec = Graphics.frame_count / Graphics.frame_rate
因为@total_sec是要不断更新,所以必须放进update这函数里。


现在说的就是一种人们不懂计算时分秒的地方,请在update函数里加建:
  1. sec = @total_sec % 60 #计秒
  2. min = @total_sec / 60 % 60   #计分
  3. hrs = @total_sec / 60 / 60      #计时
复制代码
这种方法不会影响@total_sec的增加,但又不是用秒进分,分进时。
咦?%是什么?有看过流星前辈Scene教程第一弹的人会知道哪儿曾出现过%=
只是有关系的。比如:"@abc %= 4"的意思是把@abc除以四得出来的余数代回@abc,
也可说为当@abc到达4的时候,便归零。
说"sec = @total_sec % 60"就是将@total_sec除以60,把余数代入sec。
举例:sec = 172 % 60
           sec = 52 # 到这不明白吗?min就不说了
hrs是这里的最大单位,所以不用%,让它自动增加。


说到这步,看过流星前辈的window教学的人都知道应该怎吧这几个数字显示出来,不过也说说吧。
  1. playtime = sprintf("%02d:%02d:%02d", hrs, min, sec) # 建立在update函数里
复制代码
sprintf(格式化字符串),后边的hour, min, sec就以顺序的方法,
以每一个%02d来表示于" "之内。一可以这样说吧:
playtime = hrs + ":" + min + ":" + sec # 这只是一个说法,是不能使用的


建立了PlayTime这个变数后,就要显示了,这样(在update里):

  1. self.contents.font.color = normal_color
  2. self.contents.draw_text(x, 30, 126, WLH, playtime, 2)
复制代码
第一句是表示定义绘画文字时候用正常颜色(白色)
第二句就是格式是self.contents.draw_text(x坐标, y坐标, 宽, 高, 显示的变数, 靠左或居中或靠右)
# 0:靠左    1:居中   2:靠右
width和height都没有定,所以所以用预设的。


你还要建立"游戏时间"这个东西,在建立完playtime
  1. time_text = "游戏时间:"
  2. self.contents.draw_text(x, -30, 160, 90, time_text,0)
复制代码
未免留底的问题,要在update的第一行插入:
  1. self.contents.clear # 清除
复制代码
意思就是先清除了,然后建立过,免得留痕。


这时候测试,你发现它会在不够一秒之后才能显示数字(测试方法:用场景,记得要用update)
一开始看到是没有字的,过很短很短的时间就会显示出来了。
这是因为update会在初始化(initialize)之后才运行,所以就会有此情况,
在initialize函数里super的下一行加入:
  1. update
复制代码
就这update?没错,就是在初始化的时候立即进行update函数的动作。


在测试,就没问题了,若有问题对照一下完成的作品:
  1. class WindowPlayTime < Window_Base
  2.   
  3.   def initialize(x, y, w = 160, h = 90)
  4.     super(x, y, w, h)
  5.     update
  6.   end

  7.   def update
  8.     self.contents.clear
  9.     @total_sec = Graphics.frame_count / Graphics.frame_rate
  10.     hrs = @total_sec / 60 / 60
  11.     min = @total_sec / 60 % 60
  12.     sec = @total_sec % 60
  13.     playtime = sprintf("%02d:%02d:%02d", hrs, min, sec)
  14.     self.contents.font.color = normal_color
  15.     self.contents.draw_text(x, 30, 126, WLH, playtime, 2)
  16.     time_text = "游戏时间:"
  17.     self.contents.draw_text(x, -30, 160, 90, time_text,0)
  18.   end
  19. end
复制代码
总结(我们学到了):
1.super让父窗口接收方法。
2.%的符号用法
3.sprintf(格式化字符串)
4.self.contents.draw_text用法(文字显示法)
(若上面的东西一个也不懂就白费了这个教程了!)


好了这节课就到这里!完毕!
看贴请回帖作为教程支持,
我们需要的是教学支持!!


下一课:
新手教程——函数外的变量



补充站(给明白刚才关于super那幅图的同学看的):
其实上面的方法并不是很好的,会比较耗费资源,
所以我们需要在update做一些动作,才执行这么大的动作,总之就是这样,
这样会有程序点儿和节省资源:
  1. class WindowPlayTime < Window_Base
  2.   
  3.   def initialize(x, y, w = 160, h = 90)
  4.     super(x, y, w, h)
  5.     refresh
  6.   end

  7.   def update
  8.     super
  9.     sec = (Graphics.frame_count / Graphics.frame_rate) % 60
  10.     if sec > @total_sec % 60 or sec == 0
  11.       refresh
  12.     end
  13.   end

  14.   def refresh
  15.     self.contents.clear
  16.     @total_sec = Graphics.frame_count / Graphics.frame_rate
  17.     draw_playtime(0, 0, width - 32)
  18.   end

  19.   def draw_playtime(x, y, width)
  20.     hrs = @total_sec / 60 / 60
  21.     min = @total_sec / 60 % 60
  22.     sec = @total_sec % 60
  23.     time_text = "游戏时间:"
  24.     playtime = sprintf("%02d:%02d:%02d", hrs, min, sec)
  25.     self.contents.font.color = normal_color
  26.     self.contents.draw_text(x, -30, width, height, time_text,0)
  27.     self.contents.font.color = normal_color
  28.     self.contents.draw_text(x, 30, width - 2, WLH, playtime, 2)
  29.   end
  30. end
复制代码
这里看不明白就算。
作者: 贤将娜兹玲    时间: 2010-12-6 22:12
提示: 作者被禁止或删除 内容自动屏蔽
作者: 逍游    时间: 2010-12-6 22:17
超有爱,但是看不懂:L
作者: 一瞬间的幻觉    时间: 2010-12-6 22:17
这脚本比较适合放在什么游戏里面应用呢?
作者: 一箭烂YiJL    时间: 2010-12-6 22:19
比较建议放在菜单,或者是地图
(放在地图必须调整一下高度和宽度)
作者: 逍游    时间: 2010-12-6 22:20
回复 逍游 的帖子

不是你失策,我是脚本盲,一点基础也没有,看不懂正常的
作者: 冰舞蝶恋    时间: 2010-12-6 22:21
好有爱。大人您的水平早已远远超过某梦了= =
表示目前为止所发的任何脚本都是在原基础上做修改的说
到现在都不知道如何新建一个类。。。。。。。。。。啊啊啊啊
我得去苦读教程了的说。
作者: px.凤翔九天    时间: 2010-12-6 22:34
不错的教程啊...(虽然有一些不懂(目前已经细读过f1,正买书苦学中...))但是倒是会用了。决定去掉原来的步数窗口塞上这个...
话说流行雪前辈的教程在哪里?给个连接呗。目前正好想学写window—base的子类写法呢。谢了。
作者: 一箭烂YiJL    时间: 2010-12-6 22:46
本帖最后由 一箭烂YiJL 于 2010-12-16 18:49 编辑

Window_Base的雪流星前辈就没有说(说起来很烦)
http://rpg.blue/forum.php?mod=redirect&goto=findpost&ptid=127177&pid=1449497&fromuid=85435
这是流星前辈的教学
作者: DeathKing    时间: 2010-12-6 23:31
对!说得很详细,解释清楚了super关键字的工作机理。

super是调用父类的同名方法的关键字,并且super能够传递参数。我们在Window_Base中定义好了initialize方法,并且让其他衍生的窗体(诸如WindowPlayTime)继承Window_Base类,这样,当子类的initialize方法执行时,也会执行父类定义的initialize方法。可以理解为“保持行动的统一性(确保创建出来的窗体是风格一致的。)”

另外一个小瑕疵(或许是),是图片的最上部分,@1 = Window_PlayTime(1,2)应该还有一个new方法。
作者: uufo    时间: 2010-12-6 23:36
提示: 作者被禁止或删除 内容自动屏蔽
作者: summer92    时间: 2010-12-7 10:19
索引~~索引~~~,一直为游戏时间这个DD头疼...
作者: 苏小脉    时间: 2010-12-7 11:33
赞一个,还有图解,这年头这么用心的人越来越少了:)

其实沙发的建议很好,并不是统一性的问题,而是严谨性的问题。RM 默认的这种计算方式依赖于帧计数和一个固定帧率,在游戏逻辑耗时太久的时候,就不能做到精确的计时了,特别是在硬件条件差的机器上。如果游戏逻辑在人眼能够察觉的间隔内还没有运行完毕,游戏的帧计数仍然是从间隔之前的量开始增长,这样就会导致时间“变慢”。Ruby 的 Time 对象可以获取系统时间,而现代的计算机的系统时间都是由独立的实时时钟芯片处理的,所以不但和某个游戏进程的帧率无关,和 CPU、I/O 时间也无关,即便游戏在某一处逻辑耗时太久,接下来刷新时间时获取到的也是真实的挂钟时间(实耗时间)。
作者: fux2    时间: 2010-12-7 12:08
回复 苏小脉 的帖子

是在初始化的时候才获取时间计算吗?会不会因为修改系统时间达到特殊目的?
作者: 路过的小白    时间: 2010-12-7 12:49
= =真要想修改的话,无论怎么计时都能修改的说
关键是计时目的的问题
为了帮助玩家记录通关时间?那么玩家的SL该如何处理呢
对游戏内容产生影响?那么又何苦追求计时准确性或者真实性呢
实际上除了特殊需要,在存档时记录下存档时间,让玩家知道哪个存档是最新的就够了吧= =
作者: 苏小脉    时间: 2010-12-7 13:37
回复 路过的小白 的帖子
为了帮助玩家记录通关时间?那么玩家的SL该如何处理呢

在本机,从来就没有绝对避免 S/L 的方法,只有防君子的权变措施。
对游戏内容产生影响?那么又何苦追求计时准确性或者真实性呢

在播放一个音频时,你可能希望让游戏的某个线程与播放同步,这时你就需要让该线程精确地等待这么多个单位的时间。又如需要在特定时间内完成的任务,你不想因为这个时间随着玩家的机器性能不同而不同。当然,这些都可以归类于你说的“特殊需要”,在单机游戏领域不精确计时确实并非致命。如果咱们的上下文换到联机对战以及多人在线游戏,那精确计时就是保证公平竞技的一个必要前提了。
作者: 一箭烂YiJL    时间: 2010-12-7 18:14
回复 fux2 的帖子

苏小脉前辈(好像)错了,我的教程曾说过initialize里加进update能免去开启窗口过一阵子才显示出来,
所以fux2前辈说的没错,只会在initialize时才开始计算时间,
所以对于之前所说用Time代替PlayTime的影响是很小的,
反而为补充站里的那个方法能够节省资源(原因自己参透)。
作者: kenchenrong    时间: 2010-12-7 19:46
提示: 作者被禁止或删除 内容自动屏蔽
作者: 苏小脉    时间: 2010-12-7 21:23
本帖最后由 苏小脉 于 2010-12-7 21:43 编辑

回复 一箭烂YiJL 的帖子

initialize里加进update能免去开启窗口过一阵子才显示出来,
所以fux2前辈说的没错,只会在initialize时才开始计算时间,
所以对于之前所说用Time代替PlayTime的影响是很小的,

我怎么感觉你没仔细看我十三楼的回复呢 XD
当你的预期帧率是 40 fps 的时候,如果实际帧率只有二三十,说明游戏逻辑耗时太大导致渲染频率根不上,所以无法达到预期的四十,这就是我说的那种情况。这种情况下你就会感觉游戏时间所谓的“秒”的滴答频率变小了。
你可以在 Scene_Menu 刷新的时候加一句:
  1. 5000000.times {}
复制代码
然后看看这种按帧计数计算的游戏时间的滴答频率。我在本机的 Intel Core I7-720 上测试,实际帧率从 40 降到了 30 fps,换句话说一秒只刷新了 30 次,但由于 Graphics.frame_rate 固定为 40,所以需要更新到 40 次的倍数时才会让游戏时间增长一秒,也就是实际时间的 4/3 秒才等于游戏时间的 1 秒。根据 CPU 性能的不同,这个受到的影响也不同。
反而为补充站里的那个方法能够节省资源(原因自己参透)。

这个其实默认脚本就做了,不知道“站里的那个方法”为什么没做。默认脚本判断的是:
Graphics.frame_count / Graphics.frame_rate != @total_sec
比你模上 60 来的简单。当然默认脚本里 refresh 又重新计算了 Graphics.frame_count / Graphics.frame_rate 一次,所以还是有冗余了。你完全可以让这个除法在一帧之间只进行一次。
作者: 一箭烂YiJL    时间: 2010-12-7 22:44
回复 苏小脉 的帖子

我所说的"initialize时才开始计算时间"意思只是在场景开始时才执行Window_PlayTime。
不会不断在系统中执行(意思就是Graphics.frame_count会不断增加,但Window_PlayTime不会不断执行),才不会对系统构成很大负担的意思。(所以我个人不执着在Time和PlayTime之间,何况这只是个教程,不过让其他人看到这些对话也算是教程的一部分吧!表现挺乐观的~_~)

另外我说"补充站方法能较节省资源"的原因是,补充站那儿的update会根据:
if sec > @total_sec % 60 or sec == 0
才会调用到refresh,但秒数依然要更新,所以在这句之前有:
sec = (Graphics.frame_count / Graphics.frame_rate) % 60
至于关于脚本已经预设了Graphics.frame_count / Graphics.frame_rate != @total_sec一事,
则是因为教程始终也是教程,也要解释一下的。至于冗余方面则是我考虑不周全的问题。

作者: 星之璇    时间: 2010-12-7 22:48
我咋记得是@1 = Window_PlayTime.new(1, 2)啊?
难道我搞错了……
作者: 小角色    时间: 2010-12-7 23:22
{:nm_8:}看得懂却从来不愿意写的人来支持下
作者: 苏小脉    时间: 2010-12-8 00:01
回复 一箭烂YiJL 的帖子
我所说的"initialize时才开始计算时间"意思只是在场景开始时才执行Window_PlayTime。
不会不断在系统中执行

这是理所当然的呀,场景没开始窗口实例都还没创建呢。但这一点和我们讨论的两种实现细节的差别有什么关系呢?
另外我说"补充站方法能较节省资源"的原因是,补充站那儿的update会根据:
if sec > @total_sec % 60 or sec == 0
才会调用到refresh,但秒数依然要更新,所以在这句之前有:
sec = (Graphics.frame_count / Graphics.frame_rate) % 60

我给出默认脚本那句只是想指出这里没有必要去做额外的模运算(% 60)。你说呢?
退一步讲,sec == 0 这样的表达式也可以通过让前面的表达式变为 sec != @total_sec % 60
而省略。
作者: 一箭烂YiJL    时间: 2010-12-8 17:09
本帖最后由 一箭烂YiJL 于 2010-12-8 17:14 编辑

回复 苏小脉 的帖子

第一、
我所说的"initialize时才开始计算时间"

这句话只是回复之前一个人的提问,不过又不知为什么会出现再上一贴,(一定是我那时傻了-_-")。

默认脚本的确是我想的不周到的......
第二、
  1. if sec > @total_sec % 60 or sec == 0
复制代码
则是提出了这种简单方法,(这是始终是教程),我会在这一步后面说一说,不过苏前辈说得对,用sec != @total_sec % 60会好一些(只怕要解释!=的意思)


其实我也同意前辈所说这种计法不精准,会受到实际fps的影响而数少贞数,导致不精确。
作者: 牛肉面    时间: 2010-12-8 17:53
:(看不懂的说……还需学习
作者: 一箭烂YiJL    时间: 2010-12-10 19:47
400多的点击,24回复,还有一部分是我和苏前辈的讨论,
回帖率:5.46%(异常低落) 何况说一部分是我和苏前辈的讨论...
作者: silverbullet20    时间: 2010-12-18 17:30
很不错。。顶。。
作者: 精灵使者    时间: 2010-12-19 09:16
建议改为每1秒刷新一次可以节约时间(其实也可以做秒针闪动的功能恩……)
作者: fux2    时间: 2010-12-20 23:47
回复 一箭烂YiJL 的帖子

虽然很认真,但是有的地方一笔带过新人听不懂啊,语言水平也有待提高,不过还是很赞这样有心的童鞋,希望下次更精彩!
作者: 一箭烂YiJL    时间: 2010-12-21 16:51
回复 fux2 的帖子

谢谢韩星人的支持。唉~只好承认语言解释水平的确有问题~= =
不过话说回来:呵呵~好像没什么新手看似的,
看过并且回复过的全都是一些前辈。呜呜~
作者: 六祈    时间: 2011-1-3 03:39
本帖最后由 六祈 于 2011-1-3 03:40 编辑
  1. def initialize(x, y, width = 160, height = 90)
  2.   super(x, y, w, h)
  3. end
复制代码
不知道你有没有试过,应该会发生NameError,x,y,width,height是4个传入的局部变量,而w和h则未定义。
这个错误在你后面的补充里修正了,但是图片和图片上面的却没有。
关于def foo(a,b=3)形式的参数,请百度【默认参数】或者在VX的那个RGSS每日一帖里找找tips
作者: 一箭烂YiJL    时间: 2011-1-3 17:15
本帖最后由 一箭烂YiJL 于 2011-1-3 17:23 编辑

回复 67前辈 的帖子

def initialize(x, y, width = 160, height = 90)
  super(x, y, w, h)
end

A.这个是个人笔误(太大意了~),
你没发现吗?图片、完成脚本、加强版没有错
def initialize(x, y, width = 160, height = 90) 这表示了:
建立x、y、w、h四个变数

比没发现原本这句上文不对下文的吗?(表示这是笔误~)
图片的解释是说变数对变数,错了是因为平时用惯了width和height,
而为了教学,所以我特地把width和height编程w和h,
这样方便让人明白super里的参数是用类里的变数决定。
(就是所谓的"变数名对变数名",而不是跟initialize中的"参数位置对参数位置")
还有我测试的是完成的脚本,(完成的脚本没错,)所以我以为行。

B.而什么百度【默认参数】和RGSS每日一帖我找不到~!
不过我知道参数里有"def lifan(c = 100)"之类这种默认参数,默认一百,但自可以照样:
在l调用lifan时照样可以@对象名称.lifan(200),这样就取得200这个参数成为变数。
而这个是我漏了说啦~

C.最终,都是我的错。我会改进的了,还有,谢前辈提点了。
作者: irei_0110    时间: 2011-2-5 02:08
本帖最后由 irei_0110 于 2011-2-5 02:08 编辑

图片看完后还是混乱 (哭)
想问一下 什麽是WLH?
self.contents.draw_text 视窗高度不是90吗?
下一句把 self.contents.draw_text的高也写了90 那这一个WLH的意思是?......

另外教程可见有爱 不过怎说这也是新手教程 对象当然是给不太会脚本的人看
所以 本人觉得把话说白一点 范例中的脚本再完整一点 这就更能让人明白内容了
支持新手教程~~
作者: 龙须菜    时间: 2011-2-12 09:58
支持LZ!
俺是个超级大菜鸟,俺看了LZ的新手教程1和2,仍不知如何将这些代码执行,望LZ指点一二。
:'(悲剧好久了,一直不知如何更改菜单栏啊
作者: zeroko    时间: 2011-2-17 16:01
谢谢,学到许多
但不知道是不是RGSS2语言的不同吗?
有些描述不太懂,如果说"super调用父类的同名方法"能够明白
“super让父窗口接收方法”就让我很难理解了
作者: selintt    时间: 2011-5-11 14:57
提示: 作者被禁止或删除 内容自动屏蔽
作者: 一箭烂YiJL    时间: 2011-5-11 17:21
回复 selintt 的帖子

1.通常写在场景的initialize中。但是也可以写在事件脚本中,
不过是一次而已,然后就必须弄个并行处理的事件来@1.update。(刷新窗口的意思)

2.建立了 @1 实例后就可以用@1.update,dispose(释放)同理,
释放时将实例消灭的通用实例方法。
作者: youxian57    时间: 2011-5-11 19:08
好吧。。。。。本来想学的,但是看带各种符号。。。各种复杂、的东西。。。。。果断放弃拉,不解释!~~~~~~哎!
作者: selintt    时间: 2011-5-11 19:39
提示: 作者被禁止或删除 内容自动屏蔽
作者: 一箭烂YiJL    时间: 2011-5-12 20:51
回复 selintt 的帖子

B键是Esc, X。
L键是Q。
R键是W。
可在游戏窗按F1(如不行就按Ctrl+F1)
作者: MSQ    时间: 2011-6-12 14:34
本帖最后由 MSQ 于 2011-6-12 14:47 编辑

    看了1,2的来支持下,本人也是脚本盲,现在已经有点头晕了,但还是努力去看下一课。
    话说第三课在哪?
作者: MSQ    时间: 2011-6-12 14:40
看了1,2的来支持下,本人也是脚本盲,现在已经有点头晕了,但还是努力去看下一课。
作者: MSQ    时间: 2011-6-12 14:44
那个。。。。。。
刚才卡了,还以为没发,一不小心发了两次,请楼主果断把我最后两个帖子删了。
最后表示:真对不起。
作者: 1137103210    时间: 2013-5-15 21:59
怎么在地图显示游戏时间





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1