偶久网

 找回密码
 注册会员

QQ登录

只需一步,快速开始

首页 制图资源 制图教学 查看内容

狼牌Jass扫盲基础教程。

2013-6-3 16:06| 发布者: 邪恶叔| 查看: 2507

无意发现这论坛,后来又无意发现以前不记得什么时候还注册过号,所以无意就把不久前写的教程拿过来了。球加分,球加精,球各种高亮。   

     模仿各位大神教程前必定废话连篇的定律,凑凑字数,省的这东西拿除去不到100字,那不是坑爹么。
    不知为何,现在很多人都蛋疼的要研究jass这个老物。在无数人的骚扰以及高压下(囧是人都知道我其实很懒),无奈的填掉这个坑。其实jass这种东西,也就是那么回事,理解了基本结构差不多就算入门就可以简单的上手了。并不是什么高深到没有天理的家伙,当然这篇教程不是针对所谓的0基础入门,对于基础这种东西就不做解释了,看的懂就是看的懂,看不懂那也没办法。毕竟我又不是幼儿园老师,一定要教会你们1+1=2。。。
    好吧,废话结束(写废话这种事情,还是不适合我啊,哎。) 备注:如有大神发现教程内出现错误,请指出。

=====================只是分割线而已=======================
第一章:概述

    关于jass这种东西,在这里大致只讲解两个东西。变量和函数。变量的定义在这里就不多做解释了。大致的知道就可以了。

1函数
a . 函数结构
  1. function 函数名 takes 参数列表 returns 返回值类型
        local 变量类型 变量名 (=初始值)
        ...........
        执行代码
        ..........
        return 返回值
    endfunction
复制代码
注:
1:局部变量申明必须在所有执行代码的之前,也就是上面。
2:函数名必须唯一,不能出现相同名字的函数。
3:函数可以没有传递参数和返回值,这类在takes和returns之后写上nothing即可。即代表无传递值和返回值。
4:返回值必须唯一。
5:return必须写在函数末端,无返回值的函数就不需要写return了。
6:注意一下return和returns的区别,虽然只是多了1个s,但还差距是很大滴,前者是具体数据,后者是变量类型。
7:数组不能作为传递参数和返回值。
8:参数列表可以传递N个参数,中间用逗号隔开就可以了。




2变量
a . 全局变量申明
  1. globals
        变量类型 变量名 ( = 初始值 ) //括号表示不赋值也可以
        变量类型 array 数组名 //数组不可直接赋值,要在函数内逐个赋值
    endglobals
复制代码
b . 局部变量申明:
  1. function Test takes nothing returns nothing
        local 变量类型 变量名 (=初始值)
        local 变量类型 变量名 (=初始值)
        ...........
        执行代码
        ..........
    endfunction
复制代码
注:局部变量申明必须在所有的执行语句之前。

c. 全局变量与局域变量的区别:
    全部任何函数都可以使用,局部只作用于所申明的函数。不同函数间的局域变量命名可以相同,不过最好不要与全部和参数名相同。当然,两种变量各有优缺点。全局变量占用内存大,执行速率快,局域则相反。触发内,比如最后创建的单位啊,触发单位啊,循环整数A什么的都是全局变量。变量编辑器里的变量也都是全局变量。




3示例
  1. globals
        integer i = 0 //正确,申明一个整数类型变量 i 初始值为0
        real ang //正确,申明一个实数类型变量 ang 无赋值
        unit array u //正确,申明一个单位类型变量数组u
        integer array real = 0  //错误,数组不能直接赋值
    endglobals
    function Test_A takes nothing returns nothing
        local integer i = 1  //申明一个整数局域变量i 赋值为1.
        set i = i + 1
        local real ang  //错误局域变量申明应该在所有执行代码之前。
    endfunction
    function Test_B takes real a, real b returns nothing
    //传递一个实数类型参数a,和一个实数类型参数b
        call BJDebugMsg(R2S(a) + R2S(b))  //在游戏里显示a+b的值
    endfunction
    function Test_C takes nothing returns real  //返回一个实数类型变量
        return 1+2+3 //正确
    endfunction
    function Test_D takes nothing returns real,integer
    //错误,一个函数只能返回一个数据类型
        return ....
    endfunction
    function Test_E takes nothing returns boolean
    //错误,有返回值类型结尾却没有具体的返回值
    endfunction
    function Test_F takes nothing returns nothing
        local real a = 0
        return real a
    //错误,有具体返回值。却无返回值类型。
    endfunction
    function Test_G takes real ang returns real
        return Cos(ang)
    //正确,有返回值类型也有具体返回值
    endfunction
    function Test_H takes integer array i returns nothing
    //错误,数组不能作为传递参数。
    endfunction
