Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统

作者: 深蓝色右手  来源: 博客园  发布时间: 2011-04-19 11:18  阅读: 1282 次  推荐: 0                   原文链接   [收藏]  

  谈到人工智能(AI),这个话题就太大了;大学里有《人工智能教程》专门讲这方面的知识,什么大名鼎鼎的人工神经网络、遗传算法等等均可一窥究竟,这里如赘述似乎有些班门弄斧,我们暂且丢它一边去吧。

  本节,我的主要目的是与大家共同探讨AI在RPG游戏中的应用。看过之前教程的朋友一定不会陌生,A*算法就是其中的一个重要组成部分;而本系列Demo中则使用了更为高级的改进型A*算法,不仅优化了性能,同时也大幅提升了玩家的操控体验。除此之外,AI更常见于RPG游戏中的角色,接下来我将引领大家循着AI的足迹,逐步探寻Silverlight网页游戏中的AI系统设计与Demo实现。

  以一份网上见到的AI系统设计文档作为开始,我们不妨首先分析下它的主体思路,大可断定其为直观的解析当前国内即时类MMORPG主流角色AI设计:以古老而传统的有限状态机FSM(Finite State Machine)固定决策树AI为原型,简单而直接。当然,为了让游戏体验更加有趣且不至于显得一成不变,我会考虑引入基于Random的各类概率机制,即使用随机性和灵活性更强一些的模糊状态机FuSM(Fuzzy State Machine)。所有非控角色的行为完全无规律可循难道不会更吸引人吗?

  有了大体思路后,接下来我们要做的就是将复杂而庞大的AI系统简化,使之更好理解并最终转化成Silverlight游戏程序代码。纵横剖析,我更青睐于按照策略AI (tactic)与行为AI (action)两部分予以划分。所谓“策略AI”即角色何时采取何种决策,而“行为AI”则为角色按决策执行何种行动。

  基于以上方案,我首先在角色控件的内部添加一个计时器用于AI逻辑循环,其中策略AI间隔3秒执行一次,用于敌对角色的搜捕(索敌);行为AI与角色动作切帧率同步,根据实时情判断应该执行警戒/追击/攻击/转向/逃逸/或其他等行动;而这两种AI在角色控件中分别对应两个事件,对于主角来说,玩家的操作已相当于主角的策略,因此主角只需注册行为AI事件,而其他非控角色则需要同时兼具两者以具备完整的AI能力:

/// <summary>
/// 动作(帧)切换时触发
/// </summary>
public event EventHandler ActionTrigger; /// <summary>
/// 以1秒为基础单位的时间间隔触发器
/// </summary>
public event EventHandler<IntervalTriggerArgs> IntervalTrigger; /// <summary>
/// 间隔触发器参数
/// </summary>
public class IntervalTriggerArgs : EventArgs {
public int Count { get; set; }
} int tickCount = 0;
IntervalTriggerArgs intervalTriggerArgs = new IntervalTriggerArgs();
void intervalTrigger_Tick(object sender, EventArgs e) {
intervalTriggerArgs.Count = tickCount;
if (IntervalTrigger != null) { IntervalTrigger(this, intervalTriggerArgs); }
tickCount = tickCount == Int32.MaxValue ? 0 : tickCount + 1;
}

接下来又是一个难题,我们该如何编写这两类AI?困惑时不妨借鉴一下前辈们的经验,尝试回忆一下小时候玩过的那些RPG游戏:有的游戏喜欢以复杂程度来定义怪物的AI,打个比方:

  简单AI:角色搜索目标 -> 攻击 -> 频死时回血、逃跑

  中级AI:寻找最近目标 -> 攻击 -> 半血逃逸,呼叫同伴协助

  高级AI:检索周围状况 -> 寻找最弱目标 -> 呼叫同伴,集体攻击

  以上仅仅是一个粗略的AI流程图,然而却生动而有趣的比拟了目前网游大同小异的AI设计规律。或许其中的一些环节仍可被细分成若干枝节,同时有的还会根据游戏本身功能不断的进行拓展;万变不离其宗,性能与趣味性兼具的AI设计总是倍受玩家们迷恋。

  又如《博德之门》、《冰风谷》等龙与地下城类型经典游戏,其中的角色在创建时会根据善恶与种族的选择决定性格,而性格往往就用于确定角色在游戏中的策略AI与行为AI。

  到此,角色AI逻辑编写的切入点已清晰显露。综合以上所有,相信您的思维也一样会变得豁然开朗。接下来在本系列教程Demo的基础上进行代码编写便是非常轻松的事:

/// <summary>
/// 策略AI
/// </summary>
public enum TacticAIs {
/// <summary>
/// 目标主角
/// </summary>
GoalLeader = 0,
/// <summary>
/// 自由战斗
/// </summary>
FreeFighting = 1,
/// <summary>
/// 小组作战
/// </summary>
TeamAttack = 2,
/// <summary>
/// 门派对阵
/// </summary>
MartialBattle = 3
} /// <summary>
/// 行为AI
/// </summary>
public enum ActionAIs {
/// <summary>
/// 简单
/// </summary>
Simple = 0,
/// <summary>
/// 机敏
/// </summary>
Alertness = 1,
/// <summary>
/// 固执
/// </summary>
Persistent = 2,
/// <summary>
/// 怯懦
/// </summary>
Cowardice = 3,
} /// <summary>
/// 角色策略判定
/// </summary>
void role_TacticDecide(object sender, IntervalTriggerArgs e) {
//每间隔3秒执行一次策略检索
if (e.Count % 3 == 0) {
RoleBase role = sender as RoleBase;
//异步判断角色是否处于屏幕显示区域中,是的话且无目标则进行索敌判断:在视线范围内则追击,否则警戒
this.Dispatcher.BeginInvoke(() => {
space.SetSpriteVisible(role, role.InSight(leader));
if (role.Target == null && role.IsVisible) {
bool targetFound = false;
switch (role.TacticAI) {
case TacticAIs.GoalLeader:
if (role.Target == null && role.IsVisible) {
if (role.IsHostileTo(leader) && leader.InCircle(role.Position, role.SightRange)) {
role.Target = leader;
targetFound = true;
}
}
break;
case TacticAIs.FreeFighting:
int count = 0;
int roleNum = space.AllRoles().Count;
while (count < roleNum) {
RoleBase target = space.AllRoles()[ObjectBase.RandomSeed.Next(roleNum)];
if (role.IsHostileTo(target) && target.InCircle(role.Position, role.SightRange)) {
role.Target = target;
targetFound = true;
break;
}
count++;
}
break;
case TacticAIs.TeamAttack:
for (int i = space.AllRoles().Count - 1; i >= 0; i--) {
RoleBase target = space.AllRoles()[i];
if (role.IsHostileTo(target) && target.InCircle(role.Position, role.SightRange)) {
role.Target = target;
targetFound = true;
break;
}
}
break;
case TacticAIs.MartialBattle:
count = 0;
roleNum = space.AllRoles().Count;
while (count < roleNum) {
RoleBase target = space.AllRoles()[ObjectBase.RandomSeed.Next(roleNum)];
if (role.IsHostileTo(target) && role.Profession != target.Profession && target.InCircle(role.Position, role.SightRange)) {
role.Target = target;
targetFound = true;
break;
}
count++;
}
break;
}
if (!targetFound) { role.Guard(role.Position); }
}
});
}
} /// <summary>
/// 角色行为执行
/// </summary>
void role_ActionDecide(object sender, EventArgs e) {
RoleBase attacker = sender as RoleBase;
RoleBase target = attacker.Target;
if (attacker.IsHostileTo(target) && attacker.IsVisible) {
//怯懦行为简单表现为不主动追击,发现被攻击时立刻逃跑
if (target.ActionAI == ActionAIs.Cowardice) {
target.Target = null;
target.MoveTo(new Point(ObjectBase.RandomSeed.Next((int)(target.Position.X - target.SightRange), (int)(target.Position.X + target.SightRange)), ObjectBase.RandomSeed.Next((int)(target.Position.Y - target.SightRange), (int)(target.Position.Y + target.SightRange))));
}
if (target.InCircle(attacker.Position, attacker.AttackRange)) {
attacker.TurnTowardsTo(target.Position);
attacker.Attack();
//机智行为简单表现为受到攻击时会反击,受到两个以上敌人同时攻击时将会试图逃离现场
if (target != leader) {
if (target.ActionAI == ActionAIs.Alertness) {
if (target.Target != null) {
target.Target = null;
target.MoveTo(new Point(ObjectBase.RandomSeed.Next((int)(target.Position.X - attacker.SightRange), (int)(target.Position.X + attacker.SightRange)), ObjectBase.RandomSeed.Next((int)(target.Position.Y - attacker.SightRange), (int)(target.Position.Y + attacker.SightRange))));
} else {
target.Target = attacker;
}
}
}
} else if (target.InCircle(attacker.Position, attacker.SightRange) || attacker.ActionAI == ActionAIs.Persistent) {
//固执行为简单表现为抓住一个目标一直追击,直至自己或目标死亡为止
attacker.MoveTo(new Point(ObjectBase.RandomSeed.Next((int)(target.Position.X - attacker.AttackRange), (int)(target.Position.X + attacker.AttackRange)), ObjectBase.RandomSeed.Next((int)(target.Position.Y - attacker.AttackRange), (int)(target.Position.Y + attacker.AttackRange))));
} else {
attacker.Target = null;
attacker.Stop();
}
}
}

