<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>en_balor.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 12 Aug 2024 11:40:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>en_balor.log</title>
            <url>https://velog.velcdn.com/images/en_balor/profile/505316c4-3d41-4d03-8e96-6fdf8db19548/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. en_balor.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/en_balor" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[C# Unity, npc 아이템 구매여부 및 대사]]></title>
            <link>https://velog.io/@en_balor/C-Unity-npc-%EC%95%84%EC%9D%B4%ED%85%9C-%EA%B5%AC%EB%A7%A4%EC%97%AC%EB%B6%80-%EB%B0%8F-%EB%8C%80%EC%82%AC</link>
            <guid>https://velog.io/@en_balor/C-Unity-npc-%EC%95%84%EC%9D%B4%ED%85%9C-%EA%B5%AC%EB%A7%A4%EC%97%AC%EB%B6%80-%EB%B0%8F-%EB%8C%80%EC%82%AC</guid>
            <pubDate>Mon, 12 Aug 2024 11:40:52 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>저번에 npc가 상점을 들고 있지 않아 여러 npc들의 상점 상태를 ui로 불러오는데 쓸데없이 코드가 복잡해지는 문제가 있었다.
이를 해결하기 위해서 각각 npc가 상점 스크립트를 가지고 있게 수정했다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class NPCInteraction : MonoBehaviour
{
    public GameObject dialogueUI;
    public GameObject selectMenu;
    public GameObject shopObj;
    public TextMeshProUGUI speakerText;
    public TextMeshProUGUI dialogueText;
    public Button upgrade;
    public Button exit;
    public DialogueData dialogueData;
    public LayerMask playerLayer;
    public Shop shop;
    public List&lt;ItemData&gt; shopDataList = new List&lt;ItemData&gt;();
    public GameObject continueKey;

    public bool isPlayerRange = false;
    public bool isDialogue = false;

    public int currentLineIndex = 0;

    private Dictionary&lt;string, bool&gt; itemPurcased;

    private void Awake()
    {
        itemPurcased = new Dictionary&lt;string, bool&gt;();

        foreach(var itemData in shopDataList)
        {
            itemPurcased[itemData.name] = false;
        }

        dialogueUI.SetActive(false);
        selectMenu.SetActive(false);
        shopObj.SetActive(false);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(((1 &lt;&lt; collision.gameObject.layer) &amp; playerLayer) != 0)
        {
            isPlayerRange = true;
            shop.SetShopGoods();
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (((1 &lt;&lt; collision.gameObject.layer) &amp; playerLayer) != 0)
        {
            isPlayerRange = false;
        }
    }

    public void StartDialogue(DialogueData dialogue)
    {
        continueKey.SetActive(false);
        isDialogue = true;
        UIManager.Instance.OpenUI(dialogueUI);
        UIManager.Instance.pause.DisablePlayerInput();
        dialogueData = dialogue;
        currentLineIndex = 0;
        OpenSelectMenu();
        CurrnetLine();
    }

    public void OpenSelectMenu()
    {
        selectMenu.SetActive(true);
        StartCoroutine(CheckSelectMenu());
    }

    public void NextDialogue()
    {
        if(dialogueData == null || dialogueData.dialogueLines == null)
        {
            return;
        }

        if(currentLineIndex &lt; dialogueData.dialogueLines.Count - 1)
        {
            currentLineIndex++;
            if(continueKey != null)
            {
                continueKey.SetActive(true);

            }
            CurrnetLine();
        }

        else
        {
            EndDialogue();
        }
    }

    private void CurrnetLine()
    {
        DialogueData.DialogueLine line = dialogueData.dialogueLines[currentLineIndex];
        speakerText.text = line.name;
        dialogueText.text = line.dialogueText;
    }

    public void EndDialogue()
    {
        UIManager.Instance.CloseCurrentUI();
        UIManager.Instance.pause.EnablePlayerInput();
    }

    public void PurchasedItem(ItemData itemData)
    {
        if(!HasPurchasedItem(itemData.name))
        {
            CharacterManager.Instance.Player.GetComponent&lt;Player&gt;().stats.ApplyItemEffect(itemData);
            itemPurcased[itemData.name] = true;
            UIManager.Instance.uiBar.UpdateMaxHP(itemData.healthIncrease);
        }

        else
        {
            Debug.Log(&quot;already purchased&quot; + itemData.name);
        }
    }

    public bool HasPurchasedItem(string itemName)
    {
        return itemPurcased.ContainsKey(itemName) &amp;&amp; itemPurcased[itemName];
    }

    public void OpenShop()
    {
        UIManager.Instance.OpenUI(shopObj);
    }

    private IEnumerator CheckSelectMenu()
    {
        while(isDialogue)
        {
            if(!selectMenu.activeInHierarchy)
            {
                NextDialogue();
                yield break;
            }

            yield return null;
        }
    }
}</code></pre>
<p>HasPurchassedItem에서 해당 아이템의 재고가 있는지 여부를 반환한 다음 해당 조건문을 통해 플레이어에게 아이템을 추가하는 메서드를 실행하게 했다.
대사는 Scriptable Object를 사용했고 리스트로 대사를 관리하고 있다. index를 통해 다음 대사를 출력할지 대화를 종료할지를 작성했다.
대화를 끝내는 부분은 상점을 선택하는 메뉴가 떠있을 때와 아닐때를 코루틴으로 체크해 다음 대사를 출력하게 만들었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity NPC 상점]]></title>
            <link>https://velog.io/@en_balor/C-Unity-NPC-%EC%83%81%EC%A0%90</link>
            <guid>https://velog.io/@en_balor/C-Unity-NPC-%EC%83%81%EC%A0%90</guid>
            <pubDate>Fri, 09 Aug 2024 12:13:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>이번에 npc와 상점기능을 구현하면서 했던 실수가 있다.
