几乎任何游戏都需要与用户互动的人工智能(AI),通常以对玩家不利的力量形式出现。 在某些情况下,AI应该帮助玩家,而在另一些情况下,AI应当与之战斗,但是计算机控制的所有角色都有一些相似之处。 根据项目的要求,AI可以使用简单或复杂的行为。 这些要求可能是与其他玩家进行外交交流,或者是在平台上来回徘徊。 尽管如此,有必要确保AI有效地完成其工作。
在这个项目中,我将演示一个非常简单的人工智能。 假设我们创建了一个游戏,其中玩家必须潜入敌方总部附近。 当监控摄像头注意到玩家时,会在附近制造敌人并在短时间内追赶该玩家。 这就是我们在项目中最简单的实现。 项目完成后,您将收到一个受控的玩家对象,一个用作敌方相机的圆圈,以及一个敌人的对象,当相机的对象告知其存在时,该对象会追赶玩家。
准备工作
首先,我们需要创建一个3D项目。 启动Unity之后,单击窗口顶部的
New按钮,如图1所示。
图1:创建一个新项目为您的
AI项目命名,并确保它是3D项目。 在计算机上选择一个存储项目的位置之后,单击底部的
Create Project按钮,如图2所示。
图2:项目设置屏幕创建项目后,我们要做的第一件事是在“
资产”窗口中设置文件夹以组织我们的工作。 右键单击
Assets窗口,然后选择
Create→Folder创建一个新文件夹。 将此文件夹命名为
Materials 。 然后创建第二个文件夹,并将其命名为
Scripts 。 图3显示了它的外观。
图3:创建一个新文件夹完成所有这些操作后,“
资产”窗口应类似于图4所示。
图4:“ 资产”窗口。接下来,创建一个所有对象都将站立的地板。 在“
层次结构”窗口中,选择“
创建”→“ 3D对象”→“平面”以创建将用作地板的平面对象。
图5:创建平面对象。命名此
Floor对象,并将其
X Scale值更改为7,将
Z Scale值更改为3。之后,带有所选
Floor对象的
Inspector窗口应如图6所示。
图6:设置Floor对象的属性。现在,我们需要为
地板创建一个新的材质,以将其与将要放置在场景中的其余对象区分开。 在“
资产”窗口的“
材料”文件夹中,右键单击“
资产”窗口,然后选择“
创建”→“材料” ,以创建新
材料 。
图7:创建新材料完成后,将物料命名为
Floor 。
图8: 地板材料。在选择了“
地板”的“
检查器”窗口的顶部,选择一个颜色选择器。
图9:选择一个颜色选择器。
当然,您可以为地板选择任何颜色,但是在此示例中,我选择了红棕色,如图10所示。
图10:颜色选择器。
在“
层次结构”窗口中选择“
地板”对象,然后在“
网格渲染器”组件中,选择“
材质”旁边的小箭头。
图11:准备材料更改。将
地板材料从“
资源”窗口拖动到“
检查器”窗口中“
网格渲染器”组件的“
元素0”字段。
图12:将Floor材质定义为Floor对象的材质。完成了
地板对象后,我们需要围绕墙壁区域进行创建,以使播放器不会掉落边缘。 返回
创建→3D对象→平面以创建一个新平面。 我们称该
墙为平面,并将其设置为与
Floor相同的尺寸,即
X Scale的值为7,
Z Scale的值为3。然后再创建三个墙,选择一个对象,然后按Ctrl + D 3次。性别与表格中的数据一致。
职称
| 位置X
| 位置Y
| 位置z
| 旋转x
| 旋转z
|
墙
| -35
| 21
| 0
| 0
| -90
|
墙(1) | -1
| 11
| -15
| 90
| 0
|
墙(2)
| -1
| 11
| 13.5
| -90
| 0
|
墙(3)
| 34
| 21
| 0
| 0
| 90 |
表1:所有
Wall对象的位置和旋转。
完成所有这些操作后,您需要更改摄像头的位置,以使其从上方看向地面。 选择
主摄像机对象,然后将“
Y位置”设置为30,将“
Z位置”设置为0,将“
X旋转”设置为80。
图13:设置相机对象。场景已经准备好了,是时候创建玩家角色了。 在“
层次结构”窗口中,单击
创建→3D对象→球体以创建球体对象。 将该对象命名为
Player ,然后单击“
检查器”窗口底部的“
添加组件”按钮。
图14:添加一个新组件。现在找到
刚体 。 之后,从列表中选择
Rigidbody组件并将
Rigidbody添加到
Player对象。
图15:添加刚体组件。接下来,您需要为玩家分配一个标签,该标签稍后会在代码中派上用场。 单击
检查器窗口左上角的“
标签”下拉菜单,然后选择“
播放器”标签。
图16:定义新标签。我们需要设置玩家的位置,使其不在
地板对象下方。 在示例中,我将播放器放置在左上角,其中
X位置等于26,
Y位置等于1,
Z位置等于-9。
图17:球员位置。为了使将来的代码正常工作,我们当然需要将其附加到对象上。 返回到“
层次结构”窗口,这次选择“
创建”→“ 3D对象”→“多维数据集” 。 调用此
Guard多维数据集,使用“
检查器”窗口中的“
添加组件”按钮向其中添加
Rigidbody组件和
NavMesh Agent组件。 接下来,将其放置在场景的左上角。 之后,
Guard对象的
Inspector窗口将如下所示:
图18:“ 检查器”窗口中的Guard对象。这个对象应该位于这样的位置:
图19: Guard对象的位置。最后,我们需要一个用作
守卫对象“眼睛”的对象,它将通知
守卫玩家正在触摸它。 上一次,进入“
层次结构”窗口,然后选择“
创建”→“ 3D对象”→“球体”以创建另一个球体对象。 将此对象命名为
Looker 。 这次我们不需要添加任何其他组件。 但是,我们将调整对象的大小。 选择
Looker之后 ,在“
检查器”窗口中更改以下“
变换”组件变量。
然后,将
Looker放置在地板的中间上部,如图20所示。
图20:查找器的位置。现在是为
Looker提供独特材料的正确时机,这样您就可以避免使用它。 在“
资源”窗口的“
材料”文件夹中,右键单击并创建一个新材料。 将其命名为
Looker并为其赋予明亮的红色。 之后,将此材质指定为
Looker对象的材质以更改其颜色。 之后,场景应如下所示:
图21:具有新材质的Looker对象。我们剩下的唯一事情就是为
Guard创建一个导航网格,它可以在上面移动。 Unity编辑器顶部有一个
Window菜单。 选择“
窗口”→“导航”以打开“
导航”窗口,如图22所示。
图22: 导航窗口。在“
层次结构”中选择
Floor对象,然后在“
导航”窗口中,选中“
导航静态”复选框。
图23: 静态导航 。接下来,选择窗口顶部的“
烘烤”选项。
图24:切换到“ 烘烤”菜单。将打开“
烘焙”菜单,您可以在其中更改将要创建的导航网格的属性。 在我们的示例中,无需进行任何更改。 只需单击右下角的“
烘焙”按钮。
图25:创建一个新的导航网格。此时,Unity将要求您保存场景。 保存它,之后将创建导航网格。 现在,场景将如下所示:
图26:当前场景以及添加的导航网格。现在,Unity中的所有内容都已配置完毕,现在该创建创建项目所需的脚本了。 在
Assets窗口中,右键单击并选择
Create→C#Script 。 将此脚本命名为
Player 。 再重复两次此操作,创建名称为
Guard和
Looker的脚本。
图27:创建一个新脚本。之后,“
资产”窗口中的“
脚本”文件夹将如下所示:
图28: 脚本文件夹。首先,我们将开始编写
Player脚本代码。 在
Assets窗口中双击
Player脚本以打开Visual Studio并开始创建代码。
代号
Player脚本非常简单,它所要做的就是让用户移动球对象。 在类声明下,我们需要获取到先前在项目中创建的
Rigidbody组件的链接。
private Rigidbody rb;
之后,在
Start函数中,我们命令Unity将
Player对象的当前
Rigidbody组件设置为
rb 。
rb = GetComponent<Rigidbody>();
之后,
播放器脚本将如下所示:
图29:目前的播放器脚本。现在已分配了
rb值,我们需要在按下箭头键时让
Player对象移动。 为了移动对象,我们将使用物理方法,当用户按下箭头键时向对象施加力。 为此,只需将以下代码添加到
Update函数:
if (Input.GetKey(KeyCode.UpArrow)) rb.AddForce(Vector3.forward * 20); if (Input.GetKey(KeyCode.DownArrow)) rb.AddForce(Vector3.back * 20); if (Input.GetKey(KeyCode.LeftArrow)) rb.AddForce(Vector3.left * 20); if (Input.GetKey(KeyCode.RightArrow)) rb.AddForce(Vector3.right * 20);
至此,我们完成了
Player脚本。 完成的脚本如下所示:
图30:完成的Player脚本。保存您的工作并返回Unity。 这次,在
Assets窗口中选择
Guard脚本。 为了使
Guard代码正常工作,您需要在脚本顶部添加using结构。
using UnityEngine.AI;
接下来,在类声明之后立即声明以下变量。
public GameObject player; private NavMeshAgent navmesh;
Player对象用作
Guard对象的
player变量的值。 稍后,当我们命令
Guard对象追逐玩家时,它将派上用场。 然后,
声明navmesh变量以接收对象的
NavMeshAgent组件。 当
守卫得知玩家正在触摸
Looker对象后,我们开始追逐玩家时,我们将使用它。 在
启动功能中,我们需要将
navmesh变量的值设置
为对象
的组件
NavMesh Agent :
navmesh = GetComponent<NavMeshAgent>();
然后,在
Update函数中,添加一行代码:
navmesh.destination = player.transform.position;
此行设置
Guard对象的目标。 在我们的例子中,它将采用
Player对象的当前位置并移至这一点。 操作后,物体将不断追随玩家。 问题是,响应过程如何执行? 它不会在
Guard脚本中编码,但会在
Looker脚本中编码。 在转到Looker脚本之前,请查看图31验证
Guard脚本代码。
图31:完成的Guard脚本。在
Looker内部
,我们再次需要声明以下变量:
public GameObject guard; private float reset = 5; private bool movingDown;
之后,我们对
Start函数进行注释,此脚本中不需要此函数。 让我们继续执行
Update函数并添加以下代码:
if (movingDown == false) transform.position -= new Vector3(0, 0, 0.1f); else transform.position += new Vector3(0, 0, 0.1f); if (transform.position.z > 10) movingDown = false; else if (transform.position.z < -10) movingDown = true; reset -= Time.deltaTime; if (reset < 0) { guard.GetComponent<Guard>().enabled = false; GetComponent<SphereCollider>().enabled = true; }
这是项目主要动作发生的地方,因此让我们分析代码。 首先,根据布尔变量
movingDown的值,此脚本所附加的对象将向上或向下移动。 一旦到达某个点,他就会改变方向。 接下来,
Looker将基于实时降低重置值。 一旦计时器小于零,他将从
Guard对象中获取
Guard脚本并将其禁用,此后,
Guard对象将开始移动到玩家最后一次知道的位置,然后再停止。
Looker还会再次打开其对撞机,以便整个过程可以重新开始。 现在,我们的脚本如下所示:
图32: Looker脚本。说到对撞机:是时候创建一个冲突代码来重置
Looker计时器并包括
Guard脚本了。 在
更新功能中,创建以下代码:
private void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Player") { guard.GetComponent<Guard>().enabled = true; reset = 5; GetComponent<SphereCollider>().enabled = false; } }
OnCollisionEnter Unity自动将其识别为冲突代码,因此在与另一个对象发生冲突时执行该代码。 在我们的例子中,它首先检查碰撞对象是否具有
Player标签。 如果不是,它将忽略其余代码。 否则,它将打开
Guard脚本,将
重置计时器设置为5(即五秒),并禁用其对撞机,以便玩家仍然可以在对象中移动并且不会意外卡在
Looker对象中。 该功能如图33所示。
图33: Looker的
冲突代码 。
这就是项目代码的全部准备好了! 在完成项目之前,您可以做几件事。 保存所有工作并返回Unity。
项目完成
要完成项目,我们只需要将脚本附加到相应的对象并设置几个变量即可。 首先,从“
导航”窗口转到“
检查器”窗口:
图34:过渡到“检查器”
窗口 。
之后,让我们从
Player对象开始。 在“
层次结构”窗口中选择它,然后在“
检查器”窗口的底部单击“
添加组件”按钮并添加“
播放器”脚本。 这样就完成了
Player对象。
图35:播放器
脚本组件 。
接下来,选择
Guard对象。 和以前一样,将
Guard脚本附加到对象。 这次我们需要告诉
守卫球员是谁。 为此,将
Player对象从
Hierarchy中拖动到
Guard脚本组件的
Player字段中,如图36所示。
图36:使Player对象成为Player字段的值。我们还需要禁用
Guard脚本。 在我们的项目中,
Guard将在打开脚本后追逐玩家。 仅当播放器触摸
Looker对象后,才应包含此
Guard脚本。 您需要做的就是取消选中组件中
Guard(脚本)文本旁边的框:
图37:禁用Guard脚本。最后,让我们转到
Looker对象,并将
Looker脚本附加到该对象。 这次,
Looker对象将需要一个
Guard对象作为其
Guard变量的值。 正如我们将
Player对象分配给
Guard脚本的
Player变量一样,我们将对
Guard对象和
Looker脚本执行相同的操作。 将
Guard从
层次结构拖到
Looker脚本的
Guard字段中。 并且项目完成了! 单击Unity编辑器顶部的“播放”按钮以测试您的项目。
图38:测试项目。尝试将
Player对象移动到
Looker对象(请记住,移动是通过箭头完成的!)。 请注意,此后,
Guard对象将开始追逐玩家。 他将继续追求约5秒钟,然后他将放弃。
图39:一个完全完成的项目正在实施。结论
该AI非常简单,但可以轻松扩展。 假设,如果我们假设
Looker对象是一台相机,而后卫则通过它寻找来找到您,那么让
Guard对象拥有自己的双眼是合乎逻辑的。 玩家可以靠近摄像机,但必须考虑到警卫的眼睛。 您也可以将该项目与
寻找路径的
概念结合起来:为后卫提供他将要遵循的路径,从而为玩家创造一个更加有趣的环境。
可以通过许多不同的方式来开发这种简单的AI。 您可能不想做上述任何事情,并决定自己做某事。 我建议您尝试一下,也许您会想到一个有趣的项目的想法,这值得结束。