复制代码
=====================只是分割线而已=======================
第二章:进阶研究

函数的定义

    我们可以把函数看成是能独立完成一件职能的机构,而函数起始的function和endfunction之间的部分就是这个函数职能或作用。当几个机构通过传递信息,回馈信息的方式结合在一起的时候就形成了一个完整的部门。从而完成我们指派的任务。就比如下面一个函数,我们想让他的职能是在游戏显示一行信息 “狼是个帅哥” (切,地球人都知道,这还用你说)。
  1. function Test takes nothing returns nothing
        call BJDebugMsg("狼是个帅哥")  //在游戏显示,狼是个帅哥(唷,人家好害羞。)
    endfunction
复制代码
    简单剖析上面的函数,我们可以把各种代码什么的比喻成一个员工,而它就完成着自己的任务。当大家任务都完成无误的时候,这整个机构的任务就算是完成了。然后单个或多个机构结合在一起,唷 总裁,业绩提升了呢。 这是为什么呢,废话么。因为狼是帅哥捏。(额,稍微口胡了点而已。)

函数的运行概念

    函数是如何运行(调用)的呢?只需要 call 函数名(参数列表) 就可以了唷,很简单吧。参数列表这个前面已经解释过,这里就不多做解释了。需要注意的是。被调用的函数一定要在调用函数的前面,而且传递状态下的参数列表要和被调用参数的次参数列表的类型,次序一致。(有点象绕口令,我自己都有点晕。。)
  1. function Test_A takes integer i returns nothing
        call BJDebugMsg(I2S(i))   //在游戏显示,i的值
    endfunction
    function Test_B takes nothing returns nothing
        call Test_A(174)  
    //正确,被调用的函数Test_A在调用函数Test_B的前面,函数Test_B将整数174传递到函数Test_A
        call Test_C(174)  //错误,被调用的函数Test_C在调用函数Test_B的后面。
    endfunction
    function Test_C takes integer i returns nothing
        call BJDebugMsg(i)
    endfunction
复制代码
     那有的童鞋就要问了,那前面的函数就不可以调用后面的函数了么。其实是可以的,但是有诸多限制,反正也不常用。这里就不说了。有兴趣的可以自己去研究研究

语法

a . 条件控制 (if条件)

①单层控制
  1. if 条件 then  //满足条件则做
        .......
        执行代码
        .......
    endif
复制代码
②双层控制
  1. if 条件 then  //满足条件则做
        .......
        执行代码
        .......
    else  //不满足条件则做
        .......
        执行代码
        .......
    endif
复制代码
③多层控制
  1. if 条件1 then  //满足条件1则做
        .......
        执行代码
        .......
    elseif 条件2 then  //满足条件2则做
        .......
        执行代码
        .......
    elseif 条件3 then  //满足条件3则做
    .........//满足条件N则做
    endif
复制代码

b . 循环 (loop)
  1. loop
        exitwhen 条件 //满足条件退出循
        .......
        执行代码
        .......
    endloop
复制代码
示例:
  1. function Test_A takes nothing returns nothing
        local integer i = 0 //声明一个整数类型局域变量i 赋值 0
        local integer ii = 10 //生命一个整数类型局域变量 ii 复制 10
        loop
            exitwhen i > ii //条件当i 大于 ii的时候,推出循环
            set i = i + 1 //设置i 等于 i 加 1
            call BJDebugMsg(I2S(i)) //在游戏中,显示i的值
        endloop
    endfunction
复制代码


c . 各种符号

①逻辑关系符
a.  and(且)
b.  or(或)
c.  not(非)

②比较和操作符
a.  [] (数组)
b.  []= (数组赋值)
c.  = (赋值)
d.  == (等于)
e.  != (不等于)
f.  > (大于)
g.  < (小于)
h.  >= (大于等于)
i.  <= (小于等于)


d . 示例
  1. function a takes integer i, integer ii returns nothing
        if (i != 0 and ii == 0) then  //当i 不等于 0 且 ii 等于 0 的时候显示条件符合
            call BJDebugMsg("条件符合")
        else
            call BJDebugMsg("条件不符")  //否则,显示条件不符
        endif
    endfunction
    function b takes nothing returns nothing
        local integer array i
        set i[0] = 0
        set i[1] = 10
        loop
            exitwhen i[0] > i[1] 当 i[0] 大于 i[1] 的时候退出循环
            set i[0] = i[0] + 1
            if  i[0] = 5 then  当i[0]等于5的时候显示狼好帅。
                call BJDebugMsg("狼好帅")
            endif //endif嵌套在loop里面,所以应该先结束里层的if
        endloop //然后在结束外层loop。
    endfunction
复制代码

缓存以及哈希表

