三国群英2吧 关注:44,039贴子:999,890

回复:【脚本基础教程】第七章:物理引擎(地火 / 地焰涌 / 地焰星燃)

取消只看楼主收藏回复

六、阻力
除重力外,另一个加速度维度是阻力。当阻力加速度为正数时阻力加速度的方向永远与当前速度的方向反向。在三国2脚本中,我们使用SetObjectFriction和SetObjectFriction1两个函数指定物件受到的阻力加速度大小:
void SetObjectFriction (int object, int friction);
void SetObjectFriction1 (int object, int frictionTimes0x10000);
阻力的单位是正常的单位:1点阻力就等于1点加速度,也即1 Tick中速度减小1点。不过,和重力加速度一样,1点阻力实在是太大了,只能设置整数的阻力是很不方便的;因此,三国2脚本提供了另一个函数SetObjectFriction1,它的参数中0x10000(65536)等价于1点阻力加速度,这使得我们可以设置非整数的阻力大小。例如,当设置为0x10000 / 2时,即为0.5点阻力。
我们的武将技中不会涉及阻力。不过,为了演示阻力的作用,我们仍然会利用目前的代码,并且,这一部分代码中的一部分将会保留下来。
我们希望当火球落地,开始向四面八方滚动时,火球会慢慢受到地面摩擦力的作用而停下来。为了实现这一点,我们另外编写一个函数监测每个火球的z坐标,并在火球落地后,设置火球的阻力为三分之一:

这里的do...while是有意为之。因为在刚抛出火球时,火球的z坐标刚好是0,因此需要等1 Tick,等到火球飞起来之后再检查z坐标。
在ThrowLava函数中,每抛出一个火球,我们就异步调用调用一次TraceLavaSplit函数,这样就确保了每个火球都有一个函数正在监测:

编译运行。可以看到,地面上的火球在滚动中迅速放慢,最后停下来了。

阻力加速度也可以被设置为负数,此时表现为加速。


