using System; using System.Collections; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.Serialization; // ReSharper disable once CheckNamespace namespace BlueWaterProject { [Serializable] public class ActiveSkill : MonoBehaviour { #region Properties and variables // So [Title("DataSo")] [Required("So를 추가해주세요.")] [SerializeField] private SkillIndicatorDataSo skillIndicatorDataSo; [field: SerializeField] public ActiveSkillData ActiveSkillData { get; set; } // Data [Title("Indicator Data")] [SerializeField] private DecalProjector indicator; [field: DisableIf("@true")] [field: SerializeField] public bool IsCasting { get; set; } [DisableIf("@true")] [SerializeField] private bool followMouse; private Camera mainCam; private Transform user; private Collider[] hitColliders; // Hash private static readonly int FillHash = Shader.PropertyToID("_Fill"); #endregion #region Unity built-in methods private void Awake() { InitComponent(); } private void Update() { if (followMouse) { FollowMouse(); } } #endregion #region Custorm methods private void InitComponent() { indicator = GetComponentInChildren(); if (indicator == null) { print("하위 오브젝트로 DecalProjector 컴포넌트를 찾을 수 없습니다."); } mainCam = Camera.main; } private void BasicSetting() { transform.localPosition = Vector3.zero; indicator.transform.localScale = new Vector3(ActiveSkillData.Range, ActiveSkillData.Range, 1); indicator.scaleMode = DecalScaleMode.InheritFromHierarchy; indicator.material.SetFloat(FillHash, 0f); } public void ChangeIndicatorType() { if (ActiveSkillData == null) { print("스킬의 대한 정보가 입력되지 않았습니다."); return; } HideIndicator(); BasicSetting(); hitColliders = new Collider[ActiveSkillData.MaxAttackTargets]; if (ActiveSkillData.Indicator != null) { indicator.material = ActiveSkillData.Indicator; return; } switch (ActiveSkillData.IndicatorType) { case EIndicatorType.NONE: indicator.material = null; break; case EIndicatorType.RADIUS: indicator.material = new Material(skillIndicatorDataSo.RadiusIndicator); break; case EIndicatorType.AREA: indicator.material = new Material(skillIndicatorDataSo.AreaIndicator); break; case EIndicatorType.CONE: indicator.material = new Material(skillIndicatorDataSo.ConeIndicator); break; case EIndicatorType.LINE: indicator.material = new Material(skillIndicatorDataSo.LineIndicator); break; default: throw new ArgumentOutOfRangeException(); } } private void HideIndicator() { indicator.enabled = false; indicator.material.SetFloat(FillHash, 0); IsCasting = false; followMouse = false; } public IEnumerator ShowIndicator() { indicator.transform.position = user.position; indicator.material.SetFloat(FillHash, 0); indicator.enabled = true; while (true) { indicator.transform.position = user.position; yield return null; } } private void InterruptIndicator() { HideIndicator(); } public void Execute(LayerMask targetLayer, Vector3 targetPos) { switch (ActiveSkillData.IndicatorType) { case EIndicatorType.NONE: indicator.material = null; break; case EIndicatorType.RADIUS: StartCoroutine(RadiusSkill(targetLayer, targetPos)); break; case EIndicatorType.AREA: StartCoroutine(AreaSkill(targetLayer, targetPos)); break; case EIndicatorType.CONE: break; case EIndicatorType.LINE: break; default: throw new ArgumentOutOfRangeException(); } } private IEnumerator RadiusSkill(LayerMask targetLayer, Vector3 targetPos) { indicator.transform.position = targetPos; indicator.enabled = true; if (ActiveSkillData.CastingTime > 0) { IsCasting = true; var castingTime = 1 / ActiveSkillData.CastingTime; while (IsCasting && indicator.material.GetFloat(FillHash) < 1f) { CastingMove(); var fillValue = indicator.material.GetFloat(FillHash) + Time.deltaTime * castingTime; indicator.material.SetFloat(FillHash, fillValue); yield return null; } } else if (ActiveSkillData.CastingTime == 0) { } // TODO : 터지는 효과 추가하기 HideIndicator(); if (ActiveSkillData.SkillEffect != null) { var skillEffect = Instantiate(ActiveSkillData.SkillEffect, indicator.transform.position, ActiveSkillData.SkillEffect.transform.rotation); skillEffect.Clear(); skillEffect.Play(); } Array.Clear(hitColliders, 0,ActiveSkillData.MaxAttackTargets); var maxSize = Physics.OverlapSphereNonAlloc(indicator.transform.position, ActiveSkillData.Range, hitColliders, targetLayer); for (var i = 0; i < maxSize; i++) { var iDamageable = hitColliders[i].GetComponent(); iDamageable.TakeDamage(ActiveSkillData.Damage); } } private IEnumerator AreaSkill(LayerMask targetLayer, Vector3 targetPos) { followMouse = true; indicator.enabled = true; if (ActiveSkillData.CastingTime > 0) { IsCasting = true; var castingTime = 1 / ActiveSkillData.CastingTime; while (IsCasting && indicator.material.GetFloat(FillHash) < 1f) { CastingMove(); var fillValue = indicator.material.GetFloat(FillHash) + Time.deltaTime * castingTime; indicator.material.SetFloat(FillHash, fillValue); yield return null; } } else if (ActiveSkillData.CastingTime == 0) { } // TODO : 터지는 효과 추가하기 HideIndicator(); if (ActiveSkillData.SkillEffect != null) { var skillEffect = Instantiate(ActiveSkillData.SkillEffect, indicator.transform.position, ActiveSkillData.SkillEffect.transform.rotation); skillEffect.Clear(); skillEffect.Play(); } Array.Clear(hitColliders, 0,ActiveSkillData.MaxAttackTargets); var maxSize = Physics.OverlapSphereNonAlloc(indicator.transform.position, ActiveSkillData.Range, hitColliders, targetLayer); for (var i = 0; i < maxSize; i++) { var iDamageable = hitColliders[i].GetComponent(); iDamageable.TakeDamage(ActiveSkillData.Damage); } } private void CastingMove() { switch (ActiveSkillData.CastingType) { case 0: break; case 1: break; case 2: transform.position = user.position; break; } } protected void FollowMouse() { var ray = mainCam.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out var raycastHit, 2000)) { var userPos = user.position; var targetPos = (userPos + raycastHit.point) / 2; var distance = targetPos - userPos; distance = Vector3.ClampMagnitude((distance * 2), ActiveSkillData.Range); indicator.transform.position = userPos + distance; } } public void SetUser(Transform value) => user = value; #endregion } }