feat(animation): 移除多个动画控制器并更新蓝色怪物配置 移除了littleGreen和player的动画控制器及其元文件,同时删除了 人类-剑士士兵和怪物-蓝色战士的override控制器及相关的材质文件。 更新了蓝色怪物override控制器中的动画剪辑引用,将原有的动画 资源替换为新的GUID引用。 ```
260 lines
6.2 KiB
C#
260 lines
6.2 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class NewBehaviourScript : MonoBehaviour
|
|
{
|
|
|
|
//移动速度
|
|
public float speed = 5f;
|
|
|
|
//重力加速度
|
|
public float gravity = -20f;
|
|
|
|
//活动范围
|
|
public float range = 500f;
|
|
|
|
//兼容旧场景配置,目标搜索不再受此范围限制
|
|
public float detectRange = 200f;
|
|
|
|
//攻击范围
|
|
public float attackRange = 5f;
|
|
|
|
//角色状态
|
|
private State state = State.Alert;
|
|
|
|
//攻击目标
|
|
public GameObject target;
|
|
|
|
//当前目标点
|
|
private Vector3 targetPos;
|
|
|
|
//初始位置坐标
|
|
private Vector3 startPos;
|
|
|
|
//动画控制器
|
|
private Animator animator;
|
|
|
|
// 角色控制器
|
|
private CharacterController controller;
|
|
|
|
//垂直速度
|
|
private float verticalVelocity;
|
|
|
|
//状态枚举
|
|
public enum State
|
|
{
|
|
//警戒巡逻
|
|
Alert,
|
|
|
|
//追踪状态
|
|
Track,
|
|
|
|
//攻击状态
|
|
Attack
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
//初始化
|
|
animator = GetComponent<Animator>();
|
|
controller = GetComponent<CharacterController>();
|
|
//记录初始坐标
|
|
startPos = transform.position;
|
|
randomTargetPos();
|
|
}
|
|
|
|
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
//攻击状态直接返回
|
|
if (state == State.Attack)
|
|
{
|
|
animator.SetInteger("speed", 0);
|
|
applyGravity();
|
|
return;
|
|
}
|
|
|
|
//查找场景中距离自己最近的敌对目标
|
|
target = FindNearestOpponent();
|
|
if (target != null)
|
|
{
|
|
state = State.Track;
|
|
|
|
//目标在攻击范围外则朝目标移动,否则开始攻击
|
|
if (!IsWithinAttackReach(target))
|
|
{
|
|
move(target.transform.position - transform.position);
|
|
}
|
|
else
|
|
{
|
|
attack(target);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//没有目标时回到巡逻
|
|
state = State.Alert;
|
|
move(targetPos - transform.position);
|
|
|
|
//到达目标点时候随机目标点,只判断水平距离
|
|
Vector3 patrolOffset = transform.position - targetPos;
|
|
patrolOffset.y = 0;
|
|
if (patrolOffset.sqrMagnitude < 0.01f)
|
|
{
|
|
randomTargetPos();
|
|
}
|
|
}
|
|
|
|
//根据自身Tag找到敌对Tag
|
|
private string GetOpponentTag()
|
|
{
|
|
if (CompareTag("Friend"))
|
|
{
|
|
return "Enemy";
|
|
}
|
|
|
|
if (CompareTag("Enemy"))
|
|
{
|
|
return "Friend";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
//查找距离自己最近的敌对目标
|
|
private GameObject FindNearestOpponent()
|
|
{
|
|
string opponentTag = GetOpponentTag();
|
|
if (string.IsNullOrEmpty(opponentTag))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
GameObject nearestOpponent = null;
|
|
float nearestSqrDistance = float.PositiveInfinity;
|
|
GameObject[] opponents = GameObject.FindGameObjectsWithTag(opponentTag);
|
|
|
|
foreach (var opponent in opponents)
|
|
{
|
|
//排除自己以及自己所在层级中的对象
|
|
if (opponent == gameObject || opponent.transform.root == transform.root)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float sqrDistance = (opponent.transform.position - transform.position).sqrMagnitude;
|
|
if (sqrDistance < nearestSqrDistance)
|
|
{
|
|
nearestSqrDistance = sqrDistance;
|
|
nearestOpponent = opponent;
|
|
}
|
|
}
|
|
|
|
return nearestOpponent;
|
|
}
|
|
|
|
//判断是否进入可攻击距离
|
|
private bool IsWithinAttackReach(GameObject opponent)
|
|
{
|
|
if (opponent == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 只保留很小的接触补偿,避免两边半径叠加导致隔空挥剑
|
|
float reachDistance = attackRange + 0.1f;
|
|
return Vector3.Distance(transform.position, opponent.transform.position) <= reachDistance;
|
|
}
|
|
|
|
//攻击
|
|
private void attack(GameObject friend)
|
|
{
|
|
target = friend;
|
|
|
|
//攻击动画动作
|
|
animator.SetInteger("speed", 0);
|
|
animator.SetTrigger("attack");
|
|
state = State.Attack;
|
|
}
|
|
|
|
|
|
// 在 Animator 的 Attack 动画结束时调用此方法
|
|
public void OnAttackAnimationEnd()
|
|
{
|
|
//攻击动画结束后,目标仍在攻击范围内则立即进行下一次攻击
|
|
target = FindNearestOpponent();
|
|
if (target != null && IsWithinAttackReach(target))
|
|
{
|
|
attack(target);
|
|
return;
|
|
}
|
|
|
|
//目标离开攻击范围,回到追踪或巡逻逻辑
|
|
state = State.Alert;
|
|
randomTargetPos();
|
|
}
|
|
|
|
|
|
|
|
//角色移动,持续到目标点
|
|
void move(Vector3 movement)
|
|
{
|
|
movement.y = 0;
|
|
if (movement == Vector3.zero)
|
|
{
|
|
animator.SetInteger("speed", 0);
|
|
applyGravity();
|
|
return;
|
|
}
|
|
|
|
//改变动画控制器的参数
|
|
animator.SetInteger("speed", 1);
|
|
//向目标点移动,同时应用垂直重力
|
|
Vector3 velocity = movement.normalized * speed;
|
|
velocity.y = getVerticalVelocity();
|
|
controller.Move(velocity * Time.deltaTime);
|
|
//改变角色朝向目标点,只改变y轴
|
|
transform.rotation = Quaternion.LookRotation(movement, Vector3.up);
|
|
transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y, 0);
|
|
}
|
|
|
|
|
|
private float getVerticalVelocity()
|
|
{
|
|
if (controller.isGrounded && verticalVelocity < 0)
|
|
{
|
|
verticalVelocity = -2f;
|
|
}
|
|
else
|
|
{
|
|
verticalVelocity += gravity * Time.deltaTime;
|
|
}
|
|
|
|
return verticalVelocity;
|
|
}
|
|
|
|
private void applyGravity()
|
|
{
|
|
controller.Move(Vector3.up * getVerticalVelocity() * Time.deltaTime);
|
|
}
|
|
|
|
//随机目标点
|
|
private void randomTargetPos()
|
|
{
|
|
//以startPos为中心,在水平圆形范围内随机一个点
|
|
Vector2 randomOffset = Random.insideUnitCircle * range;
|
|
targetPos = startPos + new Vector3(randomOffset.x, 0, randomOffset.y);
|
|
}
|
|
}
|
|
|