图形用户界面编程入门

本文介绍Don't Starve 图形用户界面(Graphical User Interface,简称GUI)编程的基本概念,并给出简单的操作实例。

实例下载

基本概念

在Don't Starve中,图形界面的核心由两个大类组成:WidgetScreen。Widget是用于界面上的小组件的,比如说一个物品栏,物品栏上的一个单元,都是一个简单的widget。Screen则是一整个界面,比如说选项界面,小地图界面等等。一个Screen里可以包含多个widget,一个widget也可以内含许多widget。在游戏中,widget和screen并没有明确的分界线,这里说的概念也仅仅是一种逻辑上的区分而已。由于在GUI编程中,Widget是Screen开发的基础,而且实际开发中使用的频率远远高于Screen,所以这里主要讲解Widget,而把Screen作为拓展内容。

Widget和Screen都只是一个基本类,要实现诸如按钮,文字输入等功能,则需要进行扩展,也就是继承Widget或Screen类,在此基础上更进一步增加新的东西。官方已经做好了一些常用功能的扩展,包括按钮,文字,图片,动画等等。在联机版里,为了进一步节约开发时间,官方还做了一些常用模板。这些模板是为固定情景而设计好的完整的Widget,可以简单地通过一两句代码调用,而无需再自行编写复杂的Widget。

Child

在图形编程中,常常会用到一个函数AddChild。这个函数会将传递进来的对象设置成执行这个函数的对象(我们称为Parent)的一个Child。从而在Parent执行一些操作时,会让Child同步执行。
比如说,当Parent的坐标变化时,Child的坐标也会跟着变化。再比如说,Child的原点会被设置成Parent的坐标,从而使得Child的坐标变成相对坐标,在调整时无需关心Child的屏幕坐标,只需要关心它相对于Parent的位置偏移就行了。这就使得Parent和Child变成一个紧密相连的整体,大大简化了相关的操作。

Hello Widget

大多数编程的学习,都是从最简单的Hello World开始,我们这里就从编写一个可以在游戏内顶部居中显示"Hello Klei"的Widget开始。
这需要做两件事:

  1. 编写一个Widget
  2. 把这个Widget加载到游戏里去

创建Widget

创建Widget是十分容易的,你只需要为你的widget想一个名字,比如Hello,然后在mod根目录/scripts/widgets文件夹下创建一个lua文件hello.lua。然后写一个类,继承Widget类或它的一个子类即可。
一般来说,推荐直接继承Widget,对于想要使用的特别功能,使用组合的方式来实现。这是因为,在MOD制作者编写的Widget通常是复合型的,比如可能同时含有按钮和文本,这时候,无论是继承Button类还是Text类,在逻辑上都是不合适的。正确的做法是,继承Widget类,再在成员变量里按需要添加Button和Text。

首先,创建一个mod项目,然后在mod根目录/scripts/widgets文件夹下创建一个lua文件hello.lua,然后,编写代码如下:

-- 首先,在文件的头部写上需要加载的Widget类
local Widget = require "widgets/widget" --Widget,所有widget的祖先类
local Text = require "widgets/text" --Text类,文本处理

local Hello = Class(Widget, function(self) -- 这里定义了一个Class,第一个参数是父类,第二个参数是构造函数,函数的参数第一个固定为self,后面的参数可以不写,也可以自定义。
    Widget._ctor(self, "Hello") --这一句必须写在构造函数的第一行,否则会报错。
    --这表明调用父类的构造函数(此处是Widget,如果继承Text,则应该写Text._ctor),第一个参数是固定的self,后面的参数同这个父类的构造函数的参数,此处写的是Widget的名字。
    --
    self.text = self:AddChild(Text(BODYTEXTFONT, 30,"Hello Klei")) --添加一个文本变量,接收Text实例。
end)

return Hello

如此就完成了Hello Widget的编写。

加载Widget

Widget实质上是一个小部件,它需要依附于screen才能使用。游戏里通过FrontEnd来调度不同的screen,从而显示出不同的Widget供玩家查看和操作。
不过,一般来说,用于MOD的widget,多数不需要直接依附于screen,而只需要依附于screen下的一个widget就行了。
以最常见的,为游戏中的操作界面添加widget为例,screen是HUD,但我们不需要让自己编写的widget直接依附在HUD上,只需要依附在controls这个widget上就行了。controls是一个大型的综合性widget,玩家操作界面的物品栏,制作栏,状态栏等等,都是由这个widget统一进行管理的。

下面就来把Hello Widget添加到这个controls里。

在modmain.lua里添加如下内容:

local hello = GLOBAL.require("widgets/hello") --加载hello类
local function addHelloWidget(self)
    self.hello = self:AddChild(hello())-- 为controls添加hello widget。
    self.hello:SetHAnchor(0) -- 设置原点x坐标位置,0、1、2分别对应屏幕中、左、右
    self.hello:SetVAnchor(1) -- 设置原点y坐标位置,0、1、2分别对应屏幕中、上、下
    self.hello:SetPosition(70,-50,0) -- 设置hello widget相对原点的偏移量,70,-50表明向右70,向下50,第三个参数无意义。
end
AddClassPostConstruct("widgets/controls", addHelloWidget) -- 这个函数是官方的MOD API,用于修改游戏中的类的构造函数。第一个参数是类的文件路径,根目录为scripts。第二个自定义的修改函数,第一个参数固定为self,指代要修改的类。