객체를 신경쓰지 않고 코드를 작성해버린 것인데, 이것 때문에 쓸때없이 복잡하게 코드를 만든 문제가 있었다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class Shop : MonoBehaviour
{
    public GameObject shop;
    public List&lt;ItemData&gt; shopDataList;
    public List&lt;Button&gt; itemBtn;
    public GameObject goodsList;
    public GameObject parentPosition;
    public TextMeshProUGUI itemName;
    public TextMeshProUGUI itemDescription;
    public TextMeshProUGUI itemPrice;

    private NPCInteraction npc;
    public string npcId;

    private void Start()
    {
        npc = UIManager.Instance.GetId(npcId);
        SetShopGoods();
    }

    private void SetShopGoods()
    {
        interact();
        foreach (ItemData itemdata in shopDataList)
        {
            GameObject goodsInstantiate = Instantiate(goodsList, parentPosition.transform);

            itemName = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemName&quot;).GetComponent&lt;TextMeshProUGUI&gt;();
            itemDescription = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemDescription&quot;).GetComponent&lt;TextMeshProUGUI&gt;();
            itemPrice = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemPrice&quot;).GetComponent&lt;TextMeshProUGUI&gt;();

            if (itemName != null)
            {
                itemName.text = itemdata.itemName;
            }

            if (itemDescription != null)
            {
                itemDescription.text = itemdata.description;
            }

            if (itemPrice != null)
            {
                itemPrice.text = itemdata.price.ToString();
            }

            Button itemBtn = goodsInstantiate.transform.Find(&quot;BuyBtn&quot;).GetComponent&lt;Button&gt;();

            if (itemBtn != null)
            {
                itemBtn.onClick.RemoveAllListeners();
                itemBtn.onClick.AddListener(() =&gt; npc.PurchasedItem(itemdata));
                itemBtn.interactable = !npc.HasPurchasedItem(itemdata.name);
            }
        }
    }

    public void interact()
    {
        npc = UIManager.Instance.GetId(npcId);
    }
}
</code></pre>
<p>그래도 구조를 남겨두면 참고할 수 있는 부분이 있을까해서 코드를 남겨뒀다.
일단 여기서 문제는 npc가 아이템들을 관리해야 하는데 이를 지키지 않아서 여러 npc를 배치했을 때 아이템 재고 같은 문제가 발생했다.
객체를 잘못준 것에 대해 수정작업이 필요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 상점 만들기]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EC%83%81%EC%A0%90-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EC%83%81%EC%A0%90-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 08 Aug 2024 12:25:22 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>npc와 대화 중 상점 메뉴에 들어가 아이템들을 보여주는 ui가 필요해 제작하게 되었다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">    public List&lt;ItemData&gt; shopDataList;
    public List&lt;Button&gt; itemBtn;
    public GameObject goodsList;
    public GameObject parentPosition;
    public TextMeshProUGUI itemName;
    public TextMeshProUGUI itemDescription;
    public TextMeshProUGUI itemPrice;

    private void Start()
    {
        for (int i = 0; i &lt; shopDataList.Count; i++)
        {
            GameObject goodsInstantiate = Instantiate(goodsList, parentPosition.transform);

            itemName = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemName&quot;).GetComponent&lt;TextMeshProUGUI&gt;();
            itemDescription = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemDescription&quot;).GetComponent&lt;TextMeshProUGUI&gt;();
            itemPrice = goodsInstantiate.transform.Find(&quot;ItemDisplay/ItemPrice&quot;).GetComponent&lt;TextMeshProUGUI&gt;();

            if(itemName != null)
            {
                itemName.text = shopDataList[i].itemName;
            }

            if (itemDescription != null)
            {
                itemDescription.text = shopDataList[i].description;
            }

            if (itemPrice != null)
            {
                itemPrice.text = shopDataList[i].price.ToString();
            }
        }
    }</code></pre>
<p>아이템 데이터를 담아줄 리스트를 만들고 반복문으로 리스트의 길이 만큼 반복하며 자식 오브젝트로 부터 TextMeshPro 컴포넌트를 가져온 후 아이템데이터의 텍스트를 넣어줬다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 클래스 상속 다이아몬드 문제]]></title>
            <link>https://velog.io/@en_balor/C-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%8B%A4%EC%9D%B4%EC%95%84%EB%AA%AC%EB%93%9C-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@en_balor/C-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%8B%A4%EC%9D%B4%EC%95%84%EB%AA%AC%EB%93%9C-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 07 Aug 2024 12:09:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-다이아몬드-문제">1. 다이아몬드 문제</h2>
