Welcome to GEMENTAR TOUR PUO V2 Development Documentation. Enjoy your stay!

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
wiki:npc_prototyping [2022/08/16 00:00] – [NavMesh Agent AI] adminwiki:npc_prototyping [2022/08/16 00:11] (current) – [NPC Prototyping] admin
Line 1: Line 1:
 ====== NPC Prototyping ====== ====== NPC Prototyping ======
 +
 +{{ :wiki:npcexampl.gif |}}
 +
 +<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 
 +</file>
 +
 +;#;
 +:persevere: :persevere: :persevere:
 +;#;
 + 
 +====== NPC Movement Prototype ======
  
 {{ :wiki:zombieprototypenpc.gif |}} {{ :wiki:zombieprototypenpc.gif |}}
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("Debug Value")]     [BoxGroup("Debug Value")]
     [SerializeField] private string d_tranfsormName;     [SerializeField] private string d_tranfsormName;
Line 18: Line 41:
     [BoxGroup("Debug Value")]     [BoxGroup("Debug Value")]
     [SerializeField] private float d_velocity;     [SerializeField] private float d_velocity;
 +    [BoxGroup("Debug Value")]
 +    [SerializeField] private bool d_hasPath;
 +    [BoxGroup("Debug Value")]
 +    [SerializeField] private Vector3 d_velocityMagnitude;
  
-    [BoxGroup("Movement Tweak")] [SerializeField] private float _walkSpeed = 1; 
-    [BoxGroup("Movement Tweak")] [SerializeField] private float _runSpeed = 3; 
  
-    [SerializeField] private List<Transform> _checkPoint; +    enum ZombieBehaviour 
-    private int checkPointIndex = 0; +    { 
-    private int currentIndex = 0;+        FollowCheckPoint, 
 +        EatDeadBody, 
 +        Chase, 
 +        ChaseAttack, 
 +        Die, 
 +        Idle, 
 +    } 
 + 
 +    [EnumToggleButtons] [BoxGroup("Behaviour Selection")] 
 +    [SerializeField] private ZombieBehaviour _zombieBehaviour = ZombieBehaviour.FollowCheckPoint; 
 + 
 +    public bool isDead { get; private set; } = false; 
 +    public bool isEatingHuman { get; private set; } = false; 
 + 
 +    [BoxGroup("Movement Tweak")] [SerializeField] public float _walkSpeed { get; private set; } = 1; 
 +    [BoxGroup("Movement Tweak")] [SerializeField] public float _runSpeed { get; private set; } = 5; 
 + 
 +    [BoxGroup("CheckPoint Transform")] [SerializeField] private List<Transform> _checkPointTransform; 
 +    [BoxGroup("CheckPoint Transform")] [SerializeField] private Transform _deadBodyTransform; 
 +    [BoxGroup("CheckPoint Transform")] [SerializeField] private Transform _playerTransform; 
 +    private int _checkPointIndex = 0; 
 +    private int _currentIndex = 0; 
 + 
 +    [BoxGroup("Sensor GameObject")] [SerializeField] private RangeSensor _bodyRangeSensor; 
 +    [BoxGroup("Sensor GameObject")] [SerializeField] private RangeSensor _shortRangeSensor; 
 +    [BoxGroup("Sensor GameObject")] [SerializeField] private RangeSensor _longRangeSensor;
  
     private NavMeshAgent _navMeshAgent;     private NavMeshAgent _navMeshAgent;
     private ZombieAnimationController _zombieAnimationController;     private ZombieAnimationController _zombieAnimationController;
