안녕하세요

프로그램 과정에서 막혔던 문제들에 대한 해결책 정리


페이지 목록

2025년 12월 6일 토요일

[유니티][Unity] 보스HP와 주인공HP를 계산하는 방법이 같을 때 하나의 코드로 처리하는 방법

  보스HP가 애니메이션 효과로 줄어들게 구현하고 보니 

 (7가지 색의 SingleBar 를 만들고 curHpBar는 빨주노초파남보 라면 

 밑에 있는 BelowHpBar는 주노초파남보빨 로 나오게 하고

 curHpBar가 Singlebar의 끝까지 가면 주황이나오고 belowHpBar는 노랑이 나오도록 설계하고

이를 루프를 돌리고 마지막에 hp가 0이 될 때는 검은색으로 나오게 만들었다.)


 이 것을 보스에만 구현해놓고 이제 주인공 HP에 구현하려고 보니, 이 코드를 다시 만들어야 되는 것이다. 

 그래서 재사용할 수 있게 만들어 보니 몇가지 테크닉이 들어가게 되었다.


 1. static class를 지정한다.

  사용 이유를 정확하게는 알 수 없으나 공용으로 쓰이는 메소드를 static class에 넣어 쓰면 재사용하기에 좋다고 한다.

 이 경우에 GetColorByBarIndex로 7가지 색중 어느 index를 불러와야 할지 정하는 메소드를 static으로 만들어서 공용으로 컬러 값을 가져올 수 있게 하였다.

public static class HpColorHelper

{

    public static Color GetColorByBarIndex(int index, List<Color> barColors, Color depletedColor)

    {

        if (index < 0) return depletedColor;

        if (barColors == null || barColors.Count == 0) return Color.white;

        return barColors[index % barColors.Count];

    }

}


2. baseHpView를 만들어 거기에 색깔 계산과 애니메이션효과와 현재피/전체피 텍스트를 적게 했다.

 이렇게 하면 bassHpView를 상속받으면 bassHpView에서 로딩하는 UpdateHpBar(...) 클래스를 

가져오게 되니 한번 만든 코드를 재사용할 수 있게 되었다.


3. Interface를 사용하여 Presenter에서 view를 가져온다.

 아직 정확하게 감이 잡힌 건 아닌데 interface를 쓰면 Presenter에서 view가 바뀌어도 코드 바꿀 필요가  없어서 유연성이 증가된다고 한다. 

이런 식으로 interface를 선언 하고

public interface IHpBarView

{

    void UpdateHpBar(int currentHp, int maxHp);

}

Interface를 상속 받아 같은 구조로 된 메소드에 구현 한다.

using UnityEngine;

using UnityEngine.UI;


public class BaseHpBarView : MonoBehaviour, IHpBarView

{

    [SerializeField] private Image hpImage;


    // 인터페이스 메서드 구현 → 반드시 public

    public void UpdateHpBar(int currentHp, int maxHp)

    {

        float ratio = (float)currentHp / maxHp;

        hpImage.fillAmount = ratio; // 단순 예제

    }

}


그러면 하위 클래스는 코드 없이 사용가능하다.

public class BossHpBarView : BaseHpBarView

{

    // IHpBarView 다시 구현할 필요 없음

    // 필요 시 UpdateHpBar override 가능

}


public class MainCharHpBarView : BaseHpBarView

{

    // 특화 로직 override 가능

}


Presenter에서 호출 할 떄는 아래와 같이 쓴다.

public class HpBarPresenter : MonoBehaviour

{

    private IHpBarView view;

    private int currentHp = 100;

    private int maxHp = 100;


    // Init 메서드로 런타임에 View 주입

    public void Init(IHpBarView view)

    {

        this.view = view;

        view.UpdateHpBar(currentHp, maxHp); // 초기화

    }


    public void TakeDamage(int dmg)

