请选择 进入手机版 | 继续访问电脑版

GA地精研究院

 找回密码
 立即注册
查看: 17060|回复: 59

[教程] StarCraft2 地图编辑器 Galaxy教程

[复制链接]
发表于 2012-2-13 18:46:57 | 显示全部楼层 |阅读模式
本帖最后由 疯人¢衰人 于 2012-12-10 10:29 编辑

本教程出自Goblin Academy -- 地精研究院,作者:疯人¢衰人
转载请保留
这里提供文档版本的教程,分为Docx版本和PDF版本
如各位发现错误,欢迎批评指正!

目录
零.序言…………………………………………………………………… - 楼主 -
一.Galaxy概述…………………………………………………………... - 沙发-
         1.1 Galaxy与GUI界面...………………………………………………- 板凳 -
         1.2 编写Galaxy的工具..………………………………………………- 地板 -
         1.3 SE的了解程度.…………………………………………………… - 地下室 -
二.变量..…………………………………………………………………… - L5 -
         2.1、Galaxy的变量类型……………………………………………... - L6 -
         2.2 几个常用的变量类..……………………………………………… - L7 -
         2.3 变量的声明…………………………………………………………- L8 -
         2.4数组的声明………………………………………………………… - L9 -
         2.5 结构体……………………………………………………………… - L10 -
         2.6 赋值、表达式和类型转换………………………………………… - L11 -
三.简单的代码……………………………………………………………… - L12 -
         3.1 基本语句…………………………………………………………… - L13 -
         3.2 注释………………………………………………………………... - L14 -
         3.3 顺序结构…………………………………………………………... - L15 -
         3.4选择结构………………………………………………………..…... - L16 -
         3.5 循环结构…………………………………………………………... - L17 -
四.自定义函数与触发……………………………………………………... - L18 -
         4.1 GUI下的地图脚本..…………………………………………………- L19 -
         4.2 Galaxy下的函数…………………………………………………… - L20 -
         4.3 全局变量与局域变量以及函数参数类别…………………………. - L21 -
         4.4 Galaxy下的触发与SC2地图的脚本结构…………………………. - L22 -
         4.5 函数的数据存储及函数间的数据传输………………………………- L23 -
五.算法…………………………………………………………………………- L25 -
         5.1 通过一个程序来讲解算法…………………………………………... - L25 -
         5.2结构化的算法………………………………………………………… - L26 -
         5.3 计算相关算法讲解…………………………………………………… - L27 -
         5.4 面向对象算法讲解……………………………………………………  - L28 -
         5.5 综合应用………………………………………………………………. - L29 -
         5.6 Debug方法…………………………………………………………….. - L30 -
         5.7 GUI下的自定义函数、事件、条件、动作以及库的制作…………... - L31 -
         5.8 良好的编程习惯………………………………………………………… - L32 -
  

零.序言


    “教程之前要吐槽几句,这貌似是个惯例。”

    “如果哪里你看不懂,请先:内事不明问谷哥,外事不明问度娘,啥也不知道问GA。”

    “因为我不是计算机专业的,很多定义与名词可能不符合编程习惯,请大家谅解。”
   
    “教程中没有提到,但是C语言语法支持的功能大部分都不被Galaxy支持,我也许会有疏漏的地方,欢迎各位SEer指出,我将会一一添加到教程之中。”

    “引用的部分,可能不是Galaxy语法,这点请大家注意。”

    “Word版教程中,引用部分使用[ quote]和[/ quote]标注,代码使用[ code=galaxy]和[/ code]标注。”

    “这是一篇Galaxy教程,需要一部分基础知识,如果你什么也不知道的话,请看玻璃渣的官方教程,大体了解SC2和SE后再看此篇教程。”
   
    “教程的语法说明部分,采用引用格式,加粗字体为相关说明,非加粗字体部分为固定格式。”

    “如果你真的看不懂,可以参看一些C语言编程书籍作为参考。”

    “感谢AMOcccty1l对此教程的帮助。特别感谢AMO绘制封面,尽管还是使用自己用Word艺术字做的最土的一幅。”



Galaxy教程.rar

1.41 MB, 下载次数: 1062

评分

参与人数 1威望 +6 收起 理由
oneonestar + 6 已阅

查看全部评分

 楼主| 发表于 2012-2-13 18:56:30 | 显示全部楼层
一.Galaxy概述

        Galaxy是什么?谷哥的答案是银河,度娘的答案也是银河,Galaxy的翻译确实是银河,除此之外还貌似是某款手机的名称。不过这些解释这跟我们没有什么关系。因为GA娘给出了正确答案,Galaxy是玻璃渣(Blizzard)出品的游戏SC2(StarCraftⅡ——星际争霸2)的一种类似C的脚本语言。

         《星际争霸2》官方第52批FAQ中对Galaxy的描述如下:
Q:星际2的地图编辑器还是使用魔兽争霸3的JASS程式语言吗,还是一种新版本的语言?
A:星际2的地图编辑器使用一种全新的脚本语言,我们把它叫做Galaxy——银河。这是一种很接近于C语言的语言,任何熟悉C语言的人对Galaxy都可以很快上手。

Q:它是事件驱动的还是面向对象的?
A:虽然多数本地函数是基于对游戏对象操作的,但Galaxy语言本身不是面向对象的。

        这些古老的信息大约只能用于提升考古学经验,对于我们来说Galaxy是我们在制作SC2地图中使用的一种脚本语言,我们使用它来描述如何实现我们需要的效果。你完全可以近似的认为Galaxy是一种面向对象的编程语言——一种需要SC2客户端支持的编程语言。

        在制作SC2地图的过程中,Galaxy起着不可替代的作用,尽管你有可能使用的是GUI界面下的触发编写,但是其实质上还是Galaxy。Galaxy可以说是SE中功能最强大的部分,它涉及到整个地图制作的方方面面,Galaxy编写的地图脚本将地图的其他方面,如Actor、单位数据等串联起来,才构成一张完整的地图。

        不过,对比War3(Warcraft III——魔兽争霸3)的地图,SC2的地图对于脚本支持的需求略小一些。很多内容已经不是必须用脚本来实现,使用SE(StarCraft II Editor的简称)的数据编辑器即可,不可否认,SE的数据编辑器相当强大。当然这不是说你可完全脱离Galaxy来制作地图。当你为了实现需要的效果绞尽脑汁仍然毫无办法时,尝试使用Galaxy吧。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:20:21 | 显示全部楼层