<p>C#에서는 클래스끼리 다중상속을 허용하지 않는다. 이는 다중상속 시 복잡성과 모호성이 문제될 수 있다. 클래스 A를 상속 받은 클래스 B와 C가 있다 했을 때,클래스 D는 B와 C를 모두 상속 받는 경우를 다이아몬드 같이 생겼다 해서, 다이아몬드 상속 구조라고 한다. 이렇게 되었을 때 클래스 D는 A 클래스를 두 번 상속받게 되어 어느 A 클래스를 사용할지 모호해지는 문제점이 발생한다.</p>
<h3 id="문제점">문제점</h3>
<ul>
<li>중복된 멤버 문제로 인한 모호성</li>
<li>호출할 클래스의 호출경로에 대한 모호성</li>
<li>복잡성 증가로 인한 유지보수의 어려움</li>
</ul>
<h2 id="2-대체-방안">2. 대체 방안</h2>
<p>C#에서는 클래스끼리의 다중상속을 허용하지 않아 해당 문제가 발생하지 않지만 다중상속이 필요할 경우에는 클래스의 메서드와 속성만 정의하고 구현은 없는 인터페이스를 사용해 해결할 수 있다.
이를 통해 다중상속의 장점을 가지면서 동시에 안정적인 코드의 작성이 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity NPC 대화 만들기]]></title>
            <link>https://velog.io/@en_balor/C-Unity-NPC-%EB%8C%80%ED%99%94-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-Unity-NPC-%EB%8C%80%ED%99%94-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 06 Aug 2024 11:40:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>플레이어에게 재화가 있었으나 사용처가 마땅하지 않아 팀 회의를 거친 끝에 플레이어 캐릭터의 스탯을 강화해주는 아이템을 파는 NPC를 만들어주기로 결정해 해당 기능을 구현하게 되었다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class NPCInteraction : MonoBehaviour
{
    public GameObject dialogueUI;
    public TextMeshProUGUI speakerText;
    public TextMeshProUGUI dialogueText;
    public Button upgrade;
    public Button exit;
    public DialogueData dialogueData;
    public LayerMask playerLayer;

    public bool isPlayerRange = false;
    public bool isDialogue = false;

    private int currentLineIndex = 0;

    private void Start()
    {
        dialogueUI.SetActive(false);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(((1 &lt;&lt; collision.gameObject.layer) &amp; playerLayer) != 0)
        {
            isPlayerRange = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (((1 &lt;&lt; collision.gameObject.layer) &amp; playerLayer) != 0)
        {
            isPlayerRange = false;
        }
    }

    public void StartDialogue(DialogueData dialogue)
    {
        isDialogue = true;
        UIManager.Instance.OpenUI(dialogueUI);
        dialogueData = dialogue;
        currentLineIndex = 0;
        CurrnetLine();
    }

    public void NextDialogue()
    {
        if(currentLineIndex &lt; dialogueData.dialogueLines.Count - 1)
        {
            currentLineIndex++;
            CurrnetLine();
        }

        else
        {
            EndDialogue();
        }
    }

    private void CurrnetLine()
    {
        DialogueData.DialogueLine line = dialogueData.dialogueLines[currentLineIndex];
        speakerText.text = line.name;
        dialogueText.text = line.dialogueText;
    }

    private void EndDialogue()
    {
        UIManager.Instance.CloseCurrentUI();
    }
}
</code></pre>
<p>버튼 부분은 아직 미구현상태지만 Scriptable Object에서 리스트를 이용해 대화문들을 만들어주고 이를 리스트의 인덱스를 변경해 주는 것으로 대화문이 진행 되게끔하고 크기를 벗어나면 대화문을 닫는 구조로 만들었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 면접 대비 CS 정리]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EB%A9%B4%EC%A0%91-%EB%8C%80%EB%B9%84-CS-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EB%A9%B4%EC%A0%91-%EB%8C%80%EB%B9%84-CS-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 05 Aug 2024 11:11:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-메모리-관리">1. 메모리 관리</h2>
<h3 id="1-c에서의-메모리-할당-해제">1. C#에서의 메모리 할당, 해제</h3>
<p>C#에서의 메모리 할당과 해제는 주로 가비지컬렉터에 의해 자동으로 처리된다.</p>
<h4 id="가비지-컬렉터">가비지 컬렉터</h4>
<pre><code>가비지 컬렉터는 더이상 사용되지 않는 객체를 자동으로 해제하는 기능을 말한다.
* 힙에 메모리 할당
* 참조되지 않는 객체 감지
* 해당 객체 메모리 해제</code></pre><h4 id="가비지-컬렉터의-장점과-단점">가비지 컬렉터의 장점과 단점</h4>
<p>장점</p>
<ul>
<li>가비지 컬렉터는 메모리를 자동으로 관리해 메모리 누수와 같은 문제를 줄일 수 있다.</li>
<li>메모리 접근 오류나 해제로 인한 버그를 방지할 수 있다.</li>
<li>메모리 관리에 들어가는 인력이 줄어 생산성을 높이는데 도움이 된다.</li>
<li>모든 객체는 가비지컬렉터의 관리 하에 있어 일관성을 보장한다.</li>
<li>주기적으로 메모리를 검사하고 미사용인 객체의 메모리를 해제해 최적화에 유리하다.</li>
</ul>
<p>단점</p>
<ul>
<li>가비지컬렉터가 언제 실행되는지 예측하기 어렵다.</li>
<li>추가적인 메모리를 사용해 메모리의 여유 공간이 필요하다.</li>
<li>참조가 복잡해지면 이를 처리하는데 오래걸릴 수 있고 의도치 않은 객체가 남아있을 수 있다.</li>
<li>메모리 누수를 찾기 어렵다.</li>
</ul>
<h4 id="가비지컬렉터의-세대">가비지컬렉터의 세대</h4>
<p>가비지컬렉터는 세 개의 세대로 나뉜다.</p>
<ul>
<li>0세대 : 최근에 할당된 객체, 가장 자주 수집된다.</li>
<li>1세대 : 0세대에서 살아남은 객체</li>
<li>2세대 : 장기적으로 남아있는 객채, 가장 덜 수집된다.</li>
</ul>
<p>객체가 오래 남아있을 수록 더 높은 세대로 이동한다.</p>
<p>가비지컬렉터는 위와 같이 세대로 나누어 수집해는데 이는 생명주기의 특성을 활용하기에 유용하다. 생명주기가 짧은 객체들만 신속하게 수집하고 그렇지 않은 객체들은 적은 빈도로 수집해 시스템에 끼치는 영향을 감소시킬 수 있다.</p>
<h4 id="가비지-컬렉터를-효율적으로-관리하기">가비지 컬렉터를 효율적으로 관리하기</h4>
<ol>
<li><p>객체 생성 최소화
객체를 자주 생성하고 파괴하면 리소스를 많이 잡아먹게 된다. 이를 방지하기 위해 주로 오브젝트풀 패턴을 활용해 생성과 파괴에 들어가는 리소스를 최소화한다.</p>
</li>
<li><p>큰 객체 할당 피하기
한번에 큰 메모리 블록을 할당하는 것은 부하를 줄수 있어 여러개의 작은 객체로 나누거나, 최소화 하는게 유리하다.</p>
</li>
<li><p>할당 패턴 최적화
작은 객체들을 일관되게 할당 및 해제하면 효율적으로 작동한다. 동적 배열을 가지는 List를 사용할 때는 예측 가능한 크기로 사전에 설정해 두는것이 유리하다.</p>
</li>
</ol>
<h4 id="박싱언박싱">박싱/언박싱</h4>
<ol>
<li><p>박싱
박싱은 값 형식을 참조 형식으로 변환하는 과정으로 값 형식을 담을 수 있는 새 객체를 힙에 할당 후 힙에 할당된 객체로 복사된다.</p>
</li>
<li><p>언박싱
언박싱은 참조 형식이 실제로 값을 가지고 있는지 확인 후 힙에 있는 객체의 값을 스택 영역으로 복사한다.</p>
</li>
</ol>
<h2 id="2-라이프사이클">2. 라이프사이클</h2>
<h4 id="update와-fixedupdate의-차이">Update와 FixedUpdate의 차이</h4>
<p>Update는 매 프레임마다 호출되고 프레임에 따라 호출빈도가 바뀔 수 있지만 FixedUpdate는 물리엔진을 시뮬레이션 한 이후 고정된 간격으로 호출되며 호출빈도가 일정하다는 차이가 있다.</p>
<h4 id="timescale의-값에-따른-update-fixedupdate-호출-빈도">timeScale의 값에 따른 Update, FixedUpdate 호출 빈도</h4>
<p>timeScale이0이 되면 Update메서드는 계속 호출되지만 시간 기반의 계산이 영향을 받는다.
FixedUpdate는 물리 엔진 업데이트를 위한 것으로 timeScale이 0이 되어도 호출되지만 물리 계산이 정상적으로 수행되지 않을 수 있다. 일정한 간격으로 호출되지만 물리 계산이 실제로 진행되지 않는다.</p>
<p>반대로 timeScale의 값을 늘려도 두 메서드의 호출 빈도는 변하지 않으며 시간 기반이나 물리 계산 부분에서는 영향을 받는다.</p>
<h4 id="awake-start-onenable의-차이">Awake, Start, OnEnable의 차이</h4>
<p>Awake는 객체 생성 시 가장 먼저 호출되며 활성화 될 때 자동으로 실행되고, 비활성화 이후 다시 활성화 해도 다시 실행되며 다른 스크립트의 초기화에 영향을 받지 않는다.
Start는 Awake 이후 호출되며 첫 프레임 업데이트 전에 한 번 호출된다.
OnEnable은 객체가 활성화 될 때마다 실행되며 Start보다먼저 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 스테이지 전환 시 스테이지 출력]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%A0%84%ED%99%98-%EC%8B%9C-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%A0%84%ED%99%98-%EC%8B%9C-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Fri, 02 Aug 2024 11:12:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>팀프로젝트를 진행하다 스테이지를 전환하면 해당 스테이지를 보여주는 연출이 필요해 해당 부분을 구현했다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using TMPro;

public class ChanageStage : MonoBehaviour
{
    public GameObject stageUI;
    public TextMeshProUGUI stageName;

    private CanvasGroup canvasGroup;

    private void Start()
    {
        canvasGroup = stageUI.GetComponent&lt;CanvasGroup&gt;();
        if(canvasGroup == null )
        {
            canvasGroup = gameObject.AddComponent&lt;CanvasGroup&gt;();
        }

        stageUI.SetActive(false);
    }

    public void FadeInStageUI(float duration, string stage)
    {
        //SetStageText();
        stageUI.SetActive(true);
        stageName.text = stage;
        canvasGroup.DOFade(1f, duration).SetEase(Ease.InOutQuad).OnComplete(() =&gt;
        {
            StartCoroutine(WaitFadeOut(2f, 1f));
        });
    }

    private IEnumerator WaitFadeOut(float waitTime, float fadeOutDuration)
    {
        yield return new WaitForSeconds(waitTime);
        FadeOutStageUI(fadeOutDuration);
    }

