可视化

  • OnDrawGizmos():画出所有
  • OnDrawGizmosSelected()画出选中的
    void OnDrawGizmosSelected()
      {
          Gizmos.color = Color.blue;
          Gizmos.DrawWireSphere(transform.position, sightRadius);
      }

获得范围内随机点用于巡逻

void GetNewWayPoint()
    {
        //还原停留时间
        remainLookAtTime = lookAtTime;
        float randomX = Random.Range(-patrolRange, patrolRange);
        float randomZ = Random.Range(-patrolRange, patrolRange);
        Vector3 randomPoint = new Vector3(guardPos.x + randomX, transform.position.y, guardPos.z + randomZ);
        //随机点不能是障碍物
        NavMeshHit hit;
        //NavMesh.SamplePosition用于寻找与给定点距离最近的可用点
        //1指的是navmesh area里walkable的cost
        wayPoint = NavMesh.SamplePosition(randomPoint,out hit, patrolRange, 1) ? hit.position : transform.position;     
    }

ScriptableObject

-数值型文件,用于存储数据

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//默认文件名为New Data,菜单分级中为Character Stats/Data
[CreateAssetMenu(fileName ="New Data",menuName="Character Stats/Data" )]
//继承自ScriptableObject
public class CharacterData_SO : ScriptableObject
{
    [Header("Stats Info")]
    //最大血量
    public int maxHealth;
    //当前血量
    public int currentHealth;
    //基础防御
    public int baseDefence;
    //当前防御
    public int currentDefence;
}
  • 数值文件实例
    图片说明

创建状态管理类用于管理数据

  • get/set用于快速访问和修改
    sing System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class CharacterStats : MonoBehaviour
    {
      public CharacterData_SO characterData;
      public int MaxHealth
      {
          get{if (characterData != null)return characterData.maxHealth;else return 0;}
          set{characterData.maxHealth = value;}
      }
    }

同步脚本和动画的逻辑

void SwitchAnimation()
    {
        anim.SetBool("Walk", isWalk);
        anim.SetBool("Chase", isChase);
        anim.SetBool("Follow", isFollow);
        anim.SetBool("Critical", characterStats.isCritical);
        anim.SetBool("Death", isDead);

攻击造成伤害的实现

  • 在Animation中添加帧事件

  • 打开Animation面板
    图片说明

  • 点击+号添加帧事件
    图片说明
    图片说明

  • 触发Hit事件

    //Animation Event
      void Hit()
      {
          var targetStats = attackTarget.GetComponent<CharacterStats>();
          targetStats.TakeDamage(characterStats, targetStats);
      }
  • 结算伤害

    //受到攻击
      public void TakeDamage(CharacterStats attacker,CharacterStats defender)
      {
          int damage = Mathf.Max(attacker.CurrentDamage() - defender.CurrentDefence,0);
          CurrentHealth = Mathf.Max(CurrentHealth - damage, 0);
          //暴击时触发挨打的人的受击动画
          if (attacker.isCritical)
          {
              defender.GetComponent<Animator>().SetTrigger("Hit");
          }
    
          //更新血量UI
    
          //结算经验值
    
      }

部分Animation无法添加帧事件的解决办法

  • 包含在fbx文件中的动画是无法编辑的
    图片说明
  • 使用ctrl+D将动画复制一份到fbx外,使用复制的动画即可编辑

Quaternion.Lerp

  • 差值运算,用于缓慢执行某个动作
  • 如缓慢从一个角度旋转到另一个角度
    transform.rotation = Quaternion.Lerp(transform.rotation,guardRotation,0.01f);

单位死亡时关闭相应组件

  • 避免出现一遍播放死亡动画一遍移动,或是殴打尸体的情况
    collider.enabled = false;
    agent.enabled = false;

    泛型单例模式

  • virtual为虚函数,在子类中使用override重载
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    //泛型类
    public class Singleton<T> : MonoBehaviour where T:Singleton<T>
    {
      private static T instance;
      public static T Instance
      {
          get { return instance; }
      }
      protected virtual void Awake()//虚函数
      {
          if (instance != null)
              Destroy(gameObject);
          else
              instance = (T)this;
      }
      public static bool IsInitialized
      {
          get { return instance != null; }
      }
      protected virtual void OnDestroy()
      {
          if (instance == this)
          {
              instance = null;
          }
      }
    }