1.1 Galaxy与GUI界面
        GUI是什么?

图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面。与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受。
——度娘

       注意:本教程中的GUI特指使用数据编辑器的GUI界面编写地图脚本。
1-1.png
图1-1 简体中文触发编辑器(GUI)界面


        GUI与Galaxy是互为表里关系,GUI的本质不过是由触发编辑器自动生成Galaxy脚本而已。
        让我们看一下一张新建地图的脚本:
       (方法为在选择任意非库的触发、动作后,按Ctrl +F11键。)

代码 1-1 一张新建地图的原始Galaxy脚本(由英文语言的编辑器创建)
[codes=galaxy]
//==================================================================================================
//
// Generated Map Script
//
// Name:   Just Another StarCraft II Map
// Author: Unknown Author
//
//==================================================================================================
include "TriggerLibs/NativeLib"

//--------------------------------------------------------------------------------------------------
// Library Initialization
//--------------------------------------------------------------------------------------------------
void InitLibs () {
    libNtve_InitLib();
}

//--------------------------------------------------------------------------------------------------
// Trigger Variables
//--------------------------------------------------------------------------------------------------
trigger gt_MeleeInitialization;

//--------------------------------------------------------------------------------------------------
// Trigger: Melee Initialization
//--------------------------------------------------------------------------------------------------
bool gt_MeleeInitialization_Func (bool testConds, bool runActions) {
    // Actions
    if (!runActions) {
        return true;
    }

    MeleeInitResources();
    MeleeInitUnits();
    MeleeInitAI();
    MeleeInitOptions();
    return true;
}

//--------------------------------------------------------------------------------------------------
void gt_MeleeInitialization_Init () {
    gt_MeleeInitialization = TriggerCreate("gt_MeleeInitialization_Func");
    TriggerAddEventMapInit(gt_MeleeInitialization);
}

//--------------------------------------------------------------------------------------------------
// Trigger Initialization
//--------------------------------------------------------------------------------------------------
void InitTriggers () {
    gt_MeleeInitialization_Init();
}

//--------------------------------------------------------------------------------------------------
// Map Initialization
//--------------------------------------------------------------------------------------------------
void InitMap () {
    InitLibs();
    InitTriggers();
}[/codes]

        代码1-1的内容就是图1-1显示的GUI界面下触发编辑中的内容。也就是我们存储在地图文件根目录下的MapScript.galaxy文件的内容。

        从某些方面来讲,对比War3的地图编辑器WE(World Editor),SE在触发编辑器的功能方面有很多强化的地方。以至于我们完全可以只使用GUI界面来制作地图脚本,如果你有这样的打算,我还是建议你学习一部分Galaxy,因为GUI中也有直接写Galaxy的地方(一般称为自定义脚本)。

        使用GUI和直接写Galaxy脚本的差异在于:
               使用GUI相对较为安全,出现语法问题的可能性较低;缺点是编写速度较慢,大部分情况下执行效率较直接写Galaxy略低。
               使用Galaxy编写地图脚本比较快捷,执行效率高。缺点是很容易出现语法问题,Debug难度较大,需要一部分英语水平。

               具体选择哪种编写脚本方式,请自行选择。你也完全可以混合使用。需要明确说的一点,不同于WE下J(Jass)和T(Trigger,触发)在制作地图中实现能力上的差别,使用GUI 完全可以实现Galaxy所能实现的内容。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:20:45 | 显示全部楼层
1.2 编写Galaxy的工具
        这里给出两种选择:
        1、一般来说,完全可以直接使用SE来编写Galaxy。
        2、也可以使用Galaxy++ Editor来编写Galaxy脚本。

        Galaxy++ Editor支持类C的自定义语法、函数名提示、语法检查及其他功能,并有可见即可得的对话框设计工具。
        官方说明地址(英文):
                http://www.sc2mapster.com/forums ... 9619-galaxy-editor/
                http://www.sc2mapster.com/assets/galaxy-editor-beier/
        下载地址:
                ftp://46.163.69.112/Releases/

        注:Galaxy++ Editor++ 需.net4.0完整版本支持,使用课件及可得的对话框设计工具还需要Microsoft XNA Framework Redistributable 3.1的支持。

                Microsoft .NET Framework 4(独立安装程序)下载地址:
                http://www.microsoft.com/downloa ... 0-919F-B21F31AB88B7
                Microsoft XNA Framework Redistributable 3.1下载地址:
                http://www.microsoft.com/download/en/details.aspx?id=15163

点评

