diff --git a/BlueWater/Assets/01.Scenes/02.Main_TG.unity b/BlueWater/Assets/01.Scenes/02.Main_TG.unity index cec2a979d..cfd4a877a 100644 --- a/BlueWater/Assets/01.Scenes/02.Main_TG.unity +++ b/BlueWater/Assets/01.Scenes/02.Main_TG.unity @@ -73505,7 +73505,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &187351605 Transform: m_ObjectHideFlags: 0 @@ -709237,7 +709237,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1871370245} serializedVersion: 2 - m_LocalRotation: {x: 0.67640686, y: -0.0000002980232, z: 0.0000002980232, w: 0.7365283} + m_LocalRotation: {x: 0.6764068, y: -0, z: -0, w: 0.7365283} m_LocalPosition: {x: -0.0000002384187, y: 120, z: -10.000001} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 diff --git a/BlueWater/Assets/02.Scripts/Ai/Tower/Type/ArrowTower.cs b/BlueWater/Assets/02.Scripts/Ai/Tower/Type/ArrowTower.cs index afc99df16..586e605d8 100644 --- a/BlueWater/Assets/02.Scripts/Ai/Tower/Type/ArrowTower.cs +++ b/BlueWater/Assets/02.Scripts/Ai/Tower/Type/ArrowTower.cs @@ -2,8 +2,8 @@ using System.Collections; using Sirenix.OdinInspector; using UnityEngine; +using UnityEngine.AI; using UnityEngine.Pool; -using Random = UnityEngine.Random; // ReSharper disable once CheckNamespace namespace BlueWaterProject @@ -14,6 +14,9 @@ namespace BlueWaterProject [Title("Tower Stat")] [SerializeField] private bool isDrawGizmosInFieldOfView = true; + [SerializeField] private bool isPredictAttack = true; + + [OnValueChanged(nameof(SetLayer))] [SerializeField] private EAiType aiType; [SerializeField] private float maxHp; [SerializeField] private float currentHp; @@ -25,8 +28,9 @@ namespace BlueWaterProject [Title("실시간 타겟 정보")] [SerializeField] private LayerMask targetLayer; + [SerializeField] private LayerMask arrowLayer; [SerializeField] private Collider[] colliderWithinRange; - [SerializeField] private Transform targetTransform; + [SerializeField] private Collider targetCollider; [Title("화살 오브젝트 관리")] [Tooltip("화살 오브젝트 풀링할 최대 갯수")] @@ -49,16 +53,15 @@ namespace BlueWaterProject Gizmos.color = Color.blue; Gizmos.DrawWireSphere(transform.position, atkRange); - if (!targetTransform) return; + if (!targetCollider) return; Gizmos.color = Color.red; - Gizmos.DrawLine(transform.position, targetTransform.position); + Gizmos.DrawLine(shootLocation.position, targetCollider.bounds.center); } private void Awake() { InitComponent(); - SetLayer(); } private void Start() @@ -80,7 +83,7 @@ namespace BlueWaterProject // 건물 파괴 if (changeHp == 0f) { - Destroy(gameObject); + Die(); // RemoveIslandInfo(); } } @@ -111,73 +114,113 @@ namespace BlueWaterProject colliderWithinRange = new Collider[TARGET_MAX_SIZE]; } - + private void SetLayer() { switch (aiType) { case EAiType.NONE: - print("aiType == NONE error"); + targetLayer = 0; break; case EAiType.PLAYER: case EAiType.PIRATE: - targetLayer |= LayerMask.GetMask("Enemy"); + targetLayer = LayerMask.GetMask("Enemy"); break; case EAiType.ENEMY: - targetLayer |= LayerMask.GetMask("Player") | LayerMask.GetMask("Pirate"); + targetLayer = LayerMask.GetMask("Player") | LayerMask.GetMask("Pirate"); break; default: + targetLayer = 0; + arrowLayer = 0; throw new ArgumentOutOfRangeException(); } + + arrowLayer = targetLayer | LayerMask.GetMask("Ground") | LayerMask.GetMask("Water") | + LayerMask.GetMask("Props"); } private IEnumerator Attack() { while (true) { - FindNearestTargetInRange(transform.position, atkRange); + FindNearestTargetInRange(atkRange); - if (!targetTransform) yield return new WaitForSeconds(atkCooldown); - + if (!targetCollider) + { + yield return new WaitForSeconds(atkCooldown); + continue; + } var arrow = arrowPool.Get(); - + var targetPos = isPredictAttack ? PredictTargetPosition(shootLocation, targetCollider, arrow.GetArrowSpeed()) : targetCollider.bounds.center; + arrow.SetShootingArrow(shootLocation.position, transform.position, - targetTransform.position, aiType, atk, shieldPenetrationRate, inaccuracy, false); + targetPos, aiType, atk, shieldPenetrationRate, inaccuracy, false); arrow.ShootArrowCoroutine(); + + yield return new WaitForSeconds(atkCooldown); } } - private void FindNearestTargetInRange(Vector3 centerPos, float range) + private void FindNearestTargetInRange(float range) { Array.Clear(colliderWithinRange, 0, TARGET_MAX_SIZE); - var maxColliderCount = Physics.OverlapSphereNonAlloc(centerPos, range, colliderWithinRange, + var maxColliderCount = Physics.OverlapSphereNonAlloc(transform.position, range, colliderWithinRange, targetLayer, QueryTriggerInteraction.Collide); if (maxColliderCount <= 0) { - SetTargetTransform(null); + SetTargetCollider(null); return; } var nearestDistance = Mathf.Infinity; - Transform nearestTargetTransform = null; - + Collider nearestTargetTransform = null; + for (var i = 0; i < maxColliderCount; i++) { - var distanceToTarget = Vector3.Distance(transform.position, colliderWithinRange[i].transform.position); + var distanceToTarget = Vector3.Distance(shootLocation.position, colliderWithinRange[i].bounds.center); + + if (!IsRaycastHitTarget(shootLocation.position, colliderWithinRange[i]) || distanceToTarget >= nearestDistance) continue; if (distanceToTarget >= nearestDistance) continue; nearestDistance = distanceToTarget; - nearestTargetTransform = colliderWithinRange[i].transform; + nearestTargetTransform = colliderWithinRange[i]; } - SetTargetTransform(nearestTargetTransform); + SetTargetCollider(nearestTargetTransform); + } + + private bool IsRaycastHitTarget(Vector3 startPos, Collider target) + { + var direction = ((target.bounds.center) - (startPos)).normalized; + var raycastHitTarget = Physics.Raycast(startPos, direction, out var hit, atkRange, arrowLayer, QueryTriggerInteraction.Collide); + + return raycastHitTarget && target == hit.collider; + } + + private Vector3 PredictTargetPosition(Transform shooter, Collider target, float arrowSpeed) + { + var targetVelocity = target.GetComponent().velocity; + + var targetPos = target.bounds.center; + var directionToTarget = targetPos - shooter.position; + var distanceToTarget = directionToTarget.magnitude; + var timeToImpact = distanceToTarget / arrowSpeed; + var predictedPosition = targetPos + targetVelocity * timeToImpact; + + return predictedPosition; + } + + private void Die() + { + StopAllCoroutines(); + Destroy(gameObject); } private void SetCurrentHp(float value) => currentHp = value; - private void SetTargetTransform(Transform value) => targetTransform = value; + private void SetTargetCollider(Collider value) => targetCollider = value; #endregion diff --git a/BlueWater/Assets/02.Scripts/Weapon/Arrow.cs b/BlueWater/Assets/02.Scripts/Weapon/Arrow.cs index 3da6b31ee..c07f86a34 100644 --- a/BlueWater/Assets/02.Scripts/Weapon/Arrow.cs +++ b/BlueWater/Assets/02.Scripts/Weapon/Arrow.cs @@ -222,6 +222,8 @@ namespace BlueWaterProject gameObject.SetActive(false); } + public float GetArrowSpeed() => arrowSpeed; + #endregion } } \ No newline at end of file diff --git a/BlueWater/Assets/05.Prefabs/Ai/Tower/ArrowTower.prefab b/BlueWater/Assets/05.Prefabs/Ai/Tower/ArrowTower.prefab index 3da649c43..1502c67c1 100644 --- a/BlueWater/Assets/05.Prefabs/Ai/Tower/ArrowTower.prefab +++ b/BlueWater/Assets/05.Prefabs/Ai/Tower/ArrowTower.prefab @@ -42,8 +42,8 @@ GameObject: - component: {fileID: 9031747955947254101} - component: {fileID: 8726458248037506361} - component: {fileID: 8190100621632311398} - - component: {fileID: 9087576015726480415} - component: {fileID: 3766443407819787020} + - component: {fileID: 9087576015726480415} m_Layer: 16 m_Name: ArrowTower m_TagString: Tower @@ -117,34 +117,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &9087576015726480415 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8848616787261608530} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: bd421891d0204bc5a748862f0ac9c0f1, type: 3} - m_Name: - m_EditorClassIdentifier: - isDrawGizmosInFieldOfView: 1 - aiType: 0 - maxHp: 500 - currentHp: 0 - atk: 20 - atkRange: 30 - atkCooldown: 2 - shieldPenetrationRate: 50 - inaccuracy: 0 - colliderWithinRange: [] - targetTransform: {fileID: 0} - arrowMaxSize: 100 - arrowsPoolLocation: {fileID: 0} - targetLayer: - serializedVersion: 2 - m_Bits: 0 --- !u!65 &3766443407819787020 BoxCollider: m_ObjectHideFlags: 0 @@ -164,5 +136,36 @@ BoxCollider: m_ProvidesContacts: 0 m_Enabled: 1 serializedVersion: 3 - m_Size: {x: 4.0214233, y: 4.0214243, z: 8.281539} - m_Center: {x: 0, y: 0.00000011920932, z: 2.6434815} + m_Size: {x: 2.930729, y: 3.065148, z: 3.854234} + m_Center: {x: 0.07538414, y: -0.21270323, z: 0.4423293} +--- !u!114 &9087576015726480415 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8848616787261608530} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bd421891d0204bc5a748862f0ac9c0f1, type: 3} + m_Name: + m_EditorClassIdentifier: + isDrawGizmosInFieldOfView: 1 + isPredictAttack: 1 + aiType: 2 + maxHp: 500 + currentHp: 0 + atk: 20 + atkRange: 30 + atkCooldown: 1 + shieldPenetrationRate: 50 + inaccuracy: 0 + targetLayer: + serializedVersion: 2 + m_Bits: 768 + arrowLayer: + serializedVersion: 2 + m_Bits: 66328 + colliderWithinRange: [] + targetCollider: {fileID: 0} + arrowMaxSize: 100