主要技术:Animator Override Contraller,Animator Behavior,判断角色前后关系,粒子系统
制作更多敌人 独立 CharacterStates
N 个 Enemy 的 CharacterStates 导入的 CharacterData_SO 数据不应该是一样
1 2 3 4 # CharacterStates public CharacterData_SO templateData; public CharacterData_SO characterData;
多增加一个 CharacterData_SO 类型的变量记录当前敌人数据
1 2 3 4 5 6 7 8 9 void Awake (){ if (templateData != null ) { characterData = Instantiate(templateData); } }
Animator Override Contraller
Project -> 右键 create -> Animator Override Contraller
选择 Enemy_Slime ,即可按 Enemy_Slime 动画生成对应模板
设置兽人士兵
复制 Aimator 可以用 Animator Override Contraller 的方法,修改子动画效果引用动画也会变
另一种方法直接 ctrl+d 其他敌人的Animator,修改对应动画,继承父类Emeny 添加Data,动画添加Event
击飞和眩晕效果
Player 被击飞使用 NavMeshAgent 的功能,添加眩晕效果动画速度调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # Grunt using UnityEngine.AI; public class Grunt : EnemyController { [Header("Skill" ) ] public float kickForce = 10 ; public void KickOff () { if (attackTarget != null ) { transform.LookAt(attackTarget.transform); Vector3 direction = attackTarget.transform.position - transform.position; direction.Normalize(); attackTarget.GetComponent<NavMeshAgent>().isStopped = true ; attackTarget.GetComponent<NavMeshAgent>().velocity = direction * kickForce; attackTarget.GetComponent<Animator>().SetTrigger("Dizzy" ); } } }
Animator Behavior
动画添加行为(控制机):编写 Animator Behavior 控制动画播放期间的 Agent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # StopAgent public class StopAgent : StateMachineBehaviour { override public void OnStateEnter (Animator animator, AnimatorStateInfo stateInfo, int layerIndex ) { animator.GetComponent<NavMeshAgent>().isStopped = true ; } override public void OnStateUpdate (Animator animator, AnimatorStateInfo stateInfo, int layerIndex ) { animator.GetComponent<NavMeshAgent>().isStopped = true ; } override public void OnStateExit (Animator animator, AnimatorStateInfo stateInfo, int layerIndex ) { animator.GetComponent<NavMeshAgent>().isStopped = false ; } }
Extention 扩展方法
创建 Transform 的扩展方法,判断攻击目标是否在面前
扩展方法不可以继承其他类,静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # ExtensionMethod public static class ExtensionMethod { private const float dotThreshold = 0.5f ; public static bool IsFacingTarget (this Transform transform, Transform target ) { var vecterToTarget = target.position - transform.position; vecterToTarget.Normalize(); float dot = Vector3.Dot(transform.forward, vecterToTarget); return dot >= dotThreshold; } }
1 2 >var vecterToTarget = target.position - transform.position; >vecterToTarget.Normalize();
1 >var vecterToTarget = (target.position - transform.position).normolized;
1 2 3 4 5 6 7 8 9 10 # EnemyController void Hit () { if (attackTarget != null && transform.IsFacingTarget(attackTarget.transform)) { var targetstates = attackTarget.GetComponent<CharacterStates>(); targetstates.TakeDamage(characterStates, targetstates); } }
设置石头人BOSS 扔石头效果
NavMeshAgent:stopDistance
代理在接近目标停止时的位置 受目标大小影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # Rock void Start () { rb = GetComponent<Rigidbody>(); FlyToTarget(); } public void FlyToTarget () { direction = (target.transform.position - transform.position + Vector3.up).normalized; rb.AddForce(direction * force, ForceMode.Impulse); }
在BOSS手的位置生成石头
生成 GameObject 方法:
1 Object.Instantiate(Object original, Vector3 position, Quaternion rotation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # Golem public void KickOff () { if (attackTarget != null && transform.IsFacingTarget(attackTarget.transform)) { var targetstates = attackTarget.GetComponent<CharacterStates>(); Vector3 direction = (attackTarget.transform.position - transform.position).normalized; targetstates.GetComponent<NavMeshAgent>().isStopped = true ; targetstates.GetComponent<NavMeshAgent>().velocity = direction * kickForce; targetstates.GetComponent<Animator>().SetTrigger("Dizzy" ); targetstates.TakeDamage(characterStates, targetstates); } } public void ThrowRock () { var rock = Instantiate(rockPrefab, handPos.position, Quaternion.identity); rock.GetComponent<Rock>().target = attackTarget; }
玩家反击效果 石头不同状态
不同状态(类型) 想到枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # Rock public enum RockStates { HitPlayer, HitEnemy, HitNothing}; private RockStates rockStates; void FixedUpdate () { if (rb.velocity.sqrMagnitude < 1 ) { rockStates = RockStates.HitNothing; } } private void OnCollisionEnter (Collision collision ) { switch (rockStates) { case RockStates.HitPlayer: if (collision.gameObject.CompareTag("Player" )) { collision.gameObject.GetComponent<NavMeshAgent>().isStopped = true ; collision.gameObject.GetComponent<NavMeshAgent>().velocity = direction * force; collision.gameObject.GetComponent<Animator>().SetTrigger("Dizzy" ); collision.gameObject.GetComponent<CharacterStates>().TakeDamage(damage, collision.gameObject.GetComponent<CharacterStates>()); rockStates = RockStates.HitNothing; } break ; case RockStates.HitEnemy: if (collision.gameObject.GetComponent<Golem>()) { var otherStates = collision.gameObject.GetComponent<CharacterStates>(); otherStates.TakeDamage(damage, otherStates); Instantiate(breakEffect, transform.position, Quaternion.identity); Destroy(gameObject); } break ; } } }
1 2 3 4 5 6 7 8 9 10 11 # MouseManager void MouseControl (){ if (Input.GetMouseButtonDown(0 ) && hitInfo.collider != null ) { if (hitInfo.collider.gameObject.CompareTag("Attackable" )) { OnEnemyClicked?.Invoke(hitInfo.collider.gameObject); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # PlayerController void Hit (){ if (attackTarget.CompareTag("Attackable" )) { if (attackTarget.GetComponent<Rock>() && attackTarget.GetComponent<Rock>().rockStates == Rock.RockStates.HitNothing) { attackTarget.GetComponent<Rock>().rockStates = Rock.RockStates.HitEnemy; attackTarget.GetComponent<Rigidbody>().velocity = Vector3.one; attackTarget.GetComponent<Rigidbody>().AddForce(transform.forward * 20 , ForceMode.Impulse); } } else { var targetStates = attackTarget.GetComponent<CharacterStates>(); targetStates.TakeDamage(characterStates, targetStates); } }
函数重载
重载TakeDamage(int damage, CharacterStates defener)
的另一种有参格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # CharacterStates public void TakeDamage (CharacterStates attacker, CharacterStates defener ) { int damage = Math.Max(attacker.CurrentDamage() - defener.CurrentDefence, 0 ); CurrentHealth = Math.Max(CurrentHealth - damage, 0 ); if (attacker.isCritical) { defener.GetComponent<Animator>().SetTrigger("Hit" ); } } public void TakeDamage (int damage, CharacterStates defener ) { int currentdamage = Mathf.Max(damage - defener.CurrentDefence, 0 ); CurrentHealth = Mathf.Max(CurrentHealth - currentdamage, 0 ); }
Partical System 粒子系统
Hirearchy -> Creat -> Effects -> Partical System
Rotation over Lifetime:旋转角度 旋转速度