游戏人工智能之操纵行为Steering behaviors—避墙avoidance wall
游戏人工智能中,操纵行为是很重要的一部分基础,很多地方需要用到。基本的操纵行为有靠近Seek、远离Flee、抵达Arrive、追逐Pursuit、逃离Evade、随机徘徊Wander、路径跟随Path Following、避开障碍Collision Avoidance、绕开墙Wall Avoidance、插入Interpose、隐藏Hide以及群体操控行为—聚集cohesion、分离separation、对齐alignment等等,并由这些基本的操纵行为可以组合出很多复杂的行为。
(1)今天首先谈谈避墙操纵行为的原理与实现。请看以下这幅图:
•其中,主角有三个触角(在unity里面,可以很方便的使用射线),用来探测和墙是否相交以及和碰撞有关的信息。从图可以看出,如果主角的前向触角长度一定,射线嵌入墙壁越深,则墙壁给角色的反作用力会越大,且方向应该为墙的法向量方向。故我们可以大致认为
操控力
=
大小为触须前端穿透墙的距离,方向为墙的法线方向。
以下demo实现中,实现了角色对四堵墙的避开,具体效果如下:
(2)具体实现概述:(加了很多调试代码,便于观察)
A、此为计算操纵力的函数,可以每隔一段时间检查更新,我这里是放在update里面。
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
|
[/align][align=left]
private
Vector3 Force()[/align][align=left] {
Vector3 force =
new
Vector3(0, 0, 0);
RaycastHit hit1, hit2, hit3;
//设定三根探针,记录三个碰撞点
Vector3 tempOrin = transform.position;
tempOrin.y = 2f;
//抬高a little
Vector3 leftSome = transform.forward*4 - transform.right*2;
Vector3 rightSome = transform.forward*4 + transform.right*2;
//画出三根探针或触角
Debug.DrawLine(tempOrin, tempOrin + transform.forward*spinLength, Color.red);
Debug.DrawLine(tempOrin, tempOrin + leftSome, Color.green);
Debug.DrawLine(tempOrin, tempOrin + rightSome, Color.green);
//三根探针进行射线检测
bool
isHit1 = Physics.Raycast(tempOrin, leftSome,
out
hit1,spinLength);
bool
isHit2 = Physics.Raycast(tempOrin, transform.forward,
out
hit2, spinLength);
bool
isHit3 = Physics.Raycast(tempOrin, rightSome,
out
hit3, spinLength);
if
(isHit2 || isHit3 || isHit1)
//计算嵌入墙的深度,三者的和即为返回操控力的大小
{
float
mg1 =0,mg2 = 0,mg3 = 0;
if
(isHit2)
{
mg2 = spinLength - Vector3.Magnitude(hit2.point - tempOrin);
}
if
(isHit3)
{
mg3 = spinLength - Vector3.Magnitude(hit3.point - tempOrin);
}
if
(isHit1)
{
mg1 = spinLength - Vector3.Magnitude(hit1.point - tempOrin);
}
float
mg = mg3 + mg1 + mg2;
if
((hit2.collider !=
null
&& hit2.collider.gameObject.name ==
"wall0"
)||
(hit1.collider !=
null
&&hit1.collider.gameObject.name ==
"wall0"
)||
(hit3.collider !=
null
&&hit3.collider.gameObject.name ==
"wall0"
)
)
{
Vector3 normal =
new
Vector3(0, 0, -1);
force += normal*mg;
Debug.DrawLine(hit2.point, hit2.point + mg*force, Color.green);
Debug.DrawLine(hit1.point, hit1.point + mg * force, Color.green);
Debug.DrawLine(hit3.point, hit3.point + mg * force, Color.green);
}
if
((hit2.collider !=
null
&& hit2.collider.gameObject.name ==
"wall1"
)||
(hit1.collider !=
null
&& hit1.collider.gameObject.name ==
"wall1"
) ||
(hit3.collider !=
null
&& hit3.collider.gameObject.name ==
"wall1"
)
)
{
Vector3 normal =
new
Vector3(0, 0, 1);
force += normal*mg;
Debug.DrawLine(hit2.point, hit2.point + mg * force, Color.green);
Debug.DrawLine(hit1.point, hit1.point + mg * force, Color.green);
Debug.DrawLine(hit3.point, hit3.point + mg * force, Color.green);
}
if
((hit2.collider !=
null
&& hit2.collider.gameObject.name ==
"wall2"
) ||
(hit1.collider !=
null
&& hit1.collider.gameObject.name ==
"wall2"
) ||
(hit3.collider !=
null
&& hit3.collider.gameObject.name ==
"wall2"
)
)
{
Vector3 normal =
new
Vector3(1, 0, 0);
force += normal*mg;
Debug.DrawLine(hit2.point, hit2.point + mg * force, Color.green);
Debug.DrawLine(hit1.point, hit1.point + mg * force, Color.green);
Debug.DrawLine(hit3.point, hit3.point + mg * force, Color.green);
}
if
((hit2.collider !=
null
&& hit2.collider.gameObject.name ==
"wall3"
) ||
(hit1.collider !=
null
&& hit1.collider.gameObject.name ==
"wall3"
) ||
(hit3.collider !=
null
&& hit3.collider.gameObject.name ==
"wall3"
)
)
{
Vector3 normal =
new
Vector3(-1, 0, 0);
force += normal*mg;
Debug.DrawLine(hit2.point, hit2.point + mg * force, Color.green);
Debug.DrawLine(hit1.point, hit1.point + mg * force, Color.green);
Debug.DrawLine(hit3.point, hit3.point + mg * force, Color.green);
}
}
//end of isHit123
return
force;
}
//end of Force
|
在update中,只仅仅根据其速度与加速度更新其位置与转向
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
private
void
Update()
{
Vector3 force = Force();
force.y = 0f;
//将Y轴方向上的力清零
force.y = 0f;
Vector3 acc = force/mass;
velocity += acc*Time.deltaTime;
transform.position += velocity*Time.deltaTime;
if
(velocity.sqrMagnitude > 0.01)
//完成平滑转向,转向速度方向
{
Vector3 newForward = Vector3.Slerp(transform.forward, velocity, Time.deltaTime);
newForward.y = 0;
transform.forward = newForward;
}
}
|