    {

        currentHp = Mathf.Max(0, currentHp - dmg);

        view.UpdateHpBar(currentHp, maxHp);

    }

}


 핵심은 IHpBarView를 넘겨받아 쓴다는 것이다. 그래서 baseView던지 boss던지 mainchar던지 상관없이 view를 넘겨 받을 수 있다는 게 핵심으로 보인다.


 chatGPT의 도움을 받아 작성하였습니다.

[유니티][Unity] Color 값을 메인케릭터와 보스가 같이 공유해서 사용하는 방법

 ScriptableObject를 통해 만들어진 컬러 값을 가져와서 사용하면 된다.

아래 코드 같이 만든 후 사용한다.


using NUnit.Framework;

using UnityEngine;

using System.Collections.Generic;


[CreateAssetMenu(menuName = "UI/HP Color Config")]

public class HpColorConfig : ScriptableObject

{

    public List<Color> hpBarColors;

    public Color animatedBarColor;

    public Color depletedHPColor = Color.black;    

}


CreateAssetMenu를 통해 UI  -> HP Color Config라는 메뉴가 생겼다.


Project의 Assets 폴더에서 Create -> UI -> HP Color Config를 누르면 생기는데 그 이름을 HP Color Config로 저장하자. 이것을 HP Color Config Asset이라 한다. 그걸 눌러보면 위에 선언한 Color 값들을 Inspector에서 조절할 수 있다. 이렇게 만들어 놓은 값들을 사용하기 위해 사용하려는 곳에 가서 

    [Header("Config")]

    [SerializeField] private HpColorConfig colorConfig;

로 선언하고 이 colorConfig 값에 만들어 놓은 HP Color Config Asset을 넣으면 연동이 된다.

 그 후 colorConfig.hpBarColors[index] 라는 식으로 사용하면 된다.


 chatGPT의 도움을 받아 작성하였습니다.

[유니티][Unity] Text 보다는 TextMeshPro를 써야 하는 이유와 TextMeshProugui와 tmp_text 비교

항목TextTextMeshPro
선명도❌ 낮음✅ 매우 선명
해상도 대응❌ 깨짐✅ 완벽
외곽선/그림자❌ 제한적✅ 매우 강력
성능❌ 상대적으로 나쁨✅ 더 좋음
숫자 전용 폰트❌ 불편✅ 최적화 가능


이렇게 차이가 많이 나기에 TextMeshPro를 택하는게 맞다.


TextMeshProUGUI와 TMP_Text는 조금 역할이 달라

UI는 TextMeshProUGUI를 쓰고 TMP_Text는 부모 클래스야


TextMeshProUGUI: 실제 UI에 붙는 컴포넌트로 HP바, 버튼, HUD, 데미지 숫자 전부 이걸로 써

TMP_Text는 TextMeshProUGIUI와 TextMeshPro의 공통 부모 클래스로 추상화용 타입이야. Inspector에서 붙는 컴포넌트는 아니고 스크립트에서 타입 통합용으로 사용하지.


TMP_Text text; 라고 하면 

UI용 TextMeshProUGUI 일 수도 있고 3D용 TextMeshPro 일수도 있어


그래서 언제 사용하는 건데?

UI 고정일때 (HP바,  HUD) TextMeshProUGUI를 쓰면

 가장 안전

 Inspector 연결 실수 없음

 의도 명확함


 UI/3D 텍스트 둘 다 받고자 하면 TMP_Text를 쓰지

 공용 처리 가능하고 재사용성이 올라가지만 실수로 3D TMP랑 연결될 수도 있어




  chatGPT를 참조하여 만들었습니다.

2025년 12월 5일 금요일

[유니티][Unity] Anchor 프리셋이 좌우 Stretch일 때 HP 깍이는 에니메이션 코드(feat.DOTween)

 애니메이션 효과를 구현하기 위해서는 세가지 Image가 필요하다.

