由克希吧 关注:30贴子:3,059
  • 15回复贴,共1

MUGEN(4~6)部分内容

只看楼主收藏回复

common1.cns常用状态
如果你看了玩家的DEF文件,会看见如下内容:
stcommon = common1.cns ;Common states
每个玩家分享了一些常用状态,这是游戏引擎的基础部分.这些常用状态可以在data/common1.cns中找到.
一些例子是跑步和受击的状态.在特殊状态号中完整的列表是有效的.
如果你想为某些玩家覆盖常用状态,只要在玩家CNS中创建一个相同状态号(和需要覆盖的状态号相同)的状态.
则当玩家改变到那个指定的状态号时,他将进入新的状态,而不是common1.cns中的那个.
(因为stcommon = common1.cns执行优先级很低,大多数CNS文件的优先级都高于他)
要记住当覆盖mugen中某些有特殊代码的状态时,新创建的状态也有这些特殊代码.
例如,跑动的状态(state 100)中,设置玩家的速度为你在玩家变量中指定的值.
如果你覆盖了state 100,新状态将仍然含有这些设定.
一个普通的例子就是覆盖跑动状态.
mugen跑动状态的默认特征就是让玩家以恒定的速度持续向前运动,直到你放开向前的按键.
之后就回到了站立的状态.
现在,我们希望让玩家(这里称为p1)用前跳来代替跑,就像按两次向后键就会后跳那样.
你可以在p1的cns文件中这么设置.
; RUN_FWD (overridden to dash-type)
[Statedef 100]
type = S ;Running is on the ground
physics = N ;We'll define our own physics
anim = 100 ;Anim action 100
ctrl = 0 ;No control for duration of dash
[State 100, 1] ;To start dashing forwards
type = VelSet
trigger1 = Time = [0,5]
x = 6
[State 100, 2] ;Friction after initial dash
type = VelMul
trigger1 = Time > 5
x = .85
[State 100, 3] ;
type = ChangeState
trigger1 = AnimTime = 0
value = 0
ctrl = 1
这里我们假定action 100是有限循环的.
[Velocity]下设置的run.fwd速度没有真的被忽略,但是[State 100,1]设置了x方向速度为6覆盖了这个细节.