a . 缓存

①原理
     缓存很好理解,我们可以把缓存看做一个巨大的仓库,里面有很多的柜子,而柜子里又有很多的抽屉,不过,每一个抽屉只够能下5种类型的东西。且每一种类型的东西只能装一个,这就是缓存的原理了。
    那么,在WE里房子可以看做是一个缓存,既然是一个很大的房子一个房子就足够装下很多的东西,所以一个地图一个缓存就够了。柜子,可以看成缓存的目录,柜子可以看成缓存的标签。缓存的目录和标签都是采用字符串的形式。
    然后,我们可以想象一下。把一个套套(这种重要的东西,当然要存起来了。丢了怎么办。)存进仓库的A号柜子,B号抽屉里面那么 仓库 - A号柜子 - B号抽屉 - 套套a  那么同样的,想要拿出这个套套来使用,我们就必须要找到指定的路径才能找到这个套套。而且基于抽屉里东西的唯一性,那么再放一个套套进去就会覆盖掉前一个套套。那么 仓库 - A号柜子 - B号抽屉 里的套套,就会指向套套b 。当然,在其他的柜子或抽屉里放上套套将不会影响套套a。因为是其他的路径嘛。。
    So,对于缓存也是一个道理。一个标签只能存5种类型的数据,整数,实数,布尔值,字符串和单位。每种数据只能存一个,否则后来的舒服会覆盖同种数据。

②使用
缓存在使用前必须初始化,方法是 set 缓存变量名 =  InitGameCache("缓存名"),缓存名可以随便取。
  1. GetStored数据类型(缓存,目录,标签) //取出数据
    call Store数据类型(缓存,目录,标签,具体值) //存储数据
    call FlushStored数据类型(缓存,目录,标签) //清空标签
    call FlushStoredMission(缓存,目录)  //清空目录
    call FlushGameCache(缓存) //清空缓存
    HaveStored数据类型(缓存,目录名,标签名) //检测缓存数据是否存在
复制代码
③示例
假设缓存变量名为 udg_GC (别问我udg是什么,这就是所谓的基础了。)
  1. function Test takes nothing returns nothing
        call StoreInteger(udg_GC,"A","b",15)  //将整数15存在了缓存GC的A目录b标签下
        call StoreReal(udg_GC,"A","b",0.1)  //将实数0.1存在了缓存GC的A目录b标签下
        call StoreBoolean(udg_GC,"A","b",true) //将布尔值true存在缓存GC的A目录b标签下
    endfunction
    function Test_B takes nothing returns nothing
        local integer i = GetStoredInteger(udg_GC,"A","b")  
    //取出缓存GC的A目录b标签的整数类型数据,并赋值给整数i
        local real r = GetStoredReal(udg_GC,"A","b")  
    //取出缓存GC的A目录b标签的实数类型数据,并赋值给实数r
    endfunction
复制代码
    这时候有的童鞋们就要问了,只有5种太少了吧混蛋,那我要存别的类型怎么办呢。哇嘎嘎,童鞋们这时候伟大的Return bug就出场了。RB同志说:有我在,缓存就是个受。想怎么虐就怎么虐。不过在这里就不多做赘述了,大家可以找相关教程自己研究。。。

b . 1.24的哈希表

①原理
    很简单,只不过是把缓存的标签和目录的形式改成了整数,同时1.24修正了Return bug (RB童鞋泪奔中。)同时可以直接储存一些句柄类数据。

②使用
    哈希表在使用事需要声明一个哈希表类型变量才能使用,这个可以结合前面所将到的东西。
  1. call Save数据类型(哈希表,目录,标签,具体值) //存
    Load数据类型(哈希表,目录,标签,具体值) //取
    call FlushChildHashtable(哈希表,目录) //清空目录
    call HaveSaved数据类型(哈希表,目录,标签) //检测数据是否存在
复制代码
示例什么的和缓存其实差不多,也就不多说了。相信IQ超过80的人,应该都能理解的。

=====================只是分割线而已=======================

第三章:传递参数与返回值的简单解析。

    本来写了这么多,是不想写这章的。可是大部分的在群里的提问都是传递参数是什么,返回值又是干什么怎么用的什么什么什么的。无奈之下开了这章,简单的解释一下传递参数和返回值的概念和运用。
传递参数
a . 概述
    传递参数也就是跟在"takes"后面的那一坨东西,它可以实现不同函数之间的数据传递使用,传递参数可以没有用"nothing"表示。可以是一个,也可以是N个参数同时传递。具体机构为:
  1. function Test takes (参数1) ,(参数2) ....(参数N) returns nothing
        ...........
        执行代码
        ...........
    endfunction
复制代码

