- UID
- 260987
- 阅读权限
- 150
- 注册时间
- 2016-4-30
- 最后登录
- 2025-2-16
- 在线时间
- 971 小时
- 性别
- 男
- 生日
- 1996 年 6 月 6 日
- 情感状态
- O
宗主教
- 耕战
- 1420
- 鹰币
- 134898
- 天龙币
- 0
- 回帖
- 399

|
关于1.0系列征服者AI单机剧情随机效果的简介
(注意:本贴仅针对于1.0系列单机剧情编辑而言,1.5是可以在触发中实现该功能的。本帖涉及一点基础AI知识,由于个人水平十分有限,欢迎各位研究AI的玩家批评指正)
众所周知,一部好的战役往往有其令人为之称道的设计,哪怕只有一处出彩的地方。可以是一幅精美的地图,也可以是一套天衣无缝的剧情,也可以是某处独具匠心的触发设计。而这对于玩家而言,都将大大增强其可玩性,获得玩家的肯定并提升认可度。可话说回来,一部好的作品总是凝聚了作者大量的心血,相信只要是有些经验的编辑党都会有这样的感受,制作一个总评3分以上的作品绝对不是一件随便的事情。正所谓,台上一分钟,台下十年功,展现在玩家面前的优秀作品无疑是作者千锤百炼之下诞生的。
然而,在比较多的时候,相对单一的操作流程(多线路,以及加入选择性数字AI的战役除外),多少会让玩家在进行次数到三四次甚至两三次的时候,就感觉相对乏味。人对于新鲜的事物总是心怀期待,而一旦接受重复的事情,总会多少有些乏味(印象特别深刻的除外)。所以讲的官方一些,这也是制约战役可玩性的一个方面。因此现在很多时候提倡玩家制作开放度好一些的剧情,而大部分玩家会采用多线路和其他一些具备主观选择功能作为切入点,当然这本身就是普遍性的一种方法,总体还是推荐的。其实,玩家就是想要不完全一样的东西,那么,是不是游戏中能够出现一些不确定性事件,那么从某种程度上来讲,就可以起到异曲同工的效果呢?个人认为还是会有帮助的。(要不我也就没必要发这个帖子了>_<)
首先大家要知道在CPSB[ Computer Player Strategy Builder (Guide) ]中,提供了随机加载指令load-random 和 generate-random-number(以及random-number)两种随机功能。load-random是指在游戏开始,AI一开始被调用的时候以事先设定好的几率加载另外的AI文件,这在大中型建毁类AI使用较为常见,比如以三成几率采用城快战术的AI,五成采用塔爆AI,两成采用围家战术AI,这里不作具体阐述。这里着重谈谈generate-random-number的相关信息。
generate-random-number是指生成随机数,是一个Action(动作)参数,设置了某个值x后,在该动作被执行时系统即刻从1到x(含x)中产生一个随机整数。还有一个Fact(事实)参数random-number(生成的数值)与之配套。前者的value(值x)取值范围一般为16位整数2^-15---2^15 - 1,一般取100,也与我们对于百分数的习惯相契合。而random-number则是通常作为判断条件来使用。举个最基本的例子就可以明白:
====================================================
(defrule
(true)
=>
(generate-random-number 100) ;产生一个1---100间的随机数
(disable-self) ;不重复执行
)
(defrule
(random-number <= 20) ;随机数范围在1--20时
=>
(chat-to-all "Small probability") ;送出全体信息Small probability
(disable-self)
)
(defrule
(random-number > 20) ;随机数范围在20--100时
=>
(chat-to-all "Large probability") ;送出全体信息Large probability
(disable-self)
)
====================================================
显然,这就是20%几率送出前一句话,80%送出后一句话,这是最最简单基本的应用,而且如果这样十分简易地书写,那么这套语句只能出现一次(后面会简单解释,玩家们应该都知道 )。
在最近发布的一个优质作品中(哥特的迁徙),玩过的玩家应该知道,剧情指南里就写了每次向固定玩家进贡100黄金然后列出了每次进行赌博得到黄金的各个百分比的信息,那就是一个相对实际一点的一个实际使用。
如果让我来设计类似的这一段,假设某AI玩家从5分钟起开启赌博功能:每收到玩家1的100黄金进贡作为赌博资本(如果过多也只记为100),70%几率返还60黄金,15%几率返还80黄金,%10几率返还100黄金,5%几率返还400黄金,5分钟前的任何进贡,则会收到无效信息。我会以如下方式设计(假设此时AI中只有此处使用随机数,或者该处为第一个使用随机数的一段代码,以免引入过多常数)
====================================================
(defconst Tribution 10) ;设置一个名为Tribution的goal值,目的是防止循环进贡
(defrule
(true)
=>
(set-goal Tribution 2) ;预设为2(表关闭)
(disable-self)
)
(defrule
(game-time <= 300)
(players-tribute-memory 1 gold > 0)
=>
(chat-to-all "Invalid tribution before the specified time point")
(clear-tribute-memory 1 gold)
)
;以上指定5分钟前任何进贡均无效。且由于是单人游戏,直接使用chat-to-all输出信息
(defrule
(game-time >= 300)
=>
(chat-to-all "Tribution system activated")
(disable-self)
)
;赌博功能开启
(defrule
(game-time >= 300)
(players-tribute-memory 1 gold >= 100)
=>
(set-goal Tribution 1) ;设为1,表示开启
(generate-random-number 100)
(clear-tribute-memory 1 gold)
)
;该项为反复执行的动作,不再加入disable-self。又要保证每次进贡黄金数量只进行一次判断,因此要消除每次进贡对下次的影响,应在效果使用clear-tribute-memory,否则会导致玩家不断进贡,下同
(defrule
(random-number <= 70)
(goal Tribution 1)
=>
(chat-to-player 1 "Normal result : 60 gold")
(tribute-to-player 1 gold 60)
(set-goal Tribution 2) ;用于防止循环进贡,下同
)
(defrule
(random-number > 70)
(random-number <= 85)
(goal Tribution 1)
=>
(chat-to-player 1 "Pretty luck : 80 gold")
(tribute-to-player 1 gold 80)
(set-goal Tribution 2)
)
(defrule
(random-number > 85)
(random-number <= 95)
(goal Tribution 1)
=>
(chat-to-player 1 "Regain your cost : 100 gold")
(tribute-to-player 1 gold 100)
(set-goal Tribution 2)
)
(defrule
(random-number > 95)
(goal Tribution 1)
=>
(chat-to-player 1 "You absolutely pick a perk : 400 gold")
(tribute-to-player 1 gold 400)
(set-goal Tribution 2)
)
;说白了玩家需要考虑的是确定各个随机数的范围以确定几率,然后,就完成了......
====================================================
这样一来,一个基本的赌博AI部分就完成了。这部分代码结合一个不动AI的基本部分,就做出了一个类似于可以用黄金作为赌博交易的中立玩家。
以上部分是纯粹用AI语句来实现所有的效果,其实AI系统还有一个很好的一个创意和突破,那就是触发和AI是可以互通的,编辑器触发功能是具体到某个单位或者某个群体的动作或者行为以及状态,而AI是相当于一个玩家的性格,是一种宏观的体现,其总体倾向防御,还是倾向进攻,还是就是原地不动等待玩家来进行主观上的动作,这些才是1.0系列AI的总体效果和目的。而ES的开发人员当时也就考虑到了这两个功能需要一些互补,因此在AI和触发还分别提供了“桥梁”。AI中提供了set-signal(发送信号值)语句和event-detected(检测到触发事件),触发中则相应有发送AI信号和接收AI信号触发,这四个功能分别组成两组,即实现了互通的功能。
但是遗憾的是,1.0系列中,set-signal值发送给场景后,该值无法被注销导致不能实现在触发中反复利用这个条件,因此仅能适用于只出现一次的情形。不过如果进贡的情况仅仅只有一次,那么可以用来精简对话
这里以上述代码作为引例:(以上方文字之上的最后一段代码为例)
==================================
(defrule
(random-number > 95)
=>
(chat-to-all 1 "You absolutely hit the jackpot : 400 gold")
(tribute-to-player 1 gold 400)
(disable-self) ;该种情况只可能出现一次
)
==================================
玩家经过实际测试会发现,上述功能的实现没有问题,但是字眼比较多
——————————
***:You absolutely hit the jackpot : 400 gold ;AI发送的语句在游戏中会显示玩家名
***向你进贡了400黄金 ;进贡系统提示语
——————————
而效果像下面这样会简洁的多:
——————————
<获得:400黄金> ;纯送出对话
——————————
同时你确实获得了400黄金,不再显示多余的系统提示的进贡信息。(利用进贡触发的反向技巧)
相信使用过这个功能的玩家基本知道怎么做,以下简单体现一下具体形式
(defrule
(random-number > 95)
=>
(set-signal 10) ;删去进贡语句和交谈语句,加入set-signal实现向触发发送信号
) ;并作为触发中的条件(收到AI信号),注意为0--255之间
触发中(说明信息关闭,触发开启,不循环)
条件1:收到AI信号10
效果1:送出聊天:<获得:400黄金>
效果2:进贡属性:(由玩家向AI玩家进贡-400黄金)
这样就完成了,每个情况都分别对应一个触发里的signal值,这样就优化,对话就相对简洁了
还是那句话,美中不足在于只能使用一次,不能够循环
=================================================
最后来一点简单的实际应用:(相比之下,本段AI比赌博代码还要简洁一些)
假设接上上面的赌博代码,这里还要设置一段情况为:某个英雄在杀死了敌人的某个小boss,他应该可以顺势获得一些奖励,假设有以下几种情况且几率均等:
1.生命加成(+100生命上限)
2.攻击加成(+2攻击)
3.综合加成(+60生命,+1攻击)
4.增加回复速度(利用英雄式补血法原理增加1/3回复力
5.敌军boss弱化(boss -1攻击 -50生命上限)
(只可惜不能直接改防御力,否则就更有趣味性了)
需要玩家注意的是,目前假设了该段代码是接在上一段赌博代码后的一段语句,因此考虑到系统会按照由上至下处理代码,那么如果直接采用:
(defrule
(random-number <rel-op> <value>)
=>
......
这样的形式,那么在赌博部分的generate-random-number产生随机数时会触发或可能触发此处的条件,那么这就导致了严重的bug,因此这里定义并引入一个名为"SKILL"的goal作为条件开关
具体代码如下:
====================================================
(defconst SKILL 10) ;定义一个名为SKILL的常数
; 注:如果这段随机读取代码是单独存在于AI中或是作为第一段则完全可以省去goal部分,但是由于现在是接在一段随机数语句后,因此要用goal来排除上一段代码中产生的随机数的影响,这和第一段赌博中引入goal的原因不同,赌博中是为了防止循环进贡而设定的
(defrule
(true)
=>
(set-goal SKILL 2)
(disable-self)
)
;预设一个值2(习惯用2代表否定,相当于关闭下面的关于技能的触发)
;由于要杀害某个特定目标,则需要一个来自触发的信号值来激活AI里的随机项目,最后再把随机得到的相关值用signal信号值返回至触发实现属性更改
(defrule
(event-detected trigger 1) ;来自触发效果的发送AI信号1(其条件是杀害小boss)
=>
(generate-random-number 100)
(set-goal SKILL 1) ;习惯用1代表肯定,此时相当于激活了以下的技能触发
(disable-self)
) ;生成随机数以进行下步的判断
(defrule
(random-number <= 20)
(goal SKILL 1) ;当且仅当SKILL值为1时满足该项条件,下同
=>
(set-signal 1) ;该处对应生命加成的触发,下面set-signal 2---5依次对应
(disable-self) ;上述奖励列表,不再赘述,具体触发略
)
(defrule
(random-number > 20)
(random-number <= 40)
(goal SKILL 1)
=>
(set-signal 2)
(disable-self)
)
(defrule
(random-number > 40)
(random-number <= 60)
(goal SKILL 1)
=>
(set-signal 3)
(disable-self)
)
(defrule
(random-number > 60)
(random-number <= 80)
(goal SKILL 1)
=>
(set-signal 4)
(disable-self)
)
(defrule
(random-number > 80)
(goal SKILL 1)
=>
(set-signal 5)
(disable-self)
)
;由于假设的是杀死小boss后只能出现其中一奖励种状况并且不再使用这些技能,所以全部要加上disable-self并且不用再注销event(acknowledge-event),这就完成了
====================================================
个人觉得单人剧情随机数的基础应用就差不多这两种了,如果还想深入了解或是还希望将随机效果强化,就只有各位大佬参考更为详细和深入的资料了,本人AI萌新一只,不妥之处,望批评指正。在此感谢@Apeiria回复中指出的问题,谢谢指导
|
|