안녕하세요

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


페이지 목록

2024년 12월 27일 금요일

[유니티][Unity][chatGPT] Unity2D에서 MVC로 구현한 보스가 주인공 위치에 원형의 범위기술을 사용 후 Duration시간 뒤에 터지는 기술

 Unity 2D에서 MVC(Model-View-Controller) 패턴으로 보스가 주인공 위치를 기준으로 원형 범위 공격을 수행하는 시스템을 설계하는 코드이다. MVC 패턴은 코드의 분리를 통해 가독성과 유지보수성을 높이는 것을 목표로 한다.



MVC 구조

  1. Model:

    • 데이터 및 로직을 처리합니다.
    • 스킬 반경, 경고 시간, 데미지 값 등을 관리.
  2. View:

    • 사용자에게 보여지는 경고 UI와 스킬 효과를 처리합니다.
    • 경고 UI 생성, 스킬 효과 표시 등을 담당.
  3. Controller:

    • 사용자 입력이나 로직의 흐름을 관리합니다.
    • 보스의 행동을 제어하고 Model과 View를 연결.

1. SkillModel (Model)

스킬 데이터를 관리하고 범위 내 타겟을 감지하는 로직을 구현합니다.

csharp
using UnityEngine; public class BossSkillModel { public float Radius { get; private set; } public float Damage { get; private set; } public float WarningDuration { get; private set; } public BossSkillModel(float radius, float damage, float warningDuration) { Radius = radius; Damage = damage; WarningDuration = warningDuration; } // 범위 내 타겟 감지 public Collider2D[] GetTargets(Vector2 position, LayerMask targetLayer) { return Physics2D.OverlapCircleAll(position, Radius, targetLayer); } }

2. SkillView (View)

스킬의 시각적 요소를 관리합니다.

 - 경고 UI 프리팹에 임의로 그림판으로 작성한 원을 넣어 주고, 스킬 효과 프리팹은 아직 작성하지 않았다. (스킬 효과 프리팹은 주석 처리해도 동작한다)

csharp
using UnityEngine; public class BossSkillView : MonoBehaviour { public GameObject WarningPrefab; // 경고 UI 프리팹 public GameObject EffectPrefab; // 스킬 효과 프리팹 private GameObject warningInstance; // 경고 UI 표시 public void ShowWarning(Vector2 position, float radius) { warningInstance = Instantiate(WarningPrefab, position, Quaternion.identity); warningInstance.transform.localScale = new Vector3(radius * 2, radius * 2, 1); } // 경고 UI 제거 public void HideWarning() { if (warningInstance != null) { Destroy(warningInstance); } } // 스킬 효과 표시 public void ShowSkillEffect(Vector2 position, float radius) { GameObject effect = Instantiate(EffectPrefab, position, Quaternion.identity); effect.transform.localScale = new Vector3(radius * 2, radius * 2, 1); } }

3. BossController (Controller)

보스의 행동을 관리하고 Model과 View를 연결합니다.

 - BossSkillController가 아니라 BossController인 이유는 보스가 사용하는 스킬이기 때문이고, 작은 프로젝트의 경우 BossSkillController를 따로 구분 하지 않고 하나의 BossController에서 사용해도 감당 가능하기에 BossController를 사용한다. 

   감당이 안된다면 BossSkillController를 별도로 만들어 사용해도 된다.

csharp
using UnityEngine; public class BossController : MonoBehaviour { public Transform player; // 주인공 Transform public LayerMask targetLayer; // 타겟 레이어 public SkillView skillView; // 스킬 View private SkillModel skillModel; // 스킬 데이터 void Start() { // Model 초기화 skillModel = new SkillModel(radius: 5.0f, damage: 50.0f, warningDuration: 2.0f); } public void StartSkill() { if (player == null) { Debug.LogError("Player가 설정되지 않았습니다!"); return; } // 주인공 위치 저장 Vector2 targetPosition = player.position; // View를 통해 경고 UI 표시 skillView.ShowWarning(targetPosition, skillModel.Radius); // 일정 시간 후 스킬 발동
StartCoroutine(ActivateSkillAfterDelay(targetPosition, bossSkillModel.WarningDuration)); }

    IEnumerator ActivateSkillAfterDelay(Vector2 targetPosition, float delay)     {     yield return new WaitForSeconds(delay);      ActivateSkill(targetPosition);     } private void ActivateSkill(Vector2 targetPosition) { // View에서 경고 UI 제거 skillView.HideWarning(); // View에서 스킬 효과 표시 skillView.ShowSkillEffect(targetPosition, skillModel.Radius); // Model을 통해 범위 내 타겟 감지 Collider2D[] hitTargets = skillModel.GetTargets(targetPosition, targetLayer); foreach (var target in hitTargets) { target.GetComponent<Health>()?.TakeDamage(skillModel.Damage); } Debug.Log("스킬 발동 완료!"); } }

4. Health (Model)

주인공이나 다른 타겟이 데미지를 받을 수 있도록 체력을 관리합니다.

csharp
using UnityEngine; public class Health : MonoBehaviour { public float MaxHealth = 100f; private float currentHealth; void Start() { currentHealth = MaxHealth; } public void TakeDamage(float amount) { currentHealth -= amount; Debug.Log($"{gameObject.name} 남은 체력: {currentHealth}"); if (currentHealth <= 0) { Die(); } } private void Die() { Debug.Log($"{gameObject.name} 사망"); Destroy(gameObject); } }

5. Unity 설정

1) 보스 설정

  1. Boss GameObject에 **BossController**와 SkillView 스크립트를 추가.
  2. SkillView 설정:
    • WarningPrefabEffectPrefab을 연결.
  3. BossController 설정:
    • 주인공의 Transform을 Player 필드에 연결.
    • TargetLayer에 적합한 레이어 설정(예: "Player").

2) 주인공 설정

  1. 주인공 GameObject에 Health 스크립트 추가.
  2. Inspector에서 MaxHealth 값 설정.

3) 경고 및 공격 효과 프리팹

  • WarningPrefab: 원형 이미지 기반 UI.
  • EffectPrefab: 원형 스프라이트 또는 파티클 효과.

6. 테스트

  1. BossControllerStartSkill 메서드를 호출(키 입력으로 테스트 가능).
    csharp
    void Update() { if (Input.GetKeyDown(KeyCode.F)) { StartSkill(); } }
  2. 주인공 이동:
    • 스킬 발동 위치와 데미지 처리가 주인공의 위치에 맞게 동작하는지 확인.

결과

  • 보스는 주인공의 위치를 감지하여 원형 범위 경고를 표시하고, 일정 시간 후 해당 위치에서 스킬을 발동한다.
  • 범위 내에 있는 대상(주인공 포함)은 데미지를 받는다.

MVC 패턴 장점

  1. 코드 분리:

    • 데이터(Model), 시각적 요소(View), 로직(Control)이 독립적으로 관리되므로 유지보수성 향상.
  2. 확장성:

    • 새로운 스킬 View 또는 Model을 쉽게 추가 가능.
  3. 테스트 용이성:

    • 각 컴포넌트를 독립적으로 테스트 가능.

** 이 글은 chatGPT의 도움으로 작성되었습니다.

2024년 12월 26일 목요일