    public void FadeOutStageUI(float duration)
    {
        canvasGroup.DOFade(0f, duration).SetEase(Ease.InOutQuad).OnComplete(() =&gt; stageUI.SetActive(false));
    }
}</code></pre>
<p>DOTween을 사용해 페이드 인, 아웃 효과를 준다. CanvasGroup은 해당 오브젝트들을 함께 투명화해주기 위해 사용했다.
중간의 코루틴은 텍스트가 출력되고 약간 여유를 주고 함수를 실행하기 위해 사용했다.
DOTween을 실행 후 완료되면 잠깐 기다리게 하기 위해 코루틴을 실행하고 그 후 페이드아웃효과가 완료되면 해당 오브젝트를 비활성화하게 코드를 작성했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 미니맵 만들기]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EB%AF%B8%EB%8B%88%EB%A7%B5-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EB%AF%B8%EB%8B%88%EB%A7%B5-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 01 Aug 2024 11:53:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>전에 맵의 콜라이더를 참고해서 미니맵을 그리는 작업을 했지만, 해당 그림이 잘나오는 편이 아니라서 다른 방식을 활용해 미니맵을 만들기로 했다.
콜라이더를 참고하는게 아니라 콜라이더를 담당하는 부분의 색을 변경하고 해당 부분을 참고해 맵을 만들었다.
이미지를 따로 만들어서 넣어놨는데 좀 더 간편한 방법이 있지 않을까 라는 고민이 들었다.</p>
<h2 id="2-미니맵-만들기">2. 미니맵 만들기</h2>
<p>미니맵을 만드는데 먼저 미니맵만을 비출 카메라를 만들어줬다.
맵을 그대로 비추는게 아니라 맵의 색상을 단순화 시켜서 보이게끔 하게하기 위해 오브젝트를 따로 만들어줬다.
먼저 미니맵에 미니맵 부분만 보이게 레이어를 따로 설정해주고 카메라의 컬링마스크를 변경해준다.
이걸 UI에 반영하기 위해 Texture Render를 만들어주고 Canvas 하위에 rawimage를 만들어 카메라와 rawimage의 텍스쳐를 만들어둔 Texture Render를 반영해준다.
여기서 플레이어를 구분하는 색상이 반영되지 않아 헤맸는데, 원인은 빛이 문제였고 스프라이트의 마테리얼이 빛이 있어야 색상을 정상적으로 출력하는 마테리얼이었기 때문에 마테리얼을 빛이 없어도 되는 Unlit를 사용해 해결했다</p>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MiniMap : MonoBehaviour
{
    public Transform player;
    public GameObject miniMap;
    public Camera miniMapCamera;

    public List&lt;GameObject&gt; miniMaps;

    private void Start()
    {
        SetMinimap(1);
    }

    private void LateUpdate()
    {
        if(player != null)
        {
            Vector2 newPosition = new Vector3(player.position.x,player.position.y);
            miniMapCamera.transform.position = newPosition;
        }
    }

    public void SetMinimap(int stageNum)
    {
        Debug.Log(stageNum);
        for(int i = 0; i &lt; miniMaps.Count; i++)
        {
            if(i == stageNum - 1)
            {
                miniMaps[i].SetActive(true);
            }

            else
                miniMaps[i].SetActive(false);
        }
    }
}</code></pre>
<p>하나의 씬에서 여러 스테이지를 오가기 때문에 스테이지들을 리스트로 선언해 관리해주고 해당 스테이지의 맵만 활성화 되도록 코드를 작성했다.
카메라의 위치는 플레이어를 따라가도록했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 아이템 습득시 팝업창 띄우기]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EC%95%84%EC%9D%B4%ED%85%9C-%EC%8A%B5%EB%93%9D%EC%8B%9C-%ED%8C%9D%EC%97%85%EC%B0%BD-%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EC%95%84%EC%9D%B4%ED%85%9C-%EC%8A%B5%EB%93%9D%EC%8B%9C-%ED%8C%9D%EC%97%85%EC%B0%BD-%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Wed, 31 Jul 2024 11:05:10 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>이번엔 아이템을 습득하면 아이템 아이콘과 정보가 나오는 팝업창을 만들어 봤다.
기존의 UI를 만들던 방식과 크게 다르지 않아 과정이 어렵지는 않았지만, 아무키나 눌러도 창이 닫히게끔 만들었는데 아이템 창이 너무 빨리 닫히는 문제가 있었다.
해결 방법은 코루틴을 사용해서 bool값을 변경해주고 창을 비활성화 하는 메서드를 추가해 해결했다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class ItemPopup : MonoBehaviour
{
    public ItemData popupData;
    public GameObject itemPopup;

    [Header(&quot;Popup Item&quot;)]
    public Image popupIcon;
    public TextMeshProUGUI popupName;
    public TextMeshProUGUI popupDescription;
    public GameObject continueText;

    public bool isContinue;

    public void PopupGetItem()
    {
        popupData = CharacterManager.Instance.Player.itemData;
        continueText.SetActive(false);
        isContinue = false;
        popupName.text = popupData.name;
        popupDescription.text = popupData.description;
        popupIcon.sprite = popupData.icon;

        itemPopup.SetActive(true);
        StartCoroutine(OnIsContinue());
    }

    IEnumerator OnIsContinue()
    {
        Time.timeScale = 0f;

        yield return new WaitForSecondsRealtime(2f);

        continueText.SetActive(true);
        isContinue = true;
    }

    public void ClosePopup()
    {
        itemPopup.SetActive(false);
        Time.timeScale = 1f;
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Reflection]]></title>
            <link>https://velog.io/@en_balor/C-Reflection</link>
            <guid>https://velog.io/@en_balor/C-Reflection</guid>
            <pubDate>Tue, 30 Jul 2024 11:42:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>리플렉션은 런타임 단계에서 클래스 내부 구조를 메타데이터들을 가져와 사용할 수 있는 라이브러리를 말한다. 리플렉션을 통해, 값을 넣거나, 함수를 실행시키거나, 어떤 타입인지 보호 수준은 어느정도인지를 알아낸다.
단점은 연산량이 많아 성능적으로 불리하며 필요한 부분에서 작게 사용하는 부분은 크게 문제되지 않고 현업에서도 자주 쓰일 수 있다고 하니 알아두는게 좋겠다.</p>
<h2 id="2-예제">2. 예제</h2>
<h3 id="타입-조사">타입 조사</h3>
<pre><code class="language-cs">using UnityEngine;
using System;

public class ReflectionExample : MonoBehaviour
{
    void Start()
    {
        GameObject go = new GameObject(&quot;MyObject&quot;);
        Type type = go.GetType();
        Debug.Log(&quot;Type: &quot; + type.Name); // 출력: Type: GameObject
    }
}</code></pre>
<h3 id="메서드-정보-가져오기">메서드 정보 가져오기</h3>
<pre><code class="language-cs">using UnityEngine;
using System;
using System.Reflection;

public class ReflectionExample : MonoBehaviour
{
    void Start()
    {
        Type type = typeof(Debug);
        MethodInfo method = type.GetMethod(&quot;Log&quot;, new Type[] { typeof(object) });

        if (method != null)
        {
            method.Invoke(null, new object[] { &quot;Hello, Reflection!&quot; });
        }
    }
}</code></pre>
<h3 id="동적-객체-생성">동적 객체 생성</h3>
<pre><code class="language-cs">using UnityEngine;
using System;

public class ReflectionExample : MonoBehaviour
{
    void Start()
    {
        Type type = typeof(GameObject);
        GameObject go = (GameObject)Activator.CreateInstance(type);
        go.name = &quot;CreatedWithReflection&quot;;
        Debug.Log(go.name); // 출력: CreatedWithReflection
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity UIManager 수정]]></title>
            <link>https://velog.io/@en_balor/C-Unity-UIManager-%EC%88%98%EC%A0%95</link>
            <guid>https://velog.io/@en_balor/C-Unity-UIManager-%EC%88%98%EC%A0%95</guid>
            <pubDate>Mon, 29 Jul 2024 11:28:29 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>저번에 UIManager가 Manager로써의 역할로 부족하다고 작성했었다. 다른 메뉴를 열었을 때 일시정지 메뉴가 나오는 것도 해결할겸해서 UI오브젝트 구성 수정과 UIManager가 더 다양한 역할을 담을 수 있도록 수정하려고 한다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEditor.Rendering;
using UnityEngine;

public class UIManager : MonoBehaviour
{
    public static UIManager Instance;

    public GameObject currentUI;