Background, Animation, Cur Image 가 필요하다.

 여기서 백그라운드 이미지는 피 색을 7가지로 했을 경우에 curImage의 색상보다 index -1 값을 가지게 해서 마치 다음 피통을 미리 보여주는 효과를 가지게 한다. 


 그리고 Animation 은 피가 깍이는 효과를 애니메이션 효과를 넣어서 표현하는 것이다.

 여기서 DOTween을 쓰게 되면 애니메이션을 주기위해 lerp timer를 넣어서 어쩌고 저쩌고 해서 마치 선형적으로 내려가는 듯이 보이는 효과를 주는 것을 알아서 해준다. 그래서 코드가 단순해졌다. 

 그리고 그 위에 현재 피통을 보여주고 피가 깍이는 순간 깍인 곳까지 바로 이동한다.


 그렇게 하면 피가 깍이는 순간 바로 내려가고 내려간만큼 애니메이션 효과가 느리게 따라오면서 애니메이션 효과를 주면서 피가 깍이는 듯이 보이게 된다.


 이 과정을 다른 블로그를 참조로 해서 만들었더니 대략 100줄 정도의 코드가 필요했는데 이게 Stretch 모드를 아닌 상황에서 구현이 되어 있었는데, Stretch 모드를 적용하니 먹통이 되버린 것이다...


 그래서 난감했는데 제미나이에게 물어보니 DOTween을 추천해줬다. AI가 나보다 훨씬 코드를 잘 아는 이상황이 특이점이 이미 와버린듯 하다.

 그래서 DOTWEEN을 적용하니 10줄로 깔끔하게 떨어져 버렸다.


 아래 코드를 참조하면 된다.

public void AnimateBarWidth(float targetRatio, Color curBarColor, Color backBarColor)

{

    Vector2 targetAnchorMax = new Vector2(targetRatio, mainBarRectTransform.anchorMax.y);


    // -------------------------------------------------------------

    // 1. [즉시 동작] 빠른 바 (mainBarRectTransform) 처리: HP 표시 및 색상 갱신


    // A. 앵커를 목표 위치로 즉시 설정 (뒤쪽의 느린 바가 노출되도록)

    mainBarRectTransform.anchorMax = targetAnchorMax;


    // B. 빠른 바의 색상을 Presenter가 결정한 최종 색상으로 즉시 설정합니다.

    if (mainBarImage != null)

    {

        mainBarImage.color = curBarColor;

        backImage.color = backBarColor;

    }


    // -------------------------------------------------------------

    // 2. [천천히 동작] 느린 바 (damageMarkerRectTransform) 처리: 배경 애니메이션


    damageMarkerRectTransform.DOKill(true); // 이전 트윈 정리


    // A. 색상 고정: 느린 바의 색상을 인스펙터에 설정된 고정 배경 색상으로 즉시 설정

    // * 색상 깜빡임 로직이 모두 제거됨 *

    if (damageMarkerImage != null)

    {

        damageMarkerImage.color = slowBarOriginalColor;

    }


    // B. 앵커(크기) 애니메이션 (천천히 줄어들기)

    // 이 부분이 유일한 애니메이션입니다.

    damageMarkerRectTransform.DOAnchorMax(targetAnchorMax, duration)

        .SetEase(Ease.OutQuad);


제미나이의 도움을 받아 작성하였습니다.

[유니티][Unity] HP bar UI 크기가 폰 화면에 따라 바뀌는 방법

  HP Bar를 만들었다면 게임 할 때 원하는 위치에 원하는 크기로 보이고 싶을 것이다.

 그런데, 이럴수가 폰 마다 화면액정이 다 다른데, 이걸 어떻게 하나.


 어떻게 하면 HP Bar 크기를 폰 크기에 상관 없이 일정하게 보여줄 수 있을까?

 이렇게 하려면, 폰마다의 크기에 맞춰서 크기를 보여줘야 한다.

 말하자면, 동적으로 크기가 바뀌어야 하는 것이다.


 그렇게 하려면 아래와 같이 하면 된다.

 Canvas scalar를 scale with Screen size로 동적으로 변경되도록 설정해준다.


 체력바 Rect Transform으로 이동하여 화면 상단에 좌우로 꽉차고 위아래는 화면 크기의 5%인 HP Bar를 만든다고 치자.

 1. 앵커 프리셋 설정(가장 중요)

 체력바 오브젝트(예: Image 또는 Panel)의 Rect Transform으로 이동

 1. Anchor Presets 버튼 클릭

 2. Alt 키를 누른 채, 상단 우측에 있는 가로 스트레치(Stretch, Top) 프리셋을 선택한다.

    Alt 키를 누르면 앵커와 함께 피벗(Pivot)과 포지션(Position)이 동시에 이동하여 원하는 자리에 배치됨


2. 너비 IWidth) 설정: 좌우 꽉 채우기

 앵커를 가로 스트레치로 설정했기 때문에, 너비(Width)대신 Left와 Right 오프셋 값이 활성화 됨