IP属地:浙江1楼2025-01-30 20:23回复
    被楼主禁言,将不能再进行回复
    特殊状态号
    (当然这是针对新手的模板规划,有点基础的也可以自行调整,就除了5000那些)
    除非你想覆盖常用状态,否则请避免选择这些状态号(从0-199,5000-5999).
    这里是common1.cns中的状态列表.
    状态号 描述
    0 站立
    10 站立到蹲下
    11 蹲下
    12 蹲下到站立
    20 走
    40 跳跃开始(起身阶段)
    45 空中跳跃开始
    50 跳跃空中阶段
    52 跳跃落地
    100 向前跑
    105 向后小跳
    106 向后小跳落地
    120 防御(开始阶段)
    130 站立防御 (防御中)
    131 蹲下防御 (防御中)
    132 空中防御 (防御中)
    140 防御 (结束阶段)
    150 站立防御住攻击 (震动)
    151 站立防御住攻击 (被击退)
    152 蹲下防御住攻击 (震动)
    153 蹲下防御住攻击 (被击退)
    154 空中防御住攻击 (震动)
    155 空中防御住攻击 (被击飞)
    170 输了 (时间终了)
    175 平局 (时间终了)
    190 准备开场介绍
    191 开场介绍 (覆盖此状态给人物一个开场介绍)
    5000 站立受击(震动)
    5001 站立受击(被击退)
    5010 蹲下受击(震动)
    5011 蹲下受击(被击退)
    5020 空中受击(震动)
    5030 空中受击(被击飞)
    5035 空中受击(过渡阶段)
    5040 空中受击(空中恢复,没有进入下落状态)
    5050 空中受击(下落状态)
    5070 绊倒受击(震动)
    5071 绊倒受击(被击飞)
    5080 倒地受击(震动)
    5081 倒地受击(被击退)
    5100 倒地受击(下落状态撞击地面)
    5101 倒地受击(从地面弹起)
    5110 倒地受击(躺在地上)
    5120 倒地受击(起身)
    5150 倒地受击(被击败时的躺在地上姿势)
    5200 空中受击(在地面上受身,仍然处于下落状态)
    5201 空中受击(在地面上受身)
    5210 空中受击(空中受身)
    5500 接关动画
    5900 初始化(每回合开始时)


    IP属地:浙江2楼2025-01-30 20:26
    收起回复
      2025-08-01 03:14:04
      广告
      不感兴趣
      开通SVIP免广告
      表达式
      MUGEN在大多数触发器和状态控制器中都支持算术表达式.
      这比仅使用固定值显得更加灵活和特征可定制性.
      此章节给出表达式概念和替代的描述.


      IP属地:浙江3楼2025-01-30 20:28
      回复
        数据类型
        MUGEN使用3种数据类型:32位整数,32位浮点数,特殊null值"bottom".
        整数代表从-2^31到2^31-1的所有数目,或者大约-20亿到20亿.
        浮点数是单精度浮点数.也就是说,这个数字有一个7位有效数字的小数部分.
        浮点数能用来表示很小的分数或者是非常庞大的数字.
        当你在表达式中写数字时,MUGEN从存在的小数点中推导数据类型.
        因此"7"一直是一个整数,举个例子.如果你想让7变成和浮点数一样,则你要写成"7.0".
        "bottom"是一个特殊的数据类型,任何出现"bottom"的表达式将被清零(除了一些非常有限的例外).
        它的出现标志着存在一些错误条件类型.你应该尝试如此的方式编程使bottom不再产生.
        关于详情,你可以查看bottom的专属章节.
        算术表达式的特征很大程度上取决于用来表示数字的基本数据类型.
        同时,状态控制器也许期望他们的输入以某种类型输出,如果提供了错误的类型将出错.
        (控制器指定的输出类型与实际输出类型不相符)
        当不同数据类型值需要通过一些方式合并时(例如,加),"强制类型转换"就会发生.
        通常,这表示一个整型将被转换成浮点型,同时有可能在此过程中产生误差.(强制转换)
        下面的章节中我们将注意任何有关强制类型转换情况.


        IP属地:浙江4楼2025-01-30 20:28
        回复
          状态和状态控制器参数中的表达式
          大多数情况,statedef(状态定义)或者状态控制器中的任何参数都能是一个表达式.
          那些给定类型为字符串型的参数例外.例如,hit属性,guardflags,等等.不能指定成表达式.
          同样,ignorehitpause和persistent参数(任何控制器中)也是例外,它们也不能用表达式作为参数.
          状态控制器被触发后便计算它的参数,过后将不会重新计算,除了控制器被再次触发.
          用逗号分隔的参数是从左到右计算的.要实现控制器参数的连续计算,则此控制器必须被持续触发.
          在某些控制器如HitDef的情况中,并不希望控制器处于持续触发状态中,因为这样会降低观赏性且同时会招致不受欢迎的行为.
          在这种情况下,程序员将希望试图尽可能久的延迟HitDef触发状态,以便计算HitDef参数时正好被使用.


          IP属地:浙江5楼2025-01-30 20:29
          回复
            代码效率
            处理表达式不是向电脑收费,所以你代码的可读性比微优化表达式更重要.
            然而,下面的某些好的做法将提高效率同时不影响最终的效果.
            MUGEN在状态控制器中计算条件型触发器遵循如下原则:
            首先计算triggerall,从上到下顺序计算.
            如果任何triggerall计算出0,则剩余的触发器将被跳过且将计算下一个控制器.
            如果所有triggerall计算结果都是非0,则引擎开始计算trigger1,也是从上到下顺序计算.
            如果它们中任何一个计算出0,则跳过所有剩下的trigger1转而计算第一个trigger2,诸如此类等等.
            如果一个块中的所有trigger(除了triggerall)都计算出非0,则将开始计算状态控制器参数且控制器被触发.
            换句话说,触发器计算的逻辑是类似短路型的.在类似C语言的标记法中,这种方式也许被记为
            triggerall,1 && triggerall,2 && ... && ((trigger1,1 && trigger1,2 && ...) || (trigger2,1 && trigger2,2 && ...) || ... )
            这里 trigger1,2 记作 trigger1 第2行; trigger2,1 记作 trigger2 第1行,等等.
            这个trigger组的逻辑计算将是短路型的,就像C一样.
            (注:通过那个逻辑关系式,表示了triggerall组与后面trigger1,trigger2等所有组关系是'&&',也就是说只要triggerall组一个是0,就不能触发.
            通俗说法就是triggerall里面只要有1个是0就不能触发,要触发必须triggerall全部非0.
            逻辑关系式还能看出trigger1组和trigger2组...之间都是'||',也就是这些组中只要有一个组为1就能触发(前提是triggerall已经检测为非0.)
            而在每个trigger组中,他们之间的关系是'&&',比如trigger1组中,要保证trigger1的情况下触发,必须每个trigger1都非0.
            控制器被触发的通俗说法就是:首先每个triggerall都要非0,然后每个 triggerN组 中最少要有1个非0,triggerN组 要非0,必须每个triggerN都非0. )
            由于这个系统,可以通过 组织好表达式以便条件型触发器里的数字尽可能的即小又少 来实现大大提高效率.
            状态控制器参数可以减少工作量(代码量),这些参数在触发时仅计算一次,而不是玩家在状态中每帧都计算.例如
            [State -1]
            type = ChangeState
            trigger1 = command = "a"
            trigger1 = power < 1000
            value = 3000
            [State -1]
            type = ChangeState
            trigger1 = command = "a"
            trigger1 = power >= 1000
            value = 3001
            [State -1]
            type = ChangeState
            trigger1 = command = "a"
            trigger1 = power >= 2000
            value = 3002
            可以进一步简化为:
            [State -1]
            type = ChangeState
            trigger1 = command = "a"
            value = 3000 + (power >= 1000) + (power >= 2000)
            你同时能为引擎排忧解难,通过把最容易计算出false的triggerall放在triggerall块的顶部.(可以减少引擎计算量)
            同样,trigger1块应该是最可能被触发的块,但是在trigger1块内部,最有可能计算出0的trigger1应该放置在顶部.
            对于有很多包含重复条件trigger的状态控制器,最好将这个控制器打断为2个独立的块,每个块独立设置triggerall.
            如果你有一个复杂的条件,这个条件是许多连续状态控制器的触发条件,你可以把这个条件的值存储到一个变量中,然后把这个变量作为后面触发器的控制条件.
            例如:
            trigger1 = (command="abc" && command!="holddown" && power>=1000) || (command="abc" && command!="holddown" && var(5)) || ((command != "abc" || command = "holddown") && power>=2000)
            可以被写成 (直接给 var(0) 赋值是可行的):
            trigger1 = (var(0):=(command="abc" && command !="holddown") && power>=1000) || (var(0) && var(5)) || (!var(0) && power>=2000)
            这里,你必须权衡提高可读性.(在使用子表达式和使用:=运算符这两种情况中权衡,这2种情况都会降低可读性)
            (或者使用varset控制器:
            [State -1]
            type = varset
            ...
            var(0) = command="abc" && command !="holddown"
            然后:
            trigger1 = var(0)&&power>=1000 || var(0)&&var(5) || !var(0)&&power>=2000
            由于&&运算等级高于||,所以这么写不违背原意.)


            IP属地:浙江6楼2025-01-30 20:29
            回复
              算术运算符
              算术运算符允许你执行像加法,乘法,除法等等那样的基本操作.
              MUGEN拥有下列运算符:
              (下面就不翻译原话了,大多数运算符和其他编程中的定义一样,只写出每个运算符通俗含义)
              +
              把两数相加.其他类型与浮点型相加会先把其他类型强制转换成浮点型再相加,结果为浮点型.
              -
              减号.强制类型转换和+号一样.
              *
              乘号.强制类型转换和+号一样.
              /
              除号.强制类型转换和+号一样.需要注意的是整型除以整型结果还是整型,不会出现小数点.也就是说8/3等于2.
              示例:10/3=3,12.0/3.0=4.0,20/4.0=5.0.
              除以0会产生bottom.
              %
              求余.返回x除以y的余数.只要x或y是浮点型或者y是0就会产生bottom.
              (此运算符运用时一定要注意,尤其是注意浮点数用此运算符会错误,而且通过报错信息很难找到错误位置)
              **
              幂运算.如果x,y是非负整数,x**y就是x的y次方.(定义0**0=1)
              如果幂运算的时候导致数据太大,就会出现MAX_INT(整数的上限)警告.
              如果x,y中有一个是负的或者浮点型,那2个数都会强制转换成浮点型,计算结果是实数.
              像-1**0.5这类运算会出bottom(负数不能开根号).
              !
              逻辑运算符 否.x为非0时,!x等于0.x为0时,!x等于1.
              (逻辑运算符操作数分为非0和0,非0被看做1处理.返回值为布尔型,也就是true和false.用整数表示:1对应true(真),0对应false(假).)
              &&
              逻辑运算符 且.x,y中只有当x和y都是非0,x&&y才会等于1,否则结果都是0.
              ||
              逻辑运算符 或.x,y中只有当x和y都是0,x&&y才会等于0,否则结果都是1.
              ^^
              逻辑运算符 异或.x,y中当x和y都是0或都是非0,x&&y才会等于0,否则结果都是1.
              (用0,1表示就是说,x,y数值不同时x^^y=1,数值相同时x^^y=0)
              ~
              按位取反运算符.位运算符的运算方法是先把整数转换成2进制数,然后运算,再转换成10进制.
              取反计算原理是0的地方变1,1的地方变0.10110011取反就是01001100.
              如果对浮点数取反就会出bottom.
              &
              按位与运算符.遵循0&1=0,1&1=1,0&0=0.
              运算方式同"~".
              操作数中出现浮点数会出bottom.
              |
              按位或运算符.遵循0|1=1,1|1=1,0|0=0.
              运算方式同"~".
              操作数中出现浮点数会出bottom.
              ^
              按位异或运算符.遵循0^1=1,0^0=0,1^1=0.
              运算方式同"~".
              操作数中出现浮点数会出bottom.
              =
              关系运算符 等于.用来比较两个数是否相等.如果两数都是整型或都是浮点型且他们相等,则返回1,否则返回0.
              如果有一个数是浮点型,则比较的时候会将非浮点型的数强制转换成浮点型进行比较.
              :=
              赋值 运算符.
              例子:
              var(4):=1 表示把整型值1赋给var(4).
              如果指定var(4):=1.1 则赋值的时候会将1.1转换为整型再赋给var(4)(var(4)的值为1).
              如果指定fvar(4):=1 则赋值的时候会将1转换为浮点型再赋给fvar(4)(fvar(4)的值为1.000000).
              (注:在MUGEN中":="运算符可以实现在触发条件中直接对var参数赋值.
              比如 trigger1 = var(0):=12.3 也就是直接把12.3赋给var(0)使得var(0)为12,然后再判断这个触发条件,也就是trigger1 = 12 ,此触发条件恒成立.
              !=
              关系运算符 不等于.如果x,y类型相同,如果x不等于y,则x != y 返回1,否则返回0.
              如果有一个数是浮点型,则比较的时候会将非浮点型的数强制转换成浮点型进行比较.
              <
              关系运算符 小于.如果x小于y,则x<y返回1,否则返回0.处理方式同"!=".
              <=
              关系运算符 小于等于.如果x小于等于y,则x<=y返回1,否则返回0.处理方式同"!=".
              >
              关系运算符 大于.如果x大于y,则x>y返回1,否则返回0.处理方式同"!=".
              >=
              关系运算符 大于等于.如果x大于等于y,则x>=y返回1,否则返回0.处理方式同"!=".
              =[,] !=[,] =[,) !=[,) =(,] !=(,] =(,) !=(,)
              区间运算符.
              举例来说,x=[y,z] 表示 x>=y && x<=z .而 x=(y,z) 表示 x>y && x<z .他们的区别就是是否包含边界.x=[y,z) 表示 x>=y && x<z.
              所以可以知道x!=[y,z]的含义了.也就是不包含在y和z之间.(x<y || x>z)而 x!=(y,z) 表示 x>=y || x<=z .
              运用此运算符特别要注意的就是优先级以及合法性,此运算符的优先级很低,且很多写法都非法,所以在用的时候以防万一还是加上括号.(详见运算符优先级以及表达式语法)


              IP属地:浙江7楼2025-01-30 20:29
              回复
                运算符优先级与联合性
                如果你考虑这样一个表达式:3+2*5,先计算*和先计算+所得到的结果是不同的.
                为了消除表达式的这种歧义,运算符被分配了不同的优先级别.
                这种情况下,*的优先级要高于+的优先级,所以将先计算*再计算+,所以3+2*5的结果是13.
                如果两个运算符同级,则处理方式从左到右,除了单目运算符和赋值运算符,他们联合方式从右到左.
                例如,*和/是同级的,所以5.0*5/6等于25.0/6等于4.166667.
                另一方面,5/6*5.0等于0*5.0等于0.0.
                与此相反,单目运算符从右到左计算,-!0也就是-(!0),也就是-1.
                如果表达式的某个部分用使用了括号"()",则此部分将最先运算.
                例如,表达式(3+2)*5,首先计算+,然后计算*,结果是25.
                如果嵌套多个括号,则最里层的括号首先运算.
                运算符优先级基本上和C一样.
                完整的运算符优先级列表,从高到底,如下:
                运算符 优先级
                ! ~ - (单目运算符) 最高级
                **
                * / %
                + -
                > >= < <=
                = != 区间运算符
                :=
                &
                ^
                |
                &&
                ^^
                || 最低级
                鼓励程序员使用括号使表达式在必要时保持清晰(可读性).
                否则,由于运算符优先级细微的错误而产生的bug几乎很难被查明.


                IP属地:浙江8楼2025-01-30 20:30
                回复
                  2025-08-01 03:08:04
                  广告
                  不感兴趣
                  开通SVIP免广告
                  表达式语法
                  基本上来说,任何普通算术表达式都是合法的.
                  此外,因为关系运算符(>,<=,等等)被视为返回整型,能够运算他们的返回值,有点看起来不像普通的表达式.
                  1.0 = (2 = (1 > 0) + !(0 < 1))
                  1>0返回值1,0<1返回值0.!(0<1)等于1,所以这个表达式就等同于
                  1.0 = (2 = 1 + 1)
                  因为2=1+1返回1,所以表达式可以更简化为
                  1.0 = 1.0
                  返回1.
                  语法表达式中一个值得注意的限制地方是区间运算符仅允许出现在表达式最右方.
                  若表达式中有括号,在表达式括号部分中,那部分被认为是子表达式,区间表达式允许出现在子表达式的右边.
                  下面是一个有效格式的表达式:
                  (1 = [0,2]) = (0,1)
                  但是下面的不是一个有效格式的表达式:
                  1 = [0,2] = (0,1)
                  另外,只有"="和"!="符号允许出现在区间表达式前面.
                  所以 5 > [0,2],或 4 + [1,4) 此类表达式是非法的.
                  在逗号分隔的参数列表中,比如给函数型触发器或者状态控制器参数赋值,每个列表中的表达式被认为是一个单独的子表达式,因此区间运算符可以出现在这些子表达式的末尾.
                  例子:
                  trigger1 = stateno=[0,1000] && p2stateno=[0,1000] ;非法,而且此种错误很难检测出来
                  trigger1 = (stateno=[0,1000]) && p2stateno=[0,1000] ;合法,满足区间表达式出现在表达式最右方以及子表达式右方.


                  IP属地:浙江9楼2025-01-30 20:30
                  回复
                    条件型触发器和函数型触发器
                    由于历史原因,两种不同类型都被称为"触发器".
                    第一种也许更确切的叫法是"条件型触发器",第二种也许更确切的叫法是"函数型触发器".
                    例如,在CNS中,一个典型的状态控制器也许看起来像下面的:
                    [State 1234, 5]
                    type = ChangeState
                    trigger1 = time = 0
                    value = 0
                    "trigger1 = time = 0"就是一个条件型触发器.
                    如果表达式"time=0"计算出非0值,则ChangeState控制器将被执行.
                    如果表达式"time=0"计算出0,则ChangeState控制器将不被执行.
                    也就是说条件是非0或0影响着控制器是否被触发.
                    另一方面,表达式中出现的"time"是一个函数型触发器.
                    它返回一个值,即,玩家在状态1234中的状态时间.
                    注意一个函数型触发器不触发任何东西.它只是给出一个在表达式中起作用的值.
                    为了进一步了解不同之处,让我们思考一个不同的状态控制器:
                    [State 1234, 5]
                    type = VarSet
                    trigger1 = 1
                    v = 0
                    value = time + 5
                    注意条件型触发器"trigger1 = 1"此时不含有任何函数型触发器.
                    因为表达式"1"总是计算出1,此控制器将每帧都触发.
                    为了确定什么值被赋予给了var(0),就要计算"time + 5"的值.
                    函数型触发器"time"返回玩家的状态时间.然后加上5,结果算出的值赋给var(0).
                    完整的函数型触发器列表请详见触发器篇.


                    IP属地:浙江10楼2025-01-30 20:31
                    回复
                      触发器重定向
                      在上例中,time触发器返回玩家的状态时间.但有时候希望检测玩家目标的状态时间,或者玩家父级的(如果玩家是一个helper),等等.
                      这能通过 在触发器名字前加上关键字说明应返回谁的信息 来完成.
                      此过程被称为触发器重定向.例如.
                      5 + (parent, time)
                      返回5+玩家父级的状态时间.
                      重定向键值关键列表如下:
                      parent
                      重定向触发器到玩家的父级.(玩家必须是helper)
                      root
                      重定向触发器到根级(最上级).
                      helper
                      重定向触发器到第一个出现的helper.详见触发器文档中相关触发器"NumHelper".
                      helper(ID)
                      ID应该是一个有效格式的表达式,计算出的值为正整数.
                      触发器被重定向到一个拥有此ID号的helper上.
                      target
                      重定向触发器到第一个出现的目标.
                      target(ID)
                      ID应该是一个有效格式的表达式,计算出的值为非负整数.
                      则将重定向触发器到指定ID号的目标.
                      targetID在HitDef控制器的"ID"参数中指定.
                      partner
                      重定向触发器到玩家的同伴.普通helper和中立玩家不被认为是同伴.
                      详见触发器文档中相关触发器"NumPartner".
                      enemy
                      重定向触发器到第一个出现的敌人.普通helper和中立玩家不被认为是对手.
                      详见触发器文档中相关触发器"NumEnemy".
                      enemy(n)
                      n应该是一个有效格式的表达式,计算出非负整数.
                      触发器被重定向到第n个对手.
                      enemyNear
                      重定向触发器到距离最近的对手.
                      enemyNear(n)
                      n应该是一个有效格式的表达式,计算出非负整数.
                      触发器被重定向到距离第n接近的对手.
                      PlayerID(ID)
                      n应该是一个有效格式的表达式,计算出非负整数.
                      触发器被重定向到拥有唯一ID号且等于ID的玩家.详见触发器文档中相关触发器"ID"和"玩家ExistID".
                      如果触发器被重定向到一个不存在的地址(例如,当没有helper时将触发器重定向到helper),则会返回bottom.
                      注意:
                      不支持递归重定向(例如,"root,target,time")


                      IP属地:浙江11楼2025-01-30 20:31
                      回复
                        bottom
                        表达式中有几种无法补救的错误.
                        例如,可能试图去除以0,计算负数的平方根,或者试图重定向触发器到不存在的地址.
                        在这些情况下,bottom被用于优雅的完成表达式计算的一种方式.
                        如果bottom出现在表达式的任何地方(注意下面两个异常: 见Special Forms),则整个表达式的会变成bottom.
                        例如,思考这个表达式:
                        5 + fvar(0) ** 0.5
                        如果fvar(0)计算出-1,则此表达式变为5+bottom,产生bottom.
                        bottom在条件型触发器中被认为是0.
                        因此,一个产生错误的表达式将不会引起触发器崩溃.所以,例如,在
                        type = ChangeState
                        trigger1 = helper, statetype = A
                        value = 0
                        中,ChangeState控制器将永远不会被执行如果没有helper存在,因为表达式"helper, statetype = A"将计算出bottom,被认为是0.
                        (注:此种情况在debug时会出warning,解决方式就是在这条触发条件上方加上:
                        type = ChangeState
                        trigger1 = numhelper
                        trigger1 = helper, statetype = A
                        value = 0
                        由于控制器检测的时候遵循从上到下的顺序,所以会先检测trigger1 = numhelper,如果numhelper非0,表示有helper,则将继续检测trigger1 = helper, statetype = A
                        因为有了helper存在,才不会使控制器重定向到不存在的地址.如果numhelper不存在,则根据检测顺序原理,就不会再检测trigger1 = helper, statetype = A,也就不会出warning. )
                        通常当产生bottom时,MUGEN的调试控制台会输出warning.
                        这是因为bottom的存在指示一个可能的逻辑上的错误或者是歧义.
                        例如,在上面的ChangeState的例子中,如果不存在helper,则声明"helper, statetype = A"是空的,且无法判断该认为是真还是假.


                        IP属地:浙江12楼2025-01-30 20:32
                        回复
                          特殊格式
                          ifelse和cond触发器用一种特殊的方式处理bottom.他们的格式都为:
                          <trigger_name>(<exp_cond>,<exp_true>,<exp_false>)
                          <exp_cond>是条件表达式,根据是否为非0,控制<exp_true>和<exp_false>哪个将被返回.
                          如果还没有被返回前产生了bottom,则将不会把bottom传播至表达式其余部分.
                          例如,思考表达式IfElse(time > 0, 1.0/time, 2)),如果time>0,则表达式的值为1.0/time,一个有效的浮点值.
                          如果time=0,则表达式的值为2,即使是不使用的部分除以0产生了bottom.
                          然而,即使上面的ifelse触发器从不返回bottom,它仍然有一个烦人的特征:除以0仍然会生产一个warning在debug的时候.
                          这是因为ifelse计算了所有的参数,甚至是没用到的那部分.相反,Cond将只计算实际用到的参数.
                          因此,如果我们重写表达式为 Cond(time > 0, 1.0/time, 2),当time等于0的时候参数 1.0/time 将永远不会被计算,所以也就不会生产warning了.
                          你也许想知道何时使用Cong和IfElse.
                          回答是你几乎经常要用到Cond,除非你需要<exp_true>或<exp_false>中的副作用.
                          换句话说,如果你正在给变量赋值(在一个经常需要被执行的分支中,无论<exp_cond>的值是什么),则你需要使用ifelse.
                          否则,你应该使用Cond,尤其是为了隔离bottom.


                          IP属地:浙江13楼2025-01-30 20:32
                          回复
                            避免警告
                            一些常见的错误条件在人物中经常产生大量的warning.
                            下面是一些最常见的情况和如何避免他们.
                            强制转换浮点型为整型
                            如果MUGEN期望某处是一个整型值(例如,一个var索引值),但却产生了一个浮点型值,则MUGEN将把浮点型转换成整型.
                            然而,它会抱怨,因为这也许会对你来说表示一个错误.
                            要摆脱warning,通过提供显式转换将浮点型转换为整型,也就是floor()或ceil().
                            重定向触发器到不存在的目标
                            如果你重定向到一个不存在的目标,就像你根本没有helper而你重定向到了helper,则bottom会产生且记录了warning.
                            这是因为表达式逻辑上模糊了(就是上篇文章提到的有歧义).你能通过在重定向前检测目标是否存在来避免warning.所以,
                            trigger1 = helper(1234), time > 20
                            将产生warning如果helper1234不存在.
                            trigger1 = numhelper(1234) > 0
                            trigger1 = helper(1234), time > 20
                            将不会.具体原因见上一篇文章的"注意"
                            另外,你能使用Cond触发器来隔离能潜在产生warning的代码.
                            假设我们要用一个表达式设定helper(1234)的生命值(如果他存在),如果不存在的话就设置player的生命值.则我们可以这么写:
                            Cond(numhelper(1234) > 0, (helper(1234), life), life)
                            (围绕helper(1234),life有一个额外的括号,它不是必要的,但能提高可读性)
                            缺少必要的 动作/图片素材
                            这不是表达式的问题.把缺失的补上!


                            IP属地:浙江14楼2025-01-30 20:32
                            回复
                              2025-08-01 03:02:04
                              广告
                              不感兴趣
                              开通SVIP免广告
                              触发器参数中的表达式
                              大多数函数型触发器要么没有参数要么有一个参数列表.
                              例如,time触发器没有参数,而ifelse却有3个参数.
                              ifelse(exp1,exp2,exp3)
                              这里exp1, exp2, exp3是所有有效的表达式.
                              在这种情况下,exp1, exp2, exp3都被认为是单独的子表达式,所以区间运算符可以出现在每个子表达式的最右方.
                              参数表运算顺序是从左到右.
                              由于不规则的语法,一些旧式函数型触发器不能使用表达式作为参数.
                              因为这个原因,它们不能通过标准方式被纳入表达式中.
                              对于这些类型的非标准触发器,触发器仅能与某些特定运算符和参数一起出现.
                              特别是,这些触发器不能用表达式作为参数.例如,
                              trigger1 = AnimElem = (1+1)
                              是一个无效表达式.
                              旧式函数型触发器仅出现在"trigger, relational operator, argument"格式的"条款"中.
                              这些条款被视为一个独立的单元(特别的,单个0元触发器)用于表达式的计算.
                              这表示,在这些事物中,运算符优先级的概念在旧式函数型触发器条款中不适用.
                              例如,在
                              trigger1 = AnimElem = 5 + 4
                              中,表达式分为3部分.
                              AnimElem = 5 + 4
                              |________| |_| |_|
                              "AnimElem=5"部分被视为0元触发器的名字,于是'+'运算符的等级不在'='之上,从"AnimElem=5"中就能看出来.
                              换句话说,这个表达式意味着这样的计算方式"执行触发器调用'Animelem=5',然后加上4得到结果"
                              一些旧式函数型触发器拥有可替换的触发器(可用表达式作为参数).它们是下面的:
                              AnimElem, 被 AnimElemTime 取代
                              TimeMod, 被 % 运算符取代
                              ProjHit, ProjContact, ProjGuarded; 被 ProjHitTime, ProjContactTime, ProjGuardedTime 取代
                              完整的不规则触发器列表,详见trigger部分.不规则触发器用***标示出来.


                              IP属地:浙江15楼2025-01-30 20:33
                              回复