- +    private HighlightEffect _highlight; 
-    [SerializeField] private Transform movePositionTransform;+    private ZombieHealth _health; 
 +    public UnityEvent<int> _sendEventToUI;
  
     private void Awake()     private void Awake()
     {     {
 +        _health = GetComponent<ZombieHealth>();
         _navMeshAgent = GetComponent<NavMeshAgent>();         _navMeshAgent = GetComponent<NavMeshAgent>();
 +        _highlight = GetComponent<HighlightEffect>();
         _zombieAnimationController = GetComponent<ZombieAnimationController>();         _zombieAnimationController = GetComponent<ZombieAnimationController>();
 +
 +        _playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
 +        _sendEventToUI = GameObject.FindGameObjectWithTag("Player").GetComponent<ZombieGunController>().sendEventToUI;
 +
 +        _health.sendEventOnMinusHealth.AddListener(ReceivedEventOnMinusHealth);
 +
         _navMeshAgent.updateRotation = false;         _navMeshAgent.updateRotation = false;
 +
 +        _audioSource = GetComponent<AudioSource>();
     }     }
  
     private void Update()     private void Update()
     {     {
-         +        UpdateBehaviour(); 
-        if (_navMeshAgent.remainingDistance <= _navMeshAgent.stoppingDistance) + 
-        { +        UpdateDebugList();
-            +
-            currentIndex = checkPointIndex == _checkPoint.Count - 1 ? checkPointIndex = 0 : checkPointIndex += 1; +
-             +
-        }+
  
-        MoveAgentToDestination(_checkPoint[currentIndex]); 
     }     }
  
-    private void MoveAgentToDestination(Transform transform)+    private bool _hadDead = false; 
 +    private void UpdateBehaviour()
     {     {
-        _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);+            //this.transform.rotation = Quaternion.LookRotation(_navMeshAgent.velocity.normalized, Vector3.up); 
 + 
 +            Vector3 vector3 = new Vector3(_navMeshAgent.velocity.normalized.x, 0f, _navMeshAgent.velocity.normalized.z); 
 +            this.transform.rotation = Quaternion.LookRotation(vector3, Vector3.up);
         }         }
     }     }
  
-    [Button] +    private void MakeCharacterWalk() =>  _navMeshAgent.speed = _walkSpeed; 
-    public void MakeCharacterWalk()+ 
 +    private void MakeCharacterRunning() => _navMeshAgent.speed = _runSpeed; 
 + 
 +    private void MakeCharacterPunch() => _zombieAnimationController.PlayAction(_zombieAnimationController.attack); 
 + 
 +    public bool DetectPlayerOnLongAgro()
     {     {
-        _navMeshAgent.speed = _walkSpeed;+        if (_longRangeSensor.GetNearestDetection() != null) 
 +        { 
 +            return true; 
 +        } 
 +        return false;
     }     }
  
-    [Button] +    public bool DetectPlayerOnShortAgro()
-    public void MakeCharacterRunning()+
     {     {
-        _navMeshAgent.speed = _runSpeed;+        if (_shortRangeSensor.GetNearestDetection() != null) 
 +        { 
 +            return true; 
 +        } 
 +        return false;
     }     }
  