    private void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }

        else
        {
            Destroy(gameObject);
        }
    }

    public void OpenUI(GameObject uiElement)
    {
        if(currentUI != null)
        {
            currentUI.SetActive(false);
        }

        uiElement.SetActive(true);
        currentUI = uiElement;
    }

    public void CloseCurrentUI()
    {
        if(currentUI != null)
        {
            currentUI.SetActive(false);
            Time.timeScale = 1f;
            currentUI = null;
        }


    public bool IsUIOpen(GameObject uiElement)
    {
        return currentUI == uiElement;
    }

    public bool IsAnyUIOpen()
    {
        return currentUI != null;
    }
}</code></pre>
<p>코드를 수정하면서 메뉴를 전환하는 것들을 모두 여기서 실행할 수 있도록 했다. 구조 자체는 복잡하지 않은데... 방향성을 영 엉뚱하게 생각해 시간을 너무 소요해버렸다.
간단하게 설명하자면 UI창을 열 때 현재UI에 매개로 전달할 UI를 전달해주고, UI창을 닫으면 현재 UI를 닫아주는 방식으로 수정했다.
아직 추가는 안했지만 DOTween을 사용해서 UI의 투명도를 조절해 보다 자연스럽게 창이 뜨게끔 기능을 추가할 예정이다.</p>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;

public class UIController : MonoBehaviour
{
    [SerializeField] private GameObject pauseUI;
    [SerializeField] private GameObject inventory;

    private void Start()
    {
        pauseUI.SetActive(false);
        inventory.SetActive(false);
    }

    public void OnPauseUI(InputAction.CallbackContext callbackContext)
    {
        if(callbackContext.phase == InputActionPhase.Started)
        {
            if(UIManager.Instance.currentUI != null &amp;&amp; UIManager.Instance.currentUI != pauseUI)
            {
                UIManager.Instance.CloseCurrentUI();
                return;
            }

            if(UIManager.Instance.IsUIOpen(pauseUI))
            {
                UIManager.Instance.CloseCurrentUI();
            }

            else
            {
                UIManager.Instance.OpenUI(pauseUI);
            }
        }
    }

    public void OnInventory(InputAction.CallbackContext callbackContext)
    {
        if(callbackContext.phase == InputActionPhase.Started)
        {
            if(UIManager.Instance.IsUIOpen(inventory))
            {
                UIManager.Instance.CloseCurrentUI();
            }

            else
            {
                UIManager.Instance.OpenUI(inventory);
            }
        }
    }
}</code></pre>
<p>UIController쪽도 수정을 했는데, 다른 메뉴가 열려있을 때 일시정지 화면이 나오지 않도록 조건문을 추가해줬다. 현재UI가 비어있고, 해당UI가 일시정지 화면이 아닐 때 종료를 실행하도록 했다. 이렇게 작성해서 설정화면에서 esc를 눌렀을 때 이전화면으로 돌아가는 것이 아니라 바로 메뉴를 비활성화 하게끔 수정했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity BIRP, URP]]></title>
            <link>https://velog.io/@en_balor/C-Unity-BIRP-URP</link>
            <guid>https://velog.io/@en_balor/C-Unity-BIRP-URP</guid>
            <pubDate>Fri, 26 Jul 2024 11:44:20 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>Unity의 Built-in Render Pipeline(BIRP)와 Universal Render Pipeline(URP)은 각각 고유한 특성과 장단점을 지닌 렌더링 파이프라인으로  Unity 엔진에서 그래픽 렌더링을 처리하는 방식과 성능, 기능 측면에서 차이가 있다. 다뤄 본 적 없느 내용으로 두 렌더링 파이프라인의 차이를 정리해봤다.</p>
<h2 id="2-unity-built-in-render-pipeline-birp">2. Unity Built-in Render Pipeline (BIRP)</h2>
<h4 id="특징">특징</h4>
<ul>
<li>기본 제공: Unity 엔진에 기본적으로 포함되어 있으며, 오래된 프로젝트에서 널리 사용되는 렌더링 파이프라인이다.</li>
<li>커스터마이징: 고도로 커스터마이징할 수 있지만, 이는 고급 그래픽 프로그래밍 지식을 필요하다는 단점이 있다.</li>
<li>플랫폼 호환성: 대부분의 플랫폼에서 잘 작동하며, 복잡한 설정 없이 다양한 장치에서 일관된 성능을 제공한다.</li>
<li>렌더링 경로: 기본적으로 Forward Rendering과 Deferred Rendering을 지원한다. 선택에 따라 성능과 시각적 품질을 조절이 가능하다.</li>
</ul>
<h4 id="장점">장점</h4>
<ul>
<li>호환성: 오래된 프로젝트에서 널리 사용되었기 때문에 오래된 프로젝트와 호환성이 좋다.</li>
<li>유연성: 고급 사용자에게는 유연한 커스터마이징이 가능하다.</li>
<li>성숙도: 오랜 기간 사용되었기 때문에 안정성과 문서화를 찾기 쉽다.</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>복잡성: 고급 그래픽 효과를 구현하려면 복잡한 셰이더 프로그래밍 지식이 필요하다.</li>
<li>최적화 한계: 최신 하드웨어와 성능 최적화 기능이 부족할 수 있다.</li>
</ul>
<h2 id="3-universal-render-pipeline-urp">3. Universal Render Pipeline (URP)</h2>
<p>특징</p>
<ul>
<li>현대적 설계: 최신 그래픽 기술과 성능 최적화를 위해 설계되었기 때문에 최신 프로젝트에 적합하다.<ul>
<li>모듈화: 쉽게 설정하고 사용할 수 있는 모듈식 구성 요소를 제공한다.</li>
<li>성능 최적화: 저성능 모바일 기기부터 고성능 콘솔과 PC까지 다양한 플랫폼에서 뛰어난 성능을 발휘한다.</li>
<li>SRP 지원: Scriptable Render Pipeline(SRP)을 기반으로 하여 커스터마이징과 확장이 용이하다.</li>
</ul>
</li>
</ul>
<h3 id="장점-1">장점</h3>
<ul>
<li>성능: 저성능 기기에서도 뛰어난 성능을 발휘한다.</li>
<li>사용 편의성: 그래픽 설정과 효과를 쉽게 구성할 수 있다.</li>
<li>현대적 기능: 최신 그래픽 기능과 최적화 기술을 지원한다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>오래된 프로젝트와의 호환성: 기존 Built-in Render Pipeline으로 만든 프로젝트를 URP로 실행하는데 어려움이 있을 수 있어 오래된 프로젝트와의 호환성이 좋지 않다.</li>
<li>제한된 고급 기능: 일부 고급 그래픽 효과와 기능은 URP에서 완전히 지원되지 않을 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity Quaternion]]></title>
            <link>https://velog.io/@en_balor/C-Unity-Quaternion</link>
            <guid>https://velog.io/@en_balor/C-Unity-Quaternion</guid>
            <pubDate>Thu, 25 Jul 2024 11:39:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>쿼터니언은 기존의 오일러 각을 사용하면서 생기는 문제점들을 예방한 것이다.</p>