如何通过SE直接新建 stript的脚本文件呢?我里面只能找到.map的文件啊  详情 回复 发表于 2014-11-10 02:35
下载的密码是啥呀?  详情 回复 发表于 2014-9-3 00:12
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:20:58 | 显示全部楼层
1.3 SE的了解程度
        Galaxy是面向对象的脚本语言,它不能脱离SE的其他方面单独存在,更多的情况下,你在编写Galaxy的同时,还需要处理其他的一些内容,如通过数据编辑器设置单位、设置Actor等等。因此学Galaxy需要有一定的SE基础。
        那么,我们就来看看你有怎样的水平。下面从各个方面描述了SE的基础知识,请实际的判断自己是否了解。

        ①        SE基础应用:
                ⑴        打开关闭SE。
                ⑵        保存打开地图。
                ⑶        SE升级。
                ⑷        添加并切换语言包。
        ②        地图地形
                ⑴        修改地面纹理、地形高度。
                ⑵        水体的设置与添加。
                ⑶        添加单位与装饰物。
                ⑷        设置地图属性,包括地图大小,玩家属性等。
                ⑸        放置并使用点、区域、路径、镜头。
        ③        数据编辑器
                ⑴        了解对战、战役中各个单位、技能的基本数据。
                ⑵        修改单位技能的数值属性。(伤害、血量)
                ⑶        完全新建一个单位。
                ⑷        新建或修改武器、技能、行为、效果。
                ⑸        能够设置足印、按钮等数据内容。
                ⑹        了解并掌握武器、技能、行为、效果,能够熟练的构建彼此间联系以实现需要的效果。
                ⑺        了解Actor(动作者)的基本设置。
                ⑻        简单的Actor事件处理。
                ⑼        处理单位动作者。
                ⑽        使用Actor部位运算。
                ⑾        使用修改其他类别的Actor。
        ④        触发编辑器
                ⑴        了解基本的事件、条件、动作。
                ⑵        了解各种变量类型、及使用数组变量,知道如何进行变量类型间的转换。
                ⑶        会使用If条件语句。
                ⑷        会使用while循环语句。
                ⑸        能够添加并控制单个、多个单位。
                ⑹        处理镜头。
                ⑺        对话框相关内容。
                ⑻        Datable(数据表)的存入读取。
                ⑼        Bank(数据集文件的使用)。
                ⑽        能够在GUI下独立编写程序。
                ⑾        了解Galaxy(自定义脚本)的使用。
                ⑿        自定义事件、条件、动作、函数、预设类型。

        本人水平有限,不能面面俱到的写出关于SE的各个方面内容。但以上条目基本上列举了SE的各方面知识、应用。

       我无法明确的告诉大家掌握了其中哪几条才能学习Galaxy,我只能说,你对这些内容掌握的越多,学习Galaxy的难度越小。最好是先学会使用GUI界面的触发编辑器使用,了解变量、触发等名称的含义,然后在学习Galaxy。当然,如果你有较高的编程水平,就当这句话我没说。

        官方基础教程链接:http://www.battlenet.com.cn/sc2/zh/game/maps-and-mods/
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:26:07 | 显示全部楼层
二.变量

        变量应该说是一个存储内容的箱子,我们可以把我们需要存储的内容放进去与取出来。你也可以将变量理解为一个数学表达式的未知数,如:X+1中的X。
那么我们为什么要使用变量呢?
        变量实质是编程集成化的最基本体现。变量的作用是描述一个编写时还没有确定的值在执行或运算过程中出现的位置。这样我们就不需要为每种可能的值单独编写一段代码了。这点与数学中的未知量近视。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:26:33 | 显示全部楼层
2.1、Galaxy的变量类型
         算法的处理对象就是数据,而数据有其特殊的存在方式。数据依据内容的不同区分成多种变量类型。变量类型其实就是变量能够存储的值的种类。Galaxy中的变量类型如下:
表2-1 Galaxy中的变量类型。
2-0.png
  

        表2-1内容排序依据SE中的变量类型名称顺序排序。

        大家仔细阅读的话,会发现有几种变量类型使用的是相同的变量类型,如传输信息、计时器窗口、行星、整数等都是采用了int变量类型;属性ID、水体、字符串等都采用了string变量类型。具体原因与存储方式有关,这里不做说明。

        变量类型有两类,一类是正常存储数据的变量类型,我称之为基础变量类型;一类是多个数据组合的复合类型的变量类型,我称之为复合变量类型。
关于这点大家可以先看Native.galaxy中的注释。

