三国群英2吧 关注:46,108贴子:1,017,493
  • 9回复贴,共1

【脚本基础教程】第八章:数组(武将技:升天)

取消只看楼主收藏回复

视频来自:百度贴吧


IP属地:美国1楼2020-04-12 07:07回复
    本章我们介绍数组。底层上,三国2脚本支持局部、全局甚至预设变量的数组,但是到目前为止,由于全局变量和预设变量都可以按编号访问,这一部分没有实装到三国2脚本的语言特性中。因此我们只介绍局部数组。
    本章的武将技“升天”是生死门的简化版。我们直接跳过生死门的所有额外动画效果,并利用将士兵“托举”到空中的方法对敌方造成减员。“升天”和生死门相同的是,在确定了打击目标集合后,士兵死亡的顺序是随机的,并且是一个一个完成的;这意味着我们需要得到这些士兵的一个随机排列,而这是需要依赖数组这样的数据结构实现的。
    在正文开始之前,我们先配置好magic ini:
    [MAGIC]
    SEQUENCE = 118
    NAME =升天
    MP =31
    POWER =100
    ATTACK =0
    SCRIPT =807
    ATTRIB =全體
    TITLE =
    NOTE =升天,L1
    ACTIVE =敵方士兵


    IP属地:美国4楼2020-04-12 07:17
    回复
      2026-03-17 11:10:16
      广告
      不感兴趣
      开通SVIP免广告
      一、定义数组
      在三国2脚本中,我们采用以下方式定义数组:
      类型 数组名[数组长度];
      其中,数组名的命名规则和变量名、函数名完全相同,并且数组名不可和局部变量重名。与C语言和Java一样,三国2脚本数组有固定的类型,数组中的每个元素都必须为相同类型。
      例如,下面的代码定义了一个长度为10的int数组,数组名为arr:
      int arr[10];
      和较古老的C语言标准类似,在三国2脚本中,数组长度必须为确定的正整数立即数,而不能以变量的值或表达式的值来指定数组长度。例如下面的做法会导致编译错误:
      int N = 10;
      int arr[N]; // 错误 – 此处应有常整数(不能使用变量指定数组长度)
      数组无法被初始化。例如,下面的写法是不支持的:
      int arr[5] = {1, 2, 3, 4, 5}; // 错误 – 不支持定义时初始化数组
      和C语言相同,数组在定义时,长度总是被附在数组名之后,而不是类型名的一部分。同样和C语言相同,可以在一行语句中定义多个同类型的变量和数组。例如,下面的代码定义了a、c两个string型变量和b、d两个string型数组:
      string a, b[10], c, d[20];


      IP属地:美国本楼含有高级字体5楼2020-04-12 07:20
      回复
        二、数组内容的访问
        定义了数组后就可以使用和修改数组中的元素了。作为多个元素组合成的一个有序的序列,数组中的元素均使用数组和变量在数组中的位置表示;这一位置通常被称为编号或下标。
        在三国2脚本中,数组下标从0开始,按0, 1, 2, ...计数。一个长度为N的数组,其下标分别为0, 1, 2, ..., N-1. 我们使用方括号来访问和修改数组的元素:
        int arr[5];
        arr[0] = 1; // 将arr数组下标为0的元素赋值为1
        数组的元素可以完全像变量那样使用;它可以作为表达式的一部分。例如:
        arr[3] = arr[0] + 2; // 将arr[3]赋值为表达式arr[0] + 2的结果
        和定义数组时指定数组长度的情况不同,数组的下标可以为表达式,并不仅仅局限于立即数(否则数组就没有存在的意义了)。例如,下面的代码制造了一个长度为10的斐波那契数列{1, 1, 2,3, 5, 8, 13, 21, 34, 55},其中我们用表达式 i - 1 和 i - 2 作为数组的下标:

        注意:和C语言一样,三国2脚本在编译时和运行时都不会检查数组越界,亦即指定的下标值超出数组长度允许的范围之情形。当数组发生越界时,可能会影响到其它不相关的局部变量,甚至发生难以预料的后果,因此请务必小心。
        (此外,如果您对指针操作感兴趣,有一点需要注意:三国2脚本中的“指针”指向的是EXE运行时的内存地址,通常而言,它无法(也不该)被用来指向任何当前代码中的局部变量、数组或全局变量。因此不存在把数组名当成指针使用的做法。)


        IP属地:美国本楼含有高级字体6楼2020-04-12 07:23
        回复
          三、将武将技打击目标储存在数组中
          读者可能已经知道,生死门武将技有一个特点:它总是倾向于打击敌方最靠前的士兵。在magic点cpp中,奥汀提供了专门的函数用来获取此类打击目标:
          int GetSoldierMaxX (int isLeft);
          int GetSoldierMaxX2 (int isLeft);
          这两个函数都是用来获取isLeft指定的敌对方最靠前的士兵的屏幕X坐标的(亦即,当指定isLeft为1时,获取的实际上是玩家方最靠前的士兵的X坐标)。唯一的区别是,GetSoldierMaxX在获取不到士兵时会返回敌方主将的屏幕X坐标,而GetSoldierMaxX2在这种情况下会返回-1.
          需要注意的是:由于奥汀自己没有提供遍历全场士兵的系统函数,该函数的实现是通过在战场上随机抽取N/2 + 1次士兵,取其中最靠前者的方法(N为敌对方士兵总数)。因此,它们并不能保证总是返回最靠前的士兵;它只是大概率返回靠前的士兵而已。
          我们也可以照章办理。在我们的武将技中,首先判断对方有没有士兵,如果有,就获取最靠前的士兵的屏幕X坐标,并转换为战场坐标:

          紧接着,我们使用上一章介绍的GetForceCountInRect函数,得到以该坐标为中心,两侧各9格以内的所有各列中的敌方士兵和主将总人数:

          下一步,我们将使用GetNthForceInRect函数,一个一个地获取作为目标的所有士兵。我们将所有这些士兵储存在一个名为targets的数组内:

          在上面的代码中,我们通过循环指定i,获取矩形范围内所有敌方士兵和主将。我们将得到的物件和预设变量intvDefenderMajor比较,如果不是主将则放进数组中;我们用变量j指定当前targets数组中第一个空余的位置,每次有新的士兵,我们都将士兵物件放在j下标的位置,然后令j自增1。
          最后,我们重新利用了targetCount变量,令其等于区域内的总士兵数,而不是士兵和主将的总人数。由于j是最后一个空位,之前恰好有j个士兵(下标为0到j-1),因此我们直接令targetCount = j.
          我们暂时结束这个大的if分支,即对方场上仍有士兵的情况。当对方场上没有士兵时,我们使用CastFail函数,在给定的主将头顶召唤一口铁锅(dir指定了铁锅的方向,通常情况下,和主将的方向相同):
          void CastFail (int majorHandle, int dir);
          具体代码如下:


          IP属地:美国本楼含有高级字体7楼2020-04-12 07:30
          回复
            目前的整体代码如下:


            IP属地:美国8楼2020-04-12 07:36
            回复
              四、随机排列和手动调整速度
              为了生成随机的排列,我们应用一个被称为费雪-耶兹洗牌算法(Fisher-Yates shuffle)的方法。这个算法很简单:对于从0到N-2的各个元素,我们都从其后的所有元素中随机挑一个,将其和该元素交换位置。
              我们接着上面的代码,在“未完待续”处,继续写下:

              这段逻辑很简单。对于每个给定的i,下标j的随机取值范围在[i+1, N-1]范围内,即i之后的所有可能的下标;紧接着,我们借用temp变量,将targets数组中i下标位置的士兵和j下标位置的士兵交换。如此对i从0到N-1循环一次,我们就生成了一个随机的排列。
              紧接着,我们将摄像机平移到一个随机的目标士兵身上,下标在[0, N-1]之间随机取值,然后播放生死门的音效:

              摄像机对准目标后,对于这个士兵的随机排列顺序,我们从头到尾逐个将士兵减员(升天),减员的时间间隔也是随机的:

              这样我们的主函数就完成了。


              IP属地:美国本楼含有高级字体9楼2020-04-12 07:43
              回复
                升天函数看上去很简单。但是,有一个坑的地方:士兵死亡后,EXE会对士兵进行抛出动作,此时会设置物件的重力和阻力,不能通过设置重力/阻力的方法一次搞定。因此,我们通过循环的方式,手动调整物件的速度进行“加速”。
                此外,在无重力的情况下,士兵阴影淡出的用时要更长,因此,需要手动调整透明度进行淡出动作。
                具体代码如下:


                IP属地:美国10楼2020-04-12 07:49
                回复
                  2026-03-17 11:04:16
                  广告
                  不感兴趣
                  开通SVIP免广告
                  武将技“升天”的完整代码



                  IP属地:美国本楼含有高级字体12楼2020-04-12 07:55
                  回复

                    本章引入的系统函数和官方函数
                    // 获得对方靠前的士兵的屏幕坐标;参数为我方的所属方(magic点cpp)
                    // 当没有兵时MaxX返回对方主将,MaxX2返回-1
                    int GetSoldierMaxX (int isLeft);
                    int GetSoldierMaxX2 (int isLeft);
                    // 对指定主将天降铁锅(magic点cpp)
                    void CastFail (int majorHandle, int nDir);


                    IP属地:美国本楼含有高级字体13楼2020-04-12 07:58
                    回复