-    [Button] +    public bool DetectPlayerOnBodyAgro()
-    public void MakeCharacterPunch()+
     {     {
-        _zombieAnimationController.PlayAction(_zombieAnimationController.attack);+        if (_bodyRangeSensor.GetNearestDetection() != null) 
 +        { 
 +             
 +            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, Vector3.up);
 +    }
 +
 +    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 : MonoBehaviour+public class ZombieNpcMovingController : MonoBehaviour
 { {
-    private AnimancerComponent _Animancer; +    public List<Transform> checkPoint; 
-    [SerializeField] public ClipTransition idle; +    private int checkPointIndex = 0; 
-    [SerializeField] public ClipTransition moveFoward; +    [SerializeField] private AudioClip _attackAudio;
-    [SerializeField] public ClipTransition runFoward; +
-    [SerializeField] public ClipTransition attack; +
-    [SerializeField] private AvatarMask _baseBody; +
-    [SerializeField] private AvatarMask _upperBody;+
  
-    private AnimancerLayer _baseLayer; +    private CharacterController _characterController; 
-    private AnimancerLayer _upperBodyLayer;+    private ZombieAnimationController _zombieAnimationController; 
 +    private AudioSource _audioSource;
  
-    //private CharacterController _characterController; 
-    private NavMeshAgent _navMeshAgent; 
  
-    private void Awake()+    [BoxGroup("Movement Tweak")] [SerializeField] private float _walkSpeed = 1; 
 +    [BoxGroup("Movement Tweak")] [SerializeField] private float _runSpeed = 3; 
 + 
 +    private ISteeringSensor _steering; 
 +    private SteeringSensor _steeringSensor; 
 + 
 + 
 +    private void Start()
     {     {
         Initialization();         Initialization();
  
-        InitializationAnim();+        _steeringSensor.Locomotion.MaxForwardSpeed = _walkSpeed;
     }     }
  
     private void Initialization()     private void Initialization()
     {     {
-        //_characterController = GetComponent<CharacterController>(); +        _steering = GetComponentInChildren<ISteeringSensor>(); 
-        _navMeshAgent = GetComponent<NavMeshAgent>(); +        _steeringSensor = GetComponentInChildren<SteeringSensor>(); 
-        _Animancer = GetComponentInChildren<AnimancerComponent>();+        _characterController = GetComponent<CharacterController>(); 
 +        _zombieAnimationController = GetComponent<ZombieAnimationController>(); 
 +        _audioSource = GetComponent<AudioSource>();
     }     }
  
-    private void InitializationAnim()+    void Update()
     {     {
-        _baseLayer = _Animancer.Layers[0]; +        MoveToCheckPoint();
-        _upperBodyLayer = _Animancer.Layers[1]; +
- +
-        _baseLayer.SetMask(_baseBody); +
-        _upperBodyLayer.SetMask(_upperBody); +
-        attack.Events.OnEnd = OnActionEnd;+
     }     }
  
-    private void Update()+    private void MoveToCheckPoint()
     {     {
-        //if(_characterController.velocity.magnitude > 1f) +        if (_steering.IsDestinationReached)
-        if (_navMeshAgent.velocity.magnitude > 1f)+
         {         {
-            _baseLayer.Play(moveFoward, 1f); +            int index = checkPointIndex == checkPoint.Count - 1 ? checkPointIndex = 0 : checkPointIndex += 1;
-        }+
  
-        //if (_characterController.velocity.magnitude > 2f) +            _steering.Seek.DestinationTransform = checkPoint[index];
-        if(_navMeshAgent.velocity.magnitude > 2f) +
-        { +
-            _baseLayer.Play(runFoward, 1f);+
         }         }
     }     }
  
-    bool _canBePlayed = true; +    [Button] 
-    public void PlayAction(ClipTransition clip)+    public void MakeCharacterWalk()
     {     {
-        if (!_canBePlayed) return; +        _steeringSensor.Locomotion.MaxForwardSpeed = _walkSpeed;
- +
-        _upperBodyLayer.Play(clip); +
- +
-        StartCoroutine(PlayActionDelay(clip.Clip.length));+
     }     }
  
-    private IEnumerator PlayActionDelay(float length)+    [Button] 
 +    public void MakeCharacterRunning()
     {     {
-        _canBePlayed = false; +        _steeringSensor.Locomotion.MaxForwardSpeed = _runSpeed;
-        yield return new WaitForSeconds(length); +
-        _canBePlayed = true;+
     }     }
  
-    private void OnActionEnd()+    [Button] 
 +    public void MakeCharacterPunch()
     {     {
-        _upperBodyLayer.StartFade(0, 1f);+        _zombieAnimationController.PlayAction(_zombieAnimationController.attack); 
 +        _audioSource.PlayOneShot(_attackAudio);
     }     }
 } }

QR Code
QR Code wiki:npc_prototyping (generated for current page)
Hello World!
DokuWiki with monobook... rules!