Left: 화면 왼쪽 경계(Anchor)로 부터의 거리를 0부터 설정가능

Right: 화면 오른쪽 경계(Anchor)로 부터의 거리를 0부터 설정가능


3. 높이 (Height) 설정: 화면 높이의 5%

 높이는 Canvas Scaler에 설정된 Reference Resolution을 기준으로 계산 된 값을 입력하면 됨

1. 기준 해상도 확인: Canvas Scaler에 설정된 Reference Resolution의 높이 확인 (예: 1080x1920)

2. 높이 계산: 기준 높이의 5% 계산

   1920* 0.05 = 96

3. 최종 Rect Transform 값 :

 Anchors: Left0, right 1(가로 스트레치)

Pivot: (0.5, 1)

 Left: 0

 Right: 0

 Height: 96 (사용자의 Reference Resolution 에 따라 달라짐)


 여기서 Height를 96으로 하드코딩해도 resolution에 따라 값이 변하는 이유는 canvas Scaler 컴포넌트의 Scale with screen size 설정을 했기 때문이다.


 이렇게 설정하면, 캔버스 스케일러의 기준 해상도(Reference Resolution)에 대한 상대적인 크기로 계산해 준다.

 캔버스 스케일러의 동적 스케일링 원리

 1. 기준 해상도 (Reference Reolution)의 역할

 96이라는 Height를 설정하면, Reference Resolution에서 96 크기를 가지는 것을 의미함

 2. 유니티가 런타임에 접속한 장시의 실제 해상도를 기준 해상도와 비교하여 계산된 스케일링 계수를 캔버스 전체에 적용함


[유니티][Unity] 피벗(Pivot)과 앵커(Anchor)와 포지션(Postion)에 대하여

 앵커, 피벗, 위치는 유니티 UI 시스템에서 요소의 크기, 위치, 그리고 반응성을 결정하는데 사용되는 Rect Transform 컴포넌트의 세 가지 핵심 개념이다.


 다양한 화면 크기(해상도)에 대응 하는 UI를 만들 때 매우 중요하다


 1. 앵커(Anchor)
 부모 요소(화면 또는 다른UI)의 어느 위치를 기준으로 삼을지를 결정하는 네 개의 작은 삼각형 모양의 마커이다.

  UI 요소의 위치와 크기를 결정하는 기준점  또는 기준 프레임으로 부모 요소의 크기가 변할 때, 이 기준점에 고정되거나 기준점을 따라 늘어나면서 UI의 반응성(Resilience)을 결정한다.


 앵커의 두 가지 주요 모드

 앵커 마커가 모여 있느냐, 떨어져 있느냐에 따라 요소의 위치와 크기 작동 방식이 달라진다.

 1. 고정 모드(Fixed/Grouped Anchor)

 마커 상태: 앵커 마커 네 개가 한점에 모여 있음( 예: 중앙, 좌상단 등)

 결과: 요소의 크기는 고정되고, 위치만 앵커를 따라 이동함. Position은 앵커를 기준으로 한 거리가 됨

 

 2. 스트레치 모드 (Stretch/Split Anchor)

 마커 상태: 앵커 마커가 서로 떨어져 있음 (예: 좌우로 떨어져 있음)
 결과: 요소의 크기가 부모 요소의 크기에 따라 늘어남. Left, Right, Top, Bottom 값이 활성화 되며, 이 값들은 앵커 경계선으로부터의 패딩(Padding) 거리를 지정함


