<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sikhye-de-sikhye.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요! 유니티 공부 중인 고3 학생입니다</description>
        <lastBuildDate>Tue, 13 May 2025 11:03:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sikhye-de-sikhye.log</title>
            <url>https://velog.velcdn.com/images/sikhye-de-sikhye/profile/8cf35126-beb4-461c-bbea-8f9830782e94/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sikhye-de-sikhye.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sikhye-de-sikhye" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Unity] Grid 컴포넌트]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/Unity-Grid-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@sikhye-de-sikhye/Unity-Grid-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Tue, 13 May 2025 11:03:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/ab2c9bc2-48e2-4658-99d7-db663fdebe95/image.png" alt="">
Untiy에서 Grid 컴포넌트는 셀(Cell) 기반의 구조를 만들어서, Tilemap이나 다른 셀 단위 시스템이 객체를 배치하거나 관리할 수 있게 해주는 역할을 한다. 이 셀(Cell)은 정사각형, 육각형 또는 등각 뷰(iso)로 구성될 수 있고, 이 구조는 월드 공간(World Space) 안에서의 위치 계산에 영향을 준다.</p>
<h3 id="grid의-셀-기준-좌표">Grid의 셀 기준 좌표</h3>
<p>Grid에서의 셀 좌표(Cell Position)은 정수값(Vector3Int)로 표현되며, 이는 셀의 상대적인 위치를 의미한다.
예를 들어 (0, 0, 0)좌표는 Grid의 중심 셀이고, (1, 0, 0)은 그 오른쪽 셀을 의미한다. Z 값은 대부분 0으로 두며, 동일 위치의 다른 타일 레이어 또는 타일 쌓기(stack) 등에 사용될 수 있다.</p>
<h3 id="셀---월드-위치-변환">셀 -&gt; 월드 위치 변환</h3>
<ul>
<li>Grid는 셀 좌표를 월드 공간 좌표로 변환할 수 있다.
즉, (0, 0, 0) 셀은 Grid의 Transform 위치와 정렬되어 있고, (1, 0, 0)은 그 오른쪽에 한 칸 이동한 셀이다. </li>
<li>각 셀의 실제 크기는 Grid 컴포넌트의 Cell Size 값에 따라 달라진다 (기본값은 1, 1, 1)</li>
</ul>
<h3 id="셀-레이아웃cell-layout">셀 레이아웃(Cell Layout)</h3>
<p>Grid에는 셀의 형태를 정의하는 Cell Layout 옵션이 있다.</p>
<ul>
<li>Rectangle: 일반적인 사각형 타일(2D 게임에서 가장 흔함)</li>
<li>Hexagon: 육각형 타일(벌집 구조나 전략 게임)</li>
<li>Isometric: 등각 뷰 타일(쿼터뷰 스타일)</li>
<li>Isometric Z As Y: Z축을 Y로 해석해 타일을 위로 쌓을 수 있음
각 셀 레이아웃은 셀 간 배치 방식과 모양에 영향을 준다.</li>
</ul>
<h3 id="셀-사이즈cell-size">셀 사이즈(Cell Size)</h3>
<p>이건 말 그대로 각 셀의 월드 상 크기이다.
기본은 (1, 1, 1)이고, 만약 타일 하나가 더 크거나 작게 보이도록 하려면 이 값을 조정하면 된다.</p>
<h3 id="셀-간격cell-gap">셀 간격(Cell Gap)</h3>
<ul>
<li>타일 사이에 얼마나 간격을 둘 것인지를 설정하는 속성
셀 크기 보다 절대값이 큰 음수를 입력하면 Unity는 셀 크기에 맞춰 절댓값을 자동으로 변경한다.
예를 들어, 셀 크기가 (1, 1, 0)이고 셀 간격이 (-2, -2, 0)으로 설정된 경우, 에디터는 셀 간격 값을 (-1, -1, 0)으로 자동 변경한다.</li>
<li>기본값은 (0,0,0)이다. 즉, 셀과 셀 사이에 간격이 없이 붙어 있다.</li>
<li>타일이 겹치거나 너무 빽빽해 보일 때, 이 값을 이용해 간격을 조절할 수 있다.</li>
</ul>
<h3 id="cell-sizzle">Cell Sizzle</h3>
<p>셀 좌표의 축을 변경하거나 뒤집는 기능</p>
<ul>
<li>Siwzzle은 셀 좌표의 X, Y, Z 순서를 바꾸거나 반전시켜서 Tilemap이 셀 좌표를 다르게 해석하게 만든다.</li>
</ul>
<table>
<thead>
<tr>
<th>옵션</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>XYZ</code> (기본값)</td>
<td>X = X, Y = Y, Z = Z (기본 상태)</td>
</tr>
<tr>
<td><code>XZY</code></td>
<td>X = X, Z = Y, Y = Z (Y와 Z 축을 바꿈)</td>
</tr>
<tr>
<td><code>YZX</code></td>
<td>Y = X, Z = Y, X = Z</td>
</tr>
<tr>
<td><code>ZYX</code></td>
<td>Z = X, Y = Y, X = Z</td>
</tr>
<tr>
<td><code>ZXY</code></td>
<td>Z = X, X = Y, Y = Z</td>
</tr>
<tr>
<td><code>YXZ</code></td>
<td>Y = X, X = Y, Z = Z</td>
</tr>
<tr>
<td><code>XZY_NEG</code>, <code>XYZ_NEG</code> 등</td>
<td>축을 뒤집은 버전 (예: -X, -Y, -Z)</td>
</tr>
<tr>
<td>#### 사용하는 이유</td>
<td></td>
</tr>
<tr>
<td>이건 주로 비정형 좌표계나 특수한 그리드 설정이 필요할 때 사용합니다:</td>
<td></td>
</tr>
</tbody></table>
<ul>
<li>3D 게임에서 z축 방향으로 타일을 쌓을 때</li>
<li>타일맵이 특정 방향으로 정렬되어 있어야 할 때</li>
<li>좌표계를 변형시켜서 다른 계산 방식과 맞출 때</li>
</ul>
<p>일반적인 2D 게임에서는 대부분 XYZ 그대로 쓰지만,
3D 환경 또는 특수한 정렬 구조를 만들고 싶을 때 아주 유용해요.</p>
<h3 id="타일맵-위치-계산-시-주의할-점">타일맵 위치 계산 시 주의할 점:</h3>
<ul>
<li>Tilemap.WorldToCell(worldPosition): 월드 좌표를 셀 좌표로 변환</li>
<li>Tilemap.CellToWorld(cellPosition): 셀 좌표를 월드 좌표로 변환
주의: 이때 변환되는 위치는 타일의 기준점(Pivot)이 아닌 셀 기준 좌표이다. 따라서 타일의 실제 중심에 뭔가를 배치하려면, Offset값을 고려해야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] Tilemap 컴포넌트]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/Unity-%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%B0%B8%EC%A1%B0</link>
            <guid>https://velog.io/@sikhye-de-sikhye/Unity-%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%B0%B8%EC%A1%B0</guid>
            <pubDate>Tue, 13 May 2025 10:26:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/c251eb32-f652-4daa-8c5c-52a21f4daad0/image.png" alt="">
