[Cyphers] 클레어 프리즘(RC),클렌징빔(LCRC) UI 구현/ 스킬 쿨타임 적용
2023. 10. 10.

-스킬 쿨타임을 적용하기 위해 UI 이미지들을 추가 후 스킬마다 Enable과 Disable일때로 UI를 구분해주었다.

-스킬을 사용하면 쿨타임 동안에는 Disable의 UI가 SetActive(false)에서 SetActive(true)가 되며, Enable의 UI들의 alpha값을 조절해줘야한다.(반투명상태로 나타남) 이는 어떤 스킬을 쓸때 다른 스킬들은 사용 불가일 때에만 나타난다.(클렌징 빔과 같은 스킬이 그 예이다. 클렌징빔을 홀드하는 중에는 다른 스킬을 사용할 수 없다.)

 

프리즘(RC) 스킬 쿨타임 적용

-Disable의 Bg는 Image Type을 Filled, Fill Method는 Vertical, Fill Origin은 Bottom으로 설정해준다.

-Fill Amount를 코루틴 내에서 조절한다. while문 내에서 남은시간은 -= Time.deltaTime일때, 남은 시간에 따라 남은시간/최대시간을 fillamount에 대입한다(.fillamount = leftTime/coolTime)

클렌징 빔(LCRC) 스킬 쿨타임 적용

-클렌징 빔도 프리즘(RC)과 마찬가지로 쿨타임을 적용시켜 주면 된다. 다만 클렌징빔은 홀딩할때 while문이 있으므로 코루틴을 따로 작성해주었다. 따라서 쿨타임을 나타내는 코루틴이 실행되는 동안에는 isLCRCCool을 true로 만들고, 이것이 false일때만 CoCleansingBeam 메서드를 호출하도록 하였다. 이로 인해 쌍클릭을 쿨타임중에 누르더라도 클렌징빔 스킬이 나가지 않는다.

 

쿨타임일때는 쌍클릭해도 실행되지 않도록 다음과 같이 수정

 

클렌징빔 쿨타임 구현결과

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Interactions;
using UnityEngine.InputSystem.Processors;
using static UnityEditor.PlayerSettings;
using UnityEngine.UI;
using TMPro;

public class ClareController : MonoBehaviour
{
    [SerializeField] MouseController mouse;
    [SerializeField] private Transform clareHandTrans;
    [SerializeField] private GameObject beamGo;
    [SerializeField] private GameObject beamImpactGo;
    [SerializeField] private GameObject prismGo;
    [SerializeField] private LineRenderer lr;
    [SerializeField] private Transform LCPos;
    [SerializeField] private PlayerInput playerInput;
    [SerializeField] private Transform LCRCPos;
    //----------------------UI-------------------------------------
    [SerializeField] private GameObject RCDisable;
    [SerializeField] private GameObject LCRCDisable;

    private GameObject newBeamGo;
   
    private GameObject impactGo = null;
    private List<GameObject> goList = new List<GameObject>();
    private List<GameObject> impactGoList = new List<GameObject>();
    private Vector3 moveDir;
    private Animator anim;
    private Ray ray2;
    private Ray ray3;
    private RaycastHit hit;
    private ParticleSystemRenderer psr;