代码 2-1 Native.galaxy中有关变量类型的注释
[codes=galaxy]
//--------------------------------------------------------------------------------------------------
// About Types
//--------------------------------------------------------------------------------------------------
//
// -- Complex types and automatic deletion --
//
// Many native types represent "complex" objects (i.e. larger than 4 bytes).  The script language
// automatically keeps track of these objects and deletes them from memory when they are no longer
// used (that is, when nothing in the script references them any longer).  The types which benefit
// from automatic deletion are:
//
//      abilcmd, bank, camerainfo, marker, order, playergroup, point,
//      region, soundlink, string, text, timer, transmissionsource, unitfilter, unitgroup, unitref,
//      waveinfo, wavetarget
//
// Other object types must be explicitly destroyed with the appropriate native function when you
// are done using them.
//
//
// -- Complex types and equality --
//
// Normally, comparing two "complex" objects with the == or != operators will only compare the
// object reference, not the contained object data.  However, a few types will compare the contained
// data instead.  These types are:
//
//      abilcmd, point, string, unitfilter, unitref
//
// Examples:
//
//      Point(1, 2) == Point(1, 2)                              // True
//      "test string" == "test string"                          // True (note: this is case sensitive)
//      AbilityCommand("move", 0) == AbilityCommand("move", 0)  // True
//      Order(abilCmd) == Order(abilCmd)                        // False (two different order instances)
//      RegionEmpty() == RegionEmpty()                          // False (two different region instances)
//
//
// -- Complex types and +/- operators --
//
// Besides numerical types (byte, int, fixed), a few complex types support + and/or - operators:
//
//      string, text    + operator will concatenate the strings or text
//      point           +/- operators will add or subtract the x and y components of the points[/codes]

        对此注释的翻译(来源:http://bbs.islga.org/read-htm-tid-40285.html,由头目翻译):
//--------------------------------------------------------------------------------------------------
// 关于类型
//--------------------------------------------------------------------------------------------------
//
// -- 复合类型与自动删除 --
//
// 许多原生类型代表“复合”对象(通常大于4字节)。银河脚本语言会自动追踪这些对象,当他们不再被使用时就将他们从
// 内存中移除(“不再被使用”的意思是没有任何指向他们的脚本引用)。受益于自动删除的类型有:
//
// abilcmd, bank, camerainfo, marker, order, playergroup, point,
// region, soundlink, string, text, timer, transmissionsource, unitfilter, unitgroup, unitref,
// waveinfo, wavetarget
//
// 而如果你想干掉其余对象类型则需要使用与之对应的显式销毁函数来进行销毁。
//
//
// -- 复合类型与等值比较 --
//
// 通常的,两个“复合”类型之间进行==操作或!=操作只会比较两者的引用,而非其引用的对象的值。然而,少数对象类型却会直接对其引用的对象的值进行比较。这些类型是:
//
// abilcmd, point, string, unitfilter, unitref
//
// 举例:
//
// Point(1, 2) == Point(1, 2) // 真
// "test string" == "test string" // 真 (注意:大小写敏感)
// AbilityCommand("move", 0) == AbilityCommand("move", 0) // 真
// Order(abilCmd) == Order(abilCmd) // 假 (两个不同指令实例)
// RegionEmpty() == RegionEmpty() // 假 (两个不同区域实例)
//
//
// -- 复合类型与 +/- 操作 --
//
// 除了数值类型(byte, int, fixed)以外,少数复合类型也支持+或-操作:
//
// string, text + 操作用于合并string或text
// point +/- 操作将会对点的x,y坐标进行增减

        通过等值比较的说明,我们可以看到。复合变量直接存储的内容并非是存储的值。而是这些值的引用。

        如图2-1所示,如果不考虑从引用到存储数据之间的关系,复合变量可以看做存储内容为引用的基础变量,从内存上来说,复合变量的表也是与基础变量一样的。当调用复合变量时,多运行一步依照引用值获取实际存储内容的步骤。
        这些复合变量类型与基础变量类型在使用中的差别。将在讲解相关内容时说明。

        基础变量类型包括:byte,bool,int,fixed,string。其他基本都是复合变量类型。(此论断仅是个人臆断,没有对所有变量逐一测试。)
2-1.png

        图2-1存储关系为个人猜测,我的水平无法做实际验证,仅作为理解基础变量和复合变量差异的辅助。
回复 支持 0 反对 1

使用道具 举报

 楼主| 发表于 2012-2-13 19:28:12 | 显示全部楼层
2.2 几个常用的变量类型
        ①bool是布尔值的变量类型。
                具体的值为“true”(真)与“false”(假)。bool变量主要用于if语句。

        ② int是整数的变量类型。
                值范围为[-2147483648, 2147483648],计算超出范围会导致值的异常,例如2147483648 + 1 的计算结果显示为 -2147483648。
                输入值超出范围在编译时会有错误提示。int变量用处较多,主要常用于循环计数和数组变量的序号。
                除了支持10进制,Galaxy的整数变量还支持八进制与十六进制:
                以0开头的整数是八进制整数,如011代表十进制的9。
                以0x开头的整数是十六进制整数,如0x11代表十进制的17。
                int变量可以做bool使用,0为“false”其他整数为“true”。

         ③fixed是浮点变量类型,简单来说就是带小数的数值——实数。
                值范围为[-2147483648, 2147483648],输入超出范围在编译时会有错误提示。
                fixed变量主要用于实体数据(如:单位)的详细数据值。

          ④string是字符串变量类型。
                string变量主要用于路径名、类别名和datatable的识别符。
                string变量的内容需要用””括起来(注意是英文引号)。如”abc”
                string不支持中文及中文标点符号,但是采用其他方式可以实现中文效果。
                之前提到的Galaxy++ Editor可以将中文转换成对应的Unicode内码,将需要转换的内容赋值给一个string变量即可,然后输出文件中查找对应的语句即可。

⑤        text是文本变量类型。text主要用于名称等显示的文本数据。
关于文本输出的相关帖子。
http://bbs.islga.org/read-htm-tid-39031.html
http://bbs.islga.org/read-htm-tid-39032.html
http://bbs.islga.org/read-htm-tid-54289.html
http://bbs.islga.org/read-htm-tid-76992.html



        另外要特殊说明的是数组。
        数组是什么?
        数组是有序数据的集合。数组的每一个数据都属于同一个变量类型。用统一的一个数组名和若干下标类唯一的确定数组中的元素。
        你可以将数组理解为批量处理的变量。在Galaxy脚本编写的过程中,数组的使用频率非常高。关于数组声明等内容在2.4中讲解。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:28:24 | 显示全部楼层
2.3 变量的声明
        变量在使用前需要声明。所谓变量声明就是向内存申请存储空间的过程,并用变量名标记这个存储空间。我们通过使用这个变量名来获取其存储的值。

        变量声明其语法结构如下:
变量类型    变量名;

变量声明的同时进行赋初值:
变量类型 变量名    =    初始值;

        如:
代码2-2 变量声明举例
[codes=galaxy]int a;
string b = "Yes";[/codes]
        变量类型参考2.1、2.2节中所述,变量名需要注意以下几点:
       变量名只能使用字母、数字及下划线“_”。
        变量名必须以字母开始。不能使用数字或者下划线“_”。
        变量名不能重复。包括不同变量类型的变量。
        变量名区分大小写,如 a与A是两个变量。
        变量不能使用关键词,如int等。
        Galaxy不支持多个函数同时声明,如“int a,b;”这种。

        注意, GUI下声明无此要求,原因是在GUI下声明的变量会自动转换成符合以上内容的变量名。如名为“无标题的变量 001”的变量会被自动转换转换成名为“gv_e697A0E6A087E9A298E79A84E58F98E9878F001”的变量。
        Galaxy下变量请在初始赋值后使用,GUI界面下的变量声明一般会自动赋初始值。

代码 2-3 GUI界面声明全局变量的Galaxy脚本
[codes=galaxy]//--------------------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------------------
string gv_s;

void InitGlobals () {
gv_s = "";[/codes]

代码 2-4 GUI界面声明局域变量的Galaxy脚本
[codes=galaxy]// Variable Declarations
string lv_s;

// Variable Initialization
v_s = "";
}[/codes]

        在变量声明时还可以加入一个特殊的前置标识符——“const”。其意义是表达这里声明的是一个常量。
        常量,顾名思义,就是保持不变的变量。常量多用于固定参数。如圆周率等。
        常量的声明方式如下:
        const    变量类型    变量名    =    初始值;

代码 2-5 常量声明举例
[codes=galaxy]const int x = 3;[/codes]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:37:48 | 显示全部楼层
2.4 数组的声明
        一维数组的声明方式:
变量类型    [数组大小]    变量名;

        如:
代码2-6 数组声明与赋值举例
[codes=galaxy]int [3] I;
I[0] = 2;
I[1] = 1;
I[2] = 3;[/codes]

        数组的声明,就不是单一的申请存储一个数据的空间,而是一系列数据的空间。代码2-6中实际存储的值如表2-2所示。 申请的是三个连续的空间。
表 2-2 数组的存储结构

2-00.png

        声明数组基本上与声明正常变量类似,需要额外注意的共有两点:1、数组不能动态创建。每一个数组的长度在脚本编写时就必须确定。可以使用整数常量作为数组长度。数组序号从0开始,代码2-6中声明的数组,其可用的数组元素为I[0],I[1],I[2],一共三个,如果你使用I[3],你会得到一个错误提示:(台服客户端)
嘗試進入一個超出陣列的元素。

        另外声明一个数组变量是无法赋初始值的。

        多维数组的声明与一维数组近似:        
变量类型    [数组大小1]……[数组大小N]    变量名;


        “[数组大小1]……[数组大小n]”是数组维度,说明此数组有N个存储维度。如声明一个二维整数数组I,维度大小分别为2、3,那么声明语句如下:

代码2-7 多维数组声明举例:
[codes=galaxy]        int [2][3] I;[/codes]
表 2-3 二维数组的存储结构
2-000.png


        如表2-3所示,二维数组I共有2×3=6个元素。依次为I[0][0]、I[0][1]、I[0][2]、I[1][0]、I[1][1]、I[1][2]。三维及以上维度的的数组以此类推。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:41:47 | 显示全部楼层
2.5 结构体
       在Galaxy中,除了以上提到的变量类型,还可以讲多种不同类型的数据组合成一个新的整体,以便引用。这样的数据结构在C语言中被称为结构体。

        结构体变量类型声明方式:
        struct    结构体变量类型名
{
       结构体成员1;
        结构体成员2;
        ……
};

代码 2-8 结构体类型的声明实例:
[codes=galaxy]
struct variable
{
        int a;
        string [3] b;
        string [3] c;
};[/codes]

        代码 2-8 就是一个简单的结构体类型声明。注意这里声明的是结构体类型,而不是一个结构体本身。如果你需要使用结构体,你还需要如下语句定义结构体变量。

代码 2-9 结构体定义的实例:
[codes=galaxy]        variable a;
        variable [2] b;[/codes]

        关于声明结构体变量类型,有如下需注意内容:
                1、结构体变量类型中的成员可以有若干个。这些成员可以是普通变量、数组、甚至是使用另外一个结构体变量类型声明的结构体。
                2、如果你声明的结构体变量类型的成员中有结构体。那么这个成员结构体的变量类型需要在当前声明的结构体变量类型声明语句之前声明。具体可参看代码2-8、代码2-9。如果顺序错误,会无法编译。
                3、结构体变量名的要求与普通变量相同。
                4、注意,不同于if结构、while结构或自定义函数中使用的{}。定义结构体变量类型时必须要在}后面加上一个“;”。否则编译错误。
                5、结构体变量类型名不能与其成员名相同。否则编译错误。
                6、结构体只能在全局范围内声明,不能再函数范围内声明。
                7、结构体成员不能有初值。
                8、结构体变量类型声明时,不能同时定义结构体变量。

