首先你应该搞清楚的一点AI脚本属于一个工具类脚本,工具类脚本的含义就是他应当是由策划人员来绑定游戏对象使用的。也就是说AI脚本程序员应当写的非常的灵活,策划人员可以通过修改脚本对外的变量数值接口就能控制其中的敌人AI。接着创建一个c#脚本AI.CS ,如下图所示,目前脚本对外留出枚举接口变量,策划人员在使用这条脚本时选择对应敌人类型即可。(注:这里仅仅是示例,细致的话还可以将很多敌人详细的信息写入,如:攻击速度、技能类型、移动速度、命中率、攻击百分比、等等,但是一定要让你的脚本写的比较灵活,策划人员在外面选择即可完成)因为目前是一个示例,所以我在这里只简单的区分的敌人类型。
下面时这段简单AI的脚本
AI.CS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
using
UnityEngine
;
using
System
.
Collections
;
//这里是枚举选择敌人类型
public
enum
EnemyType
{
Enemy0
,
Enemy1
}
public
class
AI
:
MonoBehaviour
{
//敌人类型枚举 有策划人员选择
public
EnemyType
enemyType
=
EnemyType
.
Enemy0
;
//主角游戏对象
public
GameObject
player
;
//敌人状态 普通状态 旋转状态 奔跑状态 追击主角状态 攻击主角状态
private
const
int
EMEMY_NORMAL
=
0
;
private
const
int
EMEMY_ROTATION
=
1
;
private
const
int
EMEMY_RUN
=
2
;
private
const
int
EMEMY_CHASE
=
3
;
private
const
int
EMEMY_ATTACK
=
4
;
//记录当前敌人状态 根据不同类型 敌人播放不同动画
private
int
state
;
//旋转状态,敌人自身旋转
private
int
rotation_state
;
//记录敌人上一次思考时间
private
float
aiThankLastTime
;
void
Start
(
)
{
//初始话标志敌人状态 以及动画为循环播放
state
=
EMEMY_NORMAL
;
this
.
animation
.
wrapMode
=
WrapMode
.
Loop
;
}
void
Update
(
)
{
//根据策划选择的敌人类型 这里面会进行不同的敌人AI
switch
(
enemyType
)
{
case
EnemyType
.
Enemy0
:
updateEnemyType0
(
)
;
break
;
case
EnemyType
.
Enemy1
:
updateEnemyType1
(
)
;
break
;
}
}
//更新第一种敌人的AI
void
updateEnemyType0
(
)
{
//这个AI比较简单, 当主角与他的距离小于10米时,他将始终朝向这主角
if
(
Vector3
.
Distance
(
player
.
transform
.
position
,
this
.
transform
.
position
)
<=
10
)
{
this
.
transform
.
LookAt
(
player
.
transform
)
;
}
}
//更新第二种敌人的AI
void
updateEnemyType1
(
)
{
//判断敌人是否开始思考
if
(
isAIthank
(
)
)
{
//敌人开始思考
AIthankEnemyState
(
3
)
;
}
else
{
//更新敌人状态
UpdateEmenyState
(
)
;
}
}
int
getRandom
(
int
count
)
{
return
new
System
.
Random
(
)
.
Next
(
count
)
;
}
bool
isAIthank
(
)
{
//这里表示敌人每3秒进行一次思考
if
(
Time
.
time
-
aiThankLastTime
>=
3.0f
)
{
aiThankLastTime
=
Time
.
time
;
return
true
;
}
return
false
;
}
//敌人在这里进行思考
void
AIthankEnemyState
(
int
count
)
{
//开始随机数字。
int
d
=
getRandom
(
count
)
;
switch
(
d
)
{
case
0
:
//设置敌人为站立状态
setEmemyState
(
EMEMY_NORMAL
)
;
break
;
case
1
:
//设置敌人为旋转状态
setEmemyState
(
EMEMY_ROTATION
)
;
break
;
case
2
:
//设置敌人为奔跑状态
setEmemyState
(
EMEMY_RUN
)
;
break
;
}
}
void
setEmemyState
(
int
newState
)
{
if
(
state
==
newState
)
return
;
state
=
newState
;
string
animName
=
"Idle"
;
switch
(
state
)
{
case
EMEMY_NORMAL
:
animName
=
"Idle"
;
break
;
case
EMEMY_RUN
:
animName
=
"Run"
;
break
;
case
EMEMY_ROTATION
:
animName
=
"Run"
;
//当敌人为旋转时, 开始随机旋转的角度系数
rotation_state
=
getRandom
(
4
)
;
break
;
case
EMEMY_CHASE
:
animName
=
"Run"
;
//当敌人进入追击状态时,将面朝主角方向奔跑
this
.
transform
.
LookAt
(
player
.
transform
)
;
break
;
case
EMEMY_ATTACK
:
animName
=
"Attack"
;
//当敌人进入攻击状态时,继续朝向主角开始攻击砍人动画
this
.
transform
.
LookAt
(
player
.
transform
)
;
break
;
}
//避免重复播放动画,这里进行判断
if
(
!
this
.
animation
.
IsPlaying
(
animName
)
)
{
//播放动画
this
.
animation
.
Play
(
animName
)
;
}
}
//在这里更新敌人状态
void
UpdateEmenyState
(
)
{
//判断敌人与主角之间的距离
float
distance
=
Vector3
.
Distance
(
player
.
transform
.
position
,
this
.
transform
.
position
)
;
//当敌人与主角的距离小于10 敌人将开始面朝主角追击
if
(
distance
<=
10
)
{
//当敌人与主角的距离小与3 敌人将开始面朝主角攻击
if
(
distance
<=
3
)
{
setEmemyState
(
EMEMY_ATTACK
)
;
}
else
{
//否则敌人将开始面朝主角追击
setEmemyState
(
EMEMY_CHASE
)
;
}
}
else
{
//敌人攻击主角时 主角迅速奔跑 当它们之间的距离再次大于10的时候 敌人将再次进入正常状态 开始思考
if
(
state
==
EMEMY_CHASE
||
state
==
EMEMY_ATTACK
)
{
setEmemyState
(
EMEMY_NORMAL
)
;
}
}
switch
(
state
)
{
case
EMEMY_ROTATION
:
//旋转状态时 敌人开始旋转, 旋转时间为1秒 这样更加具有惯性
transform
.
rotation
=
Quaternion
.
Lerp
(
transform
.
rotation
,
Quaternion
.
Euler
(
0
,
rotation_state
*
90
,
0
)
,
Time
.
deltaTime
*
1
)
;
break
;
case
EMEMY_RUN
:
//奔跑状态,敌人向前奔跑
transform
.
Translate
(
Vector3
.
forward
*
0.02f
)
;
break
;
case
EMEMY_CHASE
:
//追击状态 敌人向前开始追击
transform
.
Translate
(
Vector3
.
forward
*
0.02f
)
;
break
;
case
EMEMY_ATTACK
:
break
;
}
}
}
|
如下图所示,我们在游戏世界中添加两个敌人,此时给敌人们都绑定上AI的脚本,编辑器中设置不同的敌人类型,敌人执行各自的生命周期,当你控制主角与接近敌人时,敌人开始追击你并且向你展开攻击。
demo仅仅是一个示例,主要希望大家明白一个道理。在编写Unity游戏脚本时,一定要想想这条脚本和对象的生命周期,切记每一条脚本只管和自己有关的东西,和自己无关的东西一概不要管。不然你会发现你的脚本会越写越乱,下一篇文章我会写点我平时在Unity3D开发中时如何搭建架构的,希望大家多多讨论,互相学习。
下载地址:http://vdisk.weibo.com/s/hmJUs