    private Coroutine prismRoutine;
    private Coroutine lcBeamRoutine;
    private bool isLCRC;
    private bool isPrism;
    private bool isLCRCCool;
    private List<GameObject> beamPools = new List<GameObject>();
    private List<GameObject> impactPools = new List<GameObject>();
    // Start is called before the first frame update
    void Start()
    {
        this.anim = GetComponent<Animator>();
       
        this.BeamPool();
        this.ImpactPool();
        this.prismGo = Instantiate<GameObject>(this.prismGo);
        this.beamImpactGo = Instantiate<GameObject>(this.beamImpactGo);
        this.newBeamGo = Instantiate<GameObject>(this.beamGo);
        this.psr = this.newBeamGo.GetComponent<ParticleSystemRenderer>();   
        
        this.newBeamGo.SetActive(false);
        this.prismGo.SetActive(false);
        this.beamImpactGo.SetActive(false);
        lr.enabled = false;

    }
    private void BeamPool()
    {
        for (int i = 0; i < 5; i++)
        {
            GameObject go = Instantiate<GameObject>(this.beamGo);
            go.SetActive(false);
            this.beamPools.Add(go);//Add on List
        }
    }
    private void ImpactPool()
    {
        for(int i = 0; i < 5; i++)
        {
            GameObject go = Instantiate<GameObject>(this.beamImpactGo);
            go.SetActive(false);
            this.impactPools.Add(go); ;//Add on List
        }
    }
    private GameObject GetBeamInPool()
    {
        foreach(GameObject beam in beamPools)
        {
            if(beam.activeSelf == false)
            {//비활성화일떄에만
                return beam;
            }
        }
        return null;
    }
    private GameObject GetImpactInpool()
    {
        foreach(GameObject impact in impactPools)
        {
            if(impact.activeSelf == false)//비활성화일때만
            {
                return impact;
            }
        }
        return null;
    }
    private GameObject CreateBeam(Vector3 position)
    {
        GameObject go = this.GetBeamInPool();
        go.transform.position = position;
        go.SetActive(true);
        return go;
    }
    private GameObject CreateImpact(Vector3 position)
    {
        GameObject go = this.GetImpactInpool();
        go.transform.position = position;
        go.SetActive(true);
        return go;
    }

    // Update is called once per frame
    void Update()
    {
        //  Debug.Log(this.playerInput.actions["LCRC"].WasPressedThisFrame());
       
        if (moveDir != Vector3.zero)
        {
            if(this.moveDir.z > 0)//forward
            {
                //this.transform.rotation = Quaternion.LookRotation(moveDir); ;//진행방향으로 회전
                this.transform.Translate(Vector3.forward * 4.0f * Time.deltaTime);//이동
                if(this.moveDir.x == 0)//forward
                {
                    this.anim.SetInteger("State", 1);//move forward
                }
                else if (this.moveDir.x > 0)
                {
                    this.anim.SetInteger("State", 2);//move forward rightCross
                }
                else if (this.moveDir.x < 0)
                {
                    this.anim.SetInteger("State", 4);//move forward leftCross
                }
            }
            else if(this.moveDir.z < 0)//backward
            {
                this.transform.Translate(this.moveDir * 4.0f * Time.deltaTime);//이동
                if (this.moveDir.x == 0)//backward
                {
                    this.anim.SetInteger("State", 3);//move backward
                }
                else if (this.moveDir.x > 0)
                {
                    this.anim.SetInteger("State", 7);//move backward rightCross
                }
                else if (this.moveDir.x < 0)
                {
                    this.anim.SetInteger("State", 8);//move backward leftCross
                }
            }
            else
            {//stay but press left or right
                this.transform.Translate(this.moveDir * 4.0f * Time.deltaTime);//이동
                if (this.moveDir.x < 0)
                {
                    this.anim.SetInteger("State", 5);//move left
                }
                else
                {
                    this.anim.SetInteger("State", 6);//move right
                }
            }
        }
        else
        {
            this.anim.SetInteger("State", 0); //idle
        }
    }
    private IEnumerator CoLCBeam()
    {

        this.newBeamGo.SetActive(true);
        this.newBeamGo.transform.position = this.clareHandTrans.position;
 
        if (mouse.hit.point != Vector3.zero && mouse.hit.collider.tag != "Player")
        {
            this.newBeamGo.transform.LookAt(mouse.hit.point);

        }
        else
        {
            this.newBeamGo.transform.LookAt(this.LCPos);
     
        }
        yield return new WaitForSeconds(0.2f);
        this.newBeamGo.SetActive(false);

    }

    private IEnumerator CoLCBeamImpact(float distance)
    {
        this.newBeamGo.SetActive(true);
        this.psr.lengthScale = distance;
        this.newBeamGo.transform.position = this.clareHandTrans.position;

        if (mouse.hit.point != Vector3.zero && mouse.hit.collider.tag != "Player")
        {
            this.newBeamGo.transform.LookAt(mouse.hit.point);
        }
        else
        {
            this.newBeamGo.transform.LookAt(this.LCPos);
        }    
        yield return new WaitForSeconds(0.3f);
        this.newBeamGo.SetActive(false);
    }

