Unity3D研究院之游戏开发中的人工智能AI

  • Post author:
  • Post category:其他



很久没有写Unity3D相关的东西了,是因为这段时间我深陷一款IOS的软件开发中。不过以后我还是会回归Unity3D游戏开发的。什么语言都在用,生活与工作都挺给力的嚯嚯。今天还是打开了久违的Unity3D编辑器,那我就写点东西吧,今天我想说的是游戏中的人工智能。人工智能这个东西在游戏中是非常重要的,人工智能说简单了就是根据随机的数字让敌人执行一些动作或逻辑,说难了TA需要一个非常复杂的算法,本文我主要说说Unity3D中人工智能的脚本如何来编写。


首先你应该搞清楚的一点AI脚本属于一个工具类脚本,工具类脚本的含义就是他应当是由策划人员来绑定游戏对象使用的。也就是说AI脚本程序员应当写的非常的灵活,策划人员可以通过修改脚本对外的变量数值接口就能控制其中的敌人AI。接着创建一个c#脚本AI.CS ,如下图所示,目前脚本对外留出枚举接口变量,策划人员在使用这条脚本时选择对应敌人类型即可。(注:这里仅仅是示例,细致的话还可以将很多敌人详细的信息写入,如:攻击速度、技能类型、移动速度、命中率、攻击百分比、等等,但是一定要让你的脚本写的比较灵活,策划人员在外面选择即可完成)

因为目前是一个示例,所以我在这里只简单的区分的敌人类型。




下面时这段简单AI的脚本


AI.CS


001

using


UnityEngine;

002

using


System.Collections;

003

004

//这里是枚举选择敌人类型

005

public


enum


EnemyType

006

{


007



Enemy0,

008



Enemy1

009

}

010

011

public


class