在这里,不同的参数之间使用逗号隔开就可以。

b . 运用
  1. function Test takes nothing returns nothing
        local real a = 1
        call BJDebugMsg(R2S(a))
    endfunction
复制代码

上面一段函数,相信看了前面的教程后,不难理解。在屏幕上显示1,那我们像让他显示2,3,4或者N呢,那怎么办,难道一个一个写,事实上完全不用这么麻烦。我们可以稍微改动一下这段函数,然后经过调用就可以任意的显示我们像要的数字。
  1. function Test takes real a returns nothing
        call BJDebugMsg(R2S(a))
    endfunction
复制代码
一个小小的改动,只是多了一个传递参数而已。这时候,我们就可以通过 call 调用函数,来显示我们像要的数字了。比如我们像让他显示1999。只需要 call Test(1999)就可以了。当然,示例写的比较简单。这里就要通过自己的具体应用来把它发扬光大了。。。

返回值

a . 概述
    这个概念和传递参数的相反,传递参数是把数据传递给其他函数使用,而返回值却是从其他函数获取一个数据。需要注意的事项在前面已经写的很明白,这里就不在做解释了。。直接进入运用部分。

b . 运用
  1. function Con takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == 'A000' ) ) then
            return false
        endif
        return true
    endfunction
复制代码

上面的函数有什么用呢,它的意思是当释放的技能等于'A000'的时候(别问我'A000'是什么),将会返回ture值。那否则就反之了。其实这个函数可以精简很多,下面将提到。。

=====================只是分割线而已=======================

第四章:触发器和函数


一般的触发器是由3个函数组成的,事件函数(初始化函数),条件函数,动作函数,那么jass要完成一个技能啊系统啊什么的也是需要这3个步骤的。当然,也可以扩展出很多其他的自定义函数以及使用方法什么的。。。。那么废话不多说,马上就要拨开云雾见青天了。。我们在WE中新建一个触发器a。
[attach]122670[/attach]
然后我们把它转换成文本格式,也就是jass。。得到一堆代码。。
  1. function Trig_a_Conditions takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then
        return false
        endif
        return true
    endfunction
    function Trig_a_Actions takes nothing returns nothing
        call KillUnit( GetSpellTargetUnit() )
    endfunction
    //===========================================================================
    function InitTrig_a takes nothing returns nothing
        set gg_trg_a = CreateTrigger( )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_a, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( gg_trg_a, Condition( function Trig_a_Conditions ) )
        call TriggerAddAction( gg_trg_a, function Trig_a_Actions )
    endfunction
复制代码
哇哦,看上去是不是有点小复杂呢。没关系,我们来一点点的剖析。
  1. function Trig_a_Conditions takes nothing returns boolean
        if ( not ( GetSpellAbilityId() == 'AHtb' ) ) then //释放的技能不等于'AHtb'返回false(假),否则返回true
            return false
        endif
        return true
    endfunction
复制代码

咦,看看注释,怎么觉得这功能很熟悉呢。好吧,如果你这么想的话,那么某狼会毫不犹豫的大吼一声:我XX你个XX,熟悉你妹啊,一眼就应该看出来这是条件函数啊混蛋。。。
  1. function Trig_a_Actions takes nothing returns nothing
        call KillUnit( GetSpellTargetUnit() ) //杀死技能释放目标
    endfunction
复制代码

咦,别妄想了。在想就成妄想症了。没错,这就是动作函数。。那么下面的就是重点了,重点啊重点。(口胡啊口胡)
  1. function InitTrig_a takes nothing returns nothing
        set gg_trg_a = CreateTrigger( )
        //设置触发器a为新建触发(这个前缀gg_trg_是触发器变量前缀注意其实触发器也是变量)
        call TriggerRegisterAnyUnitEventBJ( gg_trg_a, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        //为触发器a添加事件,任意单位发动技能效果
        call TriggerAddCondition( gg_trg_a, Condition( function Trig_a_Conditions ) )
        //为触发器a添加条件, 函数为 Trig_a_Conditions
        call TriggerAddAction( gg_trg_a, function Trig_a_Actions )
        //为触发器a添加动作,动作函数为 Trig_a_Actions
    endfunction
复制代码

噢,TXXD。原来是这样啊。没错,其实就是这么简单。所以说jass也不是什么很难的东西。。。囧了个囧。。我在说什么呢,自言自语么。。。

=====================只是分割线而已=======================

家庭作业
怎么,童鞋们,学完了么?接下来我们一起来写作业吧(囧orz。。。)好吧好吧,大家来写个击退函数吧。记得要支持多淫哦,写过的童鞋就在下面跟帖好了。





喵了个咪,终于写完了。球加精,球加分,球交作业。。
返回顶部