[Copilot][Gemini][ChatGPT] 코파일럿 사용평가 - 코파일럿으로 코딩방법도 알려줘서 신기해서 다른 AI검색도 설치해 보았다

  코파일럿을 통해 코딩 방법에 대해 물어 보면서 웹 검색보다 빠르게 코드까지 보여주면서 알려주는게 신세계를 경험하였다. 예전에 잠시 코파일럿을 써봤을 때 일상적인 이야기에 대해서 물어보면 답변이 그닥 시원찮았기 때문에 별거 없다는 인식이 강했는데, 이번에 게임을 만들어가면서 모르는 걸 물어 봤을 때 MVC 모델이라던지 상속과 다형성 측면에서 접근 한다던지 생각하지도 못했던 길로 가이드를 해주는 게 이 정도까지 알려준다고? 하는 느낌을 지울 수가 없었다. 

 그래서 AI검색에 대해 관심이 가면서 Gemini와 chatGPT 까지 설치하게 되었다. 제미니와 chatGPT도 유사한 수준에서 답변을 제공받을 수 있었기에 3가지를 돌려 가면서 사용하면서 어떤 게 좋은지 좀 더 알아 봐야 겠다.

 이렇게 코딩에 대해 자세히 알려주다보니, 일상 질문들도 해보았는데, 이전에 대비해서 꽤나 상세하게 알려준다는 느낌이였다. 

 AI 검색 이전에는 웹 페이지를 검색해서(구글링) 구글이 보여주는 신뢰도 높은 결과물의 블로그를 들어가서 확인해보는 방식이였다면 AI 검색은 질문하면 답을 바로 보여주기에 빠르다는 느낌이 강하게 들었다.

 그런데 그냥 빠르면 사용하지 않겠지만, 구글링해서 이 블로그를 들어가보고 원하는 답변이 아니면 또 다른 블로그를 찾아 다니는 탐색 과정에서 시간이 많이 소모되게 되는데, AI 답변은 딱 내가 원하는 답을 바로 찾아 주기에 효율성이 좋았고, 물론 그 답변이 항상 정확하게 매칭되는 것은 아니였지만 몇가지 추가 질문을 통해 원하는 대답과 유사한 답변을 얻게 되는 확률이 꽤 높았다. 

 요약하자면, 구글링을 통해 여러 블로그를 뒤적이며 찾아가는 시간이 줄어들고, 질문을 하면 바로 답을 해주는 빠른 속도 + 꽤 상세하고 정확한 답변의 강점이 코딩하면서 모르는 내용을 알아 가는 시간을 많이 줄여준다는 느낌이 들었다. 

 오히려 일상적인 질문 보다 코딩에 대한 질문에 특화 되어있다는 느낌이 들 정도로 코딩 부분에 대한 질문이 더 좋았다는 느낌을 받았다. 물론, 내가 모르는 부분에 대해서 답변을 해줬기 때문에 그 내용을 좀 더 깊이 습득하면 AI의 결과가 꼭 맞지는 않았다는 걸 알게될 수도 있겠지만, 잘 모르는 상황에서 질문을 했을 때 그 대답이 꽤나 이대로 하면 구현 되겠는데? 하는 정도로 상세하게 답변을 해준다는 점이 놀라웠다고 하겠다.

 방대한 데이터를 요하는 일상 질문 보다 오히려 특정 타겟팅이 잡히는 코딩이라는 전문 분야에 대해 오히려 학습이 더 쉬웠나? 하는 느낌을 받았다. 

 쓰다보면 이런 얘기를 했다가 다른 식의 질문을 하면 또 그 내용에 대해 답변을 하면서 질문에 따라 답이 조금씩 달라지는 부분은 내가 잘 취사 선택해야 되겠다는 생각은 들었다. 

[C#][Gemini] 상속과 인터페이스의 차이점(인터페이스도 다형성을 사용할 수 있다)

 

상속과 인터페이스, 무엇이 다를까요?

C#에서 클래스는 상속을 통해 다른 클래스의 특징을 물려받고, 인터페이스를 구현하여 특정 기능을 약속할 수 있습니다.

  • 상속: 부모 클래스의 모든 멤버(필드, 메소드, 속성 등)를 자식 클래스에서 사용할 수 있도록 하는 관계입니다. 자식 클래스는 부모 클래스의 기능을 확장하거나 변경할 수 있습니다.
  • 인터페이스: 클래스가 반드시 구현해야 하는 메소드의 집합입니다. 인터페이스는 객체가 어떤 기능을 제공해야 하는지를 정의하며, 다양한 클래스에서 동일한 인터페이스를 구현하여 공통된 기능을 제공할 수 있도록 합니다.

왜 상속과 인터페이스를 사용할까요?

  • 코드 재사용: 이미 만들어진 클래스를 상속하거나 인터페이스를 구현하여 코드를 재사용하고 개발 시간을 단축할 수 있습니다.
  • 다형성: 다양한 객체를 동일한 타입으로 다룰 수 있어 코드의 유연성을 높입니다.
  • 모듈화: 시스템을 작은 단위로 나누어 관리하고 재사용성을 높일 수 있습니다.

예시 코드

C#
// 인터페이스
interface IFlyable {
    void Fly();
}

// 부모 클래스
class Animal {
    public void Move() {
        Console.WriteLine("움직인다.");
    }
}

// 자식 클래스 (상속 및 인터페이스 구현)
class Bird : Animal, IFlyable {
    public void Fly() {
        Console.WriteLine("날아다닌다.");
    }
}

// 자식 클래스 (상속만)
class Dog : Animal { }
  • Bird 클래스는 Animal 클래스를 상속받아 Move() 메소드를 사용할 수 있으며, IFlyable 인터페이스를 구현하여 Fly() 메소드를 제공합니다.
  • Dog 클래스는 Animal 클래스만 상속받아 Move() 메소드만 사용할 수 있습니다.
C#
Animal animal = new Bird();
IFlyable flyable = new Bird();

animal.Move(); // 출력: 움직인다.
flyable.Fly(); // 출력: 날아다닌다.

위 코드에서 Bird 객체는 Animal 타입으로도, IFlyable 타입으로도 사용될 수 있습니다.

상속과 인터페이스의 차이점

특징상속인터페이스
관계is-a 관계 (Bird is a Animal)can-do 관계 (Bird can fly)
구현부모 클래스의 모든 멤버를 상속받음인터페이스에서 정의된 메소드를 모두 구현해야 함
다중 상속불가능여러 인터페이스를 구현할 수 있음
상태상태를 가질 수 있음상태를 가질 수 없음 (메소드 선언만 가능)

언제 어떤 것을 사용해야 할까요?

  • 상속: 클래스 간의 is-a 관계가 명확하고, 부모 클래스의 기능을 확장하거나 변경해야 할 때 사용합니다.
  • 인터페이스: 다양한 클래스에서 동일한 기능을 제공해야 할 때, 또는 클래스 간의 의존성을 줄이고 유연성을 높이고 싶을 때 사용합니다.

핵심 포인트

  • 상속과 인터페이스는 객체지향 프로그래밍에서 코드 재사용성과 유연성을 높이는 중요한 개념입니다.
  • 상속은 클래스 간의 계층 구조를 만들고, 인터페이스는 클래스가 제공해야 하는 기능을 정의합니다.
  • 다형성을 통해 하나의 변수로 다양한 객체를 참조할 수 있습니다.

다른 예시

  • 다양한 동물 예시: 고양이, 강아지, 새 등 다양한 동물을 클래스로 만들고, 공통적인 특징(Animal)과 각 동물의 특징(Flyable, Swimmable 등)을 인터페이스와 상속으로 구현해 보세요.
  • 게임 캐릭터 예시: 몬스터, 플레이어, NPC 등 다양한 캐릭터를 클래스로 만들고, 공격, 방어, 이동 등의 기능을 인터페이스로 구현해 보세요.

심화 학습

  • 추상 클래스: 상속과 인터페이스의 중간 개념으로, 추상 메소드를 가질 수 있는 클래스입니다.
  • Generic: 다양한 타입의 데이터를 다룰 수 있도록 하는 기능입니다.
  • Extension Method: 기존 클래스에 새로운 메소드를 추가할 수 있는 기능입니다.

이러한 개념들을 이해하면 C#으로 더욱 효율적이고 유연한 프로그램을 개발할 수 있습니다.

 ** 본 내용은 Google AI 챗봇(Gemini-제미니)과의 대화를 바탕으로 작성되었습니다. 

2024년 12월 25일 수요일

[유니티][Unity][Copilot] monobehaviour을 사용하지 않는 클래스에서 UI 변경하는 방법

MonoBehaviour를 사용하지 않는 클래스에서도 UI 변경을 할 수가 있었다. 그 방법은 해당 클래스를 MonoBehaviour의 Start나 Update 에 올리는 것이다. Unity에서는 Unity 엔진에 의해 UI 요소들이 그려지는데, Start Update 내에 올려 놓으면 자동으로 처리하는 방식이기 때문이다. 

  1. 참조가 올바르게 설정되어 있음:

    • MonoBehaviour가 없는 클래스에서 Image를 변경할 때, 해당 Image에 대한 참조가 올바르게 설정되어 있기 때문에 변경된 사항이 반영됩니다.

  2. Unity의 업데이트 루프:

    • Unity의 렌더링 파이프라인은 매 프레임마다 UI 요소를 업데이트합니다. 따라서 UI 요소가 변경되면, 그 변경 사항이 다음 렌더링 프레임에 반영됩니다.

  3. UI 요소의 자동 갱신:

    • Image와 같은 UI 요소는 Unity에 의해 자동으로 갱신됩니다. 따라서 MonoBehaviourUpdate 메서드에서 주기적으로 상태를 확인하고 업데이트할 필요가 없습니다.

예제 코드

UIManager 클래스 (MonoBehaviour를 상속)

csharp
using UnityEngine;
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    public Image healthBar;

    private Player player;

    private void Start()
    {
        player = new Player();
        player.ChangeHealthBar(healthBar, Color.green);
    }

    private void Update()
    {
        // 매 프레임마다 UI를 자동으로 갱신합니다.
        // 이는 Unity 엔진이 관리합니다.
    }
}

