敌人
继续上一篇博客
基本配置
- 资料包中给我们提供了三种类型的敌人,我们以Zombunny为例。将这个预制体拖至Hierachy层级面板中,设置层级为Shootable。
- 添加刚体组件,配置跟玩家配置一样。
- 添加胶囊碰撞体,胶囊碰撞体检测的是与子弹的碰撞。
- 添加球形碰撞体,这个球形碰撞体检测的是玩家是否进入敌人攻击范围,因此把IsTrigger勾选上。
什么是Collider?
- 刚体如果想发生碰撞,一定需要碰撞体
- 碰撞体决定了碰撞发生时的边界条件
- 刚体决定了碰撞发生后物体的运动效果
- 没有碰撞体的刚体,会在物理模拟中相互穿透
- 只要一方是触发器,并且有刚体,不管另一方是出发器还是碰撞体,都会打印各自的触发器方法。
- 一般,我们将触发器方法写在玩家碰到的物体上,玩家一般不参与触发方法,只写碰撞方法。
- 当IsTrigger = false时,碰撞体根据物理引擎引发碰撞,产生碰撞的效果,可以调用OnCollisionEnter/ Stay / Exit 函数。
- 当IsTrigger = true时,碰撞体被物理引擎忽略,没有碰撞的效果,可以调用OnCollisionEnter/ Stay / Exit 函数。
自动导航
- 在敌人物体上添加Nav Mesh Agent组件
最后进行烘焙
敌人角色动画设置
- 跟玩家的角色动画控制机类似,包含Idle , Move 和 Death三个状态。
- 在这里我们设置Move为默认状态。
- 在Parameters窗口添加两个Trigger参数,一个是PlayerDead,一个是Dead
- 在状态机连接中设置过渡参数
编写敌人移动脚步
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
Transform player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
NavMeshAgent nav;
void Awake()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
playerHealth = player.GetComponent<PlayerHealth>();
enemyHealth = GetComponent<EnemyHealth>();
nav = GetComponent<NavMeshAgent>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (enemyHealth.currentHealth > 0 && playerHealth.currentHealth >0)
{
//怪物身上nav组件对象
nav.SetDestination(player.position);
}
else
{
nav.enabled = false;
}
}
}
编写敌人攻击脚步
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAttack : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f;
public int attackDamage = 10;
Animator anim;
GameObject player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
bool playerInRange;
float timer;
void Awake()
{
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
enemyHealth = GetComponent<EnemyHealth>();
anim = GetComponent<Animator>();
}
//分别用来处理进入和离开怪物的sphereCollider范围的时间
//当角色离开出发器时,判断其是否为角色。
void OnTriggerEnter(Collider other)
{
if (other.gameObject == player)
{
playerInRange = true; //角色在攻击范围
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject == player)
{
playerInRange = false;
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
//控制攻击频率,timer累加到大于两个间隔 且 在攻击范围内
if ((timer >= timeBetweenAttacks && playerInRange) && (enemyHealth.currentHealth > 0))
{
Attack();
}
if (playerHealth.currentHealth <=0)
{
anim.SetTrigger("PlayerDead");
}
}
void Attack()
{
timer = 0f; //重置了计时器,这个计时器用于控制攻击的时间间隔
//调用PlayerHealth脚本
if (playerHealth.currentHealth > 0)
{
playerHealth.TakeDamage(attackDamage);
}
}
}