新浪微博
腾讯微博
会员列表
统计排行
基本信息
到访IP统计
管理团队
管理统计
在线会员
会员排行
版块排行
帖子排行
标签排行
帮助
下拉
用户名
电子邮箱
用户名
密 码
记住登录
登录
找回密码
注册
快捷通道
关闭
您还没有登录,快捷通道只有在登录后才能使用。
立即登录
还没有帐号? 赶紧
注册一个
首页
论坛
Qt下载
作品展
群组
个人中心
捐赠、管理与开发
邮件订阅
帖子
文章
日志
用户
版块
群组
帖子
搜索
QTCN开发网
>
新闻资讯
>
[C++Qt]人工智能算法在游戏中演示
发帖
回复
返回列表
新帖
7773
阅读
4
回复
[转载]
[C++Qt]人工智能算法在游戏中演示
[复制链接]
上一主题
下一主题
离线
jeffreylee
UID:139371
注册时间
2012-12-14
最后登录
2023-07-06
在线时间
211小时
发帖
158
搜Ta的帖子
精华
1
金钱
3318
威望
224
贡献值
0
好评度
184
访问TA的空间
加好友
用道具
论坛版主
关闭
个人中心可以申请新版勋章哦
立即申请
知道了
加关注
发消息
只看楼主
倒序阅读
楼主
发表于: 2013-04-16
转自:
http://bbs.csdn.net/topics/390425195
人工智能是一个非常具有潜力的分支,顾名思义,它可以通过计算机指令模拟人的行为,在游戏中的人工智能就非常地多样了。对于FPS、TAB、RPG、STG、ADV等游戏,有着不同的人工智能,但都基于几种理论:有限状态机、遗传算法、神经网络等。下面我就采用游戏中最基础也是最常用的人工智能算法来进行演示。
我的
开发
环境是:
Windows
: WindowsXP(SP3) + MinGW4.4/MinGW4.7 + Qt4.8.3/Qt5.0.1 + QtCreator2.6/QtCreator2.7
Linux
: Ubuntu12.10 + gcc4.7 + Qt4.8.1/Qt4.8.4/Qt5.0.1 + QtCreator2.6/QtCreator2.7
演示程序下载地址:
这里
源代码下载地址:
这里
注意:由于Qt5.0.1中一个未知的Bug,
编译
运行后角色无法移动,所以请使用Qt4.6+来进行编译。
程序截图如下所示:
通过子
窗口
的控制选项,我们可以设置我们的初音ミク、镜音リン和镜音レン的控制方法,可以选择人工控制也可以选择AI控制。如果选择人工控制,那么可以通过按下“上下左右”键控制角色的移动,如果选择AI控制,那么角色会沿着场景作顺时针移动。
整个项目的代码量较多,我将选择有关人工智能的内容进行讲解,也希望大家沿着我的思路思考下去。
首先如何让角色沿着窗口作顺时针旋转?一个简单的想法就是:当角色将要达到窗口顶端那么角色将往右移动;角色将要到达窗口右端那么角色将往下移动,以此类推。按照这样的思路,我写了这样的AI代码:
复制代码
if ( m_pCharacter->pos( ).y( ) > 20.0 )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
这也是我AI的第一个版本,但是正如QtMikuSnake7_ver_1应用程序截图中所示,它并不能达到应有的效果,角色一直在右上角打转。看来第一个版本有
问题
。问题在哪儿呢?这是由于我们将向上的判定优先于向下的判定,导致了角色在右上角处转至下后又转回了右上角。了解了这个问题之后第一个想法就是为向上判定添加约束条件,使其能够在右上角处正确地转至向下判定而不会折返。下面是AI的第二个版本:
复制代码
if ( m_pCharacter->pos( ).y( ) > 20.0 &&
m_pCharacter->m_Direction != Character::_Down_ )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 &&
m_pCharacter->m_Direction != Character::_Up_ )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 &&
m_pCharacter->m_Direction != Character::_Right_ )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
为了保险,按照这种思路,将每一个方向判定都添加了约束条件,即判定当前的方向是何方向。按理说角色要往上走,那么当前的方向就肯定不是往下走,角色要往左走,当前的方向就肯定不是往右走。好了,运行一下,结果发现如QtMikuSnake7_ver_2所示的效果一样,角色在右下角处至左移了一格就往上走了。看来又是一次失误。
分析原因,发现向上判定的约束虽然解决了当前方向向下时仍进行向上判定的问题,可是未解决当前方向向左时仍然出现向上判定优先于向左判定的情况。看来还是需要再对向上判定进行进一步约束,按照“上右下左”的移动顺序,我们了解向上判定需要两个约束,向右判定需要一个约束,而向下判定不需要额外的约束,向左判定就更不需要了。下面AI的第三个版本:
复制代码
if ( m_pCharacter->pos( ).y( ) > 20.0 &&
m_pCharacter->m_Direction != Character::_Down_ &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) == 0.0 )// 为防止角色在左下角卡死设立的判断
{
m_pCharacter->SetDirection( Character::_Up_ );
}
注意到最下面一个判断,因为角色走在左下角的时候会因为都满足不了这些判定条件而陷入“卡死”状态,所以我们要进行“解锁”操作——将当前的方向设为向上,这样又可以满足向上的判定了。下面是程序QtMikuSnake7_ver_3的截图。
似乎这个问题圆满地解决了。但是我觉得这个代码还是写得太被动了,因为这些代码都是出了问题而一个一个地打补丁打上去的,非常被动。我们得换一个角度考虑。试想,如果一条语句能够“排队”,当让它执行的时候它排在最前面,执行完毕后它轮到最末尾,给下一条语句机会,要是这样的话,我们可以让上、右、下、左四条语句依次排队,一条语句一条语句地轮着运行。
这完全有可能实现!回想起来了吗?这不就是
数据
结构中经典的队列结构!可是,怎样才能让语句智能地移动到后面执行呢?这里需要使用一个类对这条语句进行封装。因为语句的格式相当有条理:if 判定条件 then 执行语句。我是这样封装的:
复制代码
class Clause: public QObject
{
public:
Clause( Character* pParent = 0 ):
QObject( pParent ), m_pCharacter( pParent ){ }
virtual bool JudgeSentence( void ) = 0;
virtual void Statement( void ) = 0;
protected:
Character* m_pCharacter;
};
随后我设定一个队列,在Qt中有个现成的QQueue。
复制代码
QQueue<Clause*> m_Clauses;
接着进行语句类的定义,让其继承Clause类:
复制代码
class DirUpClause: public Clause
{
public:
DirUpClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).y( ) > 20.0;
}
void Statement( void )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
}
};
class DirDownClause: public Clause
{
public:
DirDownClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).y( ) < 340.0;
}
void Statement( void )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
}
};
class DirLeftClause: public Clause
{
public:
DirLeftClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).x( ) > 0.0;
}
void Statement( void )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
}
};
class DirRightClause: public Clause
{
public:
DirRightClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).x( ) < 608.0;
}
void Statement( void )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
}
};
最后我们在更新对象状态代码中进行一个简单地调用就可以了。
复制代码
foreach ( Clause* pClause, m_Clauses )
{
if ( pClause->JudgeSentence( ) )
{
pClause->Statement( );
emit TriggerTransition( );
break;
}
else
{
m_Clauses.dequeue( );
m_Clauses.enqueue( pClause );
break;
}
}
上面的代码中,当条件满足的时候进行语句的执行,当条件不满足的时候将该语句从队列的头部移至队列的尾部。这样写虽然代码会比较多,但是思路清晰,对更复杂的状态维护起着重要的作用。执行起来效率也比较高,因为少了一些不必要的判定。下面是程序QtMikuSnake7_ver_4的截图。
上面的算法仅仅是一个很简单的演示,对于角色的运动还有诸如追击、自主规避、寻路等AI算法,对于复杂的游戏,状态的维护非常复杂且容易
出错
,而这个
错误
又不像程序宕机那样容易觉察,这时需要一个特定的职位——脚本设计师来解决此类问题。脚本设计师面对着各类的数据,通过自身熟练的脚本语言来对程序的各类参数进行微调,一款成功的游戏总有脚本设计师付出的辛勤汗水。所以说脚本设计师都是艺术家。
后记:程序QtMikuSnake7对上一个版本的帧框大小进行了修正,刷新不会出现残影的现象,有关AI测试代码也将在下一个版本中被去除。
共
条评分
更多资讯,请关注我的微博
回复
举报
分享到
淘江湖
新浪
QQ微博
QQ空间
开心
人人
豆瓣
网易微博
百度
鲜果
白社会
飞信
离线
toby520
UID:121778
注册时间
2011-09-05
最后登录
2025-04-28
在线时间
2936小时
发帖
1265
搜Ta的帖子
精华
0
金钱
28786
威望
1375
贡献值
12
好评度
1365
访问TA的空间
加好友
用道具
论坛版主
加关注
发消息
只看该作者
1楼
发表于: 2013-04-16
Lee发的帖子根本没法阅读呢,能否修改修改
共
条评分
QtQML多多指教开发社区 http://qtclub.heilqt.com
将QtCoding进行到底
关注移动互联网,关注金融
开发跨平台客户端,服务于金融行业
专业定制界面
群号:312125701 373955953(qml控件定做)
回复
举报
离线
makai
UID:141952
注册时间
2013-03-22
最后登录
2013-07-27
在线时间
3小时
发帖
3
搜Ta的帖子
精华
0
金钱
30
威望
13
贡献值
0
好评度
3
访问TA的空间
加好友
用道具
新手上路
加关注
发消息
只看该作者
2楼
发表于: 2013-04-16
回 1楼(toby520) 的帖子
估计是技术性太强了吧!嘿嘿
共
条评分
编程改变生活
回复
举报
离线
XChinux
UID:2
注册时间
2004-11-08
最后登录
2025-04-28
在线时间
7016小时
发帖
11242
搜Ta的帖子
精华
61
金钱
147522
威望
9967
贡献值
702
好评度
8189
访问TA的空间
加好友
用道具
总版主
加关注
发消息
只看该作者
3楼
发表于: 2013-04-16
这回能看了吧。
共
条评分
二笔 openSUSE Vim N9 BB10
XChinux@163.com
网易博客
腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
回复
举报
离线
nmiirq
UID:44347
注册时间
2008-05-15
最后登录
2022-05-21
在线时间
307小时
发帖
460
搜Ta的帖子
精华
0
金钱
4630
威望
462
贡献值
0
好评度
461
访问TA的空间
加好友
用道具
骑士
加关注
发消息
只看该作者
4楼
发表于: 2013-04-17
图裂了。。。
共
条评分
回复
举报
发帖
回复
返回列表
http://www.qtcn.org/bbs
访问内容超出本站范围,不能确定是否安全
继续访问
取消访问
快速回复
限100 字节
您目前还是游客,请
登录
或
注册
进入高级模式
文字颜色
发 布
回复后跳转到最后一页
上一个
下一个
关闭
补充发布信息
验证码:
发 布
隐藏
快速跳转
站务及资讯
网站公告
新闻资讯
Qt官方发布
网站管理
Qt应用及资源
Qt 作品展
Qt代码秀
Qt应用版
Qt技术讨论区
Qt安装与发布
Qt中文处理
Qt基础编程
Qt QML开发
Qt嵌入式开发
Qt移动平台开发
其它技术开发讨论区
Qt图书专区
《C++ GUI Qt 4编程》(第2版)专栏
《Qt高级编程》专栏
《零基础学Qt4编程》专栏
《Qt设计模式》(第2版) 图书专栏
Python Qt GUI快速编程
Qt项目开发区
天池项目
Qt开放平台开发库
机械CAD
扇贝词典
非活跃项目区
社区中心
帖子回收站(Trash)
招聘、求职、供求、广告等
休闲娱乐
关闭
关闭
选中
1
篇
全选