2023-08-02 07:45:11 +00:00
|
|
|
using System;
|
2023-08-15 20:36:04 +00:00
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
2023-08-21 18:08:11 +00:00
|
|
|
using System.Linq;
|
2023-08-15 20:36:04 +00:00
|
|
|
using Sirenix.OdinInspector;
|
2023-08-29 03:41:24 +00:00
|
|
|
using Unity.VisualScripting;
|
2023-08-02 07:45:11 +00:00
|
|
|
using UnityEngine;
|
2023-08-08 07:53:35 +00:00
|
|
|
using UnityEngine.AI;
|
2023-08-03 03:38:50 +00:00
|
|
|
using Random = UnityEngine.Random;
|
2023-08-02 07:45:11 +00:00
|
|
|
|
|
|
|
// ReSharper disable once CheckNamespace
|
2023-08-03 05:49:05 +00:00
|
|
|
namespace BlueWaterProject
|
2023-08-02 07:45:11 +00:00
|
|
|
{
|
2023-08-29 03:41:24 +00:00
|
|
|
public enum AiType
|
|
|
|
{
|
|
|
|
NONE = -1,
|
|
|
|
PLAYER,
|
|
|
|
PIRATE,
|
|
|
|
ENEMY
|
|
|
|
}
|
|
|
|
|
2023-08-09 07:44:09 +00:00
|
|
|
public enum AttackerType
|
|
|
|
{
|
2023-08-28 19:52:23 +00:00
|
|
|
NONE = -1,
|
2023-08-21 18:08:11 +00:00
|
|
|
OFFENSE,
|
|
|
|
DEFENSE
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum OffenseType
|
|
|
|
{
|
2023-08-28 19:52:23 +00:00
|
|
|
NONE = -1,
|
2023-08-21 18:08:11 +00:00
|
|
|
NORMAL,
|
|
|
|
ONLY_HOUSE
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum DefenseType
|
|
|
|
{
|
2023-08-28 19:52:23 +00:00
|
|
|
NONE = -1,
|
2023-08-29 03:41:24 +00:00
|
|
|
NORMAL
|
2023-08-09 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
2023-08-02 07:45:11 +00:00
|
|
|
[Serializable]
|
2023-08-21 18:08:11 +00:00
|
|
|
public class AiController : MonoBehaviour, IDamageable, IFieldOfView, IAiMover
|
2023-08-02 07:45:11 +00:00
|
|
|
{
|
2023-08-03 03:38:50 +00:00
|
|
|
#region Property and variable
|
2023-08-21 18:08:11 +00:00
|
|
|
|
|
|
|
[Title("AiType")]
|
|
|
|
[EnableIf("alwaysFalse")]
|
|
|
|
[EnumToggleButtons]
|
|
|
|
[SerializeField] protected AttackerType attackerType;
|
2023-08-23 02:09:21 +00:00
|
|
|
private bool alwaysFalse;
|
2023-08-21 18:08:11 +00:00
|
|
|
|
|
|
|
[EnableIf("alwaysFalse")]
|
|
|
|
[ShowIf("attackerType", AttackerType.OFFENSE)]
|
|
|
|
[SerializeField] private OffenseType offenseType;
|
|
|
|
|
|
|
|
[EnableIf("alwaysFalse")]
|
|
|
|
[ShowIf("attackerType", AttackerType.DEFENSE)]
|
|
|
|
[SerializeField] private DefenseType defenseType;
|
2023-08-02 07:45:11 +00:00
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
[Title("Skin")]
|
|
|
|
[Tooltip("SkinnedMeshRenderer, MeshRenderer의 Material을 모두 담고 있는 리스트")]
|
|
|
|
[SerializeField] protected List<Material> skinMaterialList = new(10);
|
|
|
|
|
|
|
|
[Tooltip("캐릭터 외곽선의 기본 색상")]
|
|
|
|
[SerializeField] protected Color defaultSkinColor = Color.black;
|
|
|
|
|
|
|
|
[Tooltip("캐릭터에 마우스 커서가 올라가 있을 때 색상")]
|
|
|
|
[SerializeField] protected Color mouseEnterHighlightSkinColor = Color.white;
|
|
|
|
|
|
|
|
[Tooltip("캐릭터가 선택되었을 때 색상")]
|
|
|
|
[SerializeField] protected Color selectedSkinColor = Color.blue;
|
2023-08-21 18:08:11 +00:00
|
|
|
|
|
|
|
protected bool isAttacking;
|
2023-08-17 07:57:46 +00:00
|
|
|
private Vector3 commandedPos;
|
2023-08-08 07:53:35 +00:00
|
|
|
|
2023-08-29 03:41:24 +00:00
|
|
|
protected Transform backpackContainer;
|
|
|
|
protected Transform leftWeaponContainer;
|
|
|
|
protected Transform leftShieldContainer;
|
|
|
|
protected Transform headContainer;
|
|
|
|
protected Transform rightWeaponContainer;
|
|
|
|
protected Transform bodyContainer;
|
|
|
|
protected Transform flagContainer;
|
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
public IslandInfo IslandInfo { get; set; }
|
2023-08-03 05:32:17 +00:00
|
|
|
protected Animator aiAnimator;
|
2023-08-08 07:53:35 +00:00
|
|
|
protected NavMeshAgent navMeshAgent;
|
2023-08-17 07:57:46 +00:00
|
|
|
private UnitController myUnitController;
|
2023-08-15 20:36:04 +00:00
|
|
|
private UnitController mouseEnterUnitController;
|
|
|
|
private UnitSelection unitSelection;
|
2023-08-17 07:57:46 +00:00
|
|
|
private CapsuleCollider myCollider;
|
|
|
|
private CapsuleCollider hitBoxCollider;
|
2023-08-28 19:52:23 +00:00
|
|
|
protected CloseWeapon closeWeapon;
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
private static readonly int SpeedHash = Animator.StringToHash("Speed");
|
|
|
|
protected static readonly int AttackHash = Animator.StringToHash("Attack");
|
|
|
|
private static readonly int DamageHash = Animator.StringToHash("TakeDamage");
|
|
|
|
private static readonly int DeathTypeHash = Animator.StringToHash("DeathType");
|
|
|
|
private static readonly int DeathHash = Animator.StringToHash("Death");
|
2023-08-17 02:56:45 +00:00
|
|
|
private static readonly int ShieldHash = Animator.StringToHash("Shield");
|
2023-08-15 23:34:55 +00:00
|
|
|
private static readonly int OutlineColorHash = Shader.PropertyToID("_OutlineColor");
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
protected static readonly WaitForSeconds FindTargetWaitTime = new(0.5f);
|
2023-08-02 07:45:11 +00:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Unity built-in function
|
|
|
|
|
2023-08-17 01:48:13 +00:00
|
|
|
#if UNITY_EDITOR
|
2023-08-21 18:08:11 +00:00
|
|
|
protected virtual void OnDrawGizmosSelected()
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
|
|
|
DrawGizmosInFieldOfView();
|
|
|
|
}
|
2023-08-17 01:48:13 +00:00
|
|
|
#endif
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-03 08:00:14 +00:00
|
|
|
protected virtual void Awake()
|
2023-08-02 07:45:11 +00:00
|
|
|
{
|
2023-08-15 20:36:04 +00:00
|
|
|
FindMaterial();
|
2023-08-30 02:10:16 +00:00
|
|
|
InitComponent();
|
2023-08-02 07:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void Start()
|
|
|
|
{
|
2023-08-30 02:10:16 +00:00
|
|
|
InitStart();
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
switch (attackerType)
|
|
|
|
{
|
|
|
|
case AttackerType.NONE:
|
|
|
|
break;
|
|
|
|
case AttackerType.OFFENSE:
|
|
|
|
StartCoroutine(nameof(FindTargetInOffense));
|
|
|
|
break;
|
|
|
|
case AttackerType.DEFENSE:
|
|
|
|
StartCoroutine(nameof(FindTarget));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
}
|
2023-08-15 20:36:04 +00:00
|
|
|
Attack();
|
2023-08-03 03:38:50 +00:00
|
|
|
}
|
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
private void OnDisable()
|
|
|
|
{
|
|
|
|
RemoveIslandInfo();
|
|
|
|
StopAllCoroutines();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
{
|
|
|
|
aiAnimator.SetFloat(SpeedHash, navMeshAgent.velocity.normalized.magnitude);
|
|
|
|
}
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
private void FixedUpdate()
|
2023-08-08 07:53:35 +00:00
|
|
|
{
|
2023-08-15 20:36:04 +00:00
|
|
|
UpdateLookAtTarget();
|
2023-08-15 23:34:55 +00:00
|
|
|
UpdateMovement();
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void OnMouseEnter()
|
|
|
|
{
|
|
|
|
mouseEnterUnitController = gameObject.GetComponentInParent<UnitController>();
|
|
|
|
|
|
|
|
if (mouseEnterUnitController == unitSelection.SelectedUnitController) return;
|
|
|
|
|
2023-08-28 19:52:23 +00:00
|
|
|
foreach (var soldier in mouseEnterUnitController.unit.UnitList)
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
|
|
|
soldier.MouseEnterHighlight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnMouseExit()
|
|
|
|
{
|
|
|
|
if (!mouseEnterUnitController || mouseEnterUnitController == unitSelection.SelectedUnitController) return;
|
|
|
|
|
2023-08-28 19:52:23 +00:00
|
|
|
foreach (var soldier in mouseEnterUnitController.unit.UnitList)
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
|
|
|
soldier.ResetHighlight();
|
|
|
|
}
|
|
|
|
|
|
|
|
mouseEnterUnitController = null;
|
2023-08-08 07:53:35 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 03:38:50 +00:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region interface property and function
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
#region IAiStat
|
|
|
|
|
|
|
|
[field: Space(10f)]
|
|
|
|
[field: Title("AiStat")]
|
2023-08-03 03:38:50 +00:00
|
|
|
[field: SerializeField] public AiStat AiStat { get; set; } = new();
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-28 19:52:23 +00:00
|
|
|
public float GetCurrentHp() => AiStat.CurrentHp;
|
|
|
|
public void SetCurrentHp(float value) => AiStat.CurrentHp = value;
|
2023-08-09 07:44:09 +00:00
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
public void TakeDamage(AiStat attacker, AiStat defender, Vector3? attackPos = null)
|
2023-08-03 03:38:50 +00:00
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
if (!TargetTransform && attackPos != null)
|
|
|
|
{
|
2023-08-21 18:08:11 +00:00
|
|
|
//BeAttackedMovement((Vector3)attackPos);
|
2023-08-17 07:57:46 +00:00
|
|
|
}
|
|
|
|
|
2023-08-03 03:38:50 +00:00
|
|
|
// 회피 성공 체크
|
2023-08-28 19:52:23 +00:00
|
|
|
if (Random.Range(0, 100) < defender.AvoidanceRate)
|
2023-08-03 03:38:50 +00:00
|
|
|
{
|
|
|
|
// TODO : 회피 처리
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var finalDamage = Utils.CalcDamage(attacker, defender);
|
|
|
|
|
|
|
|
// 방패 막기 체크
|
|
|
|
if (finalDamage == 0f)
|
|
|
|
{
|
2023-08-17 02:56:45 +00:00
|
|
|
aiAnimator.SetTrigger(ShieldHash);
|
2023-08-03 03:38:50 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-08-28 19:52:23 +00:00
|
|
|
var changeHp = Mathf.Max(defender.CurrentHp - finalDamage, 0);
|
2023-08-03 03:38:50 +00:00
|
|
|
SetCurrentHp(changeHp);
|
|
|
|
|
|
|
|
// 죽었는지 체크
|
|
|
|
if (changeHp == 0f)
|
|
|
|
{
|
2023-08-21 18:08:11 +00:00
|
|
|
RemoveIslandInfo();
|
|
|
|
|
|
|
|
StopAllCoroutines();
|
|
|
|
navMeshAgent.enabled = false;
|
2023-08-17 07:57:46 +00:00
|
|
|
myCollider.enabled = false;
|
|
|
|
hitBoxCollider.enabled = false;
|
|
|
|
|
2023-08-08 07:53:35 +00:00
|
|
|
var randomValue = Random.Range(0, 2);
|
|
|
|
aiAnimator.SetInteger(DeathTypeHash, randomValue);
|
|
|
|
|
2023-08-03 03:38:50 +00:00
|
|
|
// TODO : 죽었을 때 처리(죽는 애니메이션 이후 사라지는 효과 등)
|
|
|
|
aiAnimator.SetTrigger(DeathHash);
|
2023-08-08 07:53:35 +00:00
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
Invoke(nameof(DestroyObject), 3f);
|
2023-08-03 03:38:50 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-08-02 07:45:11 +00:00
|
|
|
|
2023-08-03 03:38:50 +00:00
|
|
|
aiAnimator.SetTrigger(DamageHash);
|
2023-08-02 07:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
#region IFieldOfView
|
|
|
|
|
|
|
|
[field: Space(10f)]
|
|
|
|
[field: Title("FieldOfView")]
|
|
|
|
[field: SerializeField] public bool IsDrawGizmosInFieldOfView { get; set; } = true;
|
|
|
|
|
|
|
|
[field: SerializeField] public LayerMask TargetLayer { get; set; }
|
|
|
|
|
|
|
|
[field: SerializeField] public float ViewRadius { get; set; }
|
|
|
|
|
|
|
|
[field: SerializeField] public Collider[] ColliderWithinRange { get; set; } = new Collider[TARGET_MAX_SIZE];
|
2023-08-21 18:08:11 +00:00
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
[field: SerializeField] public IAiStat IaiStat { get; set; }
|
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
[field: SerializeField] public Transform TargetTransform { get; set; }
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
|
|
|
|
private const int TARGET_MAX_SIZE = 30;
|
2023-08-17 01:48:13 +00:00
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
public void DrawGizmosInFieldOfView()
|
|
|
|
{
|
|
|
|
if (!IsDrawGizmosInFieldOfView) return;
|
|
|
|
|
|
|
|
var myPos = transform.position;
|
|
|
|
|
|
|
|
Gizmos.color = Color.green;
|
|
|
|
Gizmos.DrawWireSphere(myPos, ViewRadius);
|
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
if (!TargetTransform) return;
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
Debug.DrawLine(myPos, TargetTransform.position, Color.red);
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public IEnumerator FindTarget()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
Array.Clear(ColliderWithinRange, 0, TARGET_MAX_SIZE);
|
|
|
|
|
|
|
|
var myPos = transform.position;
|
|
|
|
var maxColliderCount = Physics.OverlapSphereNonAlloc(myPos, ViewRadius, ColliderWithinRange,
|
|
|
|
TargetLayer, QueryTriggerInteraction.Collide);
|
|
|
|
|
|
|
|
if (maxColliderCount <= 0)
|
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
TargetTransform = null;
|
2023-08-15 20:36:04 +00:00
|
|
|
yield return FindTargetWaitTime;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
var nearestDistance = Mathf.Infinity;
|
|
|
|
Transform nearestTargetTransform = null;
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
for (var i = 0; i < maxColliderCount; i++)
|
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
var distanceToTarget = Vector3.Distance(transform.position, ColliderWithinRange[i].transform.position);
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
if (distanceToTarget >= nearestDistance) continue;
|
|
|
|
|
|
|
|
nearestDistance = distanceToTarget;
|
|
|
|
nearestTargetTransform = ColliderWithinRange[i].transform;
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
TargetTransform = nearestTargetTransform;
|
2023-08-15 20:36:04 +00:00
|
|
|
|
|
|
|
yield return FindTargetWaitTime;
|
|
|
|
}
|
|
|
|
}
|
2023-08-21 18:08:11 +00:00
|
|
|
|
|
|
|
public IEnumerator FindTargetInOffense()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (CanAttack())
|
|
|
|
{
|
|
|
|
yield return FindTargetWaitTime;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (offenseType)
|
|
|
|
{
|
|
|
|
case OffenseType.NONE:
|
|
|
|
break;
|
|
|
|
case OffenseType.NORMAL:
|
|
|
|
if (IslandInfo.EnemyList.Count > 0)
|
|
|
|
{
|
|
|
|
SetNearestTargetDestination(IslandInfo.EnemyList);
|
|
|
|
}
|
|
|
|
else if (IslandInfo.HouseList.Count > 0)
|
|
|
|
{
|
|
|
|
SetNearestTargetDestination(IslandInfo.HouseList);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OffenseType.ONLY_HOUSE:
|
|
|
|
if (navMeshAgent.pathStatus == NavMeshPathStatus.PathPartial)
|
|
|
|
{
|
|
|
|
SetNearestTargetDestination(IslandInfo.TargetAllList);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IslandInfo.HouseList.Count > 0)
|
|
|
|
{
|
|
|
|
SetNearestTargetDestination(IslandInfo.HouseList);
|
|
|
|
}
|
|
|
|
else if (IslandInfo.EnemyList.Count > 0)
|
|
|
|
{
|
|
|
|
SetNearestTargetDestination(IslandInfo.EnemyList);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
}
|
|
|
|
|
|
|
|
yield return FindTargetWaitTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SetNearestTargetDestination<T>(List<T> targetList)
|
|
|
|
{
|
|
|
|
if (targetList.Count <= 0) return;
|
|
|
|
|
|
|
|
var nearestTarget = targetList.OrderBy(t =>
|
|
|
|
{
|
|
|
|
var targetTransform = (Transform)(object)t;
|
|
|
|
var targetCollider = targetTransform.GetComponent<Collider>();
|
|
|
|
|
|
|
|
if (!targetCollider)
|
|
|
|
{
|
|
|
|
return float.MaxValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var closestPoint = targetCollider.ClosestPoint(transform.position);
|
|
|
|
return Vector3.Distance(transform.position, closestPoint);
|
|
|
|
})
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
if (nearestTarget == null) return;
|
|
|
|
|
|
|
|
TargetTransform = (Transform)(object)nearestTarget;
|
|
|
|
navMeshAgent.SetDestination(TargetTransform.position);
|
|
|
|
}
|
2023-08-15 20:36:04 +00:00
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
public virtual void UpdateLookAtTarget()
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
2023-08-21 18:08:11 +00:00
|
|
|
if (CanAttack())
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
|
|
|
navMeshAgent.updateRotation = false;
|
|
|
|
|
2023-08-17 07:57:46 +00:00
|
|
|
var targetPos = TargetTransform.position;
|
2023-08-15 20:36:04 +00:00
|
|
|
targetPos.y = transform.position.y;
|
|
|
|
transform.LookAt(targetPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
navMeshAgent.updateRotation = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
#region IAiMover
|
|
|
|
|
|
|
|
[field: Space(10f)]
|
2023-08-16 05:40:33 +00:00
|
|
|
[field: Title("AiMover")]
|
2023-08-17 07:57:46 +00:00
|
|
|
[field: SerializeField] public MoveType AttackMoveType { get; set; }
|
|
|
|
|
|
|
|
[field: SerializeField] public MoveType BeAttackedMoveType { get; set; }
|
2023-08-15 23:34:55 +00:00
|
|
|
|
|
|
|
[field: SerializeField] public bool IsCommanded { get; set; }
|
|
|
|
|
|
|
|
public void UpdateMovement()
|
|
|
|
{
|
2023-08-21 18:08:11 +00:00
|
|
|
// if (IsCommanded)
|
|
|
|
// {
|
|
|
|
// if (navMeshAgent.destination == commandedPos)
|
|
|
|
// {
|
|
|
|
// if (navMeshAgent.remainingDistance <= navMeshAgent.stoppingDistance)
|
|
|
|
// {
|
|
|
|
// IsCommanded = false;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// if (isAttacking) return;
|
|
|
|
//
|
|
|
|
// navMeshAgent.SetDestination(commandedPos);
|
|
|
|
// }
|
|
|
|
// }
|
2023-08-17 07:57:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void BeAttackedMovement(Vector3 attackPos)
|
|
|
|
{
|
|
|
|
if (TargetTransform) return;
|
|
|
|
|
|
|
|
switch (BeAttackedMoveType)
|
|
|
|
{
|
|
|
|
case MoveType.NONE:
|
|
|
|
break;
|
|
|
|
case MoveType.FIXED:
|
|
|
|
break;
|
|
|
|
case MoveType.MOVE:
|
2023-08-28 19:52:23 +00:00
|
|
|
if (Vector3.Distance(transform.position, attackPos) > AiStat.AtkRange)
|
2023-08-17 07:57:46 +00:00
|
|
|
{
|
|
|
|
myUnitController.MoveCommand(attackPos);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
2023-08-15 23:34:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void MoveTarget(Vector3 targetPos)
|
|
|
|
{
|
|
|
|
IsCommanded = true;
|
2023-08-17 07:57:46 +00:00
|
|
|
commandedPos = targetPos;
|
2023-08-15 23:34:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
#endregion
|
|
|
|
|
2023-08-03 08:00:14 +00:00
|
|
|
#region Custom function
|
2023-08-21 18:08:11 +00:00
|
|
|
|
2023-08-30 02:10:16 +00:00
|
|
|
private void InitComponent()
|
|
|
|
{
|
|
|
|
backpackContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Bip001/Bip001 Pelvis/Bip001 Spine/Backpack_container"));
|
|
|
|
leftWeaponContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 L Clavicle/Bip001 L UpperArm/Bip001 L Forearm/Bip001 L Hand/L_hand_container"));
|
|
|
|
leftShieldContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 L Clavicle/Bip001 L UpperArm/Bip001 L Forearm/Bip001 L Hand/L_shield_container"));
|
|
|
|
headContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Neck/Bip001 Head/Head_container"));
|
|
|
|
rightWeaponContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 R Clavicle/Bip001 R UpperArm/Bip001 R Forearm/Bip001 R Hand/R_hand_container"));
|
|
|
|
bodyContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Body_container"));
|
|
|
|
flagContainer = Utils.GetComponentAndAssert<Transform>(transform.
|
|
|
|
Find("Flag_container"));
|
|
|
|
|
|
|
|
aiAnimator = Utils.GetComponentAndAssert<Animator>(transform);
|
|
|
|
navMeshAgent = Utils.GetComponentAndAssert<NavMeshAgent>(transform);
|
|
|
|
myUnitController = Utils.GetComponentAndAssert<UnitController>(transform.parent);
|
|
|
|
myCollider = Utils.GetComponentAndAssert<CapsuleCollider>(transform);
|
|
|
|
hitBoxCollider = Utils.GetComponentAndAssert<CapsuleCollider>(transform.Find("HitBox"));
|
|
|
|
unitSelection = FindObjectOfType<UnitSelection>();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void InitStart()
|
|
|
|
{
|
|
|
|
InitViewModel();
|
|
|
|
SetLayer();
|
|
|
|
SetCurrentHp(AiStat.MaxHp);
|
|
|
|
SetMoveSpeed(AiStat.MoveSpd);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void InitViewModel()
|
2023-08-29 03:41:24 +00:00
|
|
|
{
|
|
|
|
SetActiveViewModel(backpackContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).Backpack);
|
|
|
|
SetActiveViewModel(leftWeaponContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).LeftWeapon);
|
|
|
|
SetActiveViewModel(leftShieldContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).LeftShield);
|
|
|
|
SetActiveViewModel(headContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).Head);
|
|
|
|
SetActiveViewModel(rightWeaponContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).RightWeapon);
|
|
|
|
SetActiveViewModel(bodyContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).Body);
|
|
|
|
SetActiveViewModel(flagContainer, DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).Flag);
|
|
|
|
|
|
|
|
if (DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).RightWeapon == -1) return;
|
|
|
|
|
|
|
|
closeWeapon = rightWeaponContainer.GetChild(DataManager.Inst.GetAiViewDictionaryKey(AiStat.ViewIdx).RightWeapon).AddComponent<CloseWeapon>();
|
|
|
|
closeWeapon.gameObject.layer = LayerMask.NameToLayer("Weapon");
|
|
|
|
closeWeapon.SetAttackerType(attackerType);
|
|
|
|
}
|
|
|
|
|
2023-08-30 02:10:16 +00:00
|
|
|
#if UNITY_EDITOR
|
|
|
|
public void InitStartInEditor()
|
|
|
|
{
|
|
|
|
InitComponent();
|
|
|
|
InitViewModelInEditor();
|
|
|
|
SetLayer();
|
|
|
|
SetCurrentHp(AiStat.MaxHp);
|
|
|
|
SetMoveSpeed(AiStat.MoveSpd);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void InitViewModelInEditor()
|
|
|
|
{
|
|
|
|
SetActiveViewModel(backpackContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).Backpack);
|
|
|
|
SetActiveViewModel(leftWeaponContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).LeftWeapon);
|
|
|
|
SetActiveViewModel(leftShieldContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).LeftShield);
|
|
|
|
SetActiveViewModel(headContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).Head);
|
|
|
|
SetActiveViewModel(rightWeaponContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).RightWeapon);
|
|
|
|
SetActiveViewModel(bodyContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).Body);
|
|
|
|
SetActiveViewModel(flagContainer, DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).Flag);
|
|
|
|
|
|
|
|
if (DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).RightWeapon == -1) return;
|
|
|
|
|
|
|
|
closeWeapon = rightWeaponContainer.GetChild(DataManager.Inst.GetAiViewSoKey(AiStat.ViewIdx).RightWeapon).AddComponent<CloseWeapon>();
|
|
|
|
closeWeapon.gameObject.layer = LayerMask.NameToLayer("Weapon");
|
|
|
|
closeWeapon.SetAttackerType(attackerType);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-08-29 03:41:24 +00:00
|
|
|
private void SetActiveViewModel(Transform container, int model)
|
|
|
|
{
|
|
|
|
foreach (Transform item in container)
|
|
|
|
{
|
|
|
|
if (!item.gameObject.activeSelf) continue;
|
|
|
|
|
|
|
|
item.gameObject.SetActive(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (model != -1)
|
|
|
|
{
|
|
|
|
container.GetChild(model).gameObject.SetActive(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
protected virtual void Attack()
|
|
|
|
{
|
|
|
|
StartCoroutine(nameof(AttackAnimation));
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator AttackAnimation()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (!CanAttack())
|
|
|
|
{
|
|
|
|
isAttacking = false;
|
|
|
|
yield return FindTargetWaitTime;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
isAttacking = true;
|
2023-08-28 19:52:23 +00:00
|
|
|
closeWeapon.SetIsAttacked(false);
|
|
|
|
closeWeapon.SetAttackerStat(AiStat);
|
2023-08-21 18:08:11 +00:00
|
|
|
aiAnimator.SetTrigger(AttackHash);
|
|
|
|
|
|
|
|
while (isAttacking)
|
|
|
|
{
|
|
|
|
yield return null;
|
|
|
|
}
|
|
|
|
|
2023-08-28 19:52:23 +00:00
|
|
|
yield return new WaitForSeconds(AiStat.AtkCooldown);
|
2023-08-21 18:08:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual bool CanAttack()
|
|
|
|
{
|
|
|
|
if (!TargetTransform || !IslandInfo.TargetAllList.Contains(TargetTransform)) return false;
|
|
|
|
|
|
|
|
var targetInAttackRange = Vector3.Distance(transform.position, TargetTransform.position) <=
|
2023-08-28 19:52:23 +00:00
|
|
|
AiStat.AtkRange;
|
2023-08-21 18:08:11 +00:00
|
|
|
|
|
|
|
if (targetInAttackRange)
|
|
|
|
{
|
|
|
|
SetAgentIsStopped(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetAgentIsStopped(false);
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-15 23:34:55 +00:00
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
private void FindMaterial()
|
|
|
|
{
|
|
|
|
var skinnedMeshRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
|
|
|
|
var meshRenderers = GetComponentsInChildren<MeshRenderer>();
|
|
|
|
|
|
|
|
foreach (var skin in skinnedMeshRenderers)
|
|
|
|
{
|
|
|
|
if (!skin.gameObject.activeSelf) continue;
|
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
skinMaterialList.Add(skin.material);
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var skin in meshRenderers)
|
|
|
|
{
|
|
|
|
if (!skin.gameObject.activeSelf) continue;
|
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
skinMaterialList.Add(skin.material);
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-15 23:34:55 +00:00
|
|
|
private void SetOutlineColor(Color color)
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
2023-08-15 23:34:55 +00:00
|
|
|
foreach (var skin in skinMaterialList)
|
2023-08-15 20:36:04 +00:00
|
|
|
{
|
2023-08-15 23:34:55 +00:00
|
|
|
skin.SetColor(OutlineColorHash, color);
|
2023-08-15 20:36:04 +00:00
|
|
|
}
|
|
|
|
}
|
2023-08-15 23:34:55 +00:00
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
private void RemoveIslandInfo()
|
|
|
|
{
|
|
|
|
if (!IslandInfo) return;
|
|
|
|
|
|
|
|
IslandInfo.RemoveListElement(IslandInfo.EnemyList, transform);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetAgentIsStopped(bool value)
|
|
|
|
{
|
|
|
|
if (navMeshAgent.enabled)
|
|
|
|
{
|
|
|
|
navMeshAgent.isStopped = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-30 02:10:16 +00:00
|
|
|
protected virtual void SetLayer()
|
|
|
|
{
|
|
|
|
switch (AiStat.AiType)
|
|
|
|
{
|
|
|
|
case AiType.NONE:
|
|
|
|
break;
|
|
|
|
case AiType.PLAYER:
|
|
|
|
gameObject.layer = LayerMask.NameToLayer("Player");
|
|
|
|
hitBoxCollider.gameObject.layer = LayerMask.NameToLayer("Player");
|
|
|
|
TargetLayer = LayerMask.GetMask("Enemy");
|
|
|
|
break;
|
|
|
|
case AiType.PIRATE:
|
|
|
|
gameObject.layer = LayerMask.NameToLayer("Pirate");
|
|
|
|
hitBoxCollider.gameObject.layer = LayerMask.NameToLayer("Pirate");
|
|
|
|
TargetLayer = LayerMask.GetMask("Enemy");
|
|
|
|
break;
|
|
|
|
case AiType.ENEMY:
|
|
|
|
gameObject.layer = LayerMask.NameToLayer("Enemy");
|
|
|
|
hitBoxCollider.gameObject.layer = LayerMask.NameToLayer("Enemy");
|
|
|
|
TargetLayer = LayerMask.GetMask("Player") | LayerMask.GetMask("Pirate") | LayerMask.GetMask("Props");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
public void SetAttackerType(AttackerType type) => attackerType = type;
|
|
|
|
public void SetOffenseType(OffenseType type) => offenseType = type;
|
|
|
|
public void SetDefenseType(DefenseType type) => defenseType = type;
|
2023-08-17 07:57:46 +00:00
|
|
|
public void ResetHighlight() => SetOutlineColor(defaultSkinColor);
|
|
|
|
public void MouseEnterHighlight() => SetOutlineColor(mouseEnterHighlightSkinColor);
|
|
|
|
public void SelectedHighlight() => SetOutlineColor(selectedSkinColor);
|
2023-08-15 20:36:04 +00:00
|
|
|
private void DestroyObject() => Destroy(gameObject);
|
2023-08-17 07:57:46 +00:00
|
|
|
public void OnAttacking(int boolValue) => isAttacking = boolValue == 1;
|
2023-08-08 07:53:35 +00:00
|
|
|
public NavMeshAgent GetNavMeshAgent() => navMeshAgent;
|
|
|
|
public Animator GetAnimator() => aiAnimator;
|
2023-08-21 18:08:11 +00:00
|
|
|
public void SetMoveSpeed(float value) => navMeshAgent.speed = value;
|
2023-08-02 07:45:11 +00:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|