Player 클래스 (MonoBehaviour를 상속하지 않음)

csharp
using UnityEngine.UI;
using UnityEngine;

public class Player
{
    public void ChangeHealthBar(Image healthBar, Color color)
    {
        if (healthBar != null)
        {
            healthBar.color = color; // Image의 색상을 변경
        }
    }
}

설명

  • UIManager 클래스:

    • MonoBehaviour를 상속받아 Unity의 생명 주기 메서드를 사용합니다.

    • Start 메서드에서 Player 클래스를 인스턴스화하고, Player 클래스의 메서드를 호출하여 Image 색상을 변경합니다.

  • Player 클래스:

    • MonoBehaviour를 상속받지 않지만 Image에 대한 참조를 받아서 직접 변경합니다.

이로 인해, MonoBehaviour가 없는 클래스에서 변경한 ImageMonoBehaviourUpdate 메서드에서 정상적으로 반영되는 것입니다. 중요한 점은 Image 컴포넌트에 대한 참조가 올바르게 설정되고, Unity의 렌더링 파이프라인이 이를 자동으로 처리한다는 것입니다.


**본 문은 Microsoft Copilot의 도움을 받아 제작되었습니다.

[유니티][Unity][Copilot] GetComponent는 같은 오브젝트 내의 컴포넌트만 검색한다

 GetComponent는 오브젝트 내의 다른 component를 검색한다.

다른 Object의 Component를 가져올려면 다른 방법을 사용해야 한다.

FindFirstObjectByType<찾아오려는Component> 를 사용하면 찾아 올 수 있다.

** 본 문은 Microsoft Copilot의 도움을 받아 제작되었습니다.