<h2 id="2-오일러각">2. 오일러각</h2>
<p>오일러 각은 회전을 세가지 축의 각도로 표현하며 각 축에 대해 각각의 회전을 지정해 적용한다.</p>
<h4 id="장점">장점</h4>
<ul>
<li>회전을 쉽게 이해할 수 있어 직관적이고 각도를 직접 설정해 회전을 조정할 수 있다.</li>
<li>각도를 직관적으로 조작하기 때문에 계산이 간단하다.</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>두 축이 정렬되어 회전 자유도가 줄어들어 특정 방향으로 회전할 수 없는 문제가 발생한다. 이는 회전을 예측하기 어렵게 만든다.</li>
<li>회전 순서에 따라 결과가 달라져 복잡한 회전 조작 시 문제가 발생할 수 있다.</li>
<li>비선형적 결과를 가지고 있어 부드러운 회전이 어렵다.</li>
</ul>
<h2 id="3-쿼터니언">3. 쿼터니언</h2>
<p>쿼터니언은 4개의 숫자로 표현하는 구조로 실수 및 허수로 이루어진 4차원 복소수이다.</p>
<h4 id="장점-1">장점</h4>
<ul>
<li>회전 표현 시 짐벌 락 문제를 피할 수 있다. 회전의 자유도가 항상 유지되어 복잡한 회전에도 문제가 생기지 않는다.</li>
<li>두 회전 사이를 보간하는 것이 쉬워 애니메이션, 카메라 이동 등에 유리하다.</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>직관적이지 않아 사용자가 이해하기 어렵다.</li>
<li>시각적 표현이 오일러보다 직관적이지 않기 때문에 회전 상태를 확인하기 어렵다.</li>
<li>쿼터니언과 오일러 각 간의 변환이 필요할 수 있어 계산이 복잡하다.</li>
</ul>
<h2 id="4-정리">4. 정리</h2>
<p>오릴러각은 직관적이고 간단한 회전 표현에 사용할 수 있지만, 짐벌락 등의 문제가 발생할 수 있으며 쿼터니언은 복잡한 구조를 사용하지만 짐벌락 문제를 피하고 보간을 지원해 자주 사용되는 방법이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity DoTween]]></title>
            <link>https://velog.io/@en_balor/C-Unity-DoTween</link>
            <guid>https://velog.io/@en_balor/C-Unity-DoTween</guid>
            <pubDate>Wed, 24 Jul 2024 12:06:30 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>이번에 UI를 제작하면서 DoTween을 처음으로 사용해봐서 내용을 정리해보려 한다.
DoTween은 애니메이션을 구현하기위한 라이브러리로 오브젝트의 위치, 회전, 색상 등을 시간을 통해 변화시키는 기능을 제공한다.</p>
<h3 id="1-주요-기능-및-특징">1. 주요 기능 및 특징</h3>
<h4 id="트위닝">트위닝</h4>
<ul>
<li>Move: 오브젝트를 이동시킴</li>
<li>Rotate: 오브젝트를 특정 회전값으로 회전</li>
<li>Scale: 오브젝트의 크기 변경</li>
<li>Color: 색상변경(UI, 마테리얼)</li>
</ul>
<h4 id="애니메이션">애니메이션</h4>
<ul>
<li>여러 트위닝을 순차적으로 실행하거나 동시에 실행할 수 있도록 묶을 수 있음</li>
</ul>
<h4 id="ease-메서드">Ease 메서드</h4>
<p>애니메이션의 속도 곡선을 정의해 자연스러운 속도 연출이 가능함</p>
<p>** DoTween을 사용하기 위해서는 먼저 패키지 매니저에서 DoTween을 설치한 후 using DG.Tweening을 추가해야한다.</p>
<h2 id="2-코드-예제">2. 코드 예제</h2>
<pre><code class="language-cs">using DG.Tweening;
using UnityEngine;

public class Example : MonoBehaviour
{
    void Start()
    {
        // 1. 오브젝트를 (3, 2, 1) 위치로 2초 동안 이동
        transform.DOMove(new Vector3(3, 2, 1), 2f);

        // 2. 오브젝트를 3초 동안 90도 회전
        transform.DORotate(new Vector3(0, 90, 0), 3f);

        // 3. UI 요소의 색상을 1초 동안 빨간색으로 변경
        GetComponent&lt;Image&gt;().DOColor(Color.red, 1f);

        // 4. 체인 애니메이션 예제
        Sequence sequence = DOTween.Sequence();
        sequence.Append(transform.DOMoveX(5, 1f))
                .Append(transform.DORotate(new Vector3(0, 180, 0), 1f))
                .Join(transform.DOScale(new Vector3(2, 2, 2), 1f));
    }
}</code></pre>
<p>위와 같은 방법을 사용하면 애니메이터를 사용하지 않고도 다양한 애니메이션 연출이 가능하다.
현재는 간단한 이동과 투명도를 조절하는 것에만 사용해봤지만 더 복잡한 애니메이션의 구현이 가능한지는 고민해봐야할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 프로그래머스 - 조건 문자열]]></title>
            <link>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A1%B0%EA%B1%B4-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A1%B0%EA%B1%B4-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Tue, 23 Jul 2024 12:00:25 GMT</pubDate>
            <description><![CDATA[<h2 id="1-문제">1. 문제</h2>
<p>문자열에 따라 다음과 같이 두 수의 크기를 비교하려고 합니다.</p>
<ul>
<li>두 수가 n과 m이라면<pre><code>  &quot;&gt;&quot;, &quot;=&quot; : n &gt;= m
  &quot;&lt;&quot;, &quot;=&quot; : n &lt;= m
  &quot;&gt;&quot;, &quot;!&quot; : n &gt; m
  &quot;&lt;&quot;, &quot;!&quot; : n &lt; m</code></pre></li>
</ul>
<p>두 문자열 ineq와 eq가 주어집니다. ineq는 &quot;&lt;&quot;와 &quot;&gt;&quot;중 하나고, eq는 &quot;=&quot;와 &quot;!&quot;중 하나입니다. 그리고 두 정수 n과 m이 주어질 때, n과 m이 ineq와 eq의 조건에 맞으면 1을 아니면 0을 return하도록 solution 함수를 완성해주세요.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System;

public class Solution {
    public int solution(string ineq, string eq, int n, int m) {
        int answer = 0;
        string ie = ineq + eq;

        if(ie == &quot;&gt;=&quot;)
            return n &gt;= m ? 1 : 0;
        if(ie == &quot;&lt;=&quot;)
            return n &lt;= m ? 1 : 0;
        if(ie == &quot;&lt;!&quot;)
            return n &lt; m ? 1 : 0;
        if(ie == &quot;&gt;!&quot;)
            return n &gt; m ? 1 : 0;

        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Unity 인벤토리 만들기]]></title>
            <link>https://velog.io/@en_balor/C-Unity-%EC%9D%B8%EB%B2%A4%ED%86%A0%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-Unity-%EC%9D%B8%EB%B2%A4%ED%86%A0%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 22 Jul 2024 11:48:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>메트로바니아 팀 프로젝트를 진행하면서 습득한 아이템 정보와 레퍼런스 삼은 할로우나이트를 참고해 장착이 가능한 인벤토리가 필요해졌다. 현재 구현한 아이템은 더블점프가 가능한 기본 아이템으로 메트로바니아의 장르 특성상 장착 여부가 필요한 아이템 종류는 아니었기에 현재 아이템을 습득하면 인벤토리에 출력되게끔 코드를 작성했다.</p>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.TextCore.Text;

public class Inventory : MonoBehaviour
{
    public ItemSlot[] slots;

    public GameObject inventoryUI;
    public Transform slotPanel;

    [Header(&quot;Selected Item&quot;)]
    private ItemSlot selectedItem;
    private int selectedItemIndex;
    public TextMeshProUGUI itemName;
    public TextMeshProUGUI itemDescription;

    private int curEquipIndex;

    private void Start()
    {
        CharacterManager.Instance.Player.addItem += AddItem;
        slots = new ItemSlot[slotPanel.childCount];

        for(int i = 0; i &lt; slots.Length; i++)
        {
            slots[i] = slotPanel.GetChild(i).GetComponent&lt;ItemSlot&gt;();
            slots[i].index = i;
            slots[i].inventory = this;
            slots[i].Clear();
        }
    }

    public bool IsOpen()
    {
        return inventoryUI.activeInHierarchy;
    }

    public void AddItem()
    {
        Debug.Log(&quot;additem&quot;);
        ItemData data = CharacterManager.Instance.Player.itemData;

        ItemSlot emptySlot = GetEmptySlot();

        if(emptySlot != null )
        {
            emptySlot.item = data;
            UpdateInventory();
            CharacterManager.Instance.Player.itemData = null;
            return;
        }
    }

    ItemSlot GetEmptySlot()
    {
        for(int i = 0; i &lt; slots.Length; i++)
        {
            if (slots[i].item == null)
            {
                return slots[i];
            }
        }

        return null;
    }

    public void UpdateInventory()
    {
        for(int i = 0; i &lt; slots.Length; i++)
        {
            if (slots[i].item != null)
            {
                slots[i].SetItem();
            }

            else
            {
                slots[i].Clear();
            }
        }
    }

    private void ClearSelectItem()
    {
        selectedItem = null;

        itemName.text = string.Empty;
        itemDescription.text = string.Empty;
    }
}</code></pre>
<h2 id="3-정리">3. 정리</h2>
<p>플레이어가 소지중인 아이템 데이터에 델리게이트를 사용해서 아이템을 추가해줬다. 델리게이트는 함수나 메서드를 참조하고 호출할 수 있는 형식으로 코드를 더 유연하고 확장 가능하게 작성할 수 있다. 앞서 사용한 Action 등이 예시이다.
변수로는 아이템들을 담을 배열인 <code>slots</code>, 인벤토리의 활성화 상태를 조절하기 위해 GameObject로 가져와 주고, 부모 오브젝트의 위치인 <code>slotPanel</code>을 가져왔다.
아래의 텍스트들은 아이템을 선택하면 이름과 소개로 변경할 수 있게 미리 가져왔다.
<code>slots</code> 배열을 초기화 해주고 플레이어의 addItem 델리게이트에 메서드를 추가해준다.
슬롯의 인덱스와 인벤토리 참조를 설정한 후 슬롯을 초기화 해준다.
AddItem으로 플레이어의 아이템 데이터에 해당 아이템을 추가해주고(해당 로직은 Item클래스에 들어있다) 아이템이 비어있을 경우 null을 반환해준 후 슬롯에 해당 아이템들을 반영해준다. 이 후 UpdateInventory를 통해 인벤토리를 최신화 한다.
UpdateInventory는 아이템이 있는 슬롯은 아이템을 설정하고, 없는 슬롯은 슬롯을 초기화 해준다.
ClearselectItem은 선택한 아이템을 초기화 해주는 메서드인데 아직 아이템을 선택하는 기능을 짜두지 않아 작동하지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 프로그래머스 - 등차수열의 특정한 항만 더하기]]></title>
            <link>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%93%B1%EC%B0%A8%EC%88%98%EC%97%AC%EB%A5%B4%EC%9D%B4-%ED%8A%B9%EC%A0%95%ED%95%9C-%ED%95%AD%EB%A7%8C-%EB%8D%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%93%B1%EC%B0%A8%EC%88%98%EC%97%AC%EB%A5%B4%EC%9D%B4-%ED%8A%B9%EC%A0%95%ED%95%9C-%ED%95%AD%EB%A7%8C-%EB%8D%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 19 Jul 2024 11:03:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-문제-설명">1. 문제 설명</h2>