<strong>Animation Frame Rate</strong>: 타일 애니메이션이 재생되는 프레임 속도를 설정한다. 이 값을 늘리거나 줄이면 타일 애니메이션의 프레임 속도가 변경된다. 예를 들어, 2로 설정하면 타일 애니메이션이 기본 프레임 속도의 두 배로 재생된다. 3으로 설정하면 타일 애니메이션이 기본 프레임 속도의 세 배로 재생된다.
<strong>Color</strong>: 이 타일맵의 타일에 색조로 적용할 색상을 선택한다. Unity에서 색조 없이 타일을 렌더링하려면 흰색(기본 색상)으로 설정하면 된다.
<strong>Tile Anchor</strong>: 타일맵에서 타일 앵커 위치를 오프셋하려면 xyz 축을 따라 양(셀 단위)을 입력한다.
<strong>Orientation</strong>: 타일맵에서 타일의 방향을 선택합니다. 다음 각 옵션은 선택한 평면을 따라 타일의 방향을 조정하여 동일한 기능을 수행한다.</p>
<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/1eb32611-1f52-452d-bc69-84c6e210be69/image.png" alt=""></p>
<ul>
<li>XY: Unity는 타일을 XY 평면을 따라 배치한다.</li>
<li>XZ: Unity는 타일을 XZ 평면을 따라 배치합니다.</li>
<li>YX: Unity는 타일을 YX 평면을 따라 배치합니다.</li>
<li>YZ: Unity는 타일을 YZ 평면을 따라 배치합니다.</li>
<li>ZX: Unity는 타일을 ZX 평면을 따라 배치합니다.</li>
<li>ZY: Unity는 타일을 ZY 평면을 따라 배치합니다.</li>
<li>Custom: 아래의 사용자 정의 방향 설정을 활성화하려면 이 옵션을 선택하세요.</li>
</ul>
<p>Offset: 사용자 지정 방향의 위치 오프셋을 설정합니다. 이 옵션은 타일맵의 방향을 사용자 지정 으로 설정한 경우에만 사용할 수 있습니다 .
Rotation: 사용자 지정 방향의 회전을 설정합니다. 이 옵션은 타일맵의 방향을 사용자 지정 으로 설정한 경우에만 사용할 수 있습니다 .
Scale:사용자 지정 방향의 크기를 설정합니다. 이 옵션은 타일맵의 방향을 사용자 지정 으로 설정한 경우에만 사용할 수 있습니다 .
Info:이것을 확장하면 타일맵에 있는 자산을 볼 수 있습니다.
<img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/6ff0c7df-a6ca-47fb-8869-da62af54e6ab/image.png" alt=""></p>
<ul>
<li>Tiles: 타일맵에 있는 타일 자산 목록을 표시합니다 .</li>
<li>Sprites: 타일맵에 있는 스프라이트 목록을 표시합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] TcpClient]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-TcpClient</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-TcpClient</guid>
            <pubDate>Mon, 12 May 2025 03:51:19 GMT</pubDate>
            <description><![CDATA[<h3 id="무슨-클래스일까">무슨 클래스일까?</h3>
<p>TcpClient는 TCP 소켓 통신을 쉽게 다루기 위한 C#의 고수준 API 클래스이다.</p>
<ul>
<li><p>내부적으로 Socket 클래스를 감싸고 있고,
IP 주소와 포트에 연결하고, 데이터를 스트림으로 읽고 쓸 수 있도록 도와준다.</p>
<ul>
<li>정확히는 System.Net.Sockets.TcpClient이다.</li>
</ul>
<h3 id="생성자">생성자</h3>
<p>new TcpClient(SERVER_ADDRESS, SERVER_PORT)</p>
<ul>
<li>이 생성자는 TCP 클라이언트를 생성하면서 즉시 서버에 연결(connect)한다.<pre><code class="language-cs">new TcpClient(&quot;127.0.0.1&quot;, 1234)</code></pre>
</li>
<li>&quot;127.0.0.1&quot; -&gt; 연결할 서버의 IP주소 (로컬호스트)</li>
<li>1234 -&gt; 연결할 포트 번호</li>
<li><blockquote>
<p>이 값들은 TcpClient의 내부 Socket 필드에 저장되고, 연결이 시도된다.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h4 id="인자들은-어디에-할당될까">인자들은 어디에 할당될까?</h4>
<p>소스 코드를 보면 TcpClient는 내부적으로 Socket 객체를 생성하고, Connect() 메서드를 호출하여 IPAddress와 port로 연결을 시도한다.</p>
<pre><code class="language-cs">// 내부적으로 이런 식으로 처리됨
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(serverIPAddress, serverPort);</code></pre>
<p>즉, 우리가 넘겨준 IP와 포트는 Socket.Connect()의 인자로 전달되어 바로 TCP 연결을 수행한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] Using 키워드]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-Using-%ED%82%A4%EC%9B%8C%EB%93%9C</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-Using-%ED%82%A4%EC%9B%8C%EB%93%9C</guid>
            <pubDate>Mon, 12 May 2025 03:36:40 GMT</pubDate>
            <description><![CDATA[<h3 id="두-가지-용법">두 가지 용법</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>네임스페이스 import</strong></td>
<td><code>using System.Net.Sockets;</code></td>
<td>다른 네임스페이스의 클래스 등을 가져오기 위한 선언</td>
</tr>
<tr>
<td><strong><code>IDisposable</code> 객체 자동 해제</strong></td>
<td><code>using TcpClient client = new TcpClient(...);</code></td>
<td>블록이 끝날 때 자동으로 자원을 해제 (파일, 소켓, DB 등)</td>
</tr>
</tbody></table>
<p>두 번째 용법의 using은 IDisopsable 인터페이스를 구현한 객체를 자동으로 Dispose() 해주는 문법이다.</p>
<ul>
<li>용어로는 이것을 using 선언문(using declaration) 또는 using 구문 (using statement)라고 부른다.</li>
</ul>
<p><a href="https://storycompiler.tistory.com/223">https://storycompiler.tistory.com/223</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] System.Enum.GetValues
]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-System.Enum.GetValues</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-System.Enum.GetValues</guid>
            <pubDate>Sun, 11 May 2025 13:31:09 GMT</pubDate>
            <description><![CDATA[<h3 id="설명">설명</h3>
<pre><code class="language-cs">System.Enum.GetValues(Type enumType)
</code></pre>
<ul>
<li>주어진 enum 타입의 모든 정의된 값을 배열로 반환한다.
반환 타입은 Array, 그래서 반복문에 사용 가능하다.</li>
</ul>
<h3 id="사용-목적">사용 목적</h3>
<ul>
<li>버튼, 필터링 등 UI 생성 시 enum 기반 자동화에 매우 유용하다.</li>
<li>드롭다운 자동 생성이나 툴 개발에도 엄청 자주 쓴다.</li>
</ul>
<h3 id="예시">예시</h3>
<pre><code class="language-cs">
public enum ItemCategory
{
    Kitchen,
    Interior,
    Exterior
}

foreach (ItemCategory category in System.Enum.GetValues(typeof(ItemCategory)))
</code></pre>
<p>이럴 때 :</p>
<pre><code class="language-cs">System.Enum.GetValues(typeof(ItemCategory))</code></pre>
<p>→ 반환결과 :</p>
<pre><code class="language-cs">Array: [ItemCategory.Kitchen, ItemCategory.Interior, ItemCategory.Exterior]
</code></pre>
<p>따라서 foreach 루프에서 category는 각각 순서대로 저 값을 가진다.</p>
<h3 id="기타-팁-enumgetnames">기타 팁: Enum.GetNames()</h3>
<ul>
<li>Enum.GetNames(typeof(ItemCategory))
→ [&quot;Kitchen&quot;, &quot;Interior&quot;, &quot;Exterior&quot;] 라는 문자열 배열 반환</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] FirstOrDefault]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-FirstOrDefault</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-FirstOrDefault</guid>
            <pubDate>Sun, 11 May 2025 13:19:13 GMT</pubDate>
            <description><![CDATA[<h3 id="개념">개념</h3>
<pre><code class="language-cs"> var existingItem = items.FirstOrDefault(i =&gt; i.itemData == itemData);</code></pre>
<p>이 구문은 리스트에서 조건에 처음으로 부합하는 항목을 찾아 반환한다.
단, 없으면 null을 반환한다. (default값이기 때문이다)</p>
<h3 id="함수-정의">함수 정의</h3>
<pre><code class="language-cs">T FirstOrDefault&lt;T&gt;(Func&lt;T, bool&gt; predicate)</code></pre>
<ul>
<li>First(...) 와 달리, 조건을 만족하는 항목이 없어도 예외를 발생시키지 않음</li>
<li>First(...) 는 조건에 맞는 항목이 없으면 InvalidOperationException 발생시킴</li>
</ul>
<h3 id="예시-코드">예시 코드</h3>
<pre><code class="language-cs">var existingItem = items.FirstOrDefault(i =&gt; i.itemData == itemData);</code></pre>
<ul>
<li>items 리스트를 순회하면서 i.itemData == itemData 조건을 만족하는 첫 번째 아이템을 찾아서 existingItem에 저장</li>
<li>찾지 못하면 existingItem은 null이 됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] Where 함수]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-Where-%ED%95%A8</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-Where-%ED%95%A8</guid>
            <pubDate>Sun, 11 May 2025 12:19:01 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<p>Where는 LINQ 메서드 중 하나로, 컬렉션(리스트 등)에서 특정 조건을 만족하는 요소들만 선택하는 필터링 함수이다.</p>
<pre><code class="language-cs">items.Where(조건식)</code></pre>
<p>→ 조건식을 만족하는 요소들만 &quot;지연 평가&quot;된 컬렉션으로 반환</p>
<p><strong>예시 코드</strong></p>
<pre><code class="language-cs">    public List&lt;InventoryItem&gt; GetItemsByCategory(ItemCategory category) 
    {
        return items.Where(i =&gt; i.itemData.Category == category).ToList();
    }</code></pre>
<p>이건 이렇게 해석할 수 있다:</p>
<p>&quot;items 리스트에서 itemData.Category가 매개변수인 category와 같은 모든 InventoryItem 객체들을 추려라&quot;</p>
<p>i는 리스트의 각 요소 (즉, InventoryItem)</p>
<p>i.itemData.Category → 아이템의 분류</p>
<p>== category → 매개변수로 받은 카테고리와 일치 여부 확인</p>
<p>결과적으로 조건에 맞는 요소만 필터링해서 반환된다.</p>
<h3 id="tolist">.ToList()</h3>
<ul>
<li>Where(...)의 반환형은 IEnumerable<T>이다.
Where(...)는 List<T>가 아닌 IEnumerable<T> 을 반환하기 때문에,
필터링된 결과를 .ToList()를 통해 리스트로 변환해준다.</li>
</ul>
<p>즉, 필터링된 결과는 아직 진짜 리스트로 materialize(구현)된 게 아님</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] System.Enum 활용]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-System.Enum</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-System.Enum</guid>
            <pubDate>Sun, 11 May 2025 11:59:35 GMT</pubDate>
            <description><![CDATA[<h3 id="정식-정의">정식 정의</h3>
<pre><code class="language-cs">namespace System
{
    public abstract class Enum : ValueType
}</code></pre>
<p>즉, 모든 enum 타입의 부모 클래스이다.
C#에서 열거형(enum)을 만들면 자동으로 System.Enum을 상속받는다.</p>
<h3 id="예시">예시</h3>
<pre><code class="language-cs">public enum ItemCategory
{
    Kitchen,
    Interior,
    Exterior
}</code></pre>
<p>→ 이 타입은 내부적으로 System.Enum을 기반으로 만들어진 값 형식이다.
즉, </p>
<pre><code class="language-cs">ItemCategory myCategory = ItemCategory.Kitchen;

System.Enum e = myCategory; 
</code></pre>
<h3 id="요약">요약</h3>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>System.Enum</code></td>
<td>모든 enum 타입의 부모 타입 (abstract class)</td>
</tr>
<tr>
<td>역할</td>
<td><strong>enum을 일반화된 방식으로 다루기 위해 사용</strong></td>
</tr>
<tr>
<td>쓰임새</td>
<td>다양한 enum을 하나의 타입으로 받아서 다형적으로 사용하고 싶을 때</td>
</tr>
</tbody></table>
<h3 id="사용-예시subcategory">사용 예시(SubCategory)</h3>
<p>이 줄은 추상 클래스에서:</p>
<pre><code class="language-cs">public virtual System.Enum SubCategory =&gt; null;</code></pre>
<p>이건 &quot;이 아이템이 속한 하위 카테고리를 리턴하게 하고 싶다, 그런데 그 하위 카테고리는 아이템마다 enum이 다를 수도 있으니까, 그냥 Enum 타입으로 뭉뚱그리자&quot;는 전략이다.</p>
<p><strong>예시 시나리오</strong></p>
<pre><code class="language-cs">public enum KitchenSubCategory { Stove, Sink, Refrigerator }
public enum InteriorSubCategory { Table, Chair, Lamp }

public class KitchenItemData : ItemData
{
    public KitchenSubCategory subCategory;
    public override ItemCategory Category =&gt; ItemCategory.Kitchen;
    public override System.Enum SubCategory =&gt; subCategory;
}

public class InteriorItemData : ItemData
{
    public InteriorSubCategory subCategory;
    public override ItemCategory Category =&gt; ItemCategory.Interior;
    public override System.Enum SubCategory =&gt; subCategory;
}
</code></pre>
<p> 이러면 서브카테고리 enum 타입이 다 다르지만,
부모 클래스에서는 공통적으로 Enum 타입으로 처리할 수 있다!</p>
<h3 id="주의할-점">주의할 점</h3>
<ul>
<li>System.Enum은 abstract class라서 직접 인스턴스를 만들 수 없다
따라서 실제 값은 반드시 enum 형식 중 하나여야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity]System.Serializable]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/UnitySystem.Serializable</link>
            <guid>https://velog.io/@sikhye-de-sikhye/UnitySystem.Serializable</guid>
            <pubDate>Sun, 11 May 2025 09:47:56 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cs">using UnityEngine;

