NTRPG.net Board
 
NTRPG.net Board : Powered by vBulletin version 2.2.5 NTRPG.net Board > CRPG > 剑与魔法[Sword and Magic] > 精华区 > 如何编写IEG(Infinity Engine Game)的AI之初步介绍——Baldur's Gate I【作者:Ctrl_Z】
  上一主题   下一主题
作者
正文 发表新主题    回复主题
Karla 查找更多关于Karla的帖子
ArchFox


注册日期: Nov 2001
来  自: 灰色荒野
发贴数量: 580

如何编写IEG(Infinity Engine Game)的AI之初步介绍——Baldur's Gate I【作者:Ctrl_Z】

【转于亚联论坛 作者:Ctrl_Z】

当你第一次接触AI的时候总是会觉得非常困难,当然如果你有一些编程经验的话,那么AI的编写看起来就会变得简单许多了。不过别担心,任何人都能够学会如何编写AI的。IEG中的AI决定了NPC们会怎么做、施放什么样的法术、或者在什么情况下会发生什么事情等等。

好,依照惯例,让我来确定一下你的硬盘上有以下这些东西:
1.VB5 Package
2.Infinty Explorer
3.ISE(Infinity Script Editor)
4.Script Compiler(可以在BG I的光盘上找到)


先来看一个例子:
IF
See(NearestEnemyOf(Player1)) /*如果我看到了离第一个玩家最近的敌人*/
THEN
RESPONSE#100
Attack(NearestEnemyOf(Player1) /*那么我就攻击离第一个玩家最近的敌人*/
END

以上的这段AI就是一个比较简单的IF-THEN-END结构语句,无论怎么说,看上去很简单吧,非常容易理解。因为每段AI里面都有IF、THEN、RESOPNSE、END,那么我们干脆把这四个单词称做关键字吧。

在IF和THEN这两个关键字里面的内容是条件,THEN和END关键字里面的内容是根据条件产生的动作、反应。就像我们日常生活中常说的“如果这样的话,那么就那样”。


RESOPNSE #xxx
这个关键字以及跟在它后面的#xxx决定了根据条件所产生的动作、反应的概率。
在上面这个例子中Attack(NearestEnemyOf(Player1) 是一定会发生的。
但是,我的朋友,千万不要被#后面的数字给迷惑了,具个例子你就会明白了。

IF
See(NearestEnemyOf(Player1))
THEN
RESPONSE #100
Attack(NearestEnemyOf(Player1)
RESPONSE #100
Attack(Player1)
END

以上这段AI说明“当我看到离第一个玩家最近的敌人的时候,我有50%的可能性攻击那个敌人,不过我也有50%的可能性攻击第一个玩家”(^_^)
当然如果你把上面这段AI中‘#’后面的数字都改成‘1’,所产生的结果是完全一样的。

THEN在什么时候用呢?
THEN是个起承上启下作用的关键字,当你把你的条件写完之后,决定开始写你的动作或反应之前,就可以输入这个关键字了。


现在我们来看一些比较具体的东西。
打开你的Infinity Explorer,在IDS目录下,你可以找到ACTION、OBJECT、TRIGGER这三个IDS文件。

1)TRIGGER里面的所有的……恩……我们把它们称做函数吧,这些函数总是出现在IF和THEN的关键字之间的,它们决定了自己所表示的条件是否成立。比如:

IF
See(Player1)
……
表示“如果我看到第一个玩家……”

如果你考虑得比较细致得话,或许你会担心程序是不是真的能够知道“我是否看到了第一个玩家”的话,那我告诉你,你大可不必这样过虑。理由也很简单:

当程序碰到“See(Plyaer1)”的时候,它会用另外一种神秘的方式观看一下游戏屏幕,当它看到了Player1的时候,那么See(Player1)=1;如果没有看到,那么See(Player1)=0。接下来的事情就交给IF了,当IF看到1的时候,就通知THEN:“你可以执行你后面的语句了!”;当IF看到0的时候,IF就跳过THEN,找END去了,THEN后面的语句就作废了。

如果在IF关键字后面有两个条件,那么当这两个条件同时成立的情况下,THEN才会执行,这就是数学上一个‘∧’运算,比如:

IF
See(Player1)
See(Player2)
……

另外想在这里提一下在BG II中新增加的OR()运算,当然也就是数学中的“∨”运算。

IF
See(Player1)
or
See(Player2)
or
See(Player3)
……
然而OR()并不是这样用的,真正的方法应该是这样:

IF
OR(3)
See(Player1)
See(Player2)
See(Player3)
……
表示“如果我看到第一玩家或者看到第二玩家或者看到第三玩家”

2)ACTION里面的所有的函数总是出现在THEN和END关键字之间的,它们是在条件成立的情况下才执行的各种动作或者反应。比如:

RESPONSE #100
Attack(Plyaer1)
……
表示“攻击第一玩家……”

那么整个语句连贯起来就表示“如果我看到了第一个玩家,那么我就攻击他,如果没有看到,就不攻击”:
IF
See(Player1)
THEN
RESPONSE #100
Attack(Player1)
END

3)OBJECT里面所有的……目标(可以这么叫吧)出现在IF-THEN-END中的任何地方。
比如Player1就是一个目标,目标可以嵌套使用,比如,你想表示“攻击离Player1最近的敌人”,那么就可以这样写:Attack(NearestEnemyOf(Player1)),其中真正的目标不是Player1,而是NearestEnemyOf(Player1)。