AI : MonoBehaviour {


012

013



//敌人类型枚举 有策划人员选择

014



public


EnemyType enemyType = EnemyType.Enemy0;

015

016



//主角游戏对象

017



public


GameObject player;

018

019



//敌人状态 普通状态 旋转状态 奔跑状态 追击主角状态 攻击主角状态

020



private


const


int


EMEMY_NORMAL=0;

021



private


const


int


EMEMY_ROTATION=1;

022



private


const


int


EMEMY_RUN = 2;

023



private


const


int


EMEMY_CHASE = 3;

024



private


const


int


EMEMY_ATTACK = 4;

025

026



//记录当前敌人状态 根据不同类型 敌人播放不同动画

027



private


int


state;

028



//旋转状态,敌人自身旋转

029



private


int


rotation_state;

030



//记录敌人上一次思考时间

031



private


float


aiThankLastTime;

032

033



void


Start ()

034



{


035



//初始话标志敌人状态 以及动画为循环播放

036



state = EMEMY_NORMAL;

037



this


.animation.wrapMode = WrapMode.Loop;

038



}

039

040



void


Update ()

041



{


042



//根据策划选择的敌人类型 这里面会进行不同的敌人AI

043



switch


(enemyType)

044



{


045



case


EnemyType.Enemy0:

046



updateEnemyType0();

047



break


;

048



case


EnemyType.Enemy1:

049



updateEnemyType1();

050



break


;

051



}

052



}

053

054



//更新第一种敌人的AI

055



void


updateEnemyType0()

056



{


057



//这个AI比较简单, 当主角与他的距离小于10米时,他将始终朝向这主角

058



if


(Vector3.Distance(player.transform.position,


this


.transform.position) <= 10)

059



{


060



this


.transform.LookAt(player.transform);

061



}

062



}

063

064



//更新第二种敌人的AI

065



void


updateEnemyType1()

066



{


067

068



//判断敌人是否开始思考

069



if


(isAIthank())

070



{


071



//敌人开始思考

072



AIthankEnemyState(3);

073



}


else

074



{


075



//更新敌人状态

076



UpdateEmenyState();

077



}

078



}

079

080



int


getRandom(


int


count)

081



{


082

083



return


new


System.Random().Next(count);

084

085



}

086

087



bool


isAIthank()

088



{


089



//这里表示敌人每3秒进行一次思考

090



if


(Time.time - aiThankLastTime >=3.0f)

091



{


092



aiThankLastTime = Time.time;

093



return


true


;

094

095



}

096



return


false


;

097



}

098

099



//敌人在这里进行思考

100



void


AIthankEnemyState(


int


count)

101



{


102



//开始随机数字。

103



int


d = getRandom(count);

104

105



switch


(d)

106



{


107



case


0:

108



//设置敌人为站立状态

109



setEmemyState(EMEMY_NORMAL);

110



break


;

111



case


1:

112



//设置敌人为旋转状态

113



setEmemyState(EMEMY_ROTATION);

114



break


;

115



case


2:

116



//设置敌人为奔跑状态

117



setEmemyState(EMEMY_RUN);

118



break


;

119



}

120

121



}

122

123



void


setEmemyState(


int


newState)

124



{


125



if


(state == newState)

126



return


;

127



state = newState;

128

129



string


animName =


"Idle"


;

130



switch


(state)

131



{


132



case


EMEMY_NORMAL:

133



animName  =


"Idle"


;

134



break


;

135



case


EMEMY_RUN:

136



animName  =


"Run"


;

137



break


;

138



case


EMEMY_ROTATION:

139



animName  =


"Run"


;

140



//当敌人为旋转时, 开始随机旋转的角度系数

141



rotation_state = getRandom(4);

142



break


;

143



case


EMEMY_CHASE:

144



animName  =


"Run"


;

145



//当敌人进入追击状态时,将面朝主角方向奔跑

146



this


.transform.LookAt(player.transform);

147



break


;

148



case


EMEMY_ATTACK:

149



animName  =


"Attack"


;

150



//当敌人进入攻击状态时,继续朝向主角开始攻击砍人动画

151



this


.transform.LookAt(player.transform);

152



break


;

153



}

154

155



//避免重复播放动画,这里进行判断

156



if


(!


this


.animation.IsPlaying(animName))

157



{


158



//播放动画

159



this


.animation.Play(animName);

160



}

161

162



}

163

164



//在这里更新敌人状态

165



void


UpdateEmenyState()

166



{


167



//判断敌人与主角之间的距离

168



float


distance = Vector3.Distance(player.transform.position,


this


.transform.position);

169



//当敌人与主角的距离小于10 敌人将开始面朝主角追击

170



if


(distance <= 10)

171



{


172



//当敌人与主角的距离小与3 敌人将开始面朝主角攻击

173



if


(distance <= 3)

174



{


175



setEmemyState(EMEMY_ATTACK);

176



}


else

177



{


178



//否则敌人将开始面朝主角追击

179



setEmemyState(EMEMY_CHASE);

180



}

181

182



}


else

183



{


184



//敌人攻击主角时 主角迅速奔跑 当它们之间的距离再次大于10的时候 敌人将再次进入正常状态 开始思考

185



if


(state == EMEMY_CHASE || state == EMEMY_ATTACK)

186



{


187



setEmemyState(EMEMY_NORMAL);

188



}

189

190



}

191

192



switch


(state)

193



{


194



case


EMEMY_ROTATION:

195



//旋转状态时 敌人开始旋转, 旋转时间为1秒 这样更加具有惯性

196



transform.rotation = Quaternion.Lerp (transform.rotation, Quaternion.Euler(0,rotation_state * 90,0),  Time.deltaTime * 1);

197



break


;

198



case


EMEMY_RUN:

199



//奔跑状态,敌人向前奔跑

200



transform.Translate(Vector3.forward *0.02f);

201



break


;

202



case


EMEMY_CHASE:

203



//追击状态 敌人向前开始追击

204



transform.Translate(Vector3.forward *0.02f);

205



break


;

206



case


EMEMY_ATTACK:

207

208



break


;

209



}

210

211



}

212

}


如下图所示,我们在游戏世界中添加两个敌人,此时给敌人们都绑定上AI的脚本,编辑器中设置不同的敌人类型,敌人执行各自的生命周期,当你控制主角与接近敌人时,敌人开始追击你并且向你展开攻击。




demo仅仅是一个示例,主要希望大家明白一个道理。在编写Unity游戏脚本时,一定要想想这条脚本和对象的生命周期,切记每一条脚本只管和自己有关的东西,和自己无关的东西一概不要管。不然你会发现你的脚本会越写越乱,下一篇文章我会写点我平时在Unity3D开发中时如何搭建架构的,希望大家多多讨论,互相学习。


下载地址:

http://vdisk.weibo.com/s/hmJUs