IP属地:美国本楼含有高级字体21楼2020-03-29 14:46
回复
    度娘又开始吞帖了。暂缓更新,明天再贴完。


    IP属地:美国23楼2020-03-29 14:54
    回复
      七、OF_BOMB和OF_TARGET标记



      IP属地:美国本楼含有高级字体25楼2020-03-30 05:19
      收起回复
        八、实例推进:爆卝炸和熔岩池
        下一步是让火球落地时产生爆卝炸效果,并产生一个岩浆池。我们直接改写第六节中侦听火球落地的代码。
        当火球落地时,我们总共需要做四件事:
        1. 销毁火球物件;
        2. 当第一颗火球落地时,制造一个“岩浆池”,并给岩浆池加上打击目标;
        3. 当最后一颗火球落地时,制造一个“爆卝炸效果”物件,并给该物件加上打击目标;
        4. 一段时间后,销毁爆卝炸效果和岩浆池。
        我们将用到以下物件:
        1. 爆卝炸效果物件,复制自龙炮系,但删除了其中播放声音的部分:
        [OBJECT]
        Name = 爆卝炸
        Sequence= 11540
        Type = %TYPE_FORCE
        Space = 0,0,0,0
        Flags = OF_NOGRAVITY,OF_WHITELIGHT
        Process = %MagicObjectProcess
        Directory = \magic\033
        Wait = #2, m033bomb01a,m033bomb01a, #10, m033bomb01b, m033bomb01c, #9999
        2. 岩浆池物件,它和地裂效果一样是一组2个物件,总共6组,编号为11521-11526和11531-11536:
        [OBJECT]
        Name =池01
        Sequence= 11521
        Type = %TYPE_FORCE
        Space = 0,0,0,0
        Flags = OF_DARKLIGHT
        Process = %MagicObjectProcess
        Directory = \magic\115
        Wait = #9999, pool01
        [OBJECT]
        Name =池內岩漿01
        Sequence= 11531
        Type = %TYPE_FORCE
        Space = 0,0,0,0
        Flags = OF_WHITELIGHT
        Process = %MagicObjectProcess
        Directory = \magic\115
        Wait = #9999, poollava01
        3. 覆盖在岩浆池上的“火苗”,使用了很多原版武将技都带有的火苗物件:
        [OBJECT]
        Name = 地板火
        Sequence= 11541
        Type = %TYPE_FORCE
        Space = 0,0,0,0
        Flags = OF_WHITELIGHT,OF_NOGRAVITY
        Process = %MagicObjectProcess
        Directory = \magic\018
        Wait = #5, M018ManFire1,M018ManFire2, M018ManFire3, M018ManFire2


        IP属地:美国本楼含有高级字体26楼2020-03-30 05:23
        回复
          我们将TraceLavaSplit函数改写为如下所示。除了开头的等待火球落地部分外,剩下的部分都是新增的代码。

          代码看上去比较多,但实际上逻辑很清晰。当第一颗火球落地时,制造地板火,并制造地板火的杀伤;从这一刻开始,制造地面上的岩浆池,并让其在6 Tick内从零放大到原始大小的一半;6 Tick后,正好最后一颗火球落地,此时直接产生爆卝炸效果。爆卝炸效果在26 Tick后淡出,熔岩池和地板火则在66 Tick后淡出。为爆卝炸效果物件和地板火物件指定杀伤目标后,使用和地裂喷发处的主火焰同样的GroundFireCallback回调函数,指定调用代码11501.


          IP属地:美国27楼2020-03-30 05:29
          回复
            编译运行。可以看到火球落地时形成的岩浆池效果、岩浆池上的火苗和爆炸效果。


            IP属地:美国28楼2020-03-30 05:33
            回复
              九、全局变量


              IP属地:美国本楼含有高级字体30楼2020-03-30 05:46
              回复
                扛不住了,发一条吞一条。 继续让度娘冷静一会儿再说。


                IP属地:美国33楼2020-03-30 06:03
                回复
                  十、多级武将技的打击目标
                  我们还没有设计多级武将技。目前,主函数中level这个参数从来没有用到过,不管使用的是R115、R116还是R117,都只有一个地裂喷发点。我们来着手增加多个地裂喷发点。
                  首先,由于我们希望多个喷发点同时喷出熔岩弹火球,因此每一个喷发点抛射火球的循环都是独立异步运行的。于是,我们将创造地裂和抛射火球的部分,移到一个专门的函数CreateVolcano中:


                  IP属地:美国本楼含有高级字体35楼2020-03-31 03:56
                  回复


                    IP属地:美国37楼2020-03-31 04:05
                    回复
                      紧接着是主函数。之前我们提到过,为了使问题简化,我们直接连续多次将武将技的打击目标点对准人堆;因此,这部分代码的修改就非常简单了,后续跟着的第2次和第3次地裂,获取打击目标点的方法和第1次完全相同。我们将仍然留在主函数内的寻找打击目标、平移摄像机相关代码用一个for循环套起来,并增加对CreateVolcano函数的异步调用:

                      为什么是68?因为MoveCamera移动摄像机用时12 Tick,加上68就是80 Tick。80 Tick等于下文中地震音效循环一次的时间——这样正好契合。
                      我们将主函数中一直留着的Delay(200)删除,也改为使用Wait语句等待所有火球落地和岩浆池消失:
                      // 等待所有地火落地,岩浆池消失
                      Wait("TrаceLаνаSplit");
                      Delay(20);


                      这样就完成了。


                      IP属地:美国本楼含有高级字体38楼2020-03-31 04:09
                      收起回复
                        最后,我们还将移植大地狂啸中画面抖动的效果及其音效。我们一直没有为该武将技中的任何元素增添音效,即使火球落地也没有爆炸的声音;我们现在就将一并处理这个问题。
                        这里,为简便起见,笔者直接在当前摄像机位置上增加随机的偏移量。这是因为在多级武将技中,摄像机要在各个地裂喷发点之间来回移动,不能像大地狂啸一样瞄准一个固定位置;但是,这样做带来的坏处是,我们事实上构造了一个二维的随机游走过程,根据相关的数学定理,随着画面的抖动,我们会离最开始摄像机对准的中心位置越来越远……由于我们的代码已经很多了,这个问题笔者选择不去处理,实际效果还是能看的。

                        这里,每3 Tick中有2 Tick会抖动一次屏幕,同时,每80 Tick重播一次地震的音效。
                        主函数中也添加相应的异步调用。当然,时间要算准:
                        // 地震效果
                        asynccall ShakeCameraAndQuakeSound(80 * (level -1) + 260);


                        IP属地:美国本楼含有高级字体39楼2020-03-31 04:17
                        收起回复
                          武将技“地火”的完整代码




                          IP属地:美国本楼含有高级字体41楼2020-03-31 10:51
                          回复


                            完成!我们终于制造出了一个复杂度和原版武将技匹敌的“指向性”杀兵技。
                            在这一章中,我们综合运用了许多之前章节介绍的知识点,来构建我们的这个武将技。在本章的中部,我们介绍了三国2脚本的物理引擎和加速度系统;虽然加速度只有两个维度,无法实现任意方向的加速度,但是对于一般情况而言这样已经足够了。针对“地火”武将技的需求,我们还介绍了球坐标相关函数的使用方法。
                            下一章中,我们将涉足数组。我们之所以把数组放在如此靠后的位置,是因为在编写武将技的绝大多数情况下,我们都完全不需要使用数组。不过,作为三国2脚本的重要语法特性,数组仍然是绕不过去的一环。


                            IP属地:美国本楼含有高级字体42楼2020-03-31 10:57
                            回复
                              本章引入的系统函数和官方函数
                              // 批量载入图形文件
                              void BatchLoadShape (string path);
                              // 得到“关键度”最高的士兵(人堆中的士兵)
                              int GetMostImportantSoldier (int isLeft);
                              //得到随机活着的士兵
                              int GetRandomSoldierHandleFromAlive (int isLeft);
                              //得到以(x0, y0)为中心,坐标在[x0-width,x0+width],
                              //[y0-width, y0+width]矩形范围内的士兵数量
                              int GetForceCountInRect (int isLeft, int x0, int y0, int width, int height);
                              //得到以(x0, y0)为中心,坐标在[x0-width,x0+width],
                              //[y0-width, y0+width]矩形范围内的第n个士兵
                              int GetNthForceInRect (int isLeft, int x0, int y0, int width, int height, int n);
                              //按照球坐标设置物件速度
                              void SetObjectSpeed_Sphere (int object, int theta, int phi, int speed);
                              //按照球坐标,以参考物件为原点设置物件位置
                              void SetCoordinateByReference_Sphere (int object, int referenceObject, int theta, int phi, int radius, int zOffset);
                              //设置物件是否受重力影响
                              void SetObjectHasGravity (int object, int hasGravity);
                              //设置重力加速度的大小(32768=1)
                              void SetObjectGravity (int object, int gravity);
                              //设置阻力加速度的大小(1=1)
                              void SetObjectFriction (int object, int friction);
                              //设置阻力加速度的大小(0x10000(65536)=1)
                              void SetObjectFriction1 (int object, int frictionTimes0x10000);
                              //设置OF_TARGET标记的物件的目标
                              void SetObjectTarget (int object, int targetObject);


                              IP属地:美国本楼含有高级字体43楼2020-03-31 11:08
                              回复