敌人对象的添加以及相关动画与脚本实现
添加一个敌人
在hierachy中创建一个空对象enemy01,将npc01预制体作为enemy0的子对象并重名名为Visual。
,
删掉visual的animator(animator的动画播放器无法作用于父对象enemy01)
给enemy01添加相关组件
animator
图中的enemy01继承自player,找到之前创建的player动画控制器,右键-create-animator override controller,
图中controller将player拖拽过来,视为继承自player,将对应的动画状态如图设置(enemy没有airborne,设置为idel)
charactor contrroller
参数如图
nav mesh agent
参数如图:
该组件是为了实现enemy01的自动寻路,这里运用到了对地图的烘焙,windows-ai-navigation,
max slope设置为0,避免enemy上楼梯,点击bake。
脚本设置
enemy01与player共用一个脚本。
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Character : MonoBehaviour
{
private CharacterController cc;//角色进行移动,通常使用character controller组件,rigidbody通常用来实现物理交互
public float speed = 10f;
private Vector3 moveVelocity;
private PlayerInput playerInput;
private float verticalVelocity;
private float Gravity=-9.8f;
private Animator animator;
public bool isPlayer=true;
private UnityEngine.AI.NavMeshAgent navAgent;//引入ai组件
private Transform targetPlayer;//记录玩家位置
private void Awake() {
cc=GetComponent<CharacterController>();
animator=GetComponent<Animator>(); //组件获取
if(!isPlayer){
navAgent=GetComponent<UnityEngine.AI.NavMeshAgent>();
targetPlayer=GameObject.Find("Player").transform;//获取目标玩家de位置
navAgent.speed=speed;//设置移动速度
}
else
{
playerInput=GetComponent<PlayerInput>();
}
}
private void CalculateEnemyMove(){
if(Vector3.Distance(transform.position,targetPlayer.position)>navAgent.stoppingDistance)//计算当前角色(由transform.position表示其位置)与目标玩家(由targetPlayer.position表示其位置)之间的直线距离
{
navAgent.SetDestination(targetPlayer.position);//调用navAgent.SetDestination(targetPlayer.position)将角色的移动目的地设置为目标玩家的位置,使其继续朝向目标移动。
animator.SetFloat("Speed",0.2f);
}else{
navAgent.SetDestination(transform.position);//执行navAgent.SetDestination(transform.position)将角色的移动目的地设置为当前角色的位置,这意味着角色将停止移动并保持在当前位置。
animator.SetFloat("Speed",0f);
}
}
private void CalculateMove(){
moveVelocity.Set(playerInput.horizontalInput,0,playerInput.verticalInput);//获取输入的值并将其转化为三维向量
moveVelocity.Normalize();//向量的归一化
moveVelocity=Quaternion.Euler(0,-45,0)*moveVelocity;//实现对向量的旋转(与相机旋转角度相同)
moveVelocity*=speed*Time.deltaTime;
animator.SetFloat("Speed",moveVelocity.magnitude);//将物体当前的速度大小(即moveVelocity的模长)赋值给Animator控制器中的名为"Speed"的动画参数,moveVelocity.magnitue获取moveVelocity的模长
//为了防止player持续转向,判断moveVelocity是否为零,若为零则不进行旋转
if(moveVelocity!=Vector3.zero){
transform.rotation=Quaternion.LookRotation(moveVelocity);
}
animator.SetBool("AirBorne",!cc.isGrounded);//调用animator的bool参数"AirBorne",若cc.isGrounded为false,即角色没有与地面接触,则将bool参数设置为true,否则为false
}
//这段代码的主要目的是在每一帧的固定更新阶段,先计算出对象应有的速度,然后基于此速度信息来实际移动游戏对象。
void FixedUpdate(){
if(isPlayer)
CalculateMove();
else
CalculateEnemyMove();
if(isPlayer){
//如果对象没有与地面接触,那么它的速度为重力加速度,否则速度为重力加速度的0.3倍
if(cc.isGrounded==false){
verticalVelocity=Gravity;
}
else{
verticalVelocity=Gravity*0.3f;//判断当角色在地面时,依旧施加力的原因是角色控制器的设计中,在角色仍在地面上时,下一帧角色仍会浮在地面上,isGrounded=false,所以需要在重力加速度上施加一个小的力
}
moveVelocity+=verticalVelocity*Vector3.up*Time.deltaTime;//将垂直方向的力与水平方向的力矢量相加,将verticalVelocity转化成三维向量
}
cc.Move(moveVelocity); //移动游戏对象
}
}
ps:isplayer默认为true,在unity中将enemy的isplayer设置为false
将player的标签设置为player
(出了一个bug,enemy动画可以正常播放,也会朝向player,但是无法移动,怀疑是bake的问题)
enemy的脚步特效设置
新建一个VFX空对象作为visual的子对象,将如图所示预制体作为VFX子对象
脚本设置
创建一个EnemyVFXmanager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;
public class EnemyVFXManager : MonoBehaviour
{
public VisualEffect visualEffect;
public void BurstFootStep(){
visualEffect.Play();
}
}