这个系列的介绍已经完成,有兴趣的朋友可以查看以下链接
Unity开发一个FPS游戏_unity 模仿开发fps 游戏-优快云博客
Unity开发一个FPS游戏之二_unity 模仿开发fps 游戏-优快云博客
Unity开发一个FPS游戏之四_unity fps-优快云博客
所有的代码和资源已经上架Unity Asset Store, 链接是FPS basic package | Systems | Unity Asset Store
在前面的两篇博客中,我已实现了一个FPS游戏的大部分功能,包括了第一人称的主角运动控制,武器射击以及敌人的智能行为。这里我将继续完善这个游戏,包括以下几个方面:
- 增加一个真实的游戏场景,模拟一个废弃的工厂环境
- 完善NPC的智能行为,增加巡逻,警戒,躲避,战术编队等行为。
- 增加子弹与场景物体交互的效果。
- 游戏道具的拾取。
- 完善UI界面,增加场景的任务描述,任务完成或失败的判定展示。
以下是完成后的演示。
mission_critical
游戏场景设计
这是一个很大的课题,暂时不是我关注的重点,为此我先直接采用Unity商店的一个免费的资源,RPG/FPS Game Assets for PC/Mobile (Industrial Set v2.0) | 3D 工业场景 | Unity Asset Store。
NPC智能行为
在之前的博客,已经实现了NPC的部分智能行为,包括了随机搜索以及跟踪玩家的行为。在这里,我将添加巡逻,警戒,躲藏、战术编队等行为。
巡逻警戒
首先是实现巡逻功能,这个可以通过定义巡逻路线经过的关键点,然后利用Unity自带的寻路功能来实现。为此,我们需要对场景的道路进行烘培,点击Window->AI->Navigation,然后选择Bake选项,对场景的可导航路线进行烘培。烘培完成后,需要给Enemy这个预制件增加一个NavMesh Agent的组件,然后在代码中就可以指定目标点的坐标,让Agent自己导航到目的地。
其次是增加警戒模式,这个模式比较简单,就是传入一个警戒点的坐标,然后敌人就在警戒点不定期的随机搜索四周。以下是对WanderingAI的代码进行修改,增加巡逻和警戒这两个行为:
[Header("Enemy behavior")]
...
public bool patrolBehavior = false;
public Vector3[] patrolPoints = null;
public bool guardBehavior = false;
public float guardAngle = 60f;
public float guardMaxRotatePeriod = 10.0f;
public float initialGuardAngle = 0f;
private long _guardTS = 0;
private float _guardChangeTime = 0;
[Flags]
private enum EnemyStatus {
...
Patrol,
Guard
}
void Start()
{
_animator = GetComponent<Animator>();
muzzleEffect = GetComponent<MuzzleEffect>();
_agent = GetComponent<NavMeshAgent>();
currentAmmo = ammo;
if (patrolBehavior && patrolPoints.Length >= 2) {
status = EnemyStatus.Patrol;
_agent.autoBraking = false;
_agent.destination = patrolPoints[destPoint];
transform.forward = patrolPoints[destPoint] - patrolPoints[0];
_animator.SetTrigger("E_Walk");
} else if (guardBehavior) {
status = EnemyStatus.Guard;
transform.localEulerAngles = new Vector3(0f, initialGuardAngle, 0f);
_guardTS = DateTime.UtcNow.Ticks;
_guardChangeTime = UnityEngine.Random.Range(1.0f, guardMaxRotatePeriod);
_animator.SetTrigger("E_Guard");
} else {
status = EnemyStatus.Idle;
}
}
// Update is called once per frame
void Update()
{
if (status == EnemyStatus.AimLeft || status == EnemyStatus.AimRight) {
AimMove();
} else if (status == EnemyStatus.Reload || status == EnemyStatus.Death) {
return;
} else if (status == EnemyStatus.Aim) {
TurnToPlayer();
} else if (status == EnemyStatus.Hide) {
Hide();
} else if (status == EnemyStatus.Damage) {
TurnToDamage();
if (!_animator.GetBool("E_IsDamage")) {
status = EnemyStatus.Idle;
prevPlayerPosition = new Vector3(100f, 100f, 100f);
}
} else {
if (status == EnemyStatus.Walk) {
Walk();
}
if (status == EnemyStatus.Sprint) {
Sprint();
}
if (status == EnemyStatus.Patrol) {
Patrol();
}
if (status == EnemyStatus.Guard) {
Guard();
}
DetectPlayer();
}
}
void Patrol() {
if (!_agent.pathPending && _agent.remainingDistance < 0.5f) {
Vector3 prevPosition = patrolPoints[destPoint];
destPoint = (destPoint + 1) % patrolPoints.Length;
_agent.destination = patrolPoints[destPoint];
transform.forward = patrolPoints[destPoint] - prevPosition;
}
}
public void SetPatrolPoints(Vector3[] points) {
patrolPoints = points;
}
private void Guard() {
float interval = (DateTime.UtcNow.Ticks - _guardTS)/10000000.0f;
if (interval >= _guardChangeTime) {
_guardTS = DateTime.UtcNow.Ticks;
_guardChangeTime = UnityEngine.Random.Range(1.0f, guardMaxRotatePeriod);
float rotateAngle = UnityEngine.Random.Range(-guardAngle, guardAngle);
transform.localEulerAngles = new Vector3(0f, initialGuardAngle + rotateAngle, 0f);
}
}
从以上代码可以看到,要设定敌人的行为是巡逻,需要传入巡逻点的坐标,然后依次把巡逻点的坐标设置为Agent的目的地即可,在巡逻的过程中,敌人同样需要检测玩家,如果发现玩家则改变为瞄准行为,这个和之前的随机搜索的行为切换是保持一致的。当行为是警戒时,将随机不定时的转动一些角度来探测玩家。
装弹躲藏
进一步改进敌人在交战时的行为,当敌人打完子弹时,现在的行为是站定了换子弹,为了提高难度,我们可以设计当敌人打完子弹,自动跑到一些隐藏点来重新装弹,之后再出来和玩家交战。为此我们需要设计一些隐藏点。我采取的方法是在场景中选取一些隐藏点,在每个地点