代码 2-10 正确的声明方式。
[codes=galaxy]struct variable_1
{
                int a;
};

struct variable_2
{
                int a;
                variable_1 b;
};
[/codes]

代码 2-11 错误的声明方式。
[codes=galaxy]struct variable_2
{
                int a;
                variable_1 b;
};

struct variable_1
{
                int a;
};
[/codes]

        结构体变量的使用,其实跟正常的变量一样。比如我们使用代码 2-9中的结构体变量类型定义一个结构体变量x。若我们需要对其成员结构体b的成员a赋值1,我们可以使用这样的语句。
        x.b.a = 1;

        使用结构体变量需注意以下几点。
                1、结构体不能作为自定义函数的参数类型或返回类型。
                2、如果结构体变量或其结构体成员、普通变量成员是数组,那么需要对应添加其数组序号。如:a[1].b[2].c[3]。

        从某些意义上将,结构体的实用性不高。除非多层结构体(即结构体变量类型的成员中包含结构体),否则结构体的更大的作用是增加代码的可读性。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:45:36 | 显示全部楼层
2.6 赋值、表达式和类型转换
        赋值就是用变量保存对应类型值的过程。赋值符号为“=”。如“a = 1;”含义是将数值1赋值给变量a。注意赋值过程是把等号右边的值赋给左边的变量。所以赋值的语法格式如下:
        变量    =    需要赋的值;

        注意赋值语句与声明语句一样需要用“;”作为结束标记。

        “需要赋的值”可以是一个表达式。但等号左右的变量类型需相同或兼容。具体兼容的变量类型将在后面变量类型转换中说明。

        关于表达式,请先看度娘说明:

表达式
  引表达式,是由数字、算符、数字分组符号(括号)、自由变量和约束变量等以能求得数值的有意义排列方法所得的组合。约束变量在表达式中已被指定数值,而自由变量则可以在表达式之外另行指定数值。
  给与自由变量一些数值指定,可能可以给与一个表达式数值,即使对于一些自由变量的值,表示式或许没有定义。因此,一个表达式代表一个函数,其输入为自由变量的定值,而其输出则为表示式因之后所产生出的数值。
  举例来说,表达式x / y,分别使自由变量x和y定值为10和5,其输出为数字2;但在y值为0时则没有定义。
  一个表达式的赋值和算符的定义以及数值的定义域是有关联的。
  两个表达式若被说是等值的,表示对于自由变量任意的定值,两个表达式都会有相同的输出,即它们代表同一个函数。
  一个表达式必须是合式的。亦即,其每个算符都必须有正确的输入数量,在正确的地方。如表达式2+3便是合式的;而表达式*2+则不是合式的,至少不是算术的一般标记方式。
