零基础开发 era 游戏 #16 屏幕显示
配置文件相关设置项#
窗口尺寸#
这里其实都是指的「渲染区域」的尺寸,也就是默认设置下显示为黑底的部分。
窗口宽度
设置渲染区域的宽度(width)。
ウィンドウ幅:760
窗口高度
设置渲染区域的高度(height)。
ウィンドウ高さ:480
注意这个高度实际应用时会减去一行的高度,用来做底部固定的输入栏。
你能用来排版显示内容的高度只有:
PRINTVL GETCONFIG("ウィンドウ高さ") - GETCONFIG("一行の高さ") ; 这里只是打个比方 实际运行会报错 ; GETCONFIG() 函数不允许读取「ウィンドウ高さ」这个设置的值
启动后是否可以动态改变窗口高度
默认为允许(YES
),如果你想保证排版的绝对洁癖犯了)可以设置为 NO
。
ウィンドウの高さを可変にする:YES
指定启动时窗口出现的位置#
首先需要启用「Emuera 启动时指定窗口位置」(设置为 YES
):
起動時のウィンドウ位置を指定する:YES
然后接下来的「指定窗口偏移的 X 座标」「指定窗口偏移的 Y 座标」两个设置才会生效。
ウィンドウ位置X:0
ウィンドウ位置Y:0
注意 Emuera 用的座标系是从左上角开始的:
自动计算居中所需偏移量:
@SYSTEM_TITLE #LOCALSIZE 1 #DIM window_width #DIM window_height #DIM screen_width #DIM screen_height window_width = CLIENTWIDTH() + 20 WHILE 1 window_height = CLIENTHEIGHT() + GETCONFIG("一行の高さ") + 60 CLEARLINE LINECOUNT PRINTFORML 当前窗口尺寸设置为 {GETCONFIG("ウィンドウ幅")} × {CLIENTHEIGHT() + GETCONFIG("一行の高さ")} px(来自配置文件) PRINTFORML 当前渲染区域尺寸为 {CLIENTWIDTH()} × {CLIENTHEIGHT()} px(减去底部的输入文本栏) PRINTFORML 当前窗口实际尺寸为 {window_width} × {window_height} px(加上右侧的滚动条和顶部的标题栏/菜单栏) IF screen_width > 0 && screen_height > 0 PRINTL PRINTFORML 当前屏幕尺寸为 {screen_width} × {screen_height} px PRINTL 想要让窗口启动时自动居中,需要设置(Ctrl + C 可以打开可复制的文本框) PRINTL 起動時のウィンドウ位置を指定する:YES PRINTFORML ウィンドウ位置X:{(screen_width - window_width) / 2} PRINTFORML ウィンドウ位置Y:{(screen_height - window_height) / 2} ENDIF PRINTL LOCAL = LINECOUNT $input_height PRINT 请输入当前的屏幕高度,单位为 px: PRINTBUTTON "[1080px (1080p)]", 1080 PRINT_SPACE 100 PRINTBUTTON "[1440px (2K)]", 1440 PRINT_SPACE 100 PRINTBUTTON "[2160px (4K)]", 2160 PRINTL INPUT CLEARLINE 1 SELECTCASE RESULT CASE 0 screen_height = 1080 CASE IS > window_height screen_height = RESULT CASEELSE CLEARLINE LINECOUNT - LOCAL PRINTFORML 输入屏幕高度错误({RESULT} px) GOTO input_height ENDSELECT PRINTFORML 屏幕高度为 {screen_height} px LOCAL = LINECOUNT $input_width PRINT 请输入当前的屏幕宽度,单位为 px: SELECTCASE screen_height CASE 1080 PRINTBUTTON "[1920px (1080p)]", 1920 CASE 1440 PRINTBUTTON "[2560px (2K)]", 2560 CASE 2160 PRINTBUTTON "[4096px (4K)]", 4096 CASEELSE PRINTBUTTON @"[{screen_height * 16 / 9}px (16:9)]", screen_height * 16 / 9 ENDSELECT PRINTL INPUT CLEARLINE 1 SELECTCASE RESULT CASE 0 screen_width = 1920 CASE IS > window_width screen_width = RESULT CASEELSE CLEARLINE LINECOUNT - LOCAL PRINTFORML 输入屏幕宽度错误({RESULT} px) GOTO input_width ENDSELECT WEND QUIT
字高#
CJK(Chinese Japanese Korean)字符,全称为「
用人话说,就是「方块字」。顾名思义,正方形的字,所以 字高 = 字宽 = 字体尺寸。
字高决定了一行最多能够显示 多少个 字符。
フォントサイズ:16
由于 Emuera 大量依赖文本的特性,让「字高」变成了整个游戏里最重要的设置之一。
而「1% 字高」也是 Emuera 的唯一指定长度计量单位。
如果是一路看教程过来的应该深有体会,简单来说,设置图片大小也是参考这个基准值。
行高#
行高决定了当前屏幕总共能够显示 多少行 文本。
一行の高さ:16
需要特别注意的是「行高」和「字高」未必是一致的。
一般来说,推荐 行高 比 字高 多一点,预留出一点余量,用来容纳 加粗 下划线 之类的。
不过具体还是要根据你选用的字体来决定。
刷新率#
每秒绘制多少次。
フレーム毎秒:5
排版逻辑#
宽可容纳几列#
PRINTFORML 当前窗口宽度为 {CLIENTWIDTH()}px PRINTFORML 每行最多可容纳 {CLIENTWIDTH() / GETCONFIG("フォントサイズ")} 个字符
高可容纳几行#
PRINTFORML 当前窗口高度为 {CLIENTHEIGHT()}px PRINTFORML 渲染区域最多可容纳 {CLIENTHEIGHT() / GETCONFIG("一行の高さ")} 行文本
即时动态计算#
@SYSTEM_TITLE #LOCALSSIZE 1 { #DIMS CONST 数字, 20 = "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" } #DIM line_count #DIM char_count #DIM line_index #DIM char_index WHILE 1 CLEARLINE LINECOUNT line_count = CLIENTHEIGHT() / GETCONFIG("一行の高さ") char_count = CLIENTWIDTH() / GETCONFIG("フォントサイズ") FOR line_index, 0, line_count - 2 SELECTCASE line_index CASE line_count - 4 LOCALS '= "" FOR char_index, 0, char_count / 10 + 1 LOCALS += @"%数字:char_index, 20, LEFT%" NEXT PRINTSINGLES LOCALS CASE line_count - 5, line_count - 3 PRINTSINGLES "一二三四五六七八九十" * (char_count / 10 + 1) CASEELSE { PRINTSINGLES @"{line_index + 1, 4, LEFT}" + "三四五六七八九十" + "一二三四五六七八九十" * (char_count / 10) } ENDSELECT NEXT PRINTFORM {++line_index, 4, LEFT}当前窗口宽度为 {CLIENTWIDTH()}px, PRINTFORML 每行最多可容纳 {char_count} 个字符 PRINTFORM {++line_index, 4, LEFT}当前窗口高度为 {CLIENTHEIGHT()}px, PRINTFORML 渲染区域最多可容纳 {line_count} 行文本 AWAIT REDRAW 2 WEND QUIT
可以随时拖动窗口边框调整高度(宽度启动后不能改变),显示的内容会自适应窗口大小。
屏幕刷新#
屏幕闪烁问题#
视觉上感觉「闪烁」其实是因为异常的刷新逻辑显示的。
理论解释
很遗憾,人的眼睛在生物学上是非常非常非常落后的设计。
但幸运的是,人类拥有这颗星球上目前已知的物种中最成熟、最智能的「大脑」。
尽管人眼能捕获到的光学信息少得可怜,但高度发达的视觉相关大脑中枢会帮助我们自动「脑补」出尽可能连贯的画面。
这就是古早的(
当你看到一连串、连贯、连续不断的画面时,你的大脑会自然而然地将其脑补为动态运动的,即俗称的「眨眼补帧」。
那我们为什么会看到「闪烁」呢?
原理很简单,由于我们看到有三幅画面,第一幅和第三幅画面能够连起来;
而第二副画面却「断裂」了,不能承前启后,共同组成连续的画面变化;
因此我们看到的第二幅画面就是「闪烁」的——它和前后的画面没有连续性的关系。
实际应用
回到 era 游戏,如果我们以 era 游戏标准的「瀑布流」形式正常刷新文本。
我们看到的画面就是一直向上「卷动」的。
出现闪烁的情况一般是因为使用了 CLEARLINE LINECONT
清空当前窗口的整个渲染区域,结果清空了之前的内容,却还没来得及渲染之后的内容,在中间这个空档的时机——
即两幅原本应该连续的画面之间——出现了空屏(一般为黑屏)的情况。
中间这个空屏的保留时间过长,长到能被人眼捕捉到,于是玩家就会察觉到「闪烁」现象。
所以想要避免产生闪烁,就需要将中间这个空屏的时间尽可能地缩短。
解决方案 REDRAW
#
一般的游戏引擎都是多线程的。
UI 线程负责渲染游戏画面并显示到屏幕上,运算线程负责后台数据的计算,各司其职。
也就是所谓的「渲染 FPS」和「物理 FPS」。
当然没有分开二者的奇葩游戏也是有的。而且有些是技术力不行,有些真就是出于某种目的而故意为之,比如最著名的 GTA 5 就是如此,还是说你想质疑 R 星的技术力?
众所周知为人诟病的无限看云(循环优化问题)我没想洗,这个也洗不动;但立项之初就必然会发现的架构设计基础逻辑,真的只能用故意来解释。
至于 Emuera 嘛……
高情商:拥有近 20 年的悠久历史。
低情商:老东西,你已经落后一万个版本了桀桀桀。
在 Emuera 里没有多线程这个说法,显示逻辑和运算逻辑是完全强耦合在一起的。
我们能做的最多是从中取得一个平衡点。
如何取这个平衡点呢?使用神奇的 REDRAW
命令。
注意断句,REDRAW 意为 Re-Draw(重绘),不是 Red-Raw。
REDRAW 0
:默认,一般询问输入时才会刷新屏幕显示,无输入不刷新REDRAW 1
:每秒按固定帧率(由フレーム毎秒
设置)即时刷新REDRAW 2
:(2
及以上)除了每秒刷新之外,执行命令时立刻 强制 刷新
当前使用的绘制方式可以通过 CURRENTREDRAW()
获得(0
被动刷新,1
主动刷新)。
我暂时没想到什么效果显而易见的例子,总之遇事不决 REDRAW 2
就行了。