OldBlueWater/BlueWater/Assets/02.Scripts/Skill/ActiveSkill/ActiveSkill.cs

288 lines
9.4 KiB
C#

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<DecalProjector>();
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>();
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>();
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
}
}