2. 위치(포지션) 및 Offset (오프셋)

 위치 값은 UI 요소의 피벗(Pivot)이 앵커가 지정한 기준점 또는 기준 프레임에서 얼마나 떨어져 있는지 나타냄


 앵커를 기준으로 측정되는 UI 요소의 실제 중심점 위치 (픽셀 단위)

요소의 정확한 화면상 좌표를 결정함. 앵커가 어디에 있느냐에 따라 값의 의미가 달라진다.

 앵커가 고정 모드 일때는 Pos X, Pos Y로 표시되고, 앵커가 스트레치 모드일 때는 Left, Right, Top, Bottom(오프셋) 값으로 표시 된다.


 Position(0,0) 이 앵커가 화면 중앙이라면 중앙을 뜻하고 좌상단이면 좌상단 모서리를 의미함


 3. 피벗 (Pivot)

 피벗은 UI 요소 자체 내에서 변환(크기 조절, 회전 등)이 일어나는 기준점임


 요소 자체의 로컬 좌표계에서 (0,0)을 나타내는 회전 및 크기 조절의 중심점


 (0,0) 부터 (1,1) 사이의 정규화 된 값으로 표현 됨. (0.5,0.5)가 기본값으로, 요소의 정중앙을 의미함


 요소를 회전시키거나 스케일을 변경할 때, 이 피벗을 중심으로 변화가 일어남. 또한 Position 값이 피벗을 기준으로 측정됨.


 세 요소의 관계 요약

 세 가지 개념은 다음과 같은 순서로 상호작용함

 1. 앵커가 부모 화면/요소 내에서 UI의 기준점/기준 영역을 정함 (어디를 따라 움직일 것인가?)

 2. 위치 (Position/Offset)가 앵커 기준점에서 피벗(Pivot) 까지의 거리를 결정하여 요소의 최종 위치를 확정함 (기준으로부터 얼마나 떨어져 있을 것인가?)
 

 3. 피벗은 요소 내에서 움직임과 변형의 중심 역할을 함 (내 안에서 어디를 중심으로 돌고 늘어날 것인가?)


 따라서 화면 크기가 바뀌어 앵커의 위치가 변하면, Position 값은 유지된 채 요소의 피벗이 애어를 따라가게 되어, UI 요소가 의도한 반응성을 갖게 됨


 위치는 피벗 위치를 변경 (0,0) -> (1,1) 로 하면 위치(Position)도 변경하게 된다.


 위치(Position)가 피벗 위치에 따라 자동으로 변경되더라도 위치는 수동으로 지정할 필요가 있다.

 위치(Position)의 가장 중요한 역할은 UI 요소의 실제 위치를 결정하는 것이기 때문이다.

 1. 포지션의 핵심 역할: 배치 (Placement)

 UI 요소를 앵커(Anchor)를 기준으로 원하는 곳에 놓기 위해 포지션 값을 사용함

 예) 앵커를 화면 정중앙 (0.5,0.5)에 설정했다고 할 때

 버튼을 중앙에서 오른쪽으로 50픽셀 떨어진 곳에 배치하고 싶다면, Pos X를 50으로 직접 입력해야 함(Pos X:50, Pos Y: 0)

 이 경우에는 유니티가 Position을 자동으로 계산해 주지 않음. 개발자가 원하는 Offset(오프셋) 거리를 포지션 값으로 지정하는 것임


 2. 피벗 변경은 일회성 보정

 피벗을 변경하는 작업은 보통 UI 요소를 배치하는 작업 중 한 번만 이루어진다. 그 이후에는 앵커를 기준으로 포지션 값을 직접 변경하며 UI요소들을 정렬하고 배치하는 것이 주된 작업 방식임


 결론은, 피벗 변경 시의 자동 조정은 위치를 고정하는 기능이고, 포지션을 수동으로 입력하는 것은 위치를 이동시키는 기능이라고 구분하여 이해하면 됨


 제미나이의 도움을 받아 작성하였습니다. 

 