表达式和其赋值曾在一九三○年代由阿隆佐•邱奇和Stephen Kleene在其Λ演算中被公式化。Λ演算对现代数学和电脑编程语言的发展都曾有过重大的影响。
——度娘

        Galaxy中的表达式也是由操作数和运算符构成。
        Galaxy中支持的运算符包括:
  1.算术运算符:*  -  +  /  %
  2.关系运算符: >  <  ==  !=  >=  <=
  3.逻辑运算符:!  &&  ||
  4.位运算符:<<  >>  ~  |  ^  &
  5.赋值运算符:=及扩展赋值运算符
  6.结构体运算符:.
  7.下标运算符:[ ]
       8.其他:如函数调用运算符:()

运算符的优先级和结合性如下:
       优先级【高到低】:
  第一级:
  圆括号【()】、下标运算符【[]】、结构体成员运算符【.】
  第二级:
  逻辑非运算符【!】、按位取反运算符【~】、负号运算符【-】
  第三级:乘法运算符【*】、除法运算符【/】、取余运算符【%】
  第四级:加法运算符【+】、减法运算符【-】
  第五级:左移动运算符【<<】、右移动运算符【>>】
  第六级:关系运算符【<  >  <=  >= 】
  第七级:等于运算符【==】、不等于运算符【!=】
  第八级:按位与运算符【&】
  第九级:按位异或运算符【^】
  第十级:按位或运算符【|】
  第十一级:逻辑与运算符【&&】
  第十二级:逻辑或运算符【||】
  第十三级:赋值运算符【= += -= *= /= %= >>= <<= &= |= ^=】
  说明:
  ①G1不要求运算对象的个数,G2是单目运算符,,其他都是双目运算符。
  ②G2条件运算符、G14赋值运算符是自右向左的【也就是右结合性】,其他都是自左向右【左结合性】
  归纳各类运算符【高到底】:
  初等运算符【()、[]】 G1
  单目运算符 G2
  算术运算符(先乘除【取余】,后加减) G3,4
  位运算符【<< >>】 G5
  关系运算符 G6,7
  位运算符【递减& ^ |】 G7,8,9
  逻辑运算符(不包括!) G11,12
  赋值运算符 G13

        下面我们就来详细说明变量类型的兼容和转换。
        能够兼容的变量类型只有int和fixed两种。确切的说,一个int类型的变量可以作为fixed类型的变量来处理。反之不行。同样也只有int变量能够与fixed在一个运算符两边,运算结果也是fixed类型的。

代码 2-12 变量类型兼容性
[codes=galaxy]        int a;
        fixed b;
        b = a + b;
        a = a + b; &#8218;
[/codes]

        其中“b = a + b;”正确,“a = a + b;”错误。

        除兼容性的自动转换,变量类型可以做强制转换,有如下几种:

代码 2-13 变量类型转换函数
[codes=galaxy]//--------------------------------------------------------------------------------------------------
// Conversions
//--------------------------------------------------------------------------------------------------
native int      BoolToInt (bool f);

native fixed    IntToFixed (int x);
native string   IntToString (int x);
native text     IntToText (int x);

native int      FixedToInt (fixed x);
native string   FixedToString (fixed x, int precision);
native text     FixedToText (fixed x, int precision);
native text     FixedToTextAdvanced (fixed inNumber, int inStyle, bool inGroup, int inMinDigits, int inMaxDigits);

native int      StringToInt (string x);
native fixed    StringToFixed (string x);[/codes]

        以上为Native.galaxy中类型转换函数声明。可以参考GUI界面对于的转换动作来使用。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:47:52 | 显示全部楼层