成果展示

开启MOD,随便选个角色进入游戏,你会在屏幕上方看到Hello Klei的字样

demo.jpg

常用Widget

官方已经编写好了大量可用的Widget,包含了大量基本功能,联机版更有许多模板可以直接使用。我们进行图形界面编程时,通常不需要再费时费力地从头编写一个全新的Widget,大多数时候只需要使用官方提供的Widget进行组合,甚至直接使用模板就足够了。
这里只介绍一些常用的Widget及其子类,说明基本功能和使用场景。在后续教程里会对常用的Widget和模板进行详细介绍,并提供可参考的实例。


Text/文本

文本类Text主要用于文本呈现和处理。
基类为Text,有一个扩展子类:TextEdit

核心函数

构造函数
: 文本类的主要功能就是文本呈现,而类的构造函数可以直接定义文本该以怎样的形式呈现(字体,大小,内容,颜色)

Text/文本

Text只提供文本呈现功能,能够设置文本的字体,大小,内容和颜色。Text的使用范围十分广泛,任何需要呈现文字的地方都需要用到Text。

TextEdit/文本编辑

TextEdit在文本呈现的基础上,额外提供了文本编辑功能,主要用于各种输入框,如聊天输入框,控制台输入框等等。
它还有一个很特殊的扩展子类TextEditLinked,用于礼品兑换码的输入框,一般不使用。

FollowText/跟随文本

跟随文本类FollowText,和Text很像,但不是Text的子类。与Text的区别在于它的位置是动态变化的。常见应用于显示动作的名字,比如,当你手持斧头时,把光标移动到树上,会出现两个字——砍树。


Image/图片

图片类Image,主要用于图片呈现,没有子类。

核心函数

构造函数
: 图片类和文本类相似,主要功能就是图片呈现,可以定义图片的atlas文件和tex名。


Button/按钮

按钮类Button主要提供一个点击操作。通过设定点击操作触发的函数,让玩家可以通过执行某些功能。

类名 描述
ImageButton 图片按钮,最常用
TextButton 文字按钮,偶尔会用
ListCursor 列表游标,和拖动条联合使用。常用场景为服务器列表
AnimButton 动画按钮,极少使用
UIAnimButton 界面动画按钮,极少使用

基类为Button,子类如下表:

类名 描述
ImageButton 图片按钮,最常用
TextButton 文字按钮,偶尔会用
ListCursor 列表游标,和拖动条联合使用。常用场景为服务器列表
AnimButton 动画按钮,极少使用
UIAnimButton 界面动画按钮,极少使用

基类Button仅仅定义了点击触发函数的功能,但没有定义它该以何种形式来呈现在玩家操作界面上。在实际使用中,通常不直接使用基类Button,而是根据情况使用它的各种子类,其中最为常用的是ImageButton。

核心函数

按钮的主要功能就是在点击后触发函数,因此设置点击触发函数的函数就是核心函数。

SetOnDown( fn )
: 设置按下按钮弹起前触发的函数

SetOnClick(fn)
: 设置按下按钮弹起后触发的函数

以上两个设置函数里设置的fn,是没有参数的。

ImageButton/图片按钮

大多数按钮都属于图片按钮,可以直接在构造图片按钮实例时传入atlas(该按钮图片的统一管理xml文件), normal(一般状态下的图片,下面的类似), focus(聚焦状态), disabled(不可用状态), down(按下状态), selected(选中状态), image_scale(图片缩放大小), image_offset(图片偏移量)等一系列参数。
对于缺失的参数,会使用缺省值代替。所以在使用时,不写任何参数也是可以的。


Badge/徽章

徽章类Badge,主要用于呈现一个圆形物体的动画,常见应用是各种指示器:饥饿度、精神度、血量、木头值(吴迪),通过设置动画播放的百分比来指示某个数值的多少。

特别提醒:Badge使用的动画通常是逆过来的,也就是100%的状态在开端,0%的状态在结尾。这是为了保证状态为100%时一定能播放出相应的动画关键帧。(动画的0帧一定能播放出来,但最后一帧则不一定)。

类名 描述
HungerBadge 饥饿指示器
SanityBadge 精神指示器
HealthBadge 血量指示器
BeaverBadge 木头值指示器

基类为Badge,子类如下表:

类名 描述
HungerBadge 饥饿指示器
SanityBadge 精神指示器
HealthBadge 血量指示器
BeaverBadge 木头值指示器

核心函数

构造函数
: animname, states分别指明要使用的动画build/bank名(build和bank名必须一致)和anim名。通常的使用方式是,拓展成一个新的子类,在父类构造函数中填写animname和states,具体可以参考已有的官方子类。

SetPercent(val, max)
: 设置动画播放的百分比。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,520评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,029评论 4 62
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,358评论 0 17
  • 汉代·刘向《新序·杂事五》:“叶公子高好龙,钩以写龙,凿以写龙,屋室雕文以写龙。于是天龙闻而下之,窥头于牖,施尾于...
    清净墨莲阅读 459评论 0 0
  • 学生公寓楼下挂着一个牌子: 请毕业生尽快搬离学生公寓并到值班处退还寝室钥匙。 今年夏天悄然而至,如同往常一样,没有...
    大白兔奶精阅读 5,634评论 11 27