值得提醒的就是要注意结尾处括号的数量,一个比较好的检验的方法就是数一下这个句子中有几个目标(无论是真的还是虚的),如:NearestEnemyOf+Player1等于两个目标,那么在结尾处一定是有两个“))”。

另外想提醒的一点就是关于嵌套,请不要套得太多,像 Protector(LastAttackerOf(LastTargetedBy(ProtectorOf(Myself)))) 这样的句子虽然是正确的但是却非常难以理解,不提倡使用,如果继续加嵌套的话,程序都会造反的。


看了这么多,也让我们自己来写一段AI吧~~
首先打开ISE。
恩……我们来写一段关于战士的AI吧。

I 首先,“如果我们的战士看到他的敌人并且离他的敌人比较远,那么战士就用远程武器攻击敌人。”


条件:1.战士看到他的敌人;2.战士离敌人比较远。
IF
See(NearestEnemyOf(Myself))
!Range(NearestEnemyOf(Myself),7)
表示“如果离我最近的敌人和我的距离不在7尺之内”。
注意句子前面的‘!’就是表示否定的意思,如果没有‘!’,那么这个句子就表示成“如果离我最近的敌人和我的距离在7尺之内”(很不习惯这种表示方法吧,没有办法,还是习惯一下吧)。


反应:战士用远程武器攻击敌人。
THEN
RESPONSE #100
EquipRanged() /*装备远程武器*/
Attack(NearestEnemyOf(Myself)) /*攻击离我最近的敌人*/

RESPONSE后面的语句会按照它们排列的顺序执行下来,所以如果把两句句子颠倒的话,那么战士只有在攻击了这个离他最近的敌人之后才会装备上远程武器——和我们期待的背道而驰。

最后不要忘记加上一个END。

于是我们整个AI就是:
IF
See(NearestEnemyOf(Myself))
!Range(NearestEnemyOf(Myself),7)
THEN
RESPONSE#100
EquipRanged()
Attack(NearestEnemyOf(Myself))
END

很简单吧。

II 第二,如果我们的战士和敌人的距离在7尺之内的话,那么只能肉搏了。

IF
See(NearestEnemyOf(Myself))
Range(NearestEnemyOf(Myslef),7) /*注意没有‘!’了*/
THEN
RESPONSE #100
EquipMostDamagingMelee() /*装备最厉害的近身武器*/
Attack(NearestEnemyOf(Myself))
END


III 要让我们的战士有一定的保护意识,比如在关键的时候‘喝喝血’(这种奇怪的说法也不知道是从哪里冒出来的)

首先检查一下战士是不是有‘血瓶’(……):

IF
HasItem("POTN08",Myself) /*注意引号的使用*/

然后看看战士的血是不是只有全部生命值的25%了或者更少:

IF
HPPercentLT(Myself,25)
其中的LT表示少于(Less Than),那么GT就表示大于(Greater Than)。

如果是的话,那么就‘喝血’吧!

RESPONSE #100
UseItem("POTN08",Myself)

结果就是只要这个‘血瓶’在战士的身上,无论是在快捷栏里还是物品栏里,都会被使用掉,哦,不,是‘喝’掉。

IF
HasItem("POTN52",Myself)
HPPercentLt(Myself,25)
THEN
RESPONSE#100
UseItem("POTN52",Myself)
END

IV 所以我们的战士的AI就完成了:
IF
See(NearestEnemyOf(Myself))
!Range(NearestEnemyOf(Myself),7)
THEN
RESPONSE#100
EquipRanged()
Attack(NearestEnemyOf(Myself))
END

IF
See(NearestEnemyOf(Myself))
Range(NearestEnemyOf(Myself),7)
THEN
RESPONSE#100
EquipMostDamagingMelee()
Attack(NearestEnemyOf(Myself))
END

IF
HasItem("POTN52",Myself)
HPPercentLT(Myself,25)
THEN
RESPONSE#100
UseItem("POTN52",Myself)
END

不难吧,啊?问你呢!

V 让程序先保存为BAF文件,然后编译成BCS文件,强烈建议另存为TXT文件,以方便以后的修改。


写在最后:相对来说BG II有很多有效的TRIGGER和ACTION,IWD的不太好用,BG I虽然相对来说是最少的,但是却是个学习的好环境。还是这句话:实践是最重要的。

衷心感谢:Potencius

最后由 Karla 在 2002-03-06 05:54 编辑

向版主报告此贴 | IP地址: 已记录

Old Post 2002-03-06 05:50
Karla现在离线 点击这里查看Karla的个人资料 点击这里给Karla发送一条悄悄话 查找更多关于Karla的帖子 添加Karla至你的好友列表 编辑/删除信息 回复/引用
全部时间为北京时间. 现在时间是 01:37. 发表新主题    回复主题
  上一主题   下一主题
快速回复主题
标题: (可选)
选项:
自动分析URL地址
邮件回复通知
显示签名


显示可打印版本 | 将本页发送给朋友 | 订阅该主题

论坛跳转:
 

论坛规定:
你不可以发表新主题
你不可以回复主题
你不可以上传附件
你不可以编辑自己的帖子
HTML代码关闭
vB代码开启
表情符号开启
贴图代码[img]开启
 
Copyright ©2001 CNVBB.COM. - Translated and hacked by: -=CNVBB=- - 联系我们