    private IEnumerator CoBeamImpact()
    {
        this.beamImpactGo.SetActive(true);
        this.beamImpactGo.transform.position = this.hit.point;
        yield return new WaitForSeconds(0.3f);
        this.beamImpactGo.SetActive(false);
    }

    public void OffCleansingBeamImpact()
    {
        this.beamImpactGo.SetActive(false);
    }
 
    private IEnumerator CoCleansingBemaCool()
    {
        float coolTime = 3f;
        float leftTime = 3f;
        Image lcrcDisableImage = this.LCRCDisable.GetComponentInChildren<Image>();
        TextMeshProUGUI lcrcCoolTxt = this.LCRCDisable.GetComponentInChildren<TextMeshProUGUI>();

        while (leftTime > 0f)
        {
            this.isLCRCCool = true;
            this.LCRCDisable.SetActive(true);
            leftTime -= Time.deltaTime;
            lcrcDisableImage.fillAmount = leftTime / coolTime;
            int intLeftTime = (int)leftTime;
            lcrcCoolTxt.text = intLeftTime.ToString();
            yield return null;
        }
        this.LCRCDisable.SetActive(false);
        this.isLCRCCool = false;
    }
    private IEnumerator CoCleansingBeam()
    {
        int count=0;
        int count2 = 0;
        int count3 = 0;
        int collsLength=0;

        this.isPrism = false;
        
            while (isLCRC) //for rotate with player
            {
            this.newBeamGo.SetActive(true);
    
            this.psr.lengthScale = 10;
                var psr = this.newBeamGo.GetComponent<ParticleSystem>().main;
                psr.startSizeXMultiplier = 3f;

                Vector3 ray3Direction = this.LCPos.position - this.LCRCPos.position;
                this.ray3 = new Ray(this.LCRCPos.position, ray3Direction);
                Debug.DrawRay(this.ray3.origin, this.ray3.direction * 10f, Color.green, 0.5f);
                this.newBeamGo.transform.position = this.LCRCPos.position;
                this.newBeamGo.transform.LookAt(this.LCPos);

                if (Physics.SphereCast(this.ray3.origin, 0.2f, this.ray3.direction, out hit, 10f) && !hit.collider.CompareTag("Player"))
                {
                    // Debug.Log(hit.collider);
                    this.beamImpactGo.SetActive(true);
                    this.beamImpactGo.transform.position = hit.point;
                    if (hit.collider.CompareTag("Prism"))
                    {  //  Debug.LogFormat("count: {0}", count);                   
                       //프리즘 범위내에 개체들을 스캔
                        var layerMask = 3 << LayerMask.NameToLayer("Tower");
                        Collider[] colls = Physics.OverlapSphere(this.prismGo.transform.position, 5f, layerMask);

                        collsLength = colls.Length;
                        if (count < colls.Length)
                        {
                            float distance = Vector3.Distance(this.prismGo.transform.position, colls[count].gameObject.transform.position);

                            this.goList.Add(this.PrismCleansingBeam(distance, colls[count].gameObject.transform.position));
                            this.impactGoList.Add(this.PrismCleansingBeamImpact(distance, colls[count].gameObject.transform.position));


                            count++;
                        }

                    }
                    else
                    {//hitting something but which is not prism
                     //  Debug.LogFormat("count2: {0}",count2);                  
                        if (count2 < collsLength)
                        {
                            this.goList[count2].SetActive(false);
                            this.impactGoList[count2].SetActive(false);
                            count2++;
                        }
                        else
                        {
                            count2 = 0;
                        }
                        count = 0;
                    }

                }
                else
                {//hit nothing
                    this.beamImpactGo.SetActive(false);
                    count = 0;
                    //   Debug.LogFormat("{0}",count3);

                    if (count3 < collsLength)
                    {
                        this.goList[count3].SetActive(false);
                        this.impactGoList[count3].SetActive(false);
                        count3++;
                    }
                    else
                    {
                        count3 = 0;
                    }

                }

            yield return null;

        }



    }