2025년 12월 3일 수요일

[유니티][Unity][MacOS][iOS][Windows] 유니티 Windows 개발 환경에서 아이폰에 앱 빌드하기

iPhone 에 빌드하려면 macOS를 이용해야만 한다.
 
 이 제약사항으로 인해 Windows -> iPhone으로 앱 빌드하는 과정이 꽤 먼길을 가게 된다.

 Git 을 이용해 코드를 받아 오게 되는데 해 보니 Unity Version Control이라는 방법도 있었다. 
 
 편한 방법을 쓰면 되겠다. 용량도 체크 해 보고

 여기서 gitlab을 사용하게 됐는데 알고보니 .gitignore로 서버에 안 올릴파일과 올릴 파일을 선별하는데

 이 전에 프로젝트가 2.5Gb라 큰 용량이 필요하다 했는데 .gitignore를 공식 사이트에서 unity 개발용으

로 받아온 후 서버에 올려 보니 10메가 정도 밖에 안되었다. 그래서 굳이 gitlab을 안 써도 됐을 거 같다.

 이렇게 gitlab으로 윈도우즈의 코드를 올리면 그 코드를 맥에서 받을 수 있다. git 을 쓰기 때문에 이렇게 안 하면 매번 빌드 새로 할 때마다 전체코드 혹은 변경된 코드들을 일일이 usb로 복사해서 넘겨야 되는데 사실상 어렵다.

 이렇게 해서 gitlab을 통해 맥북에 코드를 옮기게 되었다.
 
 옮기다 보니 알게 된 것이 ssh key 를 통해서 git을 사용하는 게 편했다. 

 https로 할려고 하니 token 을 발행해야 되는데 이게 발행 시점에만 비밀번호?를 준다. 그리고 닫는 순간 이 비번을 알 수가 없다. 그리고 엄청 길어서 이걸 제대로 쳐 넣기가 어렵다. 물론 복사 붙여 넣기 하면 되는데 윈도우즈에서 복사 붙여 넣기 해서 해결 했더니 맥에서 다시 비번을 물어보는데 이걸 가져와서 쓰기가 어렵고 번거롭다. ssh key는 각 각 windows 나 맥에서 받아 쓰면 되고 한번만 입력하면 되니 편리했다.

 이렇게 맥북에서 project 코드를 다 받고 유니티로 실행해 보니 아니 hierarchy에 Main Camera만 덩그러니 있는 거 아닌가.. 여기서 뭐가 잘못 됐지 하고 많이 헤맸는데 찾다찾다 보니 그냥 scene을 로딩 안해서 그랬던 것이었다. 매우 간단한데 처음 써보면 헤깔릴만 하지 않나 쉽다.

 이렇게 macOS에 코드를 옮기면 이제 iOS (iPhone)에 빌드를 해 넣으면 된다.

 물론 또 첨부터 이런 저런 이슈들로 한번에 안되는데 구글 AI한테 물어 보니 해결이 된다. 

 그렇게 iOS에서 처음으로 게임을 빌드 해 봤는데 UI가 너무 작게 나오고 터치 패드도 말을 안듣고 할 일이 참 많아졌다.

구글 AI의 도움을 받아 작성하였습니다.