[Cyphers] 클레어 프리즘 - 클렌징 빔 반사 추가 및 충돌처리 + 지형수정
2023. 10. 9.

- 클레어의 평타를 프리즘으로 반사하는 것처럼, 클렌징 빔도 반사되어 적을 공격할 수 있다.

- 이 포스팅에서는 클렌징 빔으로 프리즘 반사하는 효과를 구현하고, 반사된 평타와 클렌징 빔에 충돌 처리를 해 충돌 effect까지 추가하려 한다.

-클렌징 빔의 경우 새로운 빔을 생성하는 게 아니라 평타를 구현할 때 사용한 빔의 사이즈를 조절하는 방식으로 이펙트를 생성했었다. 마찬가지로 프리즘에 반사될 때에도 새롭게 이펙트풀을 만들 필요 없이 미리 만들어둔 오브젝트 풀에서 평타를 꺼내왔던 것처럼 꺼내오되, 사이즈를 조절해주면 된다.

 -이러한 재사용이 가능한 이유는 플레이어가 평타와 클렌징 빔을 동시에 사용하는 일은 없어서이다. 평타를 쓸때는 평타만 쓸 수 있고, 클렌징 빔을 쓸 때에는 클렌징 빔만 쓰기 때문이다.

 

지형수정

-리버포드 맵 중앙 좌측의 타워는 사이퍼즈 공성전에서 플레이어들이 일반적으로 가장 먼저 공략하게 되는 타워 위치이다.

-따라서 그 위치의 지형을 먼저 구현하려한다.

지형 수정 1차 완료 후

프리즘 - 클렌징 빔 반사

-클렌징빔은 평타와 달리 while문이 있다. while문 동안 계속 setActive이므로 평타의 코드와 같이 작성하려하면  while문 내에 계속 for문을 호출하게 되어 while문이 실행될동안 계속해서 새로운 빔을 켜게되는 코드가 된다. 이렇게 되면 5개의 빔이 필요한 상황에도 수많은 빔을 호출하게 된다. 따라서 코드를 수정해주어야했다.

 

-스캔된 개체의 수 만큼만 this.PrismCleansingBeam을 호출하기 위해 count라는 변수를 사용했다. count는 0부터 시작해 개체수보다 작은 경우에만 PrismCleansingBeam 메서드를 호출하는데, 이를 호출할때마다 1씩 증가시켜 for문처럼 작동하게 된다.

-이 부분까지만 작동하면 LcRc를 계속 누른상태에서 다른 곳을 쳐서 off했다가 다시 프리즘을 치는 경우에는 count가 이미 개체수만큼 증가했으므로 PrismCleansingBeam이 호출되지 않는다. 따라서 off되는 경우에 count 를 0으로 초기화시켜준다.

 

off하는 코드도 수정해준다.

Off하는 코드도 수정

-클렌징빔이므로 반사될때 빔의 두께도 조절해주기 위해 코드를 수정한다.

LCRC / LC 에서의 빔 사이즈
프리즘 LC와 프리즘 LCRC

프리즘 평타(LC)의 충돌처리 및 효과

-LC의 충돌처리부터 해주려한다. 프리즘 반사 후 발생하는 충돌 이펙트이므로 프리즘 평타를 만든 것처럼 오브젝트 풀링을 이용하여 미리 생성해둔 이펙트를 껐다 켰다 하는 방식으로 코드를 작성했다.

CoPrismLCBeamImpact 수정

-프리즘 평타 메서드를 수정하여 이펙트를 추가했다. 평타가 생성되면 Ray를 그려 충돌체크를 하고, 충돌하면 충돌 지점에 이펙트를 생성한다. (CreateImpact 메서드에서 target의 위치를 매개변수로 받는다.)

 

오브젝트 풀링으로 effect 생성
프리즘 평타(LC) 충돌처리 후 이펙트 생성

프리즘 클렌징빔(LCRC)의 충돌 처리 및 효과

-클렌징 빔의 경우 프리즘 클렌징빔의 코드에서 수정해야 한다. 평타의 충돌처리처럼 오브젝트 풀링을 이용하되, while 내의 if문을 작성했으므로, for문으로 작성한 전자의 경우와는 달라 주의해서 수정해야한다.

 

-충돌시 발생하는 effect는 LC의 effect와 동일하므로 호출만한다.

프리즘 클렌징빔 충돌처리 코드

-PrismCleansingBeamImpact 메서드에서 Ray를 쏘아 충돌체크한다. 평타와 달리 SphereCast를 사용하였다.

 

off하는 부분
프리즘 클렌징 빔(LCRC) 충돌처리 및 effect 구현결과

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;

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;
    public System.Action<Vector3> OnCleansingBeamContact;

    private GameObject newBeamGo;
    private GameObject newBeamGo2;
    private GameObject newBeamGo3;
    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 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.newBeamGo = Instantiate<GameObject>(beamGo);
        this.newBeamGo2 = Instantiate<GameObject>(beamGo);
        this.BeamPool();
        this.ImpactPool();
        this.prismGo = Instantiate<GameObject>(prismGo);
        this.beamImpactGo = Instantiate<GameObject>(beamImpactGo);
        this.psr = this.newBeamGo.GetComponent<ParticleSystemRenderer>();     
        
        this.newBeamGo.SetActive(false);
        this.newBeamGo2.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 CoCleansingBeam()
    {
        int count=0;
        int count2 = 0;
        int count3 = 0;
        int collsLength=0;
    
        this.isPrism = false;
        while (isLCRC)//for rotate with player
        {
            yield return null;
            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;
                }

            }
            
            
        }
      
    }

    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()
    {
        this.prismGo.SetActive(true);
        this.prismGo.transform.position = this.LCPos.position;
        yield return new WaitForSeconds(10f);
        this.prismGo.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)
            {
                this.isLCRC = true;
                StartCoroutine(this.CoCleansingBeam());
              
            }
            }
        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