    private void OnDrawGizmos()
    {
        if (hit.point != Vector3.zero && isLCRC)
        {
            Gizmos.color = Color.green;          
            Gizmos.DrawWireSphere(this.ray3.origin + this.ray3.direction * 10f, 0.2f);
        }
    }
    private IEnumerator CoOffCleansingBeam()
    {
        yield return null;
        var psr = this.newBeamGo.GetComponent<ParticleSystem>().main;
        psr.startSizeXMultiplier = 1f;
        
        this.newBeamGo.SetActive(false);
        this.beamImpactGo.SetActive(false);
       
        for (int i = 0; i < this.goList.Count; i++)
        {
            this.goList[i].SetActive(false);
            this.impactGoList[i].SetActive(false);
        }
    }
    private IEnumerator CoCreatePrism()
    {
        float coolTime = 7f;
        float leftTime = 7;
        Image rcDisableImage = this.RCDisable.GetComponentInChildren<Image>();
        TextMeshProUGUI rcCoolTxt = this.RCDisable.GetComponentInChildren<TextMeshProUGUI>();

        this.RCDisable.SetActive(true);
        this.prismGo.SetActive(true);
        this.prismGo.transform.position = this.LCPos.position;
        while (leftTime > 0f)
        {
            leftTime -= Time.deltaTime;
            rcDisableImage.fillAmount = leftTime/coolTime;
            int intLeftTime = (int)leftTime;
            rcCoolTxt.text = intLeftTime.ToString();
            yield return null;
        }
      //  yield return new WaitForSeconds(coolTime);
        this.prismGo.SetActive(false);
        this.RCDisable.SetActive(false);
    }


    private IEnumerator CoPrismLCBeamImpact(float distance, Vector3 target)
    {
        GameObject go = this.CreateBeam(this.prismGo.transform.position);  //create GameObject Beam
        GameObject impactGo = null;
        Vector3 targetPos = new Vector3(target.x, target.y + 1f, target.z);
        //Create Ray
        Ray ray = new Ray(this.prismGo.transform.position, targetPos - this.prismGo.transform.position);
        Debug.DrawRay(ray.origin, ray.direction * distance, Color.yellow, 0.3f);
        //Check Ray Hit
        if (Physics.Raycast(ray.origin, ray.direction, out hit, distance + 1f))
        {
        //    Debug.Log("Hit impact!!");
        //  Debug.LogFormat("hit Point: {0}", hit.point);           
            impactGo = this.CreateImpact(hit.point);
            
        }
        var parRenderer = go.GetComponent<ParticleSystemRenderer>();
        parRenderer.lengthScale = distance + 1f;
        var psr = go.GetComponent<ParticleSystem>().main;
        psr.startSizeXMultiplier = 1f;

        go.transform.LookAt(targetPos);
        yield return new WaitForSeconds(0.3f);
        go.SetActive(false);
        if (impactGo != null)
        {
            impactGo.SetActive(false);
        }
    }
    private GameObject PrismCleansingBeam(float distance, Vector3 target)
    {
        GameObject go = this.CreateBeam(this.prismGo.transform.position);//create GameObject Beam
       
        Vector3 targetPos = new Vector3(target.x, target.y + 1f, target.z);
       
        var parRenderer = go.GetComponent<ParticleSystemRenderer>();
        parRenderer.lengthScale = distance + 1f;
        var psr = go.GetComponent<ParticleSystem>().main;
        psr.startSizeXMultiplier = 3f;
        
        go.transform.LookAt(targetPos);
        return go;
    }
  private GameObject PrismCleansingBeamImpact(float distance,Vector3 target)
    {  //Create Ray
        GameObject go = null;
        Vector3 targetPos = new Vector3(target.x,target.y + 1f, target.z);

        Ray ray = new Ray(this.prismGo.transform.position, targetPos - this.prismGo.transform.position);
        Debug.DrawRay(ray.origin, ray.direction * distance, Color.green, 0.5f);
        //Check Ray Hit
        if (Physics.SphereCast(ray.origin, 0.2f, ray.direction, out hit, distance))
        {
         go = this.CreateImpact(hit.point);
        }
        return go;
    }
    private void ScannedByPrism()
    {
        var layerMask = 3 << LayerMask.NameToLayer("Tower");
        Collider[] colls = Physics.OverlapSphere(this.prismGo.transform.position, 5f, layerMask);
        for(int i=0;i<colls.Length;i++)
       // foreach(Collider coll in colls)
        {
            float distance = Vector3.Distance(this.prismGo.transform.position, colls[i].gameObject.transform.position);
         //  Debug.LogFormat("distance: {0}",distance);
            StartCoroutine(this.CoPrismLCBeamImpact(distance,colls[i].gameObject.transform.position));
        }
    }

