Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| wiki:npc_prototyping [2022/08/16 00:00] – [NavMesh Agent AI] admin | wiki:npc_prototyping [2022/08/16 00:11] (current) – [NPC Prototyping] admin | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== NPC Prototyping ====== | ====== NPC Prototyping ====== | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | <file | NPC Prototyping > | ||
| + | This is the editor view of our NPC working out! | ||
| + | It's still a lot of bugs and a lot of works need to be done to optimize it, | ||
| + | but for proof of concept I think this is acceptable | ||
| + | </ | ||
| + | |||
| + | ;#; | ||
| + | :persevere: :persevere: :persevere: | ||
| + | ;#; | ||
| + | |||
| + | ====== NPC Movement Prototype ====== | ||
| {{ : | {{ : | ||
| Line 9: | Line 23: | ||
| using UnityEngine; | using UnityEngine; | ||
| using UnityEngine.AI; | using UnityEngine.AI; | ||
| + | using DG.Tweening; | ||
| + | using Micosmo.SensorToolkit; | ||
| + | using UnityEngine.Events; | ||
| + | using HighlightPlus; | ||
| + | using System; | ||
| public class ZombieNpcMovingNashMeshController : MonoBehaviour | public class ZombieNpcMovingNashMeshController : MonoBehaviour | ||
| { | { | ||
| + | private AudioSource _audioSource; | ||
| + | [SerializeField] private AudioClip _attackAudio; | ||
| + | [SerializeField] private AudioClip _dieAudio; | ||
| + | |||
| [BoxGroup(" | [BoxGroup(" | ||
| [SerializeField] private string d_tranfsormName; | [SerializeField] private string d_tranfsormName; | ||
| Line 18: | Line 41: | ||
| [BoxGroup(" | [BoxGroup(" | ||
| [SerializeField] private float d_velocity; | [SerializeField] private float d_velocity; | ||
| + | [BoxGroup(" | ||
| + | [SerializeField] private bool d_hasPath; | ||
| + | [BoxGroup(" | ||
| + | [SerializeField] private Vector3 d_velocityMagnitude; | ||
| - | [BoxGroup(" | ||
| - | [BoxGroup(" | ||
| - | [SerializeField] private List< | + | |
| - | private int checkPointIndex | + | { |
| - | private int currentIndex | + | FollowCheckPoint, |
| + | EatDeadBody, | ||
| + | Chase, | ||
| + | ChaseAttack, | ||
| + | Die, | ||
| + | Idle, | ||
| + | } | ||
| + | |||
| + | [EnumToggleButtons] [BoxGroup(" | ||
| + | [SerializeField] private ZombieBehaviour _zombieBehaviour = ZombieBehaviour.FollowCheckPoint; | ||
| + | |||
| + | public bool isDead { get; private set; } = false; | ||
| + | public bool isEatingHuman { get; private set; } = false; | ||
| + | |||
| + | [BoxGroup(" | ||
| + | [BoxGroup(" | ||
| + | |||
| + | [BoxGroup(" | ||
| + | [BoxGroup(" | ||
| + | [BoxGroup(" | ||
| + | private int _checkPointIndex | ||
| + | private int _currentIndex | ||
| + | |||
| + | [BoxGroup(" | ||
| + | [BoxGroup(" | ||
| + | [BoxGroup(" | ||
| private NavMeshAgent _navMeshAgent; | private NavMeshAgent _navMeshAgent; | ||
| private ZombieAnimationController _zombieAnimationController; | private ZombieAnimationController _zombieAnimationController; | ||
| - | + | private HighlightEffect _highlight; | |
| - | | + | private |
| + | public UnityEvent< | ||
| private void Awake() | private void Awake() | ||
| { | { | ||
| + | _health = GetComponent< | ||
| _navMeshAgent = GetComponent< | _navMeshAgent = GetComponent< | ||
| + | _highlight = GetComponent< | ||
| _zombieAnimationController = GetComponent< | _zombieAnimationController = GetComponent< | ||
| + | |||
| + | _playerTransform = GameObject.FindGameObjectWithTag(" | ||
| + | _sendEventToUI = GameObject.FindGameObjectWithTag(" | ||
| + | |||
| + | _health.sendEventOnMinusHealth.AddListener(ReceivedEventOnMinusHealth); | ||
| + | |||
| _navMeshAgent.updateRotation = false; | _navMeshAgent.updateRotation = false; | ||
| + | |||
| + | _audioSource = GetComponent< | ||
| } | } | ||
| private void Update() | 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; | ||
| + | switch (_zombieBehaviour) | ||
| + | { | ||
| + | case ZombieBehaviour.FollowCheckPoint: | ||
| + | ResetAllBoolean(); | ||
| + | BehaviourNpcFollowCheckPoint(); | ||
| + | break; | ||
| + | case ZombieBehaviour.EatDeadBody: | ||
| + | ResetAllBoolean(); | ||
| + | MakeCharacterRunning(); | ||
| + | isEatingHuman = true; | ||
| + | MoveAgentToDestination(_deadBodyTransform); | ||
| + | break; | ||
| + | case ZombieBehaviour.Chase: | ||
| + | ResetAllBoolean(); | ||
| + | MakeCharacterRunning(); | ||
| + | MoveAgentToDestination(_playerTransform); | ||
| + | break; | ||
| + | case ZombieBehaviour.ChaseAttack: | ||
| + | ResetAllBoolean(); | ||
| + | MakeCharacterPunch(); | ||
| + | MakeCharacterRunning(); | ||
| + | MoveAgentToDestination(_playerTransform); | ||
| + | break; | ||
| + | case ZombieBehaviour.Idle: | ||
| + | ResetAllBoolean(); | ||
| + | MoveAgentToDestination(this.transform); | ||
| + | break; | ||
| + | case ZombieBehaviour.Die: | ||
| + | ResetAllBoolean(); | ||
| + | isDead = true; | ||
| + | MoveAgentToDestination(this.transform); | ||
| + | break; | ||
| + | default: | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | private void UpdateDebugList() | ||
| + | { | ||
| d_tranfsormName = transform.gameObject.name; | d_tranfsormName = transform.gameObject.name; | ||
| d_remainingDistance = _navMeshAgent.remainingDistance; | d_remainingDistance = _navMeshAgent.remainingDistance; | ||
| d_velocity = _navMeshAgent.velocity.magnitude; | d_velocity = _navMeshAgent.velocity.magnitude; | ||
| + | d_hasPath = _navMeshAgent.hasPath; | ||
| + | d_velocityMagnitude = _navMeshAgent.velocity.normalized; | ||
| + | } | ||
| + | |||
| + | private void ResetAllBoolean() | ||
| + | { | ||
| + | MakeCharacterWalk(); | ||
| + | isDead = false; | ||
| + | isEatingHuman = false; | ||
| + | } | ||
| + | |||
| + | private void BehaviourNpcFollowCheckPoint() | ||
| + | { | ||
| + | if (_navMeshAgent.remainingDistance <= _navMeshAgent.stoppingDistance) | ||
| + | { | ||
| + | _currentIndex = _checkPointIndex == _checkPointTransform.Count - 1 ? _checkPointIndex = 0 : _checkPointIndex += 1; | ||
| + | } | ||
| + | |||
| + | MoveAgentToDestination(_checkPointTransform[_currentIndex]); | ||
| + | } | ||
| + | |||
| + | private void MoveAgentToDestination(Transform transform) | ||
| + | { | ||
| + | _navMeshAgent.destination = transform.position; | ||
| if (_navMeshAgent.velocity.sqrMagnitude > Mathf.Epsilon) | if (_navMeshAgent.velocity.sqrMagnitude > Mathf.Epsilon) | ||
| { | { | ||
| - | this.transform.rotation = Quaternion.LookRotation(_navMeshAgent.velocity.normalized); | + | |
| + | |||
| + | Vector3 vector3 = new Vector3(_navMeshAgent.velocity.normalized.x, | ||
| + | this.transform.rotation = Quaternion.LookRotation(vector3, | ||
| } | } | ||
| } | } | ||
| - | | + | |
| - | | + | |
| + | | ||
| + | |||
| + | private void MakeCharacterPunch() => _zombieAnimationController.PlayAction(_zombieAnimationController.attack); | ||
| + | |||
| + | public bool DetectPlayerOnLongAgro() | ||
| { | { | ||
| - | | + | |
| + | { | ||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| } | } | ||
| - | | + | public |
| - | | + | |
| { | { | ||
| - | | + | |
| + | { | ||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| } | } | ||
| - | | + | public |
| - | | + | |
| { | { | ||
| - | | + | |
| + | { | ||
| + | |||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| } | } | ||
| + | |||
| + | #region ENUM SETTER | ||
| + | public void SetBehaviourFollowCheckPoint() => _zombieBehaviour = ZombieBehaviour.FollowCheckPoint; | ||
| + | public void SetBehaviourEatBody() => _zombieBehaviour = ZombieBehaviour.EatDeadBody; | ||
| + | public void SetBehaviourChase() => _zombieBehaviour = ZombieBehaviour.Chase; | ||
| + | |||
| + | public void SetBehaviourChaseAttack() | ||
| + | { | ||
| + | _zombieBehaviour = ZombieBehaviour.ChaseAttack; | ||
| + | transform.LookAt(_playerTransform, | ||
| + | } | ||
| + | |||
| + | public void SetBehaviourDie() => _zombieBehaviour = ZombieBehaviour.Die; | ||
| + | public void SetBehaviourIdle() => _zombieBehaviour = ZombieBehaviour.Idle; | ||
| + | #endregion | ||
| + | |||
| + | #region EVENT RECEIVED | ||
| + | private void ReceivedEventOnMinusHealth() | ||
| + | { | ||
| + | _highlight.HitFX(); | ||
| + | } | ||
| + | |||
| + | public void HitPlayerIfDetected() | ||
| + | { | ||
| + | |||
| + | _audioSource.PlayOneShot(_attackAudio); | ||
| + | |||
| + | try | ||
| + | { | ||
| + | if (_bodyRangeSensor.GetNearestDetection().TryGetComponent(out ZombieHealth health)) | ||
| + | { | ||
| + | if (health == null) return; | ||
| + | health.MinusHealth(10f); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | catch (NullReferenceException) | ||
| + | { | ||
| + | //TODO FIX THIS NULL EXCEPTION! | ||
| + | } | ||
| + | |||
| + | } | ||
| + | #endregion | ||
| + | |||
| + | |||
| } | } | ||
| Line 87: | Line 270: | ||
| <code CSharp | ZombieAnimationController Script> | <code CSharp | ZombieAnimationController Script> | ||
| - | using Animancer; | ||
| - | using Sirenix.OdinInspector; | ||
| using System.Collections; | using System.Collections; | ||
| using System.Collections.Generic; | using System.Collections.Generic; | ||
| using UnityEngine; | using UnityEngine; | ||
| - | using UnityEngine.AI; | + | using DG.Tweening; |
| + | using Micosmo.SensorToolkit; | ||
| + | using Sirenix.OdinInspector; | ||
| - | public class ZombieAnimationController | + | public class ZombieNpcMovingController |
| { | { | ||
| - | | + | public |
| - | [SerializeField] | + | private |
| - | | + | [SerializeField] private |
| - | [SerializeField] public ClipTransition runFoward; | + | |
| - | [SerializeField] public ClipTransition attack; | + | |
| - | [SerializeField] | + | |
| - | [SerializeField] private | + | |
| - | private | + | private |
| - | private | + | private |
| + | private AudioSource _audioSource; | ||
| - | //private CharacterController _characterController; | ||
| - | private NavMeshAgent _navMeshAgent; | ||
| - | private void Awake() | + | |
| + | [BoxGroup(" | ||
| + | |||
| + | private ISteeringSensor _steering; | ||
| + | private SteeringSensor _steeringSensor; | ||
| + | |||
| + | |||
| + | | ||
| { | { | ||
| Initialization(); | Initialization(); | ||
| - | | + | |
| } | } | ||
| private void Initialization() | private void Initialization() | ||
| { | { | ||
| - | | + | |
| - | | + | _steeringSensor = GetComponentInChildren< |
| - | | + | |
| + | | ||
| + | | ||
| } | } | ||
| - | | + | void Update() |
| { | { | ||
| - | | + | |
| - | _upperBodyLayer = _Animancer.Layers[1]; | + | |
| - | + | ||
| - | _baseLayer.SetMask(_baseBody); | + | |
| - | _upperBodyLayer.SetMask(_upperBody); | + | |
| - | attack.Events.OnEnd = OnActionEnd; | + | |
| } | } | ||
| - | private void Update() | + | private void MoveToCheckPoint() |
| { | { | ||
| - | | + | if (_steering.IsDestinationReached) |
| - | if (_navMeshAgent.velocity.magnitude > 1f) | + | |
| { | { | ||
| - | | + | |
| - | } | + | |
| - | //if (_characterController.velocity.magnitude > 2f) | + | _steering.Seek.DestinationTransform = checkPoint[index]; |
| - | if(_navMeshAgent.velocity.magnitude > 2f) | + | |
| - | { | + | |
| - | _baseLayer.Play(runFoward, | + | |
| } | } | ||
| } | } | ||
| - | | + | |
| - | public void PlayAction(ClipTransition clip) | + | public void MakeCharacterWalk() |
| { | { | ||
| - | | + | |
| - | + | ||
| - | _upperBodyLayer.Play(clip); | + | |
| - | + | ||
| - | StartCoroutine(PlayActionDelay(clip.Clip.length)); | + | |
| } | } | ||
| - | | + | |
| + | public void MakeCharacterRunning() | ||
| { | { | ||
| - | | + | |
| - | yield return new WaitForSeconds(length); | + | |
| - | _canBePlayed = true; | + | |
| } | } | ||
| - | | + | |
| + | public | ||
| { | { | ||
| - | | + | |
| + | _audioSource.PlayOneShot(_attackAudio); | ||
| } | } | ||
| } | } | ||