随手写下数十行代码,非常非常简单,外行人都能看得出不外乎if/else/switch/case/random,然而却实现了4种策略 + 4种行为,外带上角色职业与参数的不同将产生至少20多种不同的AI组合搭配,更重要的是非常易于新功能的拓展与维护。没错,Silverlight开发游戏就是这样简单~,让我们一同来领略下Silverlight为网游开发所带来的革命性奇迹吧(在线演示地址http://silverfuture.cn/)!

  当然,这些所有的效果也仅仅算作是AI的入门;类似仿真技术的角色AI最早出现于欧美网游大作中,比如基于仇恨系统的角色索敌AI,经典代表作便是大家游耳熟能详的《无尽的任务》(EQ)。这类游戏的AI系统设计通常以“仇恨值”最高的敌对阵营角色确定为攻击对象,所有角色的仇恨值都储存于一个动态表/库中随时检索。仇恨值的程度又与很多因素相关联,比如距离远近、等级差距、所剩生命值多少、是否在施展“嘲讽”等挑衅技能、当前行为状态(冥想/步行/跑动)、是否正在屠杀同族、时间流逝等等,且每个要素的权重值也不同,国内网游通常都喜欢将距离远近定为最高权重。当AI启动时会对同一区域内所有对象进行以上数据的统和,仇恨值得分最高的角色将被视为最有价值攻击对象,且仇恨值往往会被持续累积,直到角色死亡或施展特殊技能比如假死后方回到原点。当然,仇恨值的累积也并非都是简单的1+1,对同一怪物连续施展3次火球术将会彻底将其惹毛,没错,你太小看它了,它真的很生气;是的,游戏技术一直以来都在模仿真实的人类世界,试想下如果你被某人不小心踩上一脚相比被其连续踩上三脚我相信这样的愤怒不仅仅是简单的3倍加成吧?当你把生活中的类似环境置位到游戏中的角色AI时,你会觉得其实游戏设计原来是件多么有趣的事情。

  追求高仿真虚拟现实的大型游戏甚至愿意花费超过20%的CPU以处理逼真的角色AI逻辑,其中往往包含着复杂的人工神经网络和遗传算法;当然对于更强调操控与实战效果的网游来说,AI不需要太复杂,但是性效比该如何取舍也必定是一个值得仔细斟酌考量的环节。Silverlight 5即将发布了~,带着超强大的功能与无限喜悦,我由衷期待!!

  本节源码请到目录中下载。

  在线演示地址:http://silverfuture.cn

Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统的更多相关文章

  1. Silverlight 2.5D RPG游戏技巧与特效处理:(五)HLSL渲染动画

    原文:Silverlight 2.5D RPG游戏技巧与特效处理:(五)HLSL渲染动画 或许大家依旧对上一节中的“黑夜”及“梦回过去”记忆犹新,追问下去HLSL到底是何方神圣能实现如此炫酷之效果?层 ...

  2. 如何制作一款HTML5 RPG游戏引擎——第五篇,人物&人物特效

    上一次,我们实现了对话类,今天就来做一个游戏中必不可少的——人物类. 当然,你完全是可以自己写一个人物类,但是为了方便起见,还是决定把人物类封装到这个引擎里. 为了使这个类更有意义,我还给人物类加了几 ...

  3. HTML5 RPG游戏引擎 地图实现篇

    一,话说全国年夜事   前没有暂看到lufy的专客上,有一名伴侣念要一个RPG游戏引擎,出于兴趣筹办入手做一做.因为我研讨lufylegend有冶时间了,对它有必然的依赖性,因而便筹办将那个引擎基于 ...

  4. 如何制作一款HTML5 RPG游戏引擎——第一篇,地图类的实现

    一,话说天下大事 前不久看到lufy的博客上,有一位朋友想要一个RPG游戏引擎,出于兴趣准备动手做一做.由于我研究lufylegend有一段时间了,对它有一定的依赖性,因此就准备将这个引擎基于lufy ...

  5. 【开源】使用Angular9和TypeScript开发RPG游戏

    RPG系统构造 通过对于斗罗大陆小说的游戏化过程,熟悉Angular的结构以及使用TypeScript的面向对象开发方法. 项目地址 人物 和其他RPG游戏类似,游戏里面的人物角色大致有这样的一些属性 ...

  6. unity3d 游戏插件 溶解特效插件 - Dissolve Shader

    unity3d 游戏插件 溶解特效插件 - Dissolve Shader   链接: https://pan.baidu.com/s/1hr7w39U 密码: 3ed2

  7. 【android原生态RPG游戏框架源码】

    转载请注明原创地址:http://www.cnblogs.com/zisou/p/android-RPG.html 这份源码是在今年6月份写的,当时公司有一个技术部们的学习讨论的讲座,然后我自己就写了 ...

  8. RPG JS:免费开源的跨平台RPG游戏引擎

    RPG JS是一个2D RPG游戏制作引擎,目前版本基于Ease|JS游戏引擎,基于Canvas Engine的新版本即将发布. RPG JS是免费且开源的. RPG JS有着完善的文档支持. RPG ...

  9. RPG游戏学习——1.任务脚本系统

    [前言] 近期准备做个rpg小游戏,所以開始研究rpg的一些系统.rpg最核心的应该是任务脚本系统(其它脚本系统类似),在參考了非常多网上的资料后,简要总结例如以下. [脚本的触发运行] 一个脚本须要 ...

随机推荐

  1. Natas22 Writeup(header重定向、burp截取抓包)

    Natas22: 打开页面是一个空白页面,查看源码,看起来好像是需要我们在url中添加“revelio”参数即可,然而实验了之后发现浏览器跳转回了原来的页面. 再次仔细审计源码,会看到页面开头有一个重 ...

  2. Hive 时间操作

    Hive 时间转换 UNIX时间戳概念:因为UNIX时间戳只是一个秒数,一个UNIX时间戳在不同时区看来,时间是不同的.如UNIX时间戳0,在0时区看来是1970-01-01 00:00:00,在东八 ...

  3. 理解Raft协议

    目录 1.Paxos算法存在的问题 2.Raft算法     2.1 复制状态机     2.2. Raft算法     2.2.1 安全性问题     2.2.2 Leader选举     2.2. ...

  4. 第八周Java实验作业

    实验六 接口的定义与使用 实验时间 2018-10-18 1.实验目的与要求 (1) 掌握接口定义方法: 声明:  public interface 接口名 {...} 接口体中包含常量定义和方法定义 ...

  5. Rasa Stack:创建支持上下文的人工智能助理和聊天机器人教程

    相关概念 Rasa Stack 是一组开放源码机器学习工具,供开发人员创建支持上下文的人工智能助理和聊天机器人: • Core = 聊天机器人框架包含基于机器学习的对话管理 • NLU = 用于自然语 ...

  6. 2020.4.4号全国疫情哀悼日网页变灰色前端是如何实现的?-pink老师

    今天是4.4疫情哀悼日,纪念疫情期间牺牲的烈士和逝世同胞,因此大部分网站颜色都变灰色了,我们前端是如何实现的呢? 核心原理,使用css3的滤镜效果即可,filter grayscale 将整个界面变为 ...

  7. 模块 time datetime 时间获取和处理

    模块_time 和时间有关系的我们就要用到时间模块.在使用模块之前,应该首先导入这个模块. 1 延时 time.sleep(secs) (线程)推迟指定的时间运行.单位为秒. 2 获取当前时间戳tim ...

  8. 最适合新手入门的SpringCloud教程 6—Ribbon负载均衡「F版本」

    SpringCloud版本:Finchley.SR2 SpringBoot版本:2.0.3.RELEASE 源码地址:https://gitee.com/bingqilinpeishenme/Java ...

  9. [vijos1120]花生采摘<贪心>

    题目链接:https://vijos.org/p/1120 这怕是我打过最水的一道题了,但是这道隶属于普及组难度的题我竟然提交4次才过,这不禁让我有些后怕,所以还是含泪写下这篇博客,用来警示一下自己: ...

  10. Git之旅

    ithub安装,我选择的是windows下的版本. git配置用户信息 安装完成后,还需要最后一步设置,在命令行输入: $git config --global user.name "You ...