[Cyphers] 클레어 프리즘(RC) 구현 - 프리즘 오브젝트 생성, 평타(LC) 반사(오브젝트 풀링으로 여러개의 레이저 동시 발사)
2023. 10. 7.

프리즘은 우클릭(RC)시 에임의 위치에 생성된다.

RC 액션 추가 및 이벤트 연결

-RC도 LC처럼 LCRC일때 호출되지 않도록 처리해줘야한다. 따라서 다음과 같이 코드를 수정하였다.

일정시간이 지나면 사라지는 프리즘 오브젝트 생성

 

생성 결과-일정시간 후 사라짐
프리즘이 생성된 후에는 바로 우클릭해서 새로 생성 불가하게 코드 수정

-프리즘이 생성된 상황에서 우클릭을 또하면 새로운 프리즘이 생길 수 있으므로 이를 방지하기 위해 코드를 수정한다.

-이제 오브젝트를 생성했으니, 프리즘에 반사되는 기능을 추가시켜줘야 한다.

 

-충돌된 오브젝트가 '프리즘'일때, 평타(LC)와 클렌징빔(LCRC) 등(궁극기까지)은 ray가 반사되어 프리즘의 범위내에 있는 '적(상대 팀, 철거반, 립, 타워)'을 공격한다.

 

1. 충돌된 오브젝트가 '프리즘'이면 프리즘의 범위내에 있는 개체들을 구한다.

2. 구한 개체들을 향해 프리즘의 중앙에서 시작하는 ray를 쏜다.

=> 구한 개체들 각각으로부터 프리즘의 중앙까지의 거리와 방향이 필요하다.

 

1을 위해 프리즘의 범위내에 있는 개체들을 구하는 메서드를 작성하였다.

프리즘 오브젝트의 tag를 Prism으로 설정
OverlapSphere로 범위 내의 개체를 확인한다.

-범위 내에 있는 경우에만 CoPrismLCBeamImpact를 호출해 새로운 평타가 발사되도록 2를 구현했다.

CoPrismLCBeamImpact 메서드

-처음에는 단순히 하나의 오브젝트를 만들어두고 껐다 켰다 하도록 코드를 작성했는데(주석친 부분),

여러개의 대상이 맞게하도록 하기 위해서 오브젝트 풀링을 이용해 꺼져있는 오브젝트 중 하나를 풀에서 꺼내오도록 코드를 수정하였다. CreateBeam 매서드를 통해 풀에서 가져온다.

 

오브젝트 풀링으로 프리즘 반사시 LC 생성

오브젝트 풀링
타워를 두개 만들어서 결과 확인. 잘 반사된다.

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 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 List<GameObject> beamPools = 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.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 GameObject GetBeamInPool()
    {
        foreach(GameObject beam in beamPools)
        {
            if(beam.activeSelf == false)
            {//비활성화일떄에만
                return beam;
            }
        }
        return null;
    }
    private GameObject CreateBeam(Vector3 position)
    {
        GameObject go = this.GetBeamInPool();
        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 IEnumerator CoCleansingBeamImpact(Vector3 pos)
    {
        // Debug.Log("impact~!~!");
      // while (isLCRC)
        {
            this.beamImpactGo.SetActive(true);
            this.beamImpactGo.transform.position = pos;
            yield return null;
           // yield return new WaitForSeconds(0.1f);
        }
           // this.beamImpactGo.SetActive(false);      
    }
    public IEnumerator CoOffCleansingBeamImpact()
    {
        yield return null;
        //    this.isLCRC = false;
        Debug.Log("impactOff");
        this.beamImpactGo.SetActive(false);
    }
    public void OffCleansingBeamImpact()
    {
        this.beamImpactGo.SetActive(false);
    }
    private IEnumerator CoLCLineRenderer()
    {
        
        lr.enabled = true;
        lr.SetPosition(0, clareHandTrans.position);
        Vector3 LCPos = new Vector3(this.LCPos.position.x, this.LCPos.position.y, this.LCPos.position.z);
        Debug.Log(this.transform.forward);       
        lr.SetPosition(1, LCPos);
        yield return new WaitForSeconds(0.3f);
        lr.enabled = false;

    }
    private IEnumerator CoCleansingBeam()
    {
        while (isLCRC)//for rotate with player
        {
            yield return null;
            // duration -= Time.deltaTime;
            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);
            
            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;
              
            }
            else
            {
                this.beamImpactGo.SetActive(false);
            }
            this.newBeamGo.transform.position = this.LCRCPos.position;
            this.newBeamGo.transform.LookAt(this.LCPos);
            
        }
         
    }

    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);
    }
    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)
    {
        //this.newBeamGo2.SetActive(true);
        //this.newBeamGo2.transform.position = this.prismGo.transform.position;
        //var parRenderer = this.newBeamGo2.GetComponent<ParticleSystemRenderer>();
        //parRenderer.lengthScale = distance;
        // this.newBeamGo2.transform.LookAt(target);

        GameObject go = this.CreateBeam(this.prismGo.transform.position);       
        var parRenderer = go.GetComponent<ParticleSystemRenderer>();       
        parRenderer.lengthScale = distance+1f;
       
        go.transform.LookAt(target);
        yield return new WaitForSeconds(0.3f);
        // this.newBeamGo2.SetActive(false);
        go.SetActive(false);
    }

    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
            {
            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