[Cyphers] 클레어 평타 구현
2023. 10. 4.

평타도 일종의 스킬이다. LC(마우스 좌클릭)시 지정된 위치로 클레어의 손에서 광선이 나간다.

-CrossHair에서 쏜 Ray의 위치의 HitPoint를 받아 그 point를 향해 클레어의 손에서 광선이 나가도록(새로운 Ray를 손에서 => 받은 hitPoint 방향으로 생성)하면 된다.

-즉 카메라에서 나오는 Ray1, 손에서 나오는 Ray2라고 생각하자.

손의위치

 

-손에서 나가는 광선이 CrossHair에서 생성된 Ray의 Hitpoint 방향으로 생성되는 코드를 작성후 확인했다.

-잘 생성된다. 다만, 에임이 캐릭터를 통과할때는 이상한 방향으로 생성되는 문제가 생겼다. 이는 layerMask와 tag를 사용해 해결하면 된다.(테스트를 위해 layerMask를 주석처리해서 생긴 문제-플레이어 스스로는 colllider가 있지만 스스로에게는 충돌을 확인할 필요가 없다. )

 

-이 부분에서 생각해야할 문제가있다. "에임은 어떤 상황에 조준되고, 평타는 어떤 조건에서 쓸 수 있는가"의 문제이다.

=>평타는 에임이 조준되지 않는 허공이나 바닥같은 일반 오브젝트에도 쏠 수 있다. 에임은 타격가능한 물체일때 조준되어 플레이어에게 타격가능한 물체임을 알려주는 역할을 한다. 

- 타격 가능한 대상은 어떤 것들이 있는가?

=>타워, 상대 팀, 부술 수 있는 상자, 철거반, 센티넬(립), 수호자 등이다.

-평타를 에임이 조준되지 않는 일반 오브젝트에도 쏠 수 있다고 했지만, collider 가 있어도 충돌체크를 하면 안되는 대상이 있다. 바로 플레이어 자신이다.

태그로 플레이어를 인식하도록 수정
플레이어에 에임이 겹쳐질때의 충돌체크 문제 해결(태그로 플레이어 인식)

-layerMask로 에임 조준에 대한 부분 코드 수정을 해야한다. 현재 작성한 코드는 클레어가 Ray 가 충돌하는 모든 물체에 평타를 사용할 수 있도록 layerMask부분을 빼고 충돌 처리하는지만 확인하도록 작성했다. layerMask로 타워나 타격가능 물체일때만 state를 eState.Hit로 바꿔주도록 해야한다. 

 

-이제 Ray2가 생성되는대로 평타를 만들어주면 된다.

-클레어가 좌클릭할때 Ray2를 생성하도록 코드를 수정하였다.

좌클릭시 ray2 생성하도록 수정

-OnLC는 ClareController에 작성해주었다. 기존에 MouseController에 작성한 ray2에 관한 부분을 옮겨주었다.

좌클릭시 ray2 생성/ 특정 레이어만 Hit상태로 변화

-좌측은 hit 가능하도록 레이어를 설정하였으나, 우측의 상자는 에임이 조준되지 않도록 설정하여 잘되는지 확인하였다.

 

-이펙트를 어떻게 생성되게 해줘야 DrawRay한것처럼 만들 수 있을까 고민했다. 처음에 Prefab으로 생성한 빔을 hitpoint를 향해 손에서 hipoint를 바로보도록 했더니 늘 일정한 길이의 빔이 생성되는 문제가 생겼다. 빔은 hitpoint까지의 거리에 따라 길이가 짧아지거나 길어지게 만들어야한다.

일정한 거리의 빔생성. 수정필요

이펙트는 파티클 시스템으로 구현하였다.

 

-레이의 충돌이 있을때와 없을때로 나누어 없을때에는 hit.point를 바라보는게 아니라 생성될 평타의 위치를 정해주도록했다. 빈오브젝트로 LCPos를 만들어주고, Canvas의 자식으로 넣은뒤에 위치를 수정하였다.

LCPos

-이 LCPos는 CrossHair와 y축이 동일하며, Canvas의 자식이므로 카메라의 움직임, 즉 에임의 움직임에 영향을 받는다.

평타의 충돌검사 코드
이펙트는 미리 생성해두고 코루틴으로 active가 true, false되도록 한다.

 

레이가 충돌할때만 충돌효과가 생긴다. 사거리 내에서만 충돌

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class ClareController : MonoBehaviour
{
    [SerializeField] MouseController mouse;
    [SerializeField] private Transform clareHandTrans;
    [SerializeField] private GameObject beamGo;
    [SerializeField] private GameObject beamImpactGo;
    [SerializeField] private LineRenderer lr;
    [SerializeField] private Transform LCPos;
    [SerializeField] private Transform crossHair;
    private GameObject newBeamGo;
    private Vector3 moveDir;
    private Animator anim;
    private Ray ray2;
    private RaycastHit hit;
    // Start is called before the first frame update
    void Start()
    {
       
        this.anim = GetComponent<Animator>();
       
        this.newBeamGo = Instantiate<GameObject>(beamGo);
        this.beamImpactGo = Instantiate<GameObject>(beamImpactGo);
        // this.lr = beamGo.GetComponent<LineRenderer>();

        this.newBeamGo.SetActive(false);
        this.beamImpactGo.SetActive(false);
        lr.enabled = false;
       
        //  lr.positionCount = 0;
    }

    // Update is called once per frame
    void Update()
    {
        
        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.ray2.origin;
        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);
        }
        //this.newBeamGo.transform.Rotate(ray2.direction);
        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);
    }
    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;

    }
    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)
    {
      
        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.CoBeamImpact());
        }
        StartCoroutine(this.CoLCBeam());
      
        
        // StartCoroutine(this.CoLCLineRenderer());
    }
}
myoskin