三.简单的代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:48:28 | 显示全部楼层
3.1 基本语句
        Galaxy中一共有四种基本语句。分别为声明语句、赋值语句、执行语句、控制语句。
        声明语句,顾名思义,就是声明变量、函数、引用的基本语句。
        赋值语句,之前提到过,基本结构为“变量 = 可以作为值的内容;”。
        执行语句,一般为函数调用。
        控制语句,包括“if”选择结构和“while”循环结构的语句。
        除此四种外,还有单独成行的“{”或“}”、空白行、注释。

        在这些基本语句中,只有函数的定义语句(直接写函数体的),选择结构“if”或者循环结构“while”的“{”“}”,结构体声明的“{”,include语句,空白行、以及注释不需要用英文的“;”作为结尾,其他全部需要。否则会在编译时报错。
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2012-2-13 19:50:44 | 显示全部楼层
3.2 注释

        注释就是对代码的解释和说明。目的是为了让别人和自己很容易看懂。为了让别人一看就知道这段代码是做什么用的。
——度娘

        Galaxy中只有行注释,并且SE的Galaxy注释不支持中文。
        注释的语法格式为
//注释内容……

注释符号“//”的有效范围只有当前行第一个注释符号“//”之后的全部内容。多行注释需要每行开始都添加“//”符号。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 19:51:30 | 显示全部楼层
3.3 顺序结构
        关于Galaxy下的代码,共有三种结构:顺序结构、选择结构、循环结构。下面我们就来依次介绍这三种结构的使用。

        三种结构中,最为基本、最为常用的是顺序结构。所谓顺序结构,就是按照代码顺序依次执行的结构。

        关于顺序结构,貌似没有什么好说的。根据想好的内容,依次描述即可。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 20:00:27 | 显示全部楼层
3.4选择结构
        选择结构有两种,语法结构为:
        if (逻辑值或逻辑表达式)
        {
                语句1;
        }
        if (逻辑值或逻辑表达式)
        {
                语句1;
        }
        else
        {
                语句2;
}


        其含义为,如逻辑值或逻辑表达式的值为true,那么执行语句1,如果有else之后的部分,那么如果逻辑值或逻辑表达式的值为false,那么执行语句2。

多次选择可以使用:
        if (逻辑值或逻辑表达式)
        {
                语句1;
        }
        else if (逻辑值或逻辑表达式)
        {
                语句2;
        }
        ……
        else if (逻辑值或逻辑表达式)
        {
                语句n;
        }

        else
        {
                语句n+1;
        }

        其中可以使用若干个“else if()”注意其中间需要有个空格。另外,如果使用“else”,则必须为最后一个,否则语法错误。

        if语句可以嵌套使用,详见代码3-1。

代码3-1 if语句实例
[codes=galaxy]    if (X > Y)
    {
        if (X > Z)
        {
                OutPut = X;
        }
        else
        {
        OutPut = Z;
        }
    }
    else
    {
        if (Y > Z)
        {
                OutPut = Y;
        }
        else
        {
                OutPut = Z;
        }
    }[/codes]

        代码3-1 是一个3个变量X、Y、Z比较大小的代码。结构很简单,依次比较而已。先比较X和Y,再用其中最大的一个与Z比较。如此获得最大的值。

        在这里要重点说下逻辑表达式。
        与逻辑表达式有关的运算符有两种,分别为关系运算符和逻辑运算符。
        关系运算符是二目运算符,参与运算的两个操作数需要为同变量类型的变量,计算结果为布尔值(bool)。请参看2.6节中的表达式优先级等内容。关于复合类型与等值比较请参看2.1节中变量类型注释。

        逻辑运算符不同于关系运算符。逻辑运算符中!为单目运算符,&&与||为二目运算符。
        逻辑运算符真值表如下:
表3-1 逻辑非运算符(Y = !X)真值表
3-0.png
表3-2 逻辑与运算符(Z = X && Y)真值表
3-00.png
表 3-3 逻辑或运算符(Z = X || Y)真值表
3-000.png

        关于逻辑运算的详细内容,请自己询问度娘或者谷哥。
        注意:逻辑运算采用短路模式,即当整个表达式计算到某一段时,已经确定结果,就不会进行额外的计算,这种现象出现在与(&&)和或(||)运算中。
        如“false && (X > Y && X > Z)”与“true || (X > Y && X > Z)”中后面的“(X > Y && X > Z)”并不会实际参与运算。
        测试代码如下:
代码3-2 逻辑运算短路测试代码
[codes=galaxy]
void a()
{
    bool [2] b;
    if (true || b[2])
    {
    }
}
[/codes]
执行时没有错误提示。
奇怪么?实际上b[2]已经超过数组上限,但是因为短路,所以SC2并没有调用这个数据,结果执行过程并没有错误提示。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 20:07:28 | 显示全部楼层
3.5 循环结构
        循环结构可以说是新手学习Galaxy的第一个难点,尽管循环并没有多少难度,但是确实有不少WEer不会使用(SE现在的用户不多,不好下定论)。也许原因是循环结构的使用方式,更为抽象一些,并不像顺序结构与选择结构那么直观。事实上选择是否使用循环结构的判断条件就是你所要执行的代码之中是否存在一种规律,这种规律有两个条件:代码结构相同,部分值或结构有规律变化。
        这样抽象的说也许会让你更加的糊涂。我们举例来说明。

        比如,你购买东西时需要付款10¥,你拿出两张5¥的,共计10¥。这个过程用顺序结构描述的话,就是:
        付款5¥。
        付款5¥。

        这样描述似乎比较麻烦,我们可以这样描述。

        付款2次,每次5¥。


        这就是将代码抽象成循环的过程。循环使代码更为简洁,少量的循环也许看不出来,如果你需要付款1000¥,每次5¥,共计200次,这样的情况下,循环的作用便清晰可见。那么能够抽象成循环的代码必须是完全相同吗?不是。你可以这样付款:
        付款1¥。
        付款2¥。
        付款3¥。
        付款4¥。

        抽象成循环:

        付款4次,第一次1¥,每次多付1¥。


        这种才是最常使用的循环。相同结构,有规律的变化。然而,这“有规律的变化”也并非必备的。如此情况的付款:
        付款1¥。
        付款3¥。
        付款3¥。
        付款2¥。
        付款1¥。

        抽象成循环:

        付款5次:
                第一次1¥;
                第二次3¥;
                第三次3¥;
                第四次2¥;
                第五次1¥;


        看到这里你会问一句:“这样岂不是更麻烦了吗?”确实是更加麻烦了。所以在实际应用中是否选择使用循环,我们需要考量。评判是否使用循环,依据是执行效率。这点就需要个人在使用Galaxy过程中慢慢体悟了。

        Galaxy中的循环语法只有一种:
        while (逻辑值或逻辑表达式
        {
                循环代码;
                continue;//依据需要使用。
                break;//依据需要使用。
        }

        我们依照付款的实例来写个循环结构的代码。

代码 3-3 付款实例
[codes=galaxy]        int Cost = 0;
        int I = 1;

        //Type 1.
        while (I <= 2)
        {
                Cost += 5;
                I += 1;
        }

        //Type 2.
        int CostEachTime = 0
        while (I <= 4)
        {
                CostEachTime += 1;
                Cost += CostEachTime;
                I += 1;
        }

        //Type 3.        
        while (I <= 5)
        {
                if (I == 1)
                {
                        Cost += 1;
                }
                if (I == 2)
                {
                        Cost += 3;               
                }

                if (I == 3)
                {
                        Cost += 3;               
                }

                if (I == 4)
                {
                        Cost += 2;
                }

                if (I == 5)
                {
                        Cost += 1;
                }

                I += 1;
        }[/codes]

        注意到之前语法中的continue和break?这两个语句有什么作用呢?先看以下代码。

代码 3-4 continue和break
[codes=galaxy]    int i = 0;
    while (i < 2)
    {
        i += 1;
        TriggerDebugOutput(1,StringToText("a"),true);
                //continue;
        //break;
        TriggerDebugOutput(1,StringToText("b"),true);
    }[/codes]

        这是原始代码,输出结果是:
        a
        b
        a
        b

        不用说明原因了吧。如果我们去掉continue之前的注释符号//,输出结果是:
        a
        a

        原因很简单,continue的作用是跳过当前循环continue语句后面的全部语句。

        那么如果去掉break前面的注释符号//,输出是什么呢?
        a

        原因是break的作用是跳出全部循环。

        循环也可以嵌套使用,如:

代码 3-5 循环语句的嵌套使用。
[codes=galaxy]        int I = 0;
        int J = 0;
        string [3][4] str;
        while (I < 3)
        {
                while (J < 4)
                {
                        str[J] = IntToString(I) + "," + IntToString(J);
                        J += 1;
                }
                I += 1;
        }        [/codes]
        代码3-5的作用是为二维字符串数组str赋值。

        使用循环结构时一定要注意,不要将语句写成死循环,即永远都满足循环条件并且没有break语句存在。很多时候我们会因为忘记写“I += 1;”这类步进代码而导致进入死循环。

代码 3-6 死循环实例
[codes=galaxy]        int I = 0;
        int J = 0;
        string [3][4] str;
        while (I < 3)
        {
                while (J < 4)
                {
                        str[J] = IntToString(I) + "," + IntToString(J);
                }
                I += 1;
        }        
[/codes]

        代码3-6会在while(J < 4)中进入死循环,对比代码3-5可知,原因是忘记加入”j += 1;”语句,使J永远不会大于等于4而结束循环。

        在Galaxy中死循环会在执行时得到错误提示:
00:00:02.50 於'gt_E5AFB9E68898E5889DE5A78BE58C96_Func' 的觸發器錯誤:執行時間太長

        不过有些时候也许是你编写的脚本连续执行内容过多。才会获得此错误提示。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 20:09:47 | 显示全部楼层
四.自定义函数与触发
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-2-13 20:10:09 | 显示全部楼层
4.1 GUI下的地图脚本
        尽管这是一篇Galaxy教程,我还是不得不加入GUI界面部分的内容。我在第一章也提到过,大家最好是学会GUI之后再尝试使用Galaxy。之所以这样说,是因为GUI下你能够更直观的看到整个地图脚本构造,对于一些不复杂的脚本来说,甚至是一目了然。特别是如果你有WE的T的使用经验,学会GUI实际上比较简单的。
        请不要认为我学会了使用Galaxy就可以完全脱离GUI,实际上除非你使用导入MapScript.galaxy的方法来处理地图脚本,否则你必然要和GUI打交道。何况,库的制作不可能完全脱离GUI。
        这一节我会简要介绍下GUI下的地图脚本编写的基本知识,自定义事件、自定义动作、自定义函数以及库的制作等内容都会在后面逐步提到。

        学习Galaxy,首先要知道SC2脚本的基本结构。SC2的脚本与War3的脚本结构基本相同,也都是触发引导函数执行的方式。

        那么触发是什么?
       触发器(Trigger)是Galaxy脚本的重要组成部分,它是玩家操作(包括电脑AI操作)与脚本执行之间的纽带。确切的说,整个脚本的全部函数,都是由触发引导执行的(实际上不完全是,具体在后面会提到)。当玩家或者AI操作满足某种状态时,触发就会执行对应预先写好的程序。

4-1.png
图 4-1 触发


       从图4-1,我们可以清楚的看到,触发由4个部分构成:事件、局部变量、条件和动作。其中局部变量比较特殊,实际上它并非触发真正的组成部分,而是属于动作部分。关于事件、条件与动作的说明,请看下面Blizzard官方教程中的叙述:
       “触发器模块”就是负责控制和疏导游戏玩法以及剧情走向的重要模块。创建游戏内剧情动画、游戏中期暴兵、创建和跟踪任务目标等等游戏玩法的设计,都是通过“触发器模块”来进行的。
       “触发器”可以视作为一组游戏指令的最基本构成单元。无论你何时想要在游戏中产生何种事件,你都可以使用一个“触发器”来指示游戏,游戏会准确地按照你的要求产生事件。
       一个触发器是由以下几种元素构成:

       1.事件 即导致“触发器”运行的原因。如果我们想创建一个“当英雄死亡时游戏即结束”的触发器时,那么这个触发器的事件就应该是“单位死亡”。

       2.条件 ——条件是指该“触发器”运行所需要满足的特定条件。比如在上面提到的例子中,当“触发器”因为某个单位死亡而运行时,我们可以设置一个“条件”以限制只有当死亡的单位是英雄时才运行。

       3.动作 ——动作即是当该“触发器”运行时游戏将会执行的指令。在我们的例子中,当英雄死亡时会执行的“动作”是“玩家1游戏结束”。

       其实能够在GUI下用好事件、条件、和动作,你就可以算是一个入门的SEer了。你完全可以仅仅依靠使用这些,以及一些简单的数据设置,制作一张简单的SC2地图。当然,我们不会在此停步,我们还需要继续的学习下去。

       在GUI界面下编写脚本,还要提到自定义脚本。
       动作自定义脚本作,相当于在动作函数中插入一段自定义的Galaxy代码,注意不能包括函数或结构体等必须在全局范围内的内容,并且不能声明局部变量。
        全局自定义脚本是在全局范围内编写(在触发器列表栏中)的,除去不能有其他范围(其他触发、自定义脚本等)重名的函数、变量外,没有特殊要求。但注意,这里面必须是函数构成的,不能有不在函数范围内的非声明语句。

评分

参与人数 1印象分 +6 收起 理由
whimsyduke + 6

查看全部评分

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2019-8-23 10:06 , Processed in 0.066575 second(s), 15 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表