[C#][Copilot] #define NUM 3 을 C#(Unity)에서는 어떻게 표현하는가? (const vs readonly)

 #define NUM 3 으로 상수를 표현하는 방법을 C#에서는 권장하지 않고 const 나 static readonly를 사용한다고 한다.

 const는 상수 값을 설정하는 것이고 static readonly는 runtime에 세팅을 하게 된다.

 두 방법의 차이점이다.

const: 컴파일 시점에 값이 결정되고, 변경될 일이 없는 상수에 사용합니다. 값은 불변하고, 성능이 뛰어납니다.

static readonly: 런타임 시점에 초기화되며, 이후 값이 변경되지 않는 상수에 사용합니다. 객체 초기화 시점에 값을 설정할 수 있습니다.


 이해하기로는 상수로 처음부터 사용하는 경우는 const를 사용하고,

runtime에 들어가서 최초로 한 번만 값을 세팅하는 경우에는 static readonly를 사용하게 된다.

 즉, 런타임에 초기화 시키려면 static readonly 를 쓰고 아예 처음부터 상수로 쓰려면 const를 사용하면 된다.


** 본 문은 Microsoft Copilot의 도움을 받아 제작되었습니다.

[유니티][Unity][Copilot] Image image1 = image2로 복사와 image1.color = image2.color와 같이 내부 항목 복사의 차이점

 Image image1 = image2 로 복사를 하게 되면 참조 값만 변경이 되게 된다.

즉, image1과 image2가 같은 image2를 가르키게 되는 것이다. 

 이렇게 하면 내부 값이 아니라 참조 값만 복사되기에 각 각의 객체로 독립적으로 존재하지 않게 된다.

 그래서 각 항목을 복사해줘야지 원하는 값 전달이 이루어진다.

아래와 같다.

using UnityEngine;

using UnityEngine.UI;


public class ImageProcessor : MonoBehaviour

{

    public Image image1;

    public Image image2;


    private void Start()

    {

        if (image1 == null || image2 == null)

        {

            Debug.LogError("Image components are not assigned!");

            return;

        }


        CopyImageAttributes(image1, image2);

    }


    public void CopyImageAttributes(Image source, Image target)

    {

        if (source != null && target != null)

        {

            target.sprite = source.sprite;

            target.rectTransform.sizeDelta = source.rectTransform.sizeDelta;

            target.rectTransform.anchoredPosition = source.rectTransform.anchoredPosition;

            target.rectTransform.localScale = source.rectTransform.localScale;

            target.color = source.color;

        }

        else

        {

            Debug.LogError("Source or target image is null!");

        }

    }

}

 보통은 이렇게 5개 정도 넘겨 주면 되는데 더 다양한 값도 넘겨 줄 수 있다.
using UnityEngine;
using UnityEngine.UI;

public class ImageProcessor : MonoBehaviour
{
    public Image image1;
    public Image image2;

    private void Start()
    {
        if (image1 == null || image2 == null)
        {
            Debug.LogError("Image components are not assigned!");
            return;
        }

        CopyImageAttributes(image1, image2);
    }

    public void CopyImageAttributes(Image source, Image target)
    {
        if (source != null && target != null)
        {
            target.sprite = source.sprite;
            target.rectTransform.sizeDelta = source.rectTransform.sizeDelta;
            target.rectTransform.anchoredPosition = source.rectTransform.anchoredPosition;
            target.rectTransform.localScale = source.rectTransform.localScale;
            target.color = source.color;

            // 필요에 따라 추가적인 속성 복사
            target.material = source.material;
            target.type = source.type;
            target.preserveAspect = source.preserveAspect;
            target.fillCenter = source.fillCenter;
            target.fillMethod = source.fillMethod;
            target.fillAmount = source.fillAmount;
            target.fillClockwise = source.fillClockwise;
            target.fillOrigin = source.fillOrigin;
        }
        else
        {
            Debug.LogError("Source or target image is null!");
        }
    }
}

** 이 글은 Microsoft Copilot의 도움을 받아 제작되었습니다.

[유니티][Unity][Copilot] UI 조작하는 클래스에서 조작한 데이터가 UI에 변경되지 않은 이유

 중복으로 작성해야 되는 UI 변경 코드를 한번만 쓰기 위해서 별도 class에 작성하였는데,

이 class에서 UI를 변경해도 결과가 반영이 되지 않았다.

 왜 이런가 고민하다 살펴보니 이 class에는 monobehaviour를 상속받지 않아서 발생하는 것이었다. 

 이를 해결하기 위해서는 아래와 같이 monobehaviour가 없는 class에 그릴려고 하는 UI가 있는 class를 상속받아서 그려주는 식으로 처리를 하면 된다고 한다.


 아래와 같이 작성하면 된다.

// 비 MonoBehaviour 클래스

public class DataProcessor

{

    private UIManager uiManager;


    public DataProcessor(UIManager manager)

    {

        uiManager = manager;

    }


    public void ProcessData()

    {

        // 데이터 처리 후 UI 변경

        uiManager.UpdateUI("Processing Complete", someSprite);

    }

}


// MonoBehaviour 클래스

public class UIManager : MonoBehaviour

{

    public Text myText;

    public Image myImage;


    void Start()

    {

        if (myText == null || myImage == null)

        {

            Debug.LogError("UI components are not assigned!");

        }

    }


    public void UpdateUI(string newText, Sprite newSprite)

    {

        if (myText != null)

        {

            myText.text = newText;

        }


        if (myImage != null)

        {

            myImage.sprite = newSprite;

        }

    }

}


// 게임 관리 클래스

public class GameManager : MonoBehaviour

{

    public UIManager uiManager;

    private DataProcessor dataProcessor;

    public Sprite someSprite;


    void Start()

    {

        dataProcessor = new DataProcessor(uiManager);

    }


    void Update()

    {

        if (Input.GetKeyDown(KeyCode.Space))

        {

            dataProcessor.ProcessData();

        }

    }

}

**본 문은 Microsoft Copilot의 도움을 받아 제작되었습니다.
 새로 만든 Image가 조작한 Image를 가리고 있어서 Image 변경이 안 된 걸로 보였더군요. 본문에 있는 방법이 아니라 다른 방법을 통해 Monobehaviour가 없는 클래스에서도 UI 컨트롤이 가능합니다. 하는 방법은 아래 링크 참조바랍니다.

[유니티][Unity][Copilot] Awake와 Start는 뭐가 더 빨리 호출 되나

  Awake와 Start 메서드는 게임 오브젝트의 초기화 단계에서 호출되지만, 호출 시점에 차이가 있다. 

 

 Awake 메서드

 호출 시점: 게임 오브젝트가 활성화될 때 가장 먼저 호출한다.

 목적: 게임 오브젝트의 초기 상태 설정 및 다른 컴포넌트나 오브젝트와의 초기 설정 작업을 수행한다.

 호출 순서: 같은 게임 오브젝트의 모든 Awake 메서드는 다른 모든 Start 메서드 보다 먼저 호출된다. (중요 - Awake 가 무조건 Start 보다 빠르다)


Start 메서드

호출 시점 : Awake가 호출된 후, 첫번 째 프레임 업데이트 전에 호출된다.

목적 : Awake 메서드가 호출 된 후 수행되는 초기화 작업을 처리한다.

호출 순서: 모든 Awake 메서드가 호출된 후에 호출 된다.


호출 순서 요약

 Awake -> OnEnable -> Start 순서이다.


using UnityEngine;


public class Example : MonoBehaviour

{

    private void Awake()

    {

        Debug.Log("Awake called");

    }


    private void Start()

    {

        Debug.Log("Start called");

    }

}

이렇게 Log를 찍으면 
Awake called
Start called

이런 순서로 찍히게 된다.


요약
 Awake 메서드는 Start 메서드보다 먼저 호출된다. 그렇기에 초기화 작업을 수행할 때, Awake 메서드에서 기본 설정과 초기화 작업을 처리하고, Start 메서드에서 Awake 이 후에 필요한 추가 초기화 작업을 처리하는게 좋다.

** 본 문은 Microsoft Copilot의 도움으로 작성되었습니다.




[C#][Copilot] out 과 ref의 유사성과 차이점

C++에서 &를 통해 하던 참조 연산이 C#에서는 막혀 있었다.


 이와 유사한 out과 ref가 있었는데 둘이 다르면서도 같은데 정확하게 어디서 써야 할 지는 아직은 미궁이다.

 out은 밖에서 변수를 초기화 하지 않아도 사용이 가능하며 하지만 method 안에서 반드시 초기화를 해주고 써야 한다.

 ref는 참조하는 형식으로 사용하는데 밖에서 반드시 초기화 해주고 써야 한다.


 ref는 좀 더 수정한다는 의미에 가깝고 out은 값을 반환하는 의미가 있다고 하는데 정확한 쓰임새는 좀더 알아 봐야 하겠다.


 Copilot에 따른 차이점

특징                                         out                            ref

초기화 필요                   여부 호출 전에 초기화 불필요     호출 전에 초기화 필요

메서드 내부 초기화 여부   반드시 초기화                         필요초기화 필요 없음

값 반환                        여러 값을 반환 가능                  수정된 값을 반환

사용 목적                      값을 반환하기 위한 매개변수     값을 수정하기 위한 매개변수

사용 시점                      반환값이 여러 개 필요할 때      기존 변수 값을 변경할 때


** 본 문은 Microsoft Copilot의 도움으로 작성되었습니다.

2024년 12월 24일 화요일

[유니티][Unity][Copilot] 피깍이는 애니메이션 효과 재사용하는 방법

 인터페이스나 추상클래스를 통해 사용하는 방법도 있다고 하는데 아직까지는 어떤 식으로 적용해야 될지 정확하지가 않다.

 그래서 확인해 보니 static을 이용하여 공통 메소드를 관리하는 방법도 있다고 한다. 

 스크립트 분리와 모듈화라고 하는데 모듈화를 시켜 같은 동작하는 코드를 한 번만 사용하게 하는 것이다.

 이 과정에 대한 코드는 아래와 같다.

--공통 부분--(모듈화)

 public static class UIHelper

{

    public static void UpdateHealthUI(Slider healthSlider, Text healthText, float health, float maxHealth)

    {

        if (healthSlider != null)

        {

            healthSlider.value = health / maxHealth;

        }


        if (healthText != null)

        {

            healthText.text = "Health: " + health.ToString();

        }

    }

}

-- View에서 사용
public class PlayerView : MonoBehaviour
{
    public Slider healthSlider;
    public Text healthText;
    private float currentHealth = 100f;

    void Update()
    {
        // 매 프레임마다 UI 업데이트
        UIHelper.UpdateHealthUI(healthSlider, healthText, currentHealth, 100f);
    }
}

이런 식으로 사용하게 된다.
 static으로 만드는 장점은 다음과 같다.

1. 객체 생성 불필요

  • static 메서드는 클래스 자체의 메서드로, 특정 객체(instance)에 속하지 않습니다. 따라서 객체를 생성하지 않고도 직접 호출할 수 있습니다.

  • 예를 들어, 공통으로 사용되는 유틸리티 함수는 특정 객체에 종속되지 않으므로 static 메서드로 만들면 편리합니다.

2. 전역 접근 가능

  • static 메서드는 클래스의 인스턴스화 없이 클래스 이름을 통해 전역적으로 접근할 수 있습니다.

  • 공통 기능이나 유틸리티 메서드를 전역적으로 사용해야 할 때 유용합니다.

3. 성능 향상

  • 객체를 생성하지 않기 때문에 메모리와 성능 측면에서 이점이 있습니다.

  • 예를 들어, 단순한 연산이나 변환 작업을 수행하는 메서드는 객체를 생성하지 않고 호출할 수 있어 더 효율적입니다.

4. 상태 관리 불필요

  • static 메서드는 클래스의 인스턴스 상태와 무관합니다. 즉, 인스턴스 변수에 접근할 필요가 없는 경우 적합합니다.

  • 공통 기능을 수행하지만 특정 객체의 상태를 변경할 필요가 없는 경우 유용합니다.


**본 문은 Microsoft Copilot의 도움을 받아 제작되었습니다.

2024년 12월 23일 월요일

[유니티][코파일럿][Unity][Copilot] Tricky한 이전 값과 현재 값 비교하기 어떻게 하는건가?

 이전값과 현재값 비교는 초기화 때 이전 값에 현재값을 넣어 준 후 

 현재값과 이전 값을 비교 하면 된다.

  int curHP;

 int prevHP;

로 했을 때

 SwordMan(){

     curHP = 100;

      prevHP = curHP; 로 curHP와 동일 값이 되도록 해준다.
}

이 후 void Update() {

       if(prevHP == curHP) {

               //같을 때 처리할 것들 적기 

               //이 후 prev 값은 cur 값으로 교체해준다.

              prevHP = curHP;
       }


}

에서 비교해주면 된다.


  이 경우 curHP와 prevHP가 같은 거 아니냐 하고 생각할 수 있는데 아래 method 와 같이 curHP만 변경해주는 경우가 있기에 성립된다.

void Attacked (int atkPower) {
     curHP -= atkPower;

}

 어딘가에서 Attacked 가 호출 된 경우 Prev 값과 cur 값이 달라지게 된다.


** 글은 Microsoft Copilot의 도움을 받아 작성되었습니다. Copilot은 AI 기반 어시스턴트로, 정보 제공과 다양한 작업을 지원합니다.

[유니티][Unity] FindObjectOfType has been deprecated

 deprecated는 추천하지 않는 방법이라는 뜻으로 나중에 삭제 될 가능성이 있으니 다른 방법을 알아보라는 뜻이다.


 Visual Studio에서 추천하는 방법은 FindFirstObejctbyType 이다.


 이것은 object가 아닌 objects 도 있다고 한다. objects는 복수임으로 배열 형식으로 찾은 수만큼 알려준다고 한다. 

objects의 경우는 findObjectsOfType -> findObjectsByType 이 추천된다.

[Unity][Copilot][유니티][코파일럿] MVC 모델을 이용하여 개발을 해보자

  코파일럿을 타고 들어가다 보니 MVC 모델로 만들면 좋다고 한다.

 Model-View-Controller로 만든다는 것이다.

 Model은 데이터 관리를 하는 모델을 담당하고

 View는 UI에 그려주는 일을 담당한다.

 Controller에서는 Model과 View를 이어주는 역할을 한다.


 이렇게 만들면 유지 보수와 재사용성을 높일 수 있다고 한다. 정말 그런지 맛을 한번 보자.


--Model--

public class PlayerModel

{

    public int Health { get; set; }

    public int Score { get; set; }


    public PlayerModel(int health, int score)

    {

        Health = health;

        Score = score;

    }


    public void TakeDamage(int damage)

    {

        Health -= damage;

        if (Health < 0)

        {

            Health = 0;

        }

    }


    public void AddScore(int points)

    {

        Score += points;

    }

}


--View --

using UnityEngine;

using UnityEngine.UI;


public class PlayerView : MonoBehaviour

{

    public Text healthText;

    public Text scoreText;


    public void UpdateHealth(int health)

    {

        healthText.text = "Health: " + health;

    }


    public void UpdateScore(int score)

    {

        scoreText.text = "Score: " + score;

    }

}


--Controller--
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private PlayerModel playerModel;
    private PlayerView playerView;

    void Start()
    {
        playerModel = new PlayerModel(100, 0);
        playerView = FindObjectOfType<PlayerView>();

        // 초기 상태 업데이트
        playerView.UpdateHealth(playerModel.Health);
        playerView.UpdateScore(playerModel.Score);
    }

    void Update()
    {
        // 예: 스페이스바를 눌러 점수 추가
        if (Input.GetKeyDown(KeyCode.Space))
        {
            playerModel.AddScore(10);
            playerView.UpdateScore(playerModel.Score);
        }

        // 예: 데미지 처리
        if (Input.GetKeyDown(KeyCode.D))
        {
            playerModel.TakeDamage(10);
            playerView.UpdateHealth(playerModel.Health);
        }
    }
}


** 본 문은 Microsoft Copilot의 도움으로 작성되었습니다.

[Unity][Copilot][유니티][코파일럿] UI와 공용 컨트롤러간의 관계

 유니티를 사용하다 보니 UI Controller의 경우 특정 UI를 움직이기 때문에 공용으로 사용하기에 부적합하다. 그래서 UI Controller와 공용 컨트롤러를 구별해서 사용하면 사용하기에 좋다.

 아래와 같은 코드를 사용하여 공용 Controller를 가져와서 UI Controller에 사용하게 된다.


### 예시 코드: UIHealthController.cs

```csharp

using UnityEngine;

using UnityEngine.UI;


public class UIHealthController : MonoBehaviour

{

    public Text healthText;

    private PlayerController playerController;


    void Start()

    {

        playerController = FindObjectOfType<PlayerController>();

    }


    void Update()

    {

        healthText.text = "HP: " + playerController.Health;

    }

}


여기서 FindObjectOfType은 FindObjectOfType<T> 로 사용이 되며, T type의 첫 번째 활성화된 오브젝트를 찾아서 반환해 준다. 주로 특정 타입의 컴포넌트를 검색할 때 사용하게 된다.

 위 예시로 보면 PlayerController 컴포넌트를 찾아서 반환하라는 뜻이다.

 그런데 이 경우 Find 이기 때문에 전체 씬을 검색하게 된다. 그래서 용량이 커질 수록 비효율 적이다. 그리고 첫번째 활성화된 오브젝트만 가져오기에 첫번째만 반환하게 된다.

 그래서 최소한으로 사용하는 것이 좋다. 캐싱을 통해서 사용할 수 있다.
 private HPController hpController; 를 선언하고
Start때만 불러 온다.
 Start() {
   hpController = FindObjctOfType<HPController>();
}
이 후 Update 때에는 캐싱한 변수를 사용한다.

 Update(){
    HPController.HP;
}

**이 글은 Microsoft Copilot의 도움을 받아 작성되었습니다.

[Copilot][코파일럿][C#] =>(람다) 에 대한 설명

C#의 람다 표현식 (=>) 이해하기


람다 표현식은 C#에서 사용되는 기능이다. 


* 의미

"=>" 연산자는 람다 연산자로, 익명 함수를 정의할 때 사용된다. 

람다 표현식은 보통 코드를 간결하게 적고, 가독성을 높일 수 있다.


 사용 예제

리스트 필터링

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();

// evenNumbers: { 2, 4 }

 내가 이해한 바로는 => 오른쪽 연산의 결과를 왼쪽에 반환하는 것으로 이해를 하였다.


 장점 

1. 간결함: 코드가 간결해진다.

2. 가독성: 함수 정의를 간단하게 표현하기에 코드의 가독성이 좋아진다.

3. 유연성: 이벤트 핸들러나 일회성 함수를 정의할 때 좋다.

람다 표현식은 C#에서 강력하고 유연한 도구로, 다양한 상황에서 활용할 수 있습니다.


**본 문은 Microsoft Copilot의 도움을 받아 작성되었습니다.

[Copilot][코파일럿] 유니티에 대한 질문을 생각보다 디테일하게 답해준다.

  생각보다라고 하지만 놀라운 수준이다. 아직 초보적인 부분에 대해 물어봐서 그런지 모르겠지만, 코드까지 보여주면서 이렇게 하라고 가이드를 해주는데 확실히 그렇게 하면 해결되는 답변을 내어준다. 초보적인 수준의 질문이라 가능한 것인지 모르겠지만, 구글링을 해가면서 작업해 오고 있었는데, 구글 보다 빠르게 원하는 답을 얻을 수 있어서 당분간 코파일럿을 애용해 봐야겠다. 

[Unity][Copilot] 하나의 오브젝트에 여러개의 스크립트를 넣기

 유니티에서 하나의 오브젝트에 여러개의 스크립트를 넣어서 사용할 수 있다고 한다.

 코파일럿은 오히려 권장하는 부분이라고 한다. 

 각 기능 별로 스크립트를 만들어서 사용하게 되면 모듈화되고, 유지보수와 재사용성이 좋아진다고 한다.

 오히려 권장하는 부분인 것이다. 

 유니티에서 하나의 오브젝트는 하나의 스크립트만 매칭된다고 생각했었는데 여러개를 넣어도 된다는 건 꽤나 유익한 정보였다.


 기능 별로 스크립트를 만드는 장점

 1. 모듈화: 기능 별로 스크립트를 만들기 때문에 모듈화 시킬 수 있다.

 2. 유지 보수 : 특정 기능 별로 만들어 놨기 때문에 각 기능에 대해 해당 스크립트만 건들면 되기 때문에 유지보수가 편하다.

 3. 재사용성 : 기능 별 스크립트를 만들어 놨기 때문에 이를 그대로 다른 오브젝트에 붙여 사용하기도 쉽다.


** 이 글은 Microsoft Copilot의 도움을 받아 작성되었습니다.

[Unity] 코파일럿에 따르면 적과 주인공 HP를 관리하는 controller는 싱글톤으로 적용하는 게 안좋다고 한다.

 코파일럿이 생각보다 디테일하게 답변해주는게 신기하다. 구글링으로 잘 나오지 않는 정보들까지 디테일하게 알려주는데 일단은 설득력이 있어 보인다. 코파일럿을 이용해 보니 코드까지 보여주면서 설명을 하는게 대단하다. 그리고 싱글톤 사용해도 되나? 라고 물었는데 아니 안좋다 라고 아니라는 답변까지 할 줄은 몰랐다.

 코파일럿이 말하는 싱글톤으로 적과 주인공과 같은 다수의 HP Controller를 사용하면 안 좋은 이유이다. 싱글톤은 전역에 하나의 인스턴스만 필요로 할 때 사용하는데 적과 주인공과 같이 개별 인스턴스로 관리 필요할 경우와 맞지 않는다는 것이다. 

 그리고 적과 아군은 별개의 인스턴스로 관리되어야 되기 떄문에 부적합하고 관리도 별개로 해야 되기에 싱글톤으로 하지 말라고 한다.

 이정도로 디테일하게 이유까지 설명해주면서 답변을 줄지는 몰랐는데 코딩 하는데 생각보다 깊이있게(?) 혹은 내가 아직 잘 몰라서 인지 답변이 디테일하고 정확하다는 게 놀랍다.

요약하면 싱글톤을 사용하지 않아야 되는 이유

 1. 독립적 관리: 적과 주인공은 각각 독립적으로 HP를 관리해야하여, 하나의 싱글톤 인스턴스를 공유하면 문제가 된다.

2.  다중  인스턴스 필요: 게임 내에서 여러 적과 아군이 있기에 각 인스턴스의 HP를 개별적으로 관리해야 한다.

3. 유지 보수 용이성: : 인스턴스의 특성에 따라 다르게 관리하는 것이 코드를 유지보수하고 확장 하는데 용이합니다. 

 나중에 알고 보면 안 맞는 것일지 지켜봐야겠다.


** 이 글은 Microsoft Copilot의 도움을 받아 작성되었습니다.

[Unity] 적과 주인공HP를 같은 Controller에서 구현하는게 좋은가

 라는 질문을 구글링했으나 대답을 얻을 수 없었다.

 그런데 윈도우 코파일럿이 그게 좋단다.

 근데 규모가 커지면 controller에 너무 많은 기능이 들어가서 복잡성이 증가하고 의존성이 커진다고 한다.

 지금은 작은 규모이니 같은 controller로 만들고 나중에 상황봐서 분리해도 된다고 한다. 

 코파일럿을 믿어야 할지 난감하나, 일단은 진도 빼기 위해서는 같은 controller로 만들어 봐야겠다.

2024년 12월 22일 일요일

[Unity] 유니티 에셋 스토어에서 제한된 에셋(Restriced asset)인지 확인하는 방법

출처: https://support.unity.com/hc/en-us/articles/205623589-Can-I-use-assets-from-the-Asset-Store-in-my-commercial-game
 
대부분의 에셋은 사용해도 된다고 하는 응답글: 


 제한된 에셋 (Restricted Asset) 은 에셋 페이지에서 Overview에 Restricted License 임을 적어주거나 같이 따라오는 license.txt 파일을 확인하면 알 수 있다고 한다. (제일 처음 출처에 가보면 이미지 파일로 Overview에 제한된 에셋임을 명시한 캡쳐 사진을 볼 수 있다.)
 대부분의 에셋은 사용가능하다는 홈페이지 링크에 따르면 대부분의 경우 asset store에 올라오면 제한된 에셋(Restricted Asset)은 아닌 듯 하다. 대부분은 에셋 스토어에 올라왔으면 사용하면 된다고 하는 글들을 볼 수 있었다.
 그래도 첫번째 글에서와 같이 에셋 Overview에 제한된 에셋 이라고 적혀있는지 그리고 동봉된 License.txt 파일에 Restricted Asset 관련 문구가 있는지 체크는 해보는 게 좋을 듯 하다.

 Google AI에 "how to find restricted asset in unity asset store"로 검색했을때 AI 검색 결과로는 Unity Asset Store웹페이지 -> My Asset을 누르면 Restricted Asset은 마킹되어서 알아볼수 있게 나타난다고 하는데 관련 캡쳐 사진은 확인 할 수 없었다. 그리고 여기서도 마찬가지로 원하는 에셋을 선택해서 에셋 페이지에 제한된 에셋(Restricted Asset)관련 내용이 있는지 확인해 보라고 한다.
 모든 제한된 에셋(Restricted Asset)이 쉽게 구별할 수 있게 적혀 있지 않으니 꼼꼼하게 살펴봐야 한다고 한다.
 사용하기에 확신이 안 들면 Unity Support에 직접 문의해 보라고 한다.

[Unity] 에셋 스토어의 무료 에셋을 상업적으로 이용해도 되는가

출처: https://support.unity.com/hc/ko/articles/205623589-%EC%97%90%EC%85%8B-%EC%8A%A4%ED%86%A0%EC%96%B4%EC%97%90%EC%84%9C-%EA%B5%AC%EB%A7%A4%ED%95%9C-%EC%97%90%EC%85%8B%EC%9D%84-%EC%83%81%EC%97%85%EC%A0%81%EC%9D%B8-%EC%9A%A9%EB%8F%84%EC%9D%98-%EA%B2%8C%EC%9E%84-%EC%A0%9C%EC%9E%91%EC%97%90-%EC%9D%B4%EC%9A%A9%ED%95%B4%EB%8F%84-%EB%90%98%EB%82%98%EC%9A%94#:~:text=%EC%97%90%EC%85%8B%20%EC%8A%A4%ED%86%A0%EC%96%B4%EC%97%90%EC%84%9C%20%EC%97%90%EC%85%8B%EC%9D%84%20%EA%B5%AC%EB%A7%A4%ED%95%98%EC%8B%9C%EB%A9%B4%2C%20%EA%B2%8C%EC%9E%84%EC%9D%B4%EB%82%98%20%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98,%EC%9D%B4%EC%9A%A9%ED%95%98%EC%8B%9C%EB%8A%94%20%EA%B2%83%EC%9D%80%20%EA%B4%9C%EC%B0%AE%EC%8A%B5%EB%8B%88%EB%8B%A4.

 무료로 구매한 에셋도 상업적으로 이용 할 수 있다고 한다. 
 그러나 이용 제한된 에셋( Restricted asset)은 조건부로 이용할 수 있다고 한다. 개인적인 용도와 비상업 용도로 이용이 가능하다고 한다. 
 그리고 비표준 EULA 이면 다른 조항에 영향을 받는다고 하니 자세한 것은 출처를 확인해 보자.

 그렇다면 이용 제한된 에셋인지 어떻게 알 수 있을까?
아래 링크로 들어가서 이용 제한된 에셋에 대해 알아보자.

2024년 12월 21일 토요일

[c#] for each in 에 대하여

출처: https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/statements/iteration-statements

 list<int> numbers = new list<int>() { 0,1,2,3,4,5,6,7,8}

과 같은 리스트에서 그 안의 값들을 처음 부터 끝까지 한번씩 호출하게된다.

foreach( int number in numbers){

 Debug.Log(number);
}

(0,1,2,3,4,5,6,7,8) 순으로 돌게된다.



[Unity][C#] List First와 FirstOrDefault에 대하여

 출처: https://developer-talk.tistory.com/622

 First()의 경우 리스트의 처음값을 반환한다.

name = "Tom"

 First(x => x.name = name)

일 경우 Tom 과 같은 이름인 리스트의 처음 값을 호출한다.

First()는 값이 없을 경우 에러를 호출한다.


float dot = 0.4f

인 경우 

List<float> list = { 0.4f,0.5f,0.6f} 일 떄

FirstOrDefault() 를 하면 0.4f를 반환하고

FirstOrDefault( x => x == 0.5f) 를 하면

0.5f를 반환한다.

FirstOrDefault 값에서 없는 값을 호출할 경우 

그 형의 기본값을 반환한다.

즉 여기서는 float의 기본값을 반환하게 된다.

아마 0.0f 이지 않을까 싶다.

[Unity] 적에게 원하는 만큼만 데미지를 넣는 방법

출처: https://hoil2.tistory.com/23

https://devshovelinglife.tistory.com/485

 1. 함수로 TakeDamage() 함수를 만든 후 Animaton 에서 Add Animation Event로 원하는 시간대에 true/false를 생성하도록 하여서 TakeDamage 내부에서 true 일 때만 데미지를 넣도록 하는 방법이 있다.


2. 비슷한데  add animation event에 Take Damage 함수를 호출하도록 하는 것이다. 

 0.2초에 Take Damage 함수를 호출하도록 하면 에니메이션에서 해당 동작을 할 때에 데미지를 넣도록 에니메이션 진행 시점에 데미지를 넣는 에니메이션 효과와 데미지를 연동시키는 방법이 있다.

2024년 12월 19일 목요일

[Unity] object reference not set to an instance of an object 해결법

출처: https://blog.naver.com/dltoqha684/222337193144

 오브젝트의 instance에 object가 설정되지 않았다는 뜻입니다.

다시 말해 지금 GetComponent로 가져오려고 하였는데 가져올 게 없어서 Null을 반환했다는 것입니다. 

 GetComponent는 가져올 수 없는 것을 가져올려고 해도 컴파일 에러가 나오지 않고 Null을 반환하게 되는 것이 핵심이다.

 예를들어 GetComponent<ABCD> 로 했을 떄 ABCD가 없어도 컴파일 에러 없이 실행되고 Null 값을 반환하면서 이 Component의 변수 함수등에 접근 했을 때 Component를 가져오지 못했기에 Null Exception을 반환 시킨다.

[Unity] 다른 스크립트의 변수 함수 사용하기

출처: https://j2su0218.tistory.com/546#google_vignette


 GameObject.Find() 를 통해 찾아서 사용하는 것부터 배웠었는데 개발이 커질 수록 Find에 시간이 오래걸려서 좋지 않은 방법이라고 한다.

 싱글톤이라는 방법을 소개해 주는데 스크립트 전체를 하나의 instance로 static 하게 선언하여 전역적으로 사용하는 방법이라고 한다. 이런 경우 여러개의 instance를 만들어 사용하려면 어떻게 해야 될지 궁금하긴 하다.

public static HPBarController instance { get; private set; }


private void Awake()

{

    if (instance != null)

    {

        Destroy(this);

    }

    else instance = this;

}

와 같은 형식으로 사용한다고 한다. static 하게 Class전체의 instance를 만든 후 Awake 시에 instance를 가져오고 이 후 다시 접근 할 때는 새롭게 접근한 것은 삭제하고 기존 연결된 instance만 이용해서 사용하게 되는 것이다.

[Unity] Script {get; private set;} 의미

 get은 public 하게 본 클래스 밖에서도 할 수 있지만

set은 클래스 안에서만 가능하다는 뜻이다.

 

 밖에서 가져올 때 get은 되지만 set은 할 수 없다는 의미


출처: https://www.reddit.com/r/Unity2D/comments/v0vh55/what_does_get_private_set_mean/

[Unity] TextMeshPro에서 Outline을 바꾸면 모든 TextMeshPro에서 다 같이 적용되는 이유

TextMeshPro에서는 Material을 이용해서 사용하게 되는데 Outline을 건드리는 것은 이러한 Material 값을 변경하는 것이기 때문에 동일한 Material을 이용하는 다른 TextMeshPro에도 같이 적용 될 수 밖에 없다. 이것을 방지 하기 위해서는 다른 Material을 세팅해서 사용하라는 거 같은데 방법은 좀더 찾아 봐야 하겠다.

출처: https://www.reddit.com/r/Unity3D/comments/p0809v/why_does_all_my_text_change_color_when_i_change/

[Unity] Text가 사라지고 TextMeshPro만 있는 상황 TextMeshPro를 사용하는 방법은

 using TMPro


TMP_Text Text;


로 사용한다.


좀 더 자세한 내용은 아래 출처에서 확인 해보자.

출처:https://discussions.unity.com/t/access-textmeshpro-text-through-script/699157

2024년 12월 18일 수요일

[Unity] The non-generic type 'List' cannot be used with type arguments Error

 using System.Collections.Generic;

이 빠졌을 때 나타나는 에러이다.


윗 줄에 using System.Collections.Generic; 을 추가해주자.


출처: https://discussions.unity.com/t/lists-c/46716



[Unity] HP Bar 에서 피가 깍이거나 찰 때 에니메이션 효과 주기

 아무래도 HP Bar에서 피가 깍이거나 찰 때 그냥 바로 까이면 뭔가 없어보이게 된다.

 예를 들어 피가 10일 때 7이 되었을 때 그냥 바로 7로 깍이는 것이다.

 이런 상황에 에니메이션 효과로 10에서 7까지 주르륵 깍이는 것을 구현한다면 좀 더 자연스럽게 HP가 변화되는 효과를 주면서 보기에 편하다. 


 이를 구현한 유튜버를 소개한다.



[Unity] 무지개색 Hp Bar 만드는 방법

 로스트아크 보스 잡을 때처럼 피통을 스택으로 쌓아서 무지개색이 번갈아 나오면서 나타나도록 하고 싶었다. 

 생각해보면 피통을 100으로 잡았을 때 전체 HP가 1423이라면 무지개색 빨주노초파남보 7가지 색깔이 순서대로 돌아가면 될 것이다.

  1423/100 = 14가 나오기 때문에 14 + 1인 15번째 빨간색이 나타나야 되고 그 아래 색깔은 15 -1인 보라색으로 나타나야 한다.


그리고 현재 보이는 피통은 1423%100을 해서 나온 23이라는 피가 피통인 100에 표현되면 된다. 즉 0.23만큼 보여주면 되는 것이다. (23/100)


 이런 식으로 구현하면 될 것이라 생각하고 있었는데 명쾌하게 정확하게 내가 원하는 그 부분을 그대로 보여주는 유튜버가 있어서 소개한다.


출처: https://youtu.be/b6cRRNvA1PM?si=ui_3UsZRzMk9NyVg



2024년 12월 16일 월요일

[Unity] MovePosition 사용 시 Rigidbody.Position을 이용하는 이유

 출처 : https://blog.naver.com/kbh3983/223255473776


 MovePosition을 사용할 때 up down left right arrow 를 누를 때 마다 0.01씩 추가하거나 뺀다고 하면 벽을 만나서 움직이지 않는 경우에도 내부적으로는 카운터가 계속 더해지고 있다.


예를 들어 위에 있는 벽을 만났을 때 10번 위 화살표 버튼을 누르면 내부적으로는 이미 포지션이 0.01*10 = 0.1 까지 올라가 있는 상황이 되고 이 때 우측으로 이동하여 벽에서 나오는 순간 갑자기 10칸 위로 0.1위치로 이동한다.

 이를 방지 하기 위해서 현재 위치 + up down left right arrow 를 눌렀을 때 0.01 씩 이동을 사용하게 되면 벽에 막혔을 경우 매 프레임마다 현재 위치 + 0.01 을 하게 되는데 0.01이 더해져도 다음계산시에는 현재 위치가 벽에 막혀서 움직이지 않았기에 계속해서 현재 포지션으로 초기화 된다. 이 상황에서 벽을 빠져 나와도 현재 포지션 기준으로 이동을 하기 때문에 순간 이동이 발생하지 않는다. 


그래서 아래와 같이 Rigidbody.Position + curVector 를 사용해서 이동하게 된다.

playerRigidbody.MovePosition(playerRigidbody.position + moveDistance);

[Unity] RigidBody를 Kinematic으로 사용할까 vs dynamic에 Gravity 0 으로 사용할까

 Kinematic으로 사용하면 벽과의 충돌을 뚫고 들어가기 때문에

벽에 막히는 처리를 직접 계산해서 해야 한다.


 Dynamic 에 Gravity 0으로 사용할 경우 벽과 충돌하면 막히게 되기에 따로 처리해 주지 않아도 된다.


이 외에도 다른 부분들이 많이 있겠지만 지금으로서는 Kinematic으로 벽에 막히는 충돌을 직접 계산해서 코딩해 넣는 거 보다는 벽에 막히게끔 기본 제공해주는 Gravity 0으로 개발 하면서 좀 더 고민해 봐야겠다.

[Unity] Float과 Double 관계

출처: https://fiftiesstudy.tistory.com/189 

Unity는 기본적으로 0.3 과 같이 사용하면 double로 인식한다.

Float으로 인식할려면 f나 F를 뒤에 적어줘야 한다.


2024년 12월 15일 일요일

[Unity] 2 Kinematic rigidbody 간에 Trigger와Collision의 차이점

 2 Kinematic rigidbody간에 Collision은 발생할 수 없다.

둘 중 하나는 Dynamic rigidbody로 만들어야 상호작용이 발생한다.


Trigger는 상황이 조금 다르다.

둘 다 Kinematic 이라도 하나의 Collider를 Is Trigger 로 체크하면 둘 사이에 Trigger가 발생한다.

2024년 12월 14일 토요일

[Unity] Kinematic RigidBody가 벽을 뚫고 지나가지 않게 할 수 없는가?

 여러 군데 검색해 봤는데 


 Kinematic이 물리를 적용하지 않는다고 체크하는 것이기에 벽에 막히는 등의 물리적인 현상에 적용을 받지 않아서 벽에서 막히지 않게 된다고 한다.


 그럼 어떻게 벽에 막히게 하는가? 


 그것은 내부적인 물리계산을 통해서는 못하기 때문에 직접 Trigger를 받아서 직접 만들어야 한다고 한다.


 직접 만드는 거 말고 방법이 없는 것인지 좀 더 찾아봐야겠다.

2024년 12월 13일 금요일

[Unity] GameObject 이름으로 GameObject 찾기 (Find Gameobject Using Name)

출처: https://docs.unity3d.com/6000.0/Documentation/ScriptReference/GameObject.Find.html
https://discussions.unity.com/t/change-game-objects-name-when-in-runtime/443963/2
this.character = Instantiate(charPrefab, new Vector3(0, 0, 0), Quaternion.identity);
this.character.name = "Character"; 
 이런 식으로 character의 name에 이름을 적어준다.
 그 후 호출할때에는 this.player = GameObject.Find("Character"); 
 이런식으로 Find를 사용하여 이름을 적어주면 해당 이름의 GameObject를 찾게된다.

[Unity] Animator Setfloat

출처: https://wergia.tistory.com/212%22%3Ehttps://wergia.tistory.com/21
https://docs.unity3d.com/ScriptReference/Animator.SetFloat.html
Animation 을 만들 때 만들어 놓은 parameter 값을 script 파일에서 변경할 때 사용된다.

 SetFlaot("파라미터이름",float값); 

으로 사용한다. 

 예를들어 movementX 라는 파라미터이름을 사용했다면 아래와 같이 f값을 넣어주게 된다.

 characterAnimator = character.GetComponent(); characterAnimator.SetFloat("movementY", -1f); 

 이 외에도 Float , int, Bool, Trigger도 사용할 수 있다. 자세한 것은 아래 페이지에서 확인해 보자.

2024년 12월 12일 목요일

[Unity] StartCoroutine이란?

출처: https://www.blogger.com/blog/post/edit/4269806382658593631/6526622373607187254 https://blog.naver.com/dj3630/221459387675
 Coroutine은 애니메이션 효과를 원하는 시간동안 보여주기 위해 쓴다고 한다. 

 그냥 update 에 넣으면 왜 안되는가? 

 사실 잘 모르겠는데 이런 저런 이유로 update 안에서 넣는 경우에 원하는 시간만큼 안 나올 수가 있다고 한다. 

 그런 경우에 coroutine에 넣어서 사용하면 원하는 시간 동안 보여준다고 한다.

 for( i = 1 ; i >= 0; i-=0.1) 
 yield return new WaitForSeconds(0.1f); 

이런식으로 쓰면 waitforseconds로 인해 0.1f 동안 있다가 실행이 되고 for문을 보면 알다시피 10번 동안 실행된다. 

  그래서 0.1초 간격으로 10번 실행되게 되는 것이다. 
 이런식으로 원하는 시간동안 실행하는 함수를 만들때 쓰게 된다.