    public void OnMove(InputAction.CallbackContext ctx)
    {
        Vector2 dir = ctx.ReadValue<Vector2>();
        this.moveDir = new Vector3(dir.x, 0,dir.y);//2차원 좌표를 3차원 좌표로 변환
    }

    public void OnLC(InputAction.CallbackContext ctx)
    {      
            if (ctx.performed)
            {                     
                    Vector3 ray2Direction;
                    if (mouse.hit.point != Vector3.zero)
                    {
                        ray2Direction = mouse.hit.point - this.clareHandTrans.position;
                    }
                    else
                    {
                        ray2Direction = LCPos.position - this.clareHandTrans.position;
                    }
                    this.ray2 = new Ray(this.clareHandTrans.position, ray2Direction);
                    float lcDistance = 5f;

                    if (Physics.Raycast(this.ray2.origin, this.ray2.direction, out hit, lcDistance) && !hit.collider.CompareTag("Player"))
                    {
                        // Debug.LogFormat("LC Hit! Distance : {0}", hit.distance);
                        Debug.DrawRay(this.ray2.origin, this.ray2.direction * lcDistance, Color.yellow, 0.5f);
                        StartCoroutine(this.CoLCBeamImpact(hit.distance + 2f));
                        StartCoroutine(this.CoBeamImpact());

                        if (hit.collider.CompareTag("Prism"))
                        {
                         //   Debug.Log("Prism");
                            this.ScannedByPrism();
                        }
                    }
                    else
                    {
                        if (this.lcBeamRoutine != null)
                        { StopCoroutine(this.lcBeamRoutine); }
                        this.lcBeamRoutine = StartCoroutine(this.CoLCBeam());
                    }
                }
            
        
    }

    public void OnLCRC(InputAction.CallbackContext ctx)
    {              
        this.playerInput.actions["LC"].Disable();//if LCRC performed, Disable LC Action 
        this.playerInput.actions["RC"].Disable();//if LCRC performed, Disable RC Action 

        if (ctx.interaction is HoldInteraction && ctx.duration != 0)//holding
            {
            if (ctx.performed && !isLCRCCool)
            {
                this.isLCRC = true;
                StartCoroutine(this.CoCleansingBeam());
                StartCoroutine(this.CoCleansingBemaCool());
              
            }
            }
        else if (ctx.interaction is PressInteraction)//버튼에서 손을 떼면
        {
            if (ctx.performed)
            {
                this.isLCRC = false;
                StopCoroutine(this.CoCleansingBeam());
                StartCoroutine(this.CoOffCleansingBeam());
           //     Debug.Log("release!");                       
            }

            this.playerInput.actions["LC"].Enable();//LCRC Button Release, Enable LC action
            this.playerInput.actions["RC"].Enable();//Enable RC Action 
        }
        }
    public void OnRc(InputAction.CallbackContext ctx)
    {
        if (ctx.performed && !this.prismGo.activeSelf)//prism cannot be activated if it's already activated
        {
          //  if(this.prismGo.activeSelf)
            Debug.Log("RC!!");
          
              this.prismRoutine = StartCoroutine(this.CoCreatePrism());
            
            
        }
    }

}
myoskin