<p>두 정수 a, d와 길이가 n인 boolean 배열 included가 주어집니다. 첫째항이 a, 공차가 d인 등차수열에서 included[i]가 i + 1항을 의미할 때, 이 등차수열의 1항부터 n항까지 included가 true인 항들만 더한 값을 return 하는 solution 함수를 작성해 주세요.</p>
<h2 id="2-제한사항">2. 제한사항</h2>
<ul>
<li>1 ≤ a ≤ 100</li>
<li>1 ≤ d ≤ 100</li>
<li>1 ≤ included의 길이 ≤ 100</li>
<li>included에는 true가 적어도 하나 존재합니다.</li>
</ul>
<h2 id="3-코드">3. 코드</h2>
<pre><code class="language-cs">using System;

public class Solution {
    public int solution(int a, int d, bool[] included) {
        int answer = 0;

        for(int i = 0; i &lt; included.Length; i++)
        {
            if(included[i] == true)
                answer += (a + (d * i));
        }

        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 유니티 콜라이더를 참고해 선 그리기-위치 문제 해결]]></title>
            <link>https://velog.io/@en_balor/C-%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%BD%9C%EB%9D%BC%EC%9D%B4%EB%8D%94%EB%A5%BC-%EC%B0%B8%EA%B3%A0%ED%95%B4-%EC%84%A0-%EA%B7%B8%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%BD%9C%EB%9D%BC%EC%9D%B4%EB%8D%94%EB%A5%BC-%EC%B0%B8%EA%B3%A0%ED%95%B4-%EC%84%A0-%EA%B7%B8%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Thu, 18 Jul 2024 12:10:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>어제 미니맵을 그리기 위해 맵의 콜라이더를 참고해 선을 그리도록 하는 코드를 작성했으나 그려진 위치가 올바르게 나오지 않았고, 추가로 라인의 색상이 흰색이 아닌 마젠타로 적용되는 문제와 튀어나와있는 지형의 선이 뾰족하게 튀어나오는 문제가 있었다.
여기서 문제의 일부분을 해결한 과정을 남겨두려한다.</p>
<h3 id="문제점">문제점</h3>
<ul>
<li>선의 위치가 지정한 맵의 콜라이더와 다름</li>
<li>선의 색상이 마젠타 색상으로 나옴</li>
</ul>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class MiniMapRenderer : MonoBehaviour
{
    private LineRenderer lineRenderer;

    [SerializeField]
    private PolygonCollider2D[] tutorialCollider;

    // Start is called before the first frame update
    void Start()
    {
        lineRenderer = GetComponent&lt;LineRenderer&gt;();

        if (tutorialCollider != null &amp;&amp; tutorialCollider.Length &gt; 0)
        {
            UpdateLine();
        }
    }

    private void UpdateLine()
    {
        int totalPoints = 0;
        foreach(PolygonCollider2D colider in tutorialCollider)
        {
            totalPoints += colider.points.Length + 1;
        }

        Vector3[] linePosition = new Vector3[totalPoints];
        int index = 0;

        foreach(PolygonCollider2D collider in tutorialCollider)
        {
            Vector2[] points = collider.points;

            for(int i = 0; i &lt; points.Length; i++)
            {
                Vector3 worldPoint = collider.transform.TransformPoint(points[i]);
                linePosition[index++] = worldPoint;
            }

            linePosition[index++] = collider.transform.TransformPoint(points[0]);
        }

        lineRenderer.positionCount = linePosition.Length;
        lineRenderer.SetPositions(linePosition);
    }
}</code></pre>
<p>이번에는 위치를 해결하면서 동시에 여러개로 나뉘어 있는 맵의 콜라이더들을 배열에 담아 그리기로 했다.
스테이지는 여러개지만 일단 튜토리얼 스테이지를 참고해 선을 그리도록 하려고 튜토리얼을 담는 배열만 만들었다.
foreach문을 통해 콜라이더의 포인트 + 1(원점)을 담아주고, 해당 위치들을 새로운 배열에 담은 다음 TransformPoint를 사용해 위치를 재조정해줬다.</p>
<p>색상이 마젠타로 나오는 문제는 LineRenderer의 마테리얼이 문제였으며 해당 부분에 이미지를 넣으니 색상을 조절할 수 있게 되었다.</p>
<h2 id="3-결과">3. 결과</h2>
<p><img src="https://velog.velcdn.com/images/en_balor/post/5c07edaa-b9de-4e07-b7f9-b281915b652b/image.png" alt=""></p>
<p>원하는 위치에 선들이 생성되었지만 일부분 지형이 이상하게 그려지는 문제가있다.
아마 콜라이더의 포인트 들이 문제이거나 원점으로 돌아오는 부분 둘 중하나가 문제가 있어 보였다.
이상하게 선이 그려지는 부분들은 좀 더 개선해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 유니티 맵 콜라이더를 참고해 선 그리기]]></title>
            <link>https://velog.io/@en_balor/C-%EC%9C%A0%EB%8B%88%ED%8B%B0-%EB%A7%B5-%EC%BD%9C%EB%9D%BC%EC%9D%B4%EB%8D%94%EB%A5%BC-%EC%B0%B8%EA%B3%A0%ED%95%B4-%EC%84%A0-%EA%B7%B8%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-%EC%9C%A0%EB%8B%88%ED%8B%B0-%EB%A7%B5-%EC%BD%9C%EB%9D%BC%EC%9D%B4%EB%8D%94%EB%A5%BC-%EC%B0%B8%EA%B3%A0%ED%95%B4-%EC%84%A0-%EA%B7%B8%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Wed, 17 Jul 2024 11:45:35 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>현재 메트로바니아 장르를 만들면서 할로우 나이트를 레퍼런스 삼아서 팀프로젝트를 제작중에 있다. 할로우나이트에서 가장 불편했던게 맵 시스템이었는데, 지도는 맵 어딘가에 있는 상인에게서 구매해야 했고 플레이어의 위치를 알려주는 아이템은 스토리 진행 후 마을에서 구매가 가능했다. 메트로바니아 장르의 특성상 맵을 헤매는 경우가 많은데 이부분 때문에 길을 못찾는 경우가 많아 길 찾는 시간을 많이 소요한게 아쉬워 팀프로젝트에서는 플레이어의 위치를 더 쉽게 파악할 수 있게 맵 시스템을 추가하려고 했다.</p>
