ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 유니티로 개인과제 1
    TIL 2024. 5. 13. 22:44

    개인과제로 zep 같은 게임을 유니티로 만들고 있다.

    기능을 구현하면서 사용한 유니티 기능가 겪었던 문제들을 정리해봤다.

     

    InputSystem 을 이용하여 대시 만들기

    더보기

    만든거에 별 이유는 없고

    무료로 다운 받은 캐릭터 스프라이트에 걷기와 달리기가 있길래 한번 구현해봤다.

    설정한 InputSystem/ Dash- Button / Binding -LeftShift
    // TopDownController.cs
    public class TopDownController : MonoBehaviour
    {
        public event Action<Vector2> OnMoveEvent;
        public event Action<Vector2> OnLookEvent;
        public event Action<bool> OnDashEvent;
    
        public void  CallMoveEvent(Vector2 direaction)
        {
            OnMoveEvent?.Invoke(direaction);
        }
    
        public void CallLookEvent(Vector2 direaction)
        {
            OnLookEvent?.Invoke(direaction);
        }
        public void CallDashEvent(bool push)
        {
            OnDashEvent?.Invoke(push);
        }
    }

    강의에서 봤던 구조와 유사하게 짰다. 

    추가된 함수는 CallDashEvent()  눌렀는지 아닌지 확인 하기 위해 bool형을 매개변수로 받는다.

    //TopDownMovement.cs 
    
    private CharacterStatHandler characterStatHandler;
    private float speed = 3f;
    
    private void FixedUpdate()
     {
         if(Time.timeScale ==1)
         rb.velocity = moveDireaction * speed;
     }
     private void Start()
     {
         controller.OnMoveEvent += Move;
         controller.OnDashEvent += Dash;
     }
    
     private void Dash(bool push)
     {
         if(push)//대시 키를 누르고 있다면
         {
             speed = characterStatHandler.characterInfo.runSpeed; 
         }
         else
         {
             speed = characterStatHandler.characterInfo.moveSpeed;
         }
     }
    
     private void Move(Vector2 direaction)
     {
         moveDireaction=direaction;
     }

    Dash는 대시 키(LeftShift)를 눌렀을 때와 손가락을 뗄 때 한번 씩 호출된다.

    따라서 누르고 있을 때 (true)일 때는 속도를 높여주고

    뗄 때 (false)일 때는 원래 속도로 돌아간다.

     

    참고로 runSpeed와 moveSpeed는 캐릭터 정보에 미리 정의해 두었다.

    고양이 달린다

    ChenamchineCamera를 사용하여 플레이어 따라가기

    더보기

    필수 구현 기능 중 하나로 플레이어 따라가는게 있었다.

    보자마자 그냥 바로 시네머신 써야지~ 해서 그냥 썼다......  (코드랑 이것중 뭐가 더 좋은지는 모르겠다)

    프로젝트에 ChenamchineCamera 적용법

    Window - PackageManager -  UnityRegistry - Cinemachine

    원래 있던 MainCamera에 AddComponenet로 CinemachineBrain을 달아준다.

    그리고 Hierarchy 창에 마우스 우클릭으로 VirtualCamera를 생성한다.

    이러면 CinemachineVirtualCamera 라는 컴포넌트를 가진 오브젝트가 생성되고 

    저기에 Follow에 따라다닐 캐릭터를 끌어서 넣기만하면 잘 따라다닌다.

    https://docs.unity3d.com/Packages/com.unity.cinemachine@2.1/api/Cinemachine.html

     

    Namespace Cinemachine | Package Manager UI website

    Namespace Cinemachine Classes Property applied to AxisState. Used for custom drawing in the inspector. As a part of the Cinemachine Pipeline implementing the Noise stage, this component adds Perlin Noise to the Camera state, in the Correction channel of th

    docs.unity3d.com

    TextMeshPro 폰트 한글로 바꾸기

    더보기

    평소에 unity를 거의 2018? 2020? 버전을 썼었는데 팀 프로젝트를 위해  버전을 2022로 올리니깐 친애하던 Text가 Legacy로 가버렸다.

    원래도 문제가 많은 친구니 그냥 보내주기로 하지만 기본폰트로도 한글이 적히는 기존의 Text와는 다르게 한글이 안된다.

    그래서 기존의 폰트를 TMP font로 바꿔줘야 한다.

    Window - TextMeshPro - FontAssetCreator

     

    사용할 폰트를 넣고 

    CharacterSequence에 

    32-126,44032-55203,12593-12643,8200-9900

    를 입력한다.

     

    만약 CharacterSequence가 안뜬다면 CharacterSet CustomRange가 아닌지 확인해야 한다.

    잘 생성되었다.

    버튼이 방향키로 입력되는 현상

    더보기

    버튼이 클릭된 채로 방향키를 움직이면 다른버튼이 눌리고 닫히는 이상한 문제가 있었다.

    애꿎은 버튼만 계속 봤는데 사실 원인은 EventSystem에 있었다.

    EventSystem은 Canvs를 생성하면 자동으로 만들어지고, 반대로 실수로 지워버리면 잘 작동이 안되니 조심해야한다.

    체크한 부분에서 Move를 none으로 바꿔주니 현상이 없어졌다.

    이게 정확한 해결법인지는 아직 잘 모르겠다.

    AnimatorOverrideController

    더보기

    예전에 강의해서 언급만하고 넘어간 기능.

    이번에 플레이어의 캐릭터를 바꾸는 기능을 넣어야 했기때문에 그거에 맞게 애니메이션을 또 따로 다 만들기보다는 이 기능을 써보기로 했다.

    캐릭터마다 애니메이션은 다르지만 애니메이터의 구조가 똑같을 경우

    또 다시 파라미터 만들고 화살표 연결할 필요 없이 기반이 될 애니메이터를 넣고 해당하는 애니메이션을 넣으면 적용할 수 있다.

     

    이렇게 만든 Animator Override는 일반적인 Animator처럼 쓸 수도 있고

     public List<AnimatorOverrideController> animators;
     
     public void ChangeAnimator(int num)
    {
        animator.runtimeAnimatorController = animators[num];
    }

    나는 이렇게 리스트로 받아서 캐릭터가 바뀔 때 마다 animator를 변경해 주었다.

     

    https://docs.unity3d.com/kr/2019.4/Manual/AnimatorOverrideController.html

     

    애니메이터 오버라이드 컨트롤러 - Unity 매뉴얼

    애니메이터 오버라이드 컨트롤러는 원본의 애니메이터 컨트롤러를 확장하는 에셋으로 사용된 특정 애니메이션을 대체하지만 원본 구조, 파라미터와 로직을 유지합니다.

    docs.unity3d.com

     

    중복된 코드 정리

    더보기

    생각의 흐름대로 코드를 짜다보면 변수랑 함수명도 꼬이고  비슷한 코드를 여러번 함수에 적고있는 나를 발견할 수 있다.

    몇가지 코드를 정리했다.

    public void ClickCharacterSelect()
    {
          bool active;
         
          if (nameInputPanel.activeSelf)
          {
              active = characterSelect.activeSelf;
              characterSelect.SetActive(!active);
          }
          else
          {
              active = miniCharacterSelect.activeSelf;
              miniCharacterSelect.SetActive(!active);
          }
         
     }
     public void ClickChangeName()
     {
          if(Time.timeScale==1)
          {
              Time.timeScale = 0;
          }
          bool active;
          active = miniNameChange.activeSelf;
          miniNameChange.SetActive(!active);
      }

    둘 다 버튼을 누른다고 Click을 붙여서 함수를 만들었다. 그런데 짜면서 생각해보니 둘다 버튼을 클릭해서 

    하나의 오브젝트를 활성화하고 끄는 함수였다.

    using UnityEngine.EventSystems; 
    
    public void ButtonActive() 
     {
         Time.timeScale = 0;//버튼 활성화 중에는 캐릭터가 멈춰야함
         //클릭한 버튼의 오브젝트 정보를 받아온다.
         GameObject obj = EventSystem.current.currentSelectedGameObject.transform.GetChild(0).gameObject;
         bool active = obj.activeSelf;
         if (lastActiveObj!=null) //버튼이 켜져있는 도중 다른 버튼도 켜저 겹치는 것을 방지
         {
             lastActiveObj.SetActive(false);
         }
         obj.SetActive(!active); //현재 상태와 반대로 껐다 키기
         Time.timeScale = nameInputPanel.activeSelf ? 1 : 0;
         if (!active == false) Time.timeScale = 1;
         lastActiveObj = obj;
    
     }

    두 함수를 합쳐서 Button Active로 만들었다.

    using UnityEngine.EventSystems;

     EventSystem.current.currentSelectedGameObject : 클릭한 버튼의 오브젝트 정보를 받아온다.

     

     public void ChangeName()
      {
          if(nameInputPanel.activeSelf)
          {
              CheckNameCorrect(nameInputPanel.activeSelf? nameText: miniNameText);
          }
          if (nameText.text.Length <=2)
          {
              if(runningCorutine!=null) //마지막에 입력된 기준으로 1초를 세기위해 기존에 작동하던 코루틴 멈춤
              StopCoroutine(runningCorutine);
    
              warningText.enabled = true;
              warningText.text ="이름이 너무 짧습니다.";
    
              runningCorutine= StartCoroutine(TextClose(warningText)); //1초 뒤에 꺼짐
             
          }
          else
          {
              if(nameInputPanel.activeSelf)
              {
                  characterStatHandler.characterInfo.name = nameText.text;
                  nameInputPanel.SetActive(false);
                  Time.timeScale = 1;
              }
              else
                  characterStatHandler.characterInfo.name = miniNameText.text;
    
              characterStatHandler.UpdateStatus();
              
          }
      }

    얘는 플레이어의 이름을 변경하는 함수였다.

    처음에는 문제가 없었지만 인게임안에서도 닉네임을 변경할 수 있게 별도의 ui를 만들어주면서 똑같은 기능을 가진 함수를 2개만들어야 하나 고민하다가  재활용할 수 없을까? 하고 변경해봤다.

      void CheckNameCorrect(TMP_Text inputText,GameObject obj)
      {
          if (inputText.text.Length <= 2)
          {
              if (runningCorutine != null) //마지막에 입력된 기준으로 1초를 세기위해 기존에 작동하던 코루틴 멈춤
                  StopCoroutine(runningCorutine);
    
              warningText.enabled = true;
              warningText.text = "이름이 너무 짧습니다.";
              runningCorutine = StartCoroutine(TextClose(warningText)); //1초 뒤에 꺼짐
          }
          else
          {
              characterStatHandler.characterInfo.name = inputText.text;
              obj.SetActive(false);
              characterStatHandler.UpdateStatus();
    
              Time.timeScale = 1;
          }
      }    
      public void ChangeName()
      {
          GameObject obj = EventSystem.current.currentSelectedGameObject.transform.parent.gameObject;
          CheckNameCorrect(nameInputPanel.activeSelf? nameText: miniNameText, obj);
      }

    함수를 두개로 분리하고 위에썼던  EventSystem.current.currentSelectedGameObject을 사용해서 오브젝트 정보를 전달해줘서 

    앞으로도  같은 기능을 가진 ui가 여러개 생긴다 해도 문제 없게 바꿔주었다.

     

    'TIL' 카테고리의 다른 글

    [TIL] 객체지향 코드 설계 1  (0) 2024.05.16
    [TIL] 유니티 개인과제 2  (0) 2024.05.14
    [TIL] Unity 3 + 알고리즘 문제  (0) 2024.05.10
    [TIL] Unity 강의 2  (0) 2024.05.09
    [TIL] Unity 강의 1  (0) 2024.05.08
Designed by Tistory.