原文地址https://www.iddevnet.com/quake4/GUIEditor
QuakeIV 的gui跟doom3的关卡编辑器基本上一模一样,我觉得
反正都是doom3的引擎代码,挖过来,看一看id 的代码结构到底是什么样子的。
对于理解Doom3的代码有所帮助
本文档概述了在Quake 4中创建GUI的基础知识。游戏中存在两种类型的GUI:
整体UI GUI,如菜单系统和HUD,以及游戏中的电梯,电脑屏幕和其他交互式GUI等世界GUI。
本文档涵盖这两种类型,但往往强调菜单GUI,因为它们比世界GUI复杂得多。
但是,该信息适用于两种GUI类型。
Quake IV 中的所有* .gui文件可以由GUI编辑器或文本编辑器查看。
了解GUI脚本的最佳方法是查看Quake 4中使用的GUI文件。
要查看GUI,请提取pak001.pk4文件的内容并浏览到../q4base/GUIs文件夹。
基础
GUI编辑器可以通过editguis命令从控制台启动。您还可以通过添加+ editguis参数通过命令行启动它。
GUI编辑器包含四个主要区域:GUI显示,导航器,变压器和属性窗口。
该GUI显示 为编辑器的主要工作区域。该区域显示GUI中包含桌面的所有* Def元素。
可以在“ 视图”>“选项”下修改此区域的基本显示属性。
* Def元素作为蓝色框架显示在显示屏上,手柄控制在侧面和角落。
WindowDef(rect,visible等)的属性显示在“ 属性”窗口中。
单击属性对话框可以通过点击Alt + Enter或双击该元素来提高。
属性对话框包含三个选项卡:常规,图像和文本。
所有这些都包含元素的基本属性,将在后续章节中单独讨论该元素。
也可以在“属性”窗口中更改元素的属性,方法是单击所需的空间以更改值,
或单击空白处添加新属性。
的导航 显示GUI的* def组件树。元素是分层和嵌套的,使得在树下方的元素比先前的元素更高级,
并且嵌套在其他元素中的元素在其父元素之下缩进。
可以通过单击导航器中左侧显示的箭头折叠和展开父元素。
对于要完整显示的元素,其父窗口的大小必须大于或小于子元素。
可以在导航器中更改元素及其父子关系的顺序 - 请参阅“ 使用元素”部分。
与windowDef相关联的脚本显示在单独的编辑区域中,可以通过点击ctrl + Enter或右键单击显示
区域或导航器中的元素并选择“脚本”来启动。有关脚本的更多信息,请参阅脚本操作和动画。
基本的GUI结构和桌面
在此文档中,windowDef 用于引用任何* Def元素,因为它们共享几乎所有相同的属性。如果有所不同,那就会被注意到。
GUI由单个元素组成,其中大部分是windowDef。还有choiceDefs,editDefs和其他* Defs,如下
所述。所有windowDef包含在桌面,它是主GUI父元素。
当windowDef创建时,它嵌套在桌面下。WindowDefs可以嵌套在其他windowDef中,创建一个父子关系。
windowDef包含标志和脚本。标志是确定windowDef的基本属性(如大小,可见性,颜色等)的
值。脚本嵌套在windowDef的大括号内,并根据具体情况执行。
该桌面还包含浮动,vec4s和namedEvents的所有定义(参见第6节)。
如果您使用GUI编辑器,则通常不会手动编写一个windowDef(尽管您可以打开纯文本文件并以这
种方式手动编写GUI)。
但是,要知道什么是基本的 windowDef 脚本 看起来 像您必须进入脚本文本 本身并操纵的东西是有用的。
以下示例是GUI中几个简单的windowDef的脚本看起来如何:
windowDef元素1 { rect 0,0,640,480 可见1 backcolor 1,1,0,1 } windowDef element2 { 正零0,300,300 可见1 backcolor 1,1,0,0.5 文字“这是一些文字” textscale 0.5 forecolor 1,1,1,1 }
以下示例将是一个windowDef,它还包含一些执行操作的脚本:
windowDef元素1 { rect 0,0,640,480 可见1 backcolor 1,1,0,1 OnMouseEnter在 { 设置“backcolor”“1,1,1,1”; } onMouseExit { 设置“backcolor”“1,1,1,0.5”; } }
请参阅windowDef的每个可能标志的定义。
在Quake 4 GUI系统中可以创建两种GUI:菜单和HUD GUI和世界GUI。
菜单和HUD GUI用作基本游戏UI,
而世界GUI是玩家可以走上游戏并在游戏中使用以屏蔽世界的谜题,
获取信息或移动的屏幕(即,使电梯GUI玩家选择一个楼层移动到)。
使用元素
要移动* Def元素围绕显示区域,只需单击并拖动元素并移动。
要调整大小,只需点击并拖动一个* Def元素的控件句柄。
元素的位置和大小也可以通过“属性”对话框进行控制。
要创建另一个元素的子元素,首先选择所需的父元素。
然后按住CTRL,选择所需的子元素。然后右键单击并选择排列>制作小孩或按CTRL + \。
要反转此操作,请使用桌面重复此过程作为所需的父元素。
当选择元素时,选择的第一个元素将始终为采用操作时的父元素。
首先选择父元素后,可以通过选择多个元素来创建多个子元素。
元素的层次顺序可以在导航器中更改。
为此,选择一个元素,然后右键单击并选择排列 ,然后选择移动到前,后或前进或后退。
这也可以通过右键菜单中显示的键盘快捷键来完成。
GUI编辑器还允许您通过使用另一个元素作为参考点来调整大小或移动元素。
要使两个元素的大小相同,请选择要用作参考点的第一个元素,
然后按住CTRL选择要调整大小的第二个元素。
右键单击并选择“对齐”>“设置相同大小”。选择宽度,高度或两者以调整大小。
可以选择多个元素; 所选择的第一个元素将始终作为参考点,
并且所有其他所选元素将相应调整大小。
该对齐选项还允许您对齐沿主元素的左侧多个元素,右侧,顶部,底部或中心。
与其他行动一样,
使用纹理
元素参考材料与背景属性。GUI中使用的所有材料的大小必须大于2。
如果所使用的路径不是着色器路径,而是直接映射到图像的路径,则将使用默认着色器。
即使引用直接图像,最好在路径末尾离开.tga。
使用浏览器
点击CTRL + T 将显示GUI Viewer。这个窗口显示了加载的GUI,就像在游戏中显示一样,允许您测试GUI的功能。
编辑器选项
“ 视图 ”选项卡下有几个显示选项。放大和缩小是不言自明的。
隐藏/显示将隐藏或显示所选元素,或将隐藏所有元素。
显示轮廓将显示GUI中所有元素的轮廓。
显示网格将显示网格,这可以在视图>选项下自定义。
捕捉到网格将会将元素的位置捕捉到网格。
显示安全区域 仅用于为控制台开发的GUI,并显示哪些元素必须保留在内部的区域,以便不会被电视屏幕潜在地削减。
“ 工具”菜单选项下有两个选项。Viewer已经被讨论过了。
重新加载材料(CTRL + M)将重新加载GUI中使用的纹理。
如果您更改了纹理并希望快速刷新GUI以查看更改,这将非常有用。
在本地化当前GUI 选项会在GUI中的所有文本字符串,并保存他们,
如同* .lang文件中引用“#str_xxxxxx”的价值观。
这用于确保其他语言的GUI本地化容易。
- 使用通用动画在GUI中执行基本的,可重复的功能。
- 例如,如果您在菜单中使用弹出窗口,请创建单个动画或命名事件,将通用弹出图形提升,然
- 后设置将在if语句中检查的桌面浮动,以确定要在顶部显示的消息的。
-
组织GUI,并使用嵌套windowDefs的父子关系来保持组织。例如,如果您有几个动画
windowDef,请将它们全部放在标有“p_anims”的父窗口标记或类似的东西下。 -
如果您打算调整windowDefs通过转换频繁,它有助于创造一种无形的windowDef大小被确定
-
为新的大小,然后使用这个windowDef因此在过渡命令,
-
其中“window1_newsize”是无形的
windowDef:
转换“window1 :: rect”“$ window1 :: rect”“$ window1_newsize :: rect”“250”;
-
如果您的菜单变大,创建一些重定义动画或命名事件,将特定的windowDef组中的所有内容设
-
置为默认值是有用的。例如,您可能需要将按钮的屏幕重置为未高亮的默认状态。
-
使用动画,当你知道你需要一些事情发生在一段时间的步骤。
-
当您只想立即设置属性时,使用命名事件。
- GUI系统中的脚本中的语句被同时执行。如果您希望某些步骤发生在其他人之前,您必须使用
- 动画。
- 避免在动画中调用命名事件或使用设置的“cmd”或consoleCmd语句。如果一个帧被丢弃,并且
- 你的命令或命名事件调用在该帧中,它将不会被执行,并且会破坏GUI。
-
如果您 尝试加载GUI时您的GUIEditor崩溃,那可能是因为您的脚本错误。
-
最常见的罪魁祸首是大括号错误或不好的声明。
-
由于GUIEditor 缺少错误反馈系统,最好保存不同版本的gui文件,以防万一损坏。
-
尝试记住您正在工作的区域,如果可能,请使用高级编辑器(如Ultra Edit),以便您可以将文
-
件彼此进行比较,以发现可能导致崩溃/损坏的差异。
-
可以在桌面标题和元素属性中使用数学语句来实现某些效果,例如动态放置。
-
举个例子,看一下scoreboard.gui和mpmain.gui。不过要小心!
-
该GUIEditor 如果您打开编辑器中的桌面脚本窗口已经知道,在他们的数学陈述腐败桌面头。
-
如果发生这种情况,保存要复制和粘贴的桌面标题的副本通常是有帮助的。
例如,桌面头脚本如下:
windowDef桌面 { rect 0,0,640,480 可见1 nocursor 1 menugui 1 float“igOffset”282 - ((32 *“gui :: ig_awards”)/ 2) float“aim_fade_time”10000 - “gui :: main_notice_duration”
将会变成这样:(注意被剥离的引号,这将打破这些脚本参数)
windowDef桌面
{
rect 0,0,640,480
可见1
nocursor 1
menugui 1
float“igOffset”282 - ((32 * gui :: ig_awards)/ 2)
float“aim_fade_time”10000-gui :: main_notice_duration
-
桌面是一个windowDef ,就像其他的一样,它的属性可以这样修改。例如,如果要打开半透
-
明的叠加层,请将GUI的桌面设置为0,0,0,0.5的背景颜色。
-
GUI中的所有元素必须具有唯一的名称。当复制元素或创建新元素时,GUIEditor有时 会为其
-
提供唯一的名称,但有时不会。如果您的元素没有唯一的名称,则GUI的功能可能会被破坏。
-
如果您保存并关闭具有相同名称的多个窗口的gui,则可以手动编辑gui来修复此问题。但是,
-
如果您保存并关闭GUIEditor,您的工作可能会丢失。
- 必须将GUI变量显式发送到引用它们的每个GUI。如果您的GUI变量不起作用,请尝试检查代
- 码以确保将其发送到正确的GUI。
脚本操作和动画
在GUI中有两个基本的事情:
1.用户单击导致操作的元素
2.动作发送命令,执行单独的脚本或启动动画
为了实现这一点,windowDefs必须包含脚本。要添加脚本到windowDef在GUI编辑器,选择
windowDefs和命中Ctrl + Enter。然后在弹出的窗口中输入脚本。
可以在窗口中输入多个脚本函数,因此脚本窗口可能包含脚本,
例如onMouseEnter,onMouseExit和onAction(有关特定脚本函数的更多信息,请参见下文)。
脚本元素的语法如下所示:
OnScriptElementName { 命令“windowname :: parameter”“value”; }
常用脚本命令
设置和转换
两个最常见的脚本命令是设置和转换。两者都用于设置或更改windowDef标志的值。
脚本可以更改包含脚本或其他windowDef 的windowDef的标志。
如果脚本正在更改自己的windowDef的标志,则可以删除“windowname :: flag”语法,
并且只需要该标志(参见下面的示例)。
以下是“set”命令语法的一些示例。
-
设置“可见”“0”;
这将关闭包含脚本的windowDef的可见性。
-
设置“foo :: visible”“1”;
-
设置“cmd”“close”
这将发送关闭GUI的命令。
可以像用户在控制台中键入命令一样发送命令:
-
consolecmd“si_map mp / q4dm1”;
该特定命令将服务器映射设置为mp / q4dm1
转换用于随着时间的推移将一个标志值更改为另一个。
这通常用于鼠标悬停颜色更改和windowDef 大小更改,但也可用于其他情况。
转换命令需要两个值在一个时间之间进行转换,以执行以毫秒为单位的转换。
以下是“转换”命令语法的一些示例。
-
转换“matcolor”“1,1,1,1”“1,1,0,1”“100”;
这将在windowDef中将图像的颜色从白色转换为黄色超过100毫秒。以下功能执行相同的功能,但
只能转换windowDef的 alpha颜色:
-
转换“matcolor_w”“1”“0”“100”;
下列转换的大小windowDef从640×480到300×300超过500毫秒。
-
过渡“直角”“0,0,640,480”“0,0,300,300”“500”;
的setFocus
setFocus主要用于控制台菜单。此命令用于设置特定windowDef的鼠标焦点。语法如下:
-
setFocus“windowname”;
Stoptransitions
该stoptransitions命令来暂停动画或过渡。
这与设置notime标志类似,但主要区别在于设置不变,将允许动画完成所有的onTime步骤,
而stoptransitions将完全脱离onTime或转换语句。以下示例说明了其上下文:
windowDef按钮1 { 直线0,0,100,100 可见1 OnMouseEnter在 { //突出显示按钮 转换“matcolor_w”“0.5”“1”“150”; } onMouseExit { // unhighlight the button 转换“matcolor_w”“1”“0.5”“150”; } 的OnAction { //播放器选择按钮时停止所有动画和转场 按钮1“; } }
非交互
非交互命令用于使整个GUI非交互式,直到该标志设置为0,使其再次进行交互:
-
非交互式“1”;
脚本功能
OnMouseEnter在
-
指定当玩家移到windowDef上时发生的动作。
onMouseExit
-
指定当玩家从windowDef中移出时发生的动作。
的OnAction
onESC
-
通常在桌面中定义(对于控制台菜单,这也在其他windowDefs中定义)。定义当玩家击中
-
ESC键时要执行的操作。(对于在WindowDefs中使用的氙菜单,它定义了当玩家击中B按钮
-
时发生的操作。)
onNamedEvent
- 定义调用命名事件时要执行的操作。命名事件可以在桌面中定义,也可以是由GUI调用的代码
- 中的命名事件。
OnJoyUp,onJoyDown,onJoyRight,onJoyLeft
- 用于Xbox 360 GUI。定义当玩家使用D-pad时发生的动作。
onJoyButton1,onJoyButton2,onJoyRShoulder,onJoyShoulder
- 用于Xbox 360 GUI。定义当玩家分别按下X按钮,Y按钮,右肩和左肩按钮时发生的动作。
onJoyBack
- 用于控制台GUI。这是一个更通用的onESC脚本版本,当播放器按下控制器上的B按钮时,它将被执行。
脚本函数语法
下面的例子显示脚本窗口可能是什么样子的windowDef当玩家将鼠标移动到它,
远离它,并按下它,将执行的操作,导致两个命令来执行:
windowDef按钮1 { 正在0,0200,100 可见1 backcolor 1,1,0,1 文字“按钮1” textscale 0.26 字体“字体/低像素” OnMouseEnter在 { 转换“button1 :: backcolor”“1,1,0,1”“1,1,1,1”“100”; } onMouseExit { 转换“button1 :: backcolor”“1,1,1,1”“1,1,0,1”“100”; } 的OnAction { 设置“cmd”“play guisound_beep2; runscript map_hangar1 :: fire”; } }
onTime和动画
GUI通常使用动画来在屏幕之间转换或使背景元素动画化,在Quake 4 GUI系统中,onTime用于
定义将在指定的时间步长中发生的动画。准时使用就像在一个任何其它脚本元素windowDef:
windowDef foo { rect 0,0,1,1 可见0 不过0 onTime 0 { //这里要执行的语句 } }
注意:windowDefs不需要为其onTime脚本可见才能运行。
在具有设置为0,0,1,1的rect 属性的小
windowDef中放置动画是常见的,使其不可见,
然后使用resettime命令使其运行(请参见下面的resettime)。
语法可以用两种方法之一写成。
以下两个例子都做同样的事情:它们导致“foo”的背景颜色从白色与完整的阿尔法变为白色与零阿尔
法,然后回到完整的阿尔法,然后回到零阿尔法,最后回到完整的阿尔法。
实施例1
onTime 0 { 转换“foo :: backcolor_w”“1”“0”“100”; } onTime 100 { 转换“foo :: backcolor_w”“0”“1”“100”; } onTime 200 { 转换“foo :: backcolor_w”“1”“0”“100”; } onTime 300 { 转换“foo :: backcolor_w”“0”“1”“100”; }
实施例2
OnTime 0 { 转换“foo :: backcolor_w”“1”“0”“100”; } onTime +100 { 转换“foo :: backcolor_w”“0”“1”“100”; } onTime +100 { 转换“foo :: backcolor_w”“1”“0”“100”; } onTime +100 { 转换“foo :: backcolor_w”“0”“1”“100”; }
在第一个例子中,时间在脚本中是很难定义的。
在第二个例子中,每个onTime 在先前定义的动作之后+ X秒执行下一个动作。
决定要使用的语法取决于您要完成的任务。
第二种语法通常更容易用于非常大的动画,您可能需要调整各个时间步长,并且调整onTime脚本
中的所有时间都是不方便的。
动画可以通过resettime命令循环。
的重新设定时间 命令可以在一个工作时间脚本的末尾使用,
或者它可以从另一个脚本就像组命令被发送。
当在动画循环本身之外使用时,例如从另一个windowDef,resettime命令将从头开始一个动画。
以下是使用resettime命令循环的“白色带有零alpha”的白色完整阿尔法到动画的示例:
onTime 0 { 过渡“windowname :: backcolor”“1,1,1,1”“1,1,10”“100”; } onTime 100 { 过渡“windowname :: backcolor”“1,1,1,0”“1,1,1,1”“100”; } onTime 200 { 复位时间“0”; }
以下示例将从另一个windowDef的onAction脚本中重置名为“foo” 的windowDef中的动画时间:
的OnAction { 复位时间“foo”“0”; }
循环动画也可以用notime标志来停止。
如果您希望在玩家在GUI中某个地方点击windowDef时停止播放动画,
则语法将如下所示:
的OnAction { 设置“windowname :: notime”“1”; }
不过也可以将其设置为windowDef的标志,以保持动画不会运行,直到resettime命令发出:
windowDef animation_alpha { rect 0,0,1,1 可见0 不时1 onTime 0 { 转换“foo :: matcolor_w”“1”“0”“100”; } }
如果声明
if和else if 语句可以在GUI系统中使用。
他们的语法是基本的 以下示例使用if语句来检查桌面浮点数,
然后根据该值执行命令:
if(“desktop :: float1”== 0){ 设置“foo :: backcolor”“1,0,0,1”; } else if(“desktop :: float1”== 1){ 设置“foo :: backcolor”“1,1,1,1”; } else { 设置“foo :: backcolor”“1,1,0,1”; }
如果语句可以更高级,并检查值的范围。
您可以检查大于,小于或大于/小于或 ==的值。您也可以检查Not值。大于或等于由> =表示,
而不等于由!=表示高级值检查的示例。
if(“desktop :: float1”> = 5){ 设置“foo :: backcolor”“1,0,0,1”; } else if(“desktop :: float1”<= 2){ 设置“foo :: backcolor”“1,1,1,1”; } else if(“desktop :: float1”!= 8){ 设置“foo :: backcolor”“1,0,1,1”; } else { 设置“foo :: backcolor”“1,1,0,1”; }
它们也可以用来检查和案件或者案件。而且案件都是通过集合分隔检查&&同时或者案件由分隔||
if(“desktop :: float1”== 0 &&“desktop :: float2”== 0){ 设置“foo :: backcolor”“1,0,0,1”; } else if(“desktop :: float1”== 2 ||“desktop :: float2”== 2 ||“desktop :: float3”== 2){ 设置“foo :: backcolor”“1,1,1,1”; } else { 设置“foo :: backcolor”“1,1,0,1”; }