零基础开发 era 游戏 #16 屏幕显示

配置文件相关设置项#

窗口尺寸#

这里其实都是指的「渲染区域」的尺寸,也就是默认设置下显示为黑底的部分。

窗口宽度

设置渲染区域的宽度(width)。

ウィンドウ幅:760

窗口高度

设置渲染区域的高度(height)。

ウィンドウ高さ:480

注意这个高度实际应用时会减去一行的高度,用来做底部固定的输入栏。
你能用来排版显示内容的高度只有:

PRINTVL GETCONFIG("ウィンドウ高さ") - GETCONFIG("一行の高さ")
; 这里只是打个比方 实际运行会报错
; GETCONFIG() 函数不允许读取「ウィンドウ高さ」这个设置的值

启动后是否可以动态改变窗口高度

默认为允许(YES),如果你想保证排版的绝对整洁neat洁癖犯了)可以设置为 NO

ウィンドウの高さを可変にする:YES

指定启动时窗口出现的位置#

首先需要启用「Emuera 启动时指定窗口位置」(设置为 YES):

起動時のウィンドウ位置を指定する:YES

然后接下来的「指定窗口偏移的 X 座标」「指定窗口偏移的 Y 座标」两个设置才会生效。

ウィンドウ位置X:0
ウィンドウ位置Y:0

注意 Emuera 用的座标系是从左上角开始的:

YX

自动计算居中所需偏移量:

@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)字符,全称为「中日韩统一表意文字CJK Unified Ideographs」。
用人话说,就是「方块字」。顾名思义,正方形的字,所以 字高 = 字宽 = 字体尺寸

字高决定了一行最多能够显示 多少个 字符。

フォントサイズ: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 就行了。

lackbfun © 2021 - 2024