<h2 id="2-미니맵">2. 미니맵</h2>
<p>유니티에서 미니맵을 만드는 방법들을 검색해 봤는데 미니맵에 사용할 카메라를 하나 더 추가해 플레이어에게 정보를 전달하는 방식이었다.
하지만 이렇게 되면 문제점이 있었는데 맵의 그림이 복잡한 현재 팀프로젝트에서 미니맵에 사용하기에는 가독성이 좋지 않은게 문제였다.
때문에 미니맵에서 출력되는 부분을 간결하게 전달하는 방법을 알아보던 도중 콜라이더를 사용해 선을 그리는 방법을 찾아 코드로 작성했다.</p>
<h3 id="코드">코드</h3>
<pre><code class="language-cs">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class MiniMapRenderer : MonoBehaviour
{
    public PolygonCollider2D mapCollider;
    private LineRenderer lineRenderer;

    // Start is called before the first frame update
    void Start()
    {
        lineRenderer = GetComponent&lt;LineRenderer&gt;();

        Vector2[] points = mapCollider.points;
        lineRenderer.positionCount = points.Length + 1;

        for(int i = 0; i &lt; points.Length; i++)
        {
            lineRenderer.SetPosition(i, new Vector2(points[i].x, points[i].y));
        }

        lineRenderer.SetPosition(points.Length, new Vector2(points[0].x, points[0].y));
        lineRenderer.loop = true;
    }
}</code></pre>
<p>여기서 <code>RequireComponent</code>는 요구되는 컴포넌트를 종속성으로 자동 추가해주는 것을 말한다. 당장은 쓸 이유가 없었지만 코드를 찾던 중에 한번 작성해보는게 좋을 것 같아 추가해 봤다.</p>
<p><code>LineRenderer</code>는 두개 이상의 지점의 배열을 사용해 연결하는 직선을 그리는 컴포넌트다.
단순한 직선에서 시작해 복잡한 곡선까지 그릴 수 있어, 현재 사용중인 맵의 곡선에서도 활용이 가능했다.
<code>Vector2</code>에서 <code>PolygonCollider2D</code>를 참고해 콜라이더의 포인트들의 좌표를 배열로 저앙하고 라인렌더러의 포인트 갯수를 반영해준다.
다음 반복문을 이용해 해당 좌표들을 반영해준다.
마지막으로 맵에 사용하기 위해 모양이 만들어져야하니 가장 첫번째 위치로도 연결을 시켜준다.</p>
<h2 id="3-정리">3. 정리</h2>
<p><img src="https://velog.velcdn.com/images/en_balor/post/71eccf44-6380-4eba-b3fc-3269d4a54c36/image.png" alt=""></p>
<p>이렇게해서 맵에 선들을 그리는데에는 성공했지만 <code>PolygonCollider2D</code>의 위치가 정상적으로 반영되지 않는 문제가 있었다.
일단은 모서리 부분의 뿔처럼 튀어나온 부분이 현재 사용중인 콜라이더와 맞지 않았고 맵의 위치와 다른 곳에 선이 그려지는 문제가 있었다.
먼저 <code>PolygonCollider2D</code>의 뿔처럼 나온 부분을 맵의 디자인에 맞춰서 고치는 작업에서 막혀 해당 부분을 해결중에 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 프로그래머스 코드 처리하기]]></title>
            <link>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%94%EB%93%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@en_balor/C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%94%EB%93%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 16 Jul 2024 11:23:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-문제">1. 문제</h2>
<p>문자열 code가 주어집니다.
code를 앞에서부터 읽으면서 만약 문자가 &quot;1&quot;이면 mode를 바꿉니다. mode에 따라 code를 읽어가면서 문자열 ret을 만들어냅니다.</p>
<p>mode는 0과 1이 있으며, idx를 0 부터 code의 길이 - 1 까지 1씩 키워나가면서 code[idx]의 값에 따라 다음과 같이 행동합니다.</p>
<ul>
<li>mode가 0일 때<ul>
<li>code[idx]가 &quot;1&quot;이 아니면 idx가 짝수일 때만 ret의 맨 뒤에 code[idx]를 추가합니다.</li>
<li>code[idx]가 &quot;1&quot;이면 mode를 0에서 1로 바꿉니다.
mode가 1일 때</li>
<li>code[idx]가 &quot;1&quot;이 아니면 idx가 홀수일 때만 ret의 맨 뒤에 code[idx]를 추가합니다.</li>
<li>code[idx]가 &quot;1&quot;이면 mode를 1에서 0으로 바꿉니다.</li>
</ul>
</li>
</ul>
<p>문자열 code를 통해 만들어진 문자열 ret를 return 하는 solution 함수를 완성해 주세요.</p>
<p>단, 시작할 때 mode는 0이며, return 하려는 ret가 만약 빈 문자열이라면 대신 &quot;EMPTY&quot;를 return 합니다.</p>
<p>제한사항</p>
<ul>
<li>1 ≤ code의 길이 ≤ 100,000<pre><code>  code는 알파벳 소문자 또는 &quot;1&quot;로 이루어진 문자열입니다.</code></pre></li>
</ul>
<h2 id="2-코드">2. 코드</h2>
<pre><code class="language-cs">using System;

public class Solution {
    public string solution(string code) {
        string answer = &quot;&quot;;
        int mode = 0;

        for(int i = 0; i &lt; code.Length; i++)
        {
            if(code[i] == &#39;1&#39;)
            {
                if(mode == 0)
                    mode = 1;
                else
                    mode = 0;

            }

            else if(i % 2 == 0 &amp;&amp; mode == 0)
                answer += code[i];

            else if(i % 2 != 0 &amp;&amp; mode == 1)
                answer += code[i];

            Console.WriteLine(mode);
        }

        if(string.IsNullOrEmpty(answer))
                answer = &quot;EMPTY&quot;;

        return answer;
    }
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>