一、怪物正常行为
1、导入资源
2、创建怪物预制体
新建一个空物体,作为父级、怪物作为子级
3、导入怪物动画
在怪物身上添加角色控制器和动画组件
此时怪物会有碰撞不会穿模
创建一个动画控制器,放入攻击状态和正常状态
并设置一个Bool值,为攻击
注:模型动画预览时,需要拖入子物体模型,不能放入父级物体
如图所示:
如果模型是预制体,那么只能拖入模型,不能放入父级
如果模型与父级是同一个预制体,那么拖入父级也就相当于一个整体的人物模型
二、AI寻路组件
1、地图烘焙
2、挂载AI组件和代码
注意:
敌人可能会出现进入玩家的身体里的情况
解决方法:
AI组件中有一个停止距离,敌人的脚本中有一个攻击距离,二者之间需要有一个大小值
第一种情况:敌人的攻击距离大于停止距离,则先停止后攻击,不会进入身体中
第二种情况:敌人的攻击距离小于停止距离,则会出现一直进入身体的Bug
写敌人的逻辑时需要注意一下。
3、代码
注:使怪物始终朝向玩家,必须设置锁定Y轴的代码
否则会出现,敌人身体倾斜
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public enum BossState
{
idle,
attack,
run
}
public class EnemyControl : MonoBehaviour
{
private BossState CurrentState= BossState.idle;//枚举状态
private Animator Anim;//怪物动画控制器
private NavMeshAgent Agent;//导航网格代理
private float HpNow = 100;
private bool ISDead = false;
private Transform player;//玩家位置
public float IdleDis = 100.0f;//站立寻敌范围
public float AttackDis = 3.0f;//攻击距离
public float HpMax = 100.0f;
//声音管理组件
public AudioSource AudioSource1;
//播放声音大小
public float SoundValue = 1.0f;
//第一个攻击声音
public AudioClip AudioClip1;
private void Start()
{
HpNow = HpMax;
Anim = GetComponent<Animator>();
Agent = GetComponent<NavMeshAgent>();
player = GameObject.FindWithTag("Player").transform;
}
private void Update()
{
LookAtPlayer();
EnemyState();
}
/// <summary>
/// 始终朝向玩家
/// </summary>
public void LookAtPlayer()
{
if (!ISDead)
{
Vector3 PlayerPos = player.position;
PlayerPos.y = transform.position.y;
transform.LookAt(PlayerPos);
}
}
/// <summary>
/// 切换怪物状态
/// </summary>
public void EnemyState()
{
if (!ISDead)
{
float Distance1=Vector3.Distance(transform.position, player.position);
switch (CurrentState)
{
//静止状态
case BossState.idle:
Anim.Play("RunTree");
Anim.SetFloat("MoveSpeed", 0f);
Anim.SetBool("Attack", false);
Agent.isStopped = true;
if(Distance1> AttackDis && Distance1 <= IdleDis)
{
CurrentState=BossState.run;
}
break;
//追踪状态
case BossState.run:
Agent.isStopped = false;
Agent.SetDestination(player.position);
Anim.SetFloat("MoveSpeed", 1f);
Anim.SetBool("Attack", false);
if (Distance1 > IdleDis)
{
CurrentState= BossState.idle;
}
else if (Distance1 <= AttackDis)
{
CurrentState = BossState.attack;
}
break;
//攻击状态
case BossState.attack:
Agent.isStopped=true;
BossAttack();
if (Distance1 > AttackDis)
{
CurrentState= BossState.run ;
}
else
{
Anim.SetFloat("MoveSpeed", 0f);
}
break;
}
}
}
//Boss攻击
public void BossAttack()
{
if (Anim.GetBool("Attack"))
{
return;
}
Anim.SetBool("Attack", true);
}
//播放声音,在动画事件中调用
public void PlayAttackSound()
{
AudioSource1.PlayOneShot(AudioClip1, SoundValue);
}
}
4、混合树
将敌人的Idle和Run状态混合
混合度、播放速度、镜像
二者的区别是Play没有过度;CrossFade有过度
三、添加敌人音效
技巧:在动画事件上添加攻击音效,这样只会在敌人攻击时播放
1、代码
声明部分
2、动画事件
四、敌人伤害
1、实现原理
和之前一样,也是在动画事件中进行调用,在挥砍的那一瞬间调用
在此项目中,使用一个攻击点和一个预制体(触发器)进行实例化,判断触发伤害
动画事件和触发器一起使用
还有一种方法使用射线检测
2、制作点和实例化物体
把空物体放置在子物体下,不然会出现实例化位置移动
建一个空物体挂载代码,并制作成预制体
注意:此处选择小写的gameobject,为此物体
在实例化出来后,一定时间内销毁此物体
在玩家的每一个攻击中实例化此物体
3、伤害
写一个随机值
五、怪物受击特效
实现原理:实例化音效和特效在一个点,点挂载到玩家的身上
1、创建实例化位置
挂载到敌人的身上
创建一个空物体,添加代码和音效,音效设置为启用时播放
即可在实例化的时候 产生攻击特效和音效
2、代码
首先,实例化的物体要写消失条件
在敌人的受击方法中,添加实例化并播放受击动画