[System.Serializable]
public class InventoryItem
{
    public ItemData itemData;
    public int quantity;
}
</code></pre>
<h3 id="의미">의미</h3>
<p>이 클래스를 Unity 인스펙터에 표시할 수 있게 만들어주는 <strong>마커</strong> 역할이다.</p>
<p>기본적으로 C# 클래스는 Unity의 인스펙터에 보이지 않는다
하지만 [System.Serializable]을 붙이면,
Unity는 이 클래스를 <strong>&quot;직렬화(serialize) 가능&quot;</strong>하다고 판단하고
이 클래스를 필드로 가지고 있는 MonoBehaviour나 ScriptableObject에서 표시해준다.</p>
<h3 id="예제">예제</h3>
<pre><code class="language-cs">public class Inventory : MonoBehaviour
{
    public InventoryItem item;
}</code></pre>
<ul>
<li><p>InventoryItem이 [System.Serializable]이 없다면 item은 인스펙터에 표시 X</p>
</li>
<li><p>[System.Serializable]이 붙어 있으면 item의 itemData, quantity도 인스펙터에 표시 </p>
</li>
</ul>
<h3 id="결론">결론</h3>
<p>[System.Serializable] 은 클래스(혹은 struct)를 인스펙터에서 노출 가능하게 해준다.
인스펙터에 표시되려면 필드 타입이 직렬화 가능해야 하고,
필드 자체는 public이거나 [SerializeField]로 선언되어야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[람다식(lambda expression) 정리]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/%EB%9E%8C%EB%8B%A4%EC%8B%9Dlambda-expression-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@sikhye-de-sikhye/%EB%9E%8C%EB%8B%A4%EC%8B%9Dlambda-expression-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 28 Apr 2025 02:53:45 GMT</pubDate>
            <description><![CDATA[<h2 id="람다식">람다식</h2>
<ul>
<li>이름 없는 함수(무명 함수)를 아주 간결하게 표현하는 문법이다.
코드를 더 간결하고 직관적으로 만들기 위해 쓴다.</li>
</ul>
<h2 id="기본-구조">기본 구조</h2>
<pre><code class="language-cs">(매개변수들) =&gt; 식 또는 {코드블록}</code></pre>
<h3 id="형태">형태</h3>
<p>(x,y) =&gt; x+y : 하나의 식이면 { } 생략 가능하다.
x =&gt; x+4 : 매개변수가 1개이면 () 생략 가능하다.
(x, y) =&gt; { return x+y; } 코드가 여러줄이면 { }와 return 필요</p>
<h2 id="클로저">클로저</h2>
<p>람다식 내부에서, 외부에 선언된 변수를 참조하거나 수정할 수 있다.
이 때, 람다식은 해당 외부변수를 자신과 함께 기억하고 다니는데, 이를 클로저(closure)라고 한다.</p>
<h3 id="반복문과-클로저-문제">반복문과 클로저 문제</h3>
<p>for문 안에서 람다를 만들 때, 외부 변수를 캡처(기억)하는 특성 떄문에 모든 람다가 같은 변수를 공유하는 문제가 발생 가능하다.
<strong>예제</strong></p>
<pre><code class="language-cs">List&lt;Action&gt; actions = new List&lt;Action&gt;();
for(int i = 0; i&lt;3; i++)
{
    actions.Add() =&gt; Console.WriteLine(i)); 
}
foreach(var action in actions)
{
    action();
}</code></pre>
<p><strong>결과(예상vs실제)</strong></p>
<ul>
<li>기대하는 출력: 0\n 1\n 2\n</li>
<li>실제 출력: 3\n 3\n 3\n</li>
</ul>
<p><strong>왜 이런 결과가 나왔을까?</strong>
for문의 i는 하나의 변수이다.
람다식이 i를 캡처할 때, i의 현재 값을 복사하는 것이 아니라, i 자체를 참조한다.
for문이 끝난 후 i는 3이 되어 있고, 모든 람다가 그 3을 바라본다.</p>
<p><strong>해결 방법</strong>
i를 복사한 새로운 지역변수를 만들어서 캡처하면 된다.</p>
<pre><code class="language-cs">for(int i = 0; i&lt;3; i++)
{
    int temp = i; // 복사
    actions.Add(()=&gt; Console.WriteLine(temp));
}
foreach(var action in actions) { action(); }
</code></pre>
<ul>
<li>temp는 각 반복마다 새로 만들어진 지역변수이다.</li>
<li>그래서 각각 람다가 자신만의 temp를 캡처하게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] Resources.Load로 런타임에 에셋을 동적으로 불러오는 방법]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/Unity-Resources.Load%EB%A1%9C-%EB%9F%B0%ED%83%80%EC%9E%84%EC%97%90-%EC%97%90%EC%85%8B%EC%9D%84-%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B6%88%EB%9F%AC%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@sikhye-de-sikhye/Unity-Resources.Load%EB%A1%9C-%EB%9F%B0%ED%83%80%EC%9E%84%EC%97%90-%EC%97%90%EC%85%8B%EC%9D%84-%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%B6%88%EB%9F%AC%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 22 Apr 2025 11:16:55 GMT</pubDate>
            <description><![CDATA[<h2 id="resources-클래스란">Resources 클래스란?</h2>
<p>Resources 클래스는 런타임 중 Resources 폴더에 위치한 에셋을 동적으로 불러오는 데 사용된다.</p>
<ul>
<li>Resources.Load, Resources.LoadAsync 등을 통해 에셋 로드가 가능하다.</li>
<li>에디터에서는 Resources.FindObjectOfTypeAll을 통해 에셋 검색이 가능하다.</li>
<li>Resources 폴더는 Assets 내부 어디에서나 위치할 수 있으며, 여러 개 존재해도 된다.</li>
</ul>
<p><strong>⚠️주의할 점</strong>
Resources 폴더는 직접 생성해야 하며, 새 프로젝트에 자동으로 포함되지 않는다.</p>
<h2 id="resourcesload-사용법">Resources.Load 사용법</h2>
<p><strong>기본 문법</strong></p>
<pre><code class="language-cs">public static Object Load(string path);
public static T Load&lt;T&gt;(string path) where T : Object;</code></pre>
<ul>
<li>경로는 Resources 폴더를 기준으로 작성하면 된다.</li>
<li>확장자는 생략한다. (.png, .prefab 등)</li>
<li>대소문자 구분이 없다.</li>
<li>슬래시(/)로 하위 폴더를 접근할 수 있다.</li>
</ul>
<p><strong>예제</strong></p>
<pre><code class="language-cs">GameObject go = Resources.Load&lt;GameObject&gt;(&quot;Prefabs/MyPrefab&quot;);
Texture2D tex = Resources.Load&lt;Texture2D&gt;(&quot;Textures/Glass&quot;);
AudioClip clip = Resources.Load&lt;AudioClip&gt;(&quot;Audio/Explosion&quot;);</code></pre>
<p>가급적이면 제네릭을 사용하는 것이 타입 안정성과 가독성 측면에서 더 좋다.</p>
<h2 id="resourcesload의-장단점">Resources.Load의 장단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li><p>인스펙터에 의존하지 않고 동적으로 에셋을 로드 가능하다.</p>
</li>
<li><p>상황에 따라 편리하다. </p>
<p>&lt;상황 예시&gt;</p>
</li>
<li><p><em>스크립트에서 에셋을 동적으로 선택해야 할 때*</em>:
캐릭터가 입는 옷 종류가 20가지고, 런타임에 선택된 옷에 따라 텍스처를 바꿔야 한다면?
Resources.Load(&quot;Clothes/&quot; + selectedName)처럼 문자열만으로 동적으로 불러올 수 있다.)</p>
</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>Resources 폴더의 모든 에셋이 빌드에 포함 -&gt; 앱 용량 증가</li>
<li>에셋 주소 하드코딩 -&gt; 유지보수 어려움</li>
<li>메모리 관리가 어려움 -&gt; 필요 없어진 에셋은 직접 언로드 필요</li>
</ul>
<h2 id="메모리-관리-resourcesunloadunusedassets">메모리 관리: Resources.UnloadUnusedAssets</h2>
<p>메모리에서 더 이상 참조되지 않는 에셋들을 언로드(제거)해주는 함수이다.
비동기 함수이므로 yield return과 함께 쓰는 게 일반적이다.</p>
<pre><code class="language-cs">yield return Resources.UnloadUnusedAssets();</code></pre>
<ul>
<li>Resources.Load()를 했지만 어떤 GameObject나 ScriptableObject도 실제로 참조한 적이 없다면,
Unity는 “이 리소스가 아직 사용 중일 수 있음”으로 판단해서 메모리에서 유지한다.</li>
<li>언로드 대상이 되려면, Load()로 한 번 이상 로드되었고 그걸 참조하던 모든 객체가 Destroy되거나 참조를 해제해야 한다.</li>
</ul>
<h2 id="주요-메서드-정리">주요 메서드 정리</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>Resources.Load&lt;T&gt;(path)</code></td>
<td>지정한 경로의 에셋을 로드한다. (제네릭 사용 권장)</td>
</tr>
<tr>
<td><code>Resources.LoadAll&lt;T&gt;(path)</code></td>
<td>경로 내 모든 에셋을 배열로 로드한다.</td>
</tr>
<tr>
<td><code>Resources.LoadAsync&lt;T&gt;(path)</code></td>
<td>비동기로 에셋을 로드한다. 로딩 시간 동안 앱이 멈추지 않게 할 수 있다.</td>
</tr>
<tr>
<td><code>Resources.UnloadUnusedAssets()</code></td>
<td>사용되지 않는 에셋을 메모리에서 해제한다.</td>
</tr>
<tr>
<td><code>Resources.FindObjectsOfTypeAll&lt;T&gt;()</code></td>
<td>해당 타입의 모든 오브젝트를 반환한다. (에디터 전용)</td>
</tr>
</tbody></table>
<h2 id="전체-예제">전체 예제</h2>
<pre><code class="language-cs">using UnityEngine;

public class ExampleClass : MonoBehaviour
{
    void Start()
    {
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane);
        Renderer rend = go.GetComponent&lt;Renderer&gt;();
        rend.material.mainTexture = Resources.Load(&quot;glass&quot;) as Texture;
    }
}
</code></pre>
<p>&quot;glass&quot;는 Resources 폴더 내 텍스처이며, 형변환 없이 Resources.Load<Texture>(&quot;glass&quot;)처럼 제네릭으로 사용하는 게 더 안전하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unity] 직렬화된 값 유지하기 FormerlySerializedAs]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/Unity-%EC%A7%81%EB%A0%AC%ED%99%94%EB%90%9C-%EA%B0%92-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0-FormerlySerializedAs</link>
            <guid>https://velog.io/@sikhye-de-sikhye/Unity-%EC%A7%81%EB%A0%AC%ED%99%94%EB%90%9C-%EA%B0%92-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0-FormerlySerializedAs</guid>
            <pubDate>Mon, 21 Apr 2025 02:47:41 GMT</pubDate>
            <description><![CDATA[<h2 id="formerlyserializedasattribute">FormerlySerializedAsAttribute</h2>
<p>사용하려면 using UnityEngine.Serialization; 지시문을 추가해야 한다.</p>
<ul>
<li>Unity에서는 필드 이름이 변경되면 기존에 저장된 직렬화 데이터와 매칭되지 않아, 해당 값이 초기화되는 문제가 발생할 수 있다.
이때 FormerlySerializedAs 속성을 사용하면 이전 이름과의 연결을 유지하여 데이터를 잃지 않도록 해준다.</li>
</ul>
<p>다음과 같은 클래스가 있다고 가정해보겠다.</p>
<pre><code class="language-cs">using UnityEngine;

public class Enemy : MonoBehaviour
{
    public int hitpoints; // 체력
}</code></pre>
<p> FormerlySerializedAs(oldName) 속성을 사용하면 직렬화된 값을 잃지 않고 필드의 이름을 바꿀 수 있다.</p>
<p>예를 들어, 아래와 같이 기존에 hitpoints라는 이름의 필드를 health로 변경하면서 FormerlySerializedAs(&quot;hitpoints&quot;)를 붙이면, 기존에 저장된 hitpoints 값이 health에 유지된다.</p>
<pre><code class="language-cs">using UnityEngine;
using UnityEngine.Serialization;

public class Enemy : MonoBehaviour
{
    [FormerlySerializedAs(&quot;hitpoints&quot;)]
    public int health;
}</code></pre>
<h2 id="unity의-직렬화-관련-참고사항">Unity의 직렬화 관련 참고사항</h2>
<p>Unity는 기본적으로 public 필드를 직렬화하여 인스펙터에 표시하고 저장한다.
private 또는 protected 필드를 직렬화하려면 [SerializeField] 속성을 사용해야 한다.</p>
<h2 id="formerlyserializedas의-장점-요약">FormerlySerializedAs의 장점 요약</h2>
<ul>
<li>필드 이름 변경 시에도 기존에 저장된 데이터를 유지할 수 있다.</li>
<li>리팩토링 시 유용하다. 코드의 가독성이나 의미를 개선하기 위해 변수명을 변경하더라도, 데이터를 잃지 않게 도와준다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# Delegate 정리(Unity 예제)]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-Delegate-%EC%A0%95%EB%A6%ACUnity-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-Delegate-%EC%A0%95%EB%A6%ACUnity-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Thu, 17 Apr 2025 14:47:22 GMT</pubDate>
            <description><![CDATA[<p>람다식을 제대로 이해하려면, 먼저 delegate(델리게이트) 개념을 알아야 합니다. 
이 글은 C#의 delegate개념부터 Action, Func, Predicate, event 키워드에 대해 설명하는 글입니다.</p>
<h2 id="delegate란">Delegate란?</h2>
<ul>
<li>Delegate는 메서드를 변수처럼 담을 수 있게 해주는 타입이다. 
쉽게 말해, 메서드를 데이터처럼 다루게 해주는 기능이다.</li>
</ul>
<h3 id="정의하는-법">정의하는 법</h3>
<pre><code class="language-cs">public delegate void MyDelegate(int number);
</code></pre>
<ul>
<li>delegate는 마치 메서드 정의처럼 생겼고, 그걸 통해 <strong>이 delegate에는 어떤 형태(시그니처)의 메서드만 들어올 수 있는지</strong>를 정의해주는 것이다.</li>
<li>이 MyDelegate 타입에는 매개변수가 int 하나고, 반환값이 void인 메서드만 담을 수 있다. 즉, MyDelegate는 일종의 메서드 틀(형식) 이라고 생각하면 된다.</li>
</ul>
<p><strong>&lt;사용하는 법&gt;</strong></p>
<pre><code class="language-cs">public delegate void MyDelegate();

void PrintA() =&gt; Debug.Log(&quot;A&quot;); 
void PrintB() =&gt; Debug.Log(&quot;B&quot;);

void Start()
{
    MyDelegate myFunc;

    myFunc = PrintA; // 마치 값을 대입하듯 메서드를 담는다
    myFunc();        // A 출력

    myFunc = PrintB; // 다른 메서드로 교체 가능
    myFunc();        // B 출력
}</code></pre>
<p>delegate 변수는 해당 delegate와 시그니처가 같은 메서드들을 값처럼 대입하고 바꿀 수 있다.
비유하자면 delegate는 메서드를 넣을 수 있는 틀 같은 것이다. 틀에 맞는 모양의 메서드만 들어갈 수 있다.</p>
<h2 id="delegate를-왜-사용할까">Delegate를 왜 사용할까?</h2>
<ul>
<li>한 코드가 다른 코드에 너무 의존하지 않게 만든다.</li>
<li><ul>
<li>Deleate를 사용하지 않은 예시**</li>
</ul>
</li>
</ul>
<pre><code class="language-cs">public class GameManager : MonoBehaviour
{
    public UIManager uiManager;
    public SoundManager soundManager;

    public void PlayerDied()
    {
        uiManager.ShowGameOver();       // 직접 호출
        soundManager.PlayDeathSound();  // 직접 호출
    }
}</code></pre>
<p>-
해당 코드는 GameManager가 UIManager와 SoundManager를 직접 알고 있어야 하기 때문에, 결합도가 높은 상태이다. UIManager나 SoundManager의 내부 구현이 바뀌면 GameManager도 고쳐야 하는 문제가 발생한다.</p>
<ul>
<li>또한 테스트나 재사용이 어렵고, 수정이 많을 수록 코드가 복잡해진다.</li>
</ul>
<p><strong>Delegate를 사용한 예시</strong></p>
<pre><code class="language-cs">public class GameManager : MonoBehaviour
{
    public delegate void PlayerDeathHandler();
    public event PlayerDeathHandler OnPlayerDied;

    public void PlayerDied()
    {
        OnPlayerDied?.Invoke();  // 외부에 알림만 함
    }
}</code></pre>
<pre><code class="language-cs">public class UIManager : MonoBehaviour
{
    public GameManager gameManager;

    void Start()
    {
        gameManager.OnPlayerDied += ShowGameOver; // 이벤트에 콜백 등록
    }

    void ShowGameOver()
    {
        Debug.Log(&quot;Game Over UI 표시&quot;);
        // 실제 UI 처리
    }
}

public class SoundManager : MonoBehaviour
{
    public GameManager gameManager;

    void Start()
    {
        gameManager.OnPlayerDied += PlayDeathSound;
    }

    void PlayDeathSound()
    {
        Debug.Log(&quot;죽음 사운드 재생&quot;);
        // 실제 사운드 재생
    }
}</code></pre>
<ul>
<li>여러 시스템을 하나의 이벤트로 연결 가능하다 (Delegate는 여러 메서드를 한 번에 연결하고 한 번에 실행할 수 있는 구조다.)</li>
<li>콜백 패턴 구현 가능 (비동기 작업 완료 후 처리 등)</li>
<li>GameManager가 모든 것을 직접 알고 처리하지 않고,
&quot;플레이어가 죽었을 때&quot; 일어날 일을 외부에서 등록해서 분리시켜주는 거다.</li>
</ul>
<h2 id="delegate-선언-및-사용법">Delegate 선언 및 사용법</h2>
<pre><code class="language-cs">public delegate void OnClickAction;

public class ButtonExample : MonoBehaviour
{
    public OnClickAction onClick;

    void Start()
    {
        onClick = PrintMessage; // 메서드 대입
        onClick();              // 실행
    }

    void PrintMessage()
    {
        Debug.Log(&quot;버튼이 눌렸습니다!&quot;);
    }
}</code></pre>
<h2 id="익명-메서드-anonymous-method">익명 메서드 (Anonymous Method)</h2>
<p>익명 메서드는 이름 없이 정의된 일회성 함수이다. 
주로 한 번만 사용할 간단한 작업을 정의할 때 유용하다.</p>
<pre><code class="language-cs">using UnityEngine;

public class Test : MonoBehaviour
{
    delegate void MyDelegate();

    void Start()
    {
        MyDelegate onClick = delegate { Debug.Log(&quot;익명 메서드 실행!&quot;); };
        onClick();
    }
}
</code></pre>
<ul>
<li>너무 복잡한 로직을 익명 메서드 안에 넣으면 가독성 저하가 생긴다.</li>
<li>람다식을 사용하면 더 간결하게 같은 로직을 작성할 수 있다. 
예를 들어, 위의 예제를 람다식으로 바꾸면 다음과 같이 작성할 수 있다.<pre><code class="language-cs">using UnityEngine;
</code></pre>
</li>
</ul>
<p>public class Test : MonoBehaviour
{
    // 델리게이트 타입 선언
    delegate void MyDelegate();</p>
<pre><code>void Start()
{
    // 람다식으로 델리게이트 할당
    MyDelegate onClick = () =&gt; { Debug.Log(&quot;람다식 실행!&quot;); };

    // 호출
    onClick();
}</code></pre><p>}</p>
<pre><code>이렇게 하면 코드가 더 짧고 읽기 쉬워진다. 람다는 간단한 함수 표현식을 더욱 직관적으로 작성할 수 있도록 돕는 문법적 요소이다.

## Action, Func, Predicate
얘네들은 델리게이트의 특정 형태(제네릭 버전)이다.
- Action, Func, Predicate는 자주 쓰이는 형태를 미리 제네릭으로 만들어둔 대표 delegate 타입이라고 보면 된다.

### Action
Action은 반환값이 없는(void) 메서드를 담을 수 있는 제네릭 delegate 타입이다.
즉, **어떤 동작만 수행하고, 결과를 돌려주지 않아도 되는 경우**에 주로 사용된다.
**예시**

```cs
Action sayHello = () =&gt; Debug.Log(&quot;Hello!&quot;);
sayHello();

Action&lt;int&gt; printScore = score =&gt; Debug.Log($&quot;Score: {score}&quot;);
printScore(100); // 출력: Score: 100</code></pre><h3 id="func">Func</h3>
<p>Func는 반환값이 있는 delegate 타입이다.
가장 마지막 제네릭 타입이 반환형이고, 그 앞에 오는 타입들은 매개변수 타입이다.
<strong>예시</strong></p>
<pre><code class="language-cs">Func&lt;int, int&gt; square = x =&gt; x * x;
Debug.Log(square(4)); // 출력: 16

Func&lt;string, int&gt; getLength = str =&gt; str.Length;
Debug.Log(getLength(&quot;Unity&quot;)); // 출력: 5</code></pre>
<h3 id="predicate">Predicate</h3>
<p>Predicate<T>는 반환형이 bool이고, 매개변수가 1개인 delegate 타입이다.
  즉, 조건 판별용 함수를 담을 때 사용한다.
<strong>예시</strong></p>
<pre><code class="language-cs">Predicate&lt;int&gt; isEven = x =&gt; x % 2 == 0;
Debug.Log(isEven(10)); // 출력: True</code></pre>
<table>
<thead>
<tr>
<th>구분</th>
<th>Action</th>
<th>Func</th>
<th>Predicate</th>
<th>일반 delegate</th>
</tr>
</thead>
<tbody><tr>
<td>리턴 타입</td>
<td><code>void</code> (아무것도 반환 안 함)</td>
<td>반환값 있음 (제네릭으로 지정)</td>
<td><code>bool</code>만 반환</td>
<td>직접 지정 가능</td>
</tr>
<tr>
<td>매개변수</td>
<td>0개 ~ 여러 개</td>
<td>0개 ~ 여러 개</td>
<td>정확히 1개</td>
<td>원하는 대로 정의 가능</td>
</tr>
<tr>
<td>제네릭 기반</td>
<td>O</td>
<td>O</td>
<td>O</td>
<td>X 직접 선언해야 함</td>
</tr>
<tr>
<td>예시</td>
<td><code>Action&lt;int&gt;</code></td>
<td><code>Func&lt;int, string&gt;</code></td>
<td><code>Predicate&lt;string&gt;</code></td>
<td><code>public delegate void MyDel(int x);</code></td>
</tr>
<tr>
<td>사용 편의성</td>
<td>O 자주 씀</td>
<td>O 자주 씀</td>
<td>O 조건 검사할 때 유용</td>
<td>처음부터 직접 설계 가능</td>
</tr>
</tbody></table>
<h2 id="event-한정자">event 한정자</h2>
<p>event는 delegate 앞에 붙여 외부에서 직접 이벤트를 호출하지 못하게 막고, 
해당 이벤트가 발생하는 시점을 해당 클래스로 제한하는 역할을 한다. </p>
<ul>
<li><p>클래스 외부에서 직접 이벤트를 변경하거나 호출할 수 없게 만든다.
이벤트를 외부에서 +=(구독) 또는 -=(구독 해지) 방식으로만 조작할 수 있으며, 직접 호출할 수는 없다.</p>
<pre><code class="language-cs">public class HealthSystem
{
  // 이벤트 선언
  public event Action OnDeath;

  public void Die()
  {
      Debug.Log(&quot;죽었습니다.&quot;);
      OnDeath?.Invoke(); // 이벤트를 내부에서만 발생시킴. 외부에서는 이벤트를 실행할 수 없음.
  }
}

</code></pre>
</li>
</ul>
<pre><code>event의 핵심은 외부에서 이벤트를 구독하고 해지할 수 있지만, 이벤트를 발동시키는 책임은 해당 클래스로 제한된다는 점이다. 이로 인해 외부에서 OnDeath = null;처럼 이벤트를 null로 변경하거나 직접 Invoke를 호출하는 일이 불가능하다. 

##   Multicase Delegate(멀티캐스트)
  여러 메서드를 덧붙여 하나의 델리게이트로 묶는 방법을 제공한다. 
- += 연산자를 통해 여러 개의 메서드를 체인처럼 등록할 수 있으며, 
델리게이트가 호출되면 등록된 메서드들이 차례대로 실행된다.



```cs
public delegate void Notify();

Notify notify = () =&gt; Debug.Log(&quot;A&quot;);
notify += () =&gt; Debug.Log(&quot;B&quot;);

notify(); // 출력: A\nB</code></pre><ul>
<li>notify()를 호출하면 A와 B가 차례대로 출력된다.
즉, 델리게이트에 등록된 여러 메서드들이 순차적으로 실행된다.</li>
<li>notify -= () =&gt; Debug.Log(&quot;A&quot;);는 동작하지 않는다.
이유는 notify에 등록된 람다 표현식은 새로운 참조로 생성되기 때문에, 동일한 코드라고 하더라도 메모리 상에서 다른 객체로 취급된다.</li>
</ul>
<p>이처럼 델리게이트는 여러 메서드를 등록하고 호출할 수 있기 때문에 멀티캐스트 델리게이트라고 불린다.</p>
<p>-&gt; 메서드를 따로 정의해서 추가/제거하는 것이 안전하다.</p>
<pre><code class="language-cs">void PrintA() =&gt; Debug.Log(&quot;A&quot;);

Notify notify = PrintA;  // 메서드를 델리게이트에 직접 할당
notify += PrintA;        // 메서드를 덧붙임
notify -= PrintA;        // 메서드를 안전하게 제거

</code></pre>
<h2 id="unity에서-delegate를-자주-쓰는-곳">Unity에서 Delegate를 자주 쓰는 곳</h2>
<ol>
<li>UI 버튼 클릭 처리<ul>
<li>UI 버튼의 클릭 이벤트를 Delegate로 처리하여 다양한 행동을 유연하게 연결할 수 있다.</li>
</ul>
<ol start="2">
<li>애니메이션/효과 완료 후 동작 처리</li>
</ol>
<ul>
<li>애니메이션이나 효과가 끝났을 때 특정 작업을 수행하는 데 Delegate를 활용한다.</li>
</ul>
<ol start="3">
<li>게임 상태 변화 알림 (HP 0, 클리어 등)</li>
</ol>
<ul>
<li>게임 상태(예: HP 0, 클리어 등)가 변화할 때 필요한 알림을 Delegate로 처리하여 게임의 흐름을 관리한다.</li>
</ul>
<ol start="4">
<li>씬 전환/로드 완료 콜백</li>
</ol>
<ul>
<li>씬  전환이나 로드 완료 후 실행할 작업을 Delegate를 이용해 설정할 수 있다.</li>
</ul>
</li>
</ol>
<h2 id="🔚결론">🔚결론</h2>
<ul>
<li><p>Delegate는 메서드를 변수처럼 다루는 방법이다.
이를 통해 메서드를 동적으로 연결하고 실행할 수 있다.</p>
</li>
<li><p>Delegate는 다양한 상황에서 효율적이고 유연한 이벤트 처리를 가능하게 합니다. 특히 Action, Func, Predicate는 자주 사용되는 Delegate 유형이다.</p>
</li>
<li><p>Event는 Delegate에 대한 안전장치로, 외부에서 Delegate를 잘못 수정하거나 호출하는 것을 방지해준다.</p>
</li>
<li><p>Unity에서는 콜백 구조나 비동기 처리에 필수적인 개념으로, Delegate를 적극적으로 활용하여 유연한 코드 구조를 만들 수 있다.</p>
</li>
</ul>
<h2 id="팁">팁</h2>
<ul>
<li>del()로 직접 호출할 수도 있지만, del.Invoke()로 실행하는 것을 권장한다. del?.Invoke()로 호출하면 null일 때, 안전하게 null 체크를 할 수 있다.</li>
<li>또한 del()로 호출하면 이것이 그냥 함수인지, delegate인지 헷갈릴 수 있다. 반면, Invoke로 호출하면 알기가 쉽다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 세탁소 사장 동혁 | 백준 2720번]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-%EC%84%B8%ED%83%81%EC%86%8C-%EC%82%AC%EC%9E%A5-%EB%8F%99%ED%98%81-%EB%B0%B1%EC%A4%80-2720%EB%B2%88</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-%EC%84%B8%ED%83%81%EC%86%8C-%EC%82%AC%EC%9E%A5-%EB%8F%99%ED%98%81-%EB%B0%B1%EC%A4%80-2720%EB%B2%88</guid>
            <pubDate>Wed, 16 Apr 2025 08:04:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/1883d0e1-5972-48e0-a807-743e4a0bd0cb/image.png" alt="">
문제 링크: <a href="https://www.acmicpc.net/problem/2720">https://www.acmicpc.net/problem/2720</a></p>
<ul>
<li><strong>거스름돈(센트 단위)</strong>을 입력받아, 동전의 개수를 최소로 거슬러주는 문제이다.</li>
</ul>
<p>사용할 수 있는 동전은 다음과 같다:</p>
<p>쿼터(Quarter): 0.25달러 → 25센트</p>
<p>다임(Dime): 0.10달러 → 10센트</p>
<p>니켈(Nickel): 0.05달러 → 5센트</p>
<p>페니(Penny): 0.01달러 → 1센트</p>
<p>동전의 개수를 최소화하려면 가능한 큰 단위의 동전부터 많이 사용하는 것이 유리하다.
따라서 가장 큰 단위부터 차례대로 해당 동전으로 거슬러줄 수 있는 개수를 계산하고, 나눠준 뒤 남은 금액을 저장하여 다음 단위 동전에 대해 같은 과정을 반복한다.</p>
<pre><code class="language-cs">using System;

class Baekjoon
{
    static void Main(string[] args)
    {
        int t = int.Parse(Console.ReadLine());

        while (t&gt;0)
        {
            t--;
            int c = int.Parse(Console.ReadLine());

            int quarter = c / 25;
            c %= 25;
            int dime = c / 10;
            c %= 10;
            int nickel = c / 5;
            c %= 5;
            int penny = c;

            Console.WriteLine($&quot;{quarter} {dime} {nickel} {penny}&quot;);
        }
    }
}
</code></pre>
<p>혹시 틀린 부분이 있다면 편하게 댓글 달아주세요 :)
질문도 언제든지 환영입니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[C# 제네릭(Generic) 개념 정리]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 16 Apr 2025 03:46:05 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭generic이란">제네릭(Generic)이란?</h2>
<ul>
<li>C#에서 코드의 재사용성과 유연성을 향상해주는 도구이다.</li>
<li>데이터 형식을 일반화하여 재사용 가능한 코드를 작성할 수 있게 해준다.</li>
<li>제네릭을 사용하면 다양한 형식의 데이터를 처리하는 메서드와 클래스를 작성할 수 있으며, 컴파일 시점에서 형식 안정성(type safety)를 보장해준다.</li>
</ul>
<h3 id="제네릭은-언제-사용될까">제네릭은 언제 사용될까?</h3>
<ol>
<li>여러 데이터 형식에 대해 동일한 로직을 적용해야 할 때</li>
<li>컬렉션 타입에서 다양한 데이터 형식을 저장하고 관리해야 할 때</li>
<li>데이터 형식에 따라 다른 연산을 수행해야 할 때</li>
</ol>
<h3 id="제네릭-사용법">제네릭 사용법</h3>
<p><strong>&lt;제네릭 메서드&gt;</strong></p>
<pre><code class="language-cs">public class Utility
{
    public static void Swap&lt;T&gt;(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

// 사용 예시
int a = 10, b = 20;
Utility.Swap(ref a, ref b);</code></pre>
<p><strong>&lt;제네릭 클래스&gt;</strong></p>
<pre><code class="language-cs">public class Box&lt;T&gt;
{
    private T value;

    public void SetValue(T val) =&gt; value = val;
    public T GetValue() =&gt; value;
}

// 사용 예시
Box&lt;int&gt; intBox = new Box&lt;int&gt;();
intBox.SetValue(123);
int result = intBox.GetValue();</code></pre>
<h2 id="net의-제네릭-클래스들">.NET의 제네릭 클래스들</h2>
<p>.NET Framework에는 다양한 제네릭 클래스들이 포함되어 있다.
특히 System.Collections.Generic 네임스페이스에 있는 모든 컬렉션 관련 클래스들은 제네릭 타입이다.</p>
<ul>
<li>예시: List<T>, Dictionary&lt;TKey, TValue&gt;, Queue<T>, Stack<T>, LinkedList<T></li>
</ul>
<h2 id="제네릭-제약-조건generic-constraints">제네릭 제약 조건(Generic Constraints)</h2>
<ul>
<li>제네릭의 제약 조건은 제네릭 타입 매개변수 T에 적용되는 제한사항이다.</li>
<li>이를 통해 특정 클래스나 인터페이스를 상속/구현하도록 강제하거나, 값 형식만 허용, 기본 생성자 요구 등의 제약을 설정할 수 있다.</li>
</ul>
<p><strong>&lt;문법 예시&gt;</strong></p>
<pre><code class="language-cs">public class MyClass&lt;T&gt; where T : 제약조건
{
    // 클래스 내용
}

public void MyMethod&lt;T&gt;(T param) where T : 제약조건
{
    // 메서드 내용
}
</code></pre>
<h3 id="제약-조건-종류">제약 조건 종류</h3>
<table>
<thead>
<tr>
<th>💡 제약 조건</th>
<th>🧾 설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>where T : 클래스명</code></td>
<td>T는 해당 클래스 또는 그 파생 클래스여야 함</td>
</tr>
<tr>
<td><code>where T : 인터페이스명</code></td>
<td>T는 해당 인터페이스를 구현한 형식이어야 함</td>
</tr>
<tr>
<td><code>where T : new()</code></td>
<td>T는 매개변수가 없는 기본 생성자를 반드시 가져야 함 <br>→ 다른 생성자가 있어도 상관없음</td>
</tr>
<tr>
<td><code>where T : struct</code></td>
<td>T는 <strong>값 형식</strong>이어야 함 (Nullable 불가)</td>
</tr>
<tr>
<td><code>where T : class</code></td>
<td>T는 <strong>참조 형식</strong>이어야 함</td>
</tr>
<tr>
<td><code>where T : unmanaged</code></td>
<td>T는 <strong>비관리 형식</strong>이어야 함 (C# 7.3 이상)</td>
</tr>
<tr>
<td><code>where T : enum</code></td>
<td>T는 <strong>열거형</strong>이어야 함 (C# 7.3 이상)</td>
</tr>
<tr>
<td><code>where T : delegate</code></td>
<td>T는 <strong>대리자 형식</strong>이어야 함 (C# 7.3 이상)</td>
</tr>
</tbody></table>
<p><strong>🔎 비관리 형식(unmanaged)이란?</strong>
값 형식이며 내부 필드들까지 모두 값 형식으로 구성된 타입으로, 메모리 레이아웃이 고정되어 있고, 포인터 연산이 안전하게 가능한 타입.</p>
<h2 id="제네릭-인터페이스">제네릭 인터페이스</h2>
<ul>
<li>제네릭 인터페이스는 다양한 형식의 클래스들이 동일한 동작을 수행할 수 있도록 하는 형식 안정적인 인터페이스를 정의할 수 있다.
&lt;예제(유니티 게임 공격 시스템)&gt;<pre><code class="language-cs">public interface IAttackable&lt;T&gt;
{
  void Attack(T target);
}
</code></pre>
</li>
</ul>
<p>public class Enemy : MonoBehaviour, IAttackable<Player>
{
    public void Attack(Player target)
    {
        Debug.Log(&quot;플레이어에게 공격!&quot;);
    }
}</p>
<pre><code>
- 이런 제네릭 인터페이스는 Player, Enemy, Boss 등 다양한 대상에게 공격할 수 있는 구조를 유연하게 구성할 수 있게 해준다.

## 제네릭 싱글톤 패턴
- MonoBehaviour 기반 클래스에 싱글톤을 적용하려면 제네릭을 이용하면 재사용성이 높아진다.
```cs
public class Singleton&lt;T&gt; : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject obj = GameObject.Find(typeof(T).Name);
                if (obj == null)
                {
                    obj = new GameObject(typeof(T).Name);
                    instance = obj.AddComponent&lt;T&gt;();
                }
                else
                {
                    instance = obj.GetComponent&lt;T&gt;();
                }
            }
            return instance;
        }
    }

    public void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }
}

// 사용 예시
public class GameManager : Singleton&lt;GameManager&gt;
{
    public void InitGame() { }
}
</code></pre><h2 id="✨-마무리-팁">✨ 마무리 팁</h2>
<ul>
<li>제네릭은 코드 중복을 줄이고 타입 안정성을 높이는 데 매우 강력한 도구이다.</li>
<li>Unity나 .NET 기반 프로젝트에서도 많이 사용되며, 특히 컬렉션, 싱글톤, 인터페이스 구조에서 자주 활용된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 진법 변환2 문제 풀이 | 백준 11005번]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-%EC%A7%84%EB%B2%95-%EB%B3%80%ED%99%982-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%B1%EC%A4%80-11005%EB%B2%88</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-%EC%A7%84%EB%B2%95-%EB%B3%80%ED%99%982-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%B1%EC%A4%80-11005%EB%B2%88</guid>
            <pubDate>Mon, 14 Apr 2025 10:44:05 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/3c8d55e0-f6b9-4a31-9abc-0dcc591098b9/image.png" alt="">
문제 링크: <a href="https://www.acmicpc.net/problem/11005">https://www.acmicpc.net/problem/11005</a></p>
<ul>
<li>10진수를 n진수로 바꿔주는 문제이다</li>
</ul>
<p>10진법-&gt;N진법으로 변환하는 방법: 몫이 0이 될 때까지 10진수를 N으로 나누고 나머지를 기록한다. 나머지들을 거꾸로 이어붙이면 그것이 N진법 결과이다.
<strong>&lt;예시&gt;</strong>
예제 입력대로 60466175 36 (10진수 60461175 -&gt; 36진법)이라고 가정하겠다.</p>
<ul>
<li>60466175 ÷ 36 = 1679615 … 35</li>
<li>1679615 ÷ 36 = 46656 … 35</li>
<li>46656 ÷ 36 = 1296 … 35</li>
<li>1296 ÷ 36 = 36 … 35</li>
<li>36 ÷ 36 = 1 … 0</li>
</ul>
<p>나머지를 거꾸로 읽으면 0ZZZZ이다. 마지막 나머지(맨앞자리)가 0이므로 생략하면 ZZZZ이다.</p>
<pre><code class="language-cs">using System;

class Baekjoon
{
    static void Main(string[] args)
    {

        string[] inputs = Console.ReadLine().Split();

        int n = int.Parse(inputs[0]); // 10진수 숫자
        int b = int.Parse(inputs[1]); // 바꿀 진법
        string result = &quot;&quot;;

        while(n&gt;0) // 몫이 0이 되면 중단
        {
            int remainder = n % b;

            if (remainder &lt; 10) // 나머지가 0~9라면
                result = (char)(&#39;0&#39; + remainder) + result;
            else // 10 이상이라면
                result = (char)(&#39;A&#39; + (remainder - 10)) + result; // 낮은 자리수에서부터 시작하므로, 맨 앞에 붙여주어야 한다.

            n /= b;


        }
        Console.WriteLine(result);
    }
}
</code></pre>
<p><strong>&lt;코드 설명&gt;</strong></p>
<ul>
<li>오른쪽에서 왼쪽으로 갈 수록 자릿수가 점점 커지기 때문에, result = 해당자릿수(나머지) + result를 해서 맨 앞에 붙여주었습니다.</li>
</ul>
<p>혹시 틀린 부분이 있다면 편하게 댓글 달아주세요 :)
질문도 언제든지 환영입니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 진법 변환 문제 풀이 | 백준 2745번]]></title>
            <link>https://velog.io/@sikhye-de-sikhye/C-%EC%A7%84%EB%B2%95-%EB%B3%80%ED%99%98-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%B1%EC%A4%80-2745%EB%B2%88</link>
            <guid>https://velog.io/@sikhye-de-sikhye/C-%EC%A7%84%EB%B2%95-%EB%B3%80%ED%99%98-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%B0%B1%EC%A4%80-2745%EB%B2%88</guid>
            <pubDate>Sun, 13 Apr 2025 02:58:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sikhye-de-sikhye/post/4c1ee34c-3e2b-4325-8c6f-dd88cfd4f776/image.png" alt="">
문제 링크: <a href="https://www.acmicpc.net/problem/2745">https://www.acmicpc.net/problem/2745</a></p>
<ul>
<li>n진수(2,4,6,12,36진수)를 10진수로 바꿔 출력하는 문제이다!</li>
</ul>
<p>Convert.ToInt32(string, int)는 16진수까지만 지원하기 때문에, 36진수에는 적용할 수 없으므로 공식을 통해 문제를 풀어야 한다!!</p>
<p><strong>n진수-&gt; 10진수로 바꾸는 방법</strong></p>
<ul>
<li>맨 오른쪽부터 자릿값*(진법^자릿수위치)를 전부 더해주는 것이다.
예) 2AF 16을 입력받았다면, 16진수로 2 = 2, A = 10, F = 15이므로 
15 x 16^0 = 15 x 1 = 15
10 x 16^1 = 10 x 16 = 160
2 x 16^2 = 2 x 256 = 512
따라서, 15 + 160 + 512 = 687이다.</li>
</ul>
<p><strong>&lt;작성코드&gt;</strong></p>
<pre><code class="language-cs">using System;

class Baekjoon
{
    static void Main(string[] args)
    {

        string[] inputs = Console.ReadLine().Split();

        string number = inputs[0];
        int baseNum = int.Parse(inputs[1]);
        int result = 0;
        int power = 1; // 진법의 거듭제곱

        for(int i=number.Length-1; i&gt;=0; i--)
        {
            char c = number[i]; // 뒷자리부터
            int value; 

            if (char.IsDigit(c))
                value = c - &#39;0&#39;;
            else
                value = c - &#39;A&#39; + 10;

            result += value * power ;
            power *= baseNum;
        }

        Console.WriteLine(result);
    }
}
</code></pre>
<p><strong>&lt;코드 설명&gt;</strong></p>
<ul>
<li>char.IsDigit(char)를 이용해서 문자가 &#39;0&#39;~&#39;9&#39; 사이의 숫자라면 입력받은 숫자로 바꿔주기 위해 &#39;0&#39;을 빼주었습니다. 
예) 문자 &#39;3&#39;은 아스키코드로 51이고, &#39;0&#39;은 48이므로 &#39;3&#39; - &#39;0&#39; = 3</li>
<li>또한 문자라면 c-&#39;A&#39;+10을 하여서 16진수 값이 나오게 해주었습니다.
예) 문자 c가 B라면, B는 아스키코드로 66, A는 65이므로 &#39;B&#39;-&#39;A&#39; = 1이다.(알파벳 순서) 여기에 10을 더해주면 실제 16진수값인 11이 나온다. </li>
</ul>
<p>혹시 틀린 부분이 있다면 편하게 댓글 달아주세요 :)
질문도 언제든지 환영입니다!</p>
]]></description>
        </item>
    </channel>
</rss>