This is an old revision of the document!
Table of Contents
NPC Prototyping
ZombieNpcMovingNashMeshController Script
using Sirenix.OdinInspector; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class ZombieNpcMovingNashMeshController : MonoBehaviour { [BoxGroup("Debug Value")] [SerializeField] private string d_tranfsormName; [BoxGroup("Debug Value")] [SerializeField] private float d_remainingDistance; [BoxGroup("Debug Value")] [SerializeField] private float d_velocity; [BoxGroup("Movement Tweak")] [SerializeField] private float _walkSpeed = 1; [BoxGroup("Movement Tweak")] [SerializeField] private float _runSpeed = 3; [SerializeField] private List<Transform> _checkPoint; private int checkPointIndex = 0; private int currentIndex = 0; private NavMeshAgent _navMeshAgent; private ZombieAnimationController _zombieAnimationController; [SerializeField] private Transform movePositionTransform; private void Awake() { _navMeshAgent = GetComponent<NavMeshAgent>(); _zombieAnimationController = GetComponent<ZombieAnimationController>(); _navMeshAgent.updateRotation = false; } private void Update() { if (_navMeshAgent.remainingDistance <= _navMeshAgent.stoppingDistance) { currentIndex = checkPointIndex == _checkPoint.Count - 1 ? checkPointIndex = 0 : checkPointIndex += 1; } MoveAgentToDestination(_checkPoint[currentIndex]); } private void MoveAgentToDestination(Transform transform) { _navMeshAgent.destination = transform.position; d_tranfsormName = transform.gameObject.name; d_remainingDistance = _navMeshAgent.remainingDistance; d_velocity = _navMeshAgent.velocity.magnitude; if (_navMeshAgent.velocity.sqrMagnitude > Mathf.Epsilon) { this.transform.rotation = Quaternion.LookRotation(_navMeshAgent.velocity.normalized); } } [Button] public void MakeCharacterWalk() { _navMeshAgent.speed = _walkSpeed; } [Button] public void MakeCharacterRunning() { _navMeshAgent.speed = _runSpeed; } [Button] public void MakeCharacterPunch() { _zombieAnimationController.PlayAction(_zombieAnimationController.attack); } }
ZombieAnimationController Script
using Animancer; using Sirenix.OdinInspector; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class ZombieAnimationController : MonoBehaviour { private AnimancerComponent _Animancer; [SerializeField] public ClipTransition idle; [SerializeField] public ClipTransition moveFoward; [SerializeField] public ClipTransition runFoward; [SerializeField] public ClipTransition attack; [SerializeField] private AvatarMask _baseBody; [SerializeField] private AvatarMask _upperBody; private AnimancerLayer _baseLayer; private AnimancerLayer _upperBodyLayer; //private CharacterController _characterController; private NavMeshAgent _navMeshAgent; private void Awake() { Initialization(); InitializationAnim(); } private void Initialization() { //_characterController = GetComponent<CharacterController>(); _navMeshAgent = GetComponent<NavMeshAgent>(); _Animancer = GetComponentInChildren<AnimancerComponent>(); } private void InitializationAnim() { _baseLayer = _Animancer.Layers[0]; _upperBodyLayer = _Animancer.Layers[1]; _baseLayer.SetMask(_baseBody); _upperBodyLayer.SetMask(_upperBody); attack.Events.OnEnd = OnActionEnd; } private void Update() { //if(_characterController.velocity.magnitude > 1f) if (_navMeshAgent.velocity.magnitude > 1f) { _baseLayer.Play(moveFoward, 1f); } //if (_characterController.velocity.magnitude > 2f) if(_navMeshAgent.velocity.magnitude > 2f) { _baseLayer.Play(runFoward, 1f); } } bool _canBePlayed = true; public void PlayAction(ClipTransition clip) { if (!_canBePlayed) return; _upperBodyLayer.Play(clip); StartCoroutine(PlayActionDelay(clip.Clip.length)); } private IEnumerator PlayActionDelay(float length) { _canBePlayed = false; yield return new WaitForSeconds(length); _canBePlayed = true; } private void OnActionEnd() { _upperBodyLayer.StartFade(0, 1f); } }
Behaviour Tree
Node CANVAS!
By using node canvas we can create a behaviour tree visually, this is the first time we used visual behaviour tree.
NPC Agro prototype
Agro
There's basically three layers of detection - outer layer when zombie heard shoots they will go agro, and this layer player need to escape from it to stop the agro. - first inner when player move close to zombie. - nearest layer for zombie detect when can it hit player.
NavMesh Agent AI
NavMesh
We use navigation mesh to map place that NPC can move along, Unity NavMesh also handle navigation.



