<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SeoHwiDo</title>
    <link>https://baeksa01.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 8 Oct 2026 22:03:52 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SeoHwiDo</managingEditor>
    <item>
      <title>[Unreal5, C++] 길드 경영 + 3인칭 전투 게임 개발 3주차</title>
      <link>https://baeksa01.tistory.com/entry/Unreal5-C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-3%EC%A3%BC%EC%B0%A8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;9월 21~27일에는 길드본부의 모집 화면에 모험가 후보 생성&amp;middot;특성 표시&amp;middot;고용 요청을 연결하고, 같은 날 후보를 다시 뽑는 기능을 추가했다. 기능이 늘면서 여러 클래스에 흩어져 있던 화면 생성과 입력 전환도 UIManager로 모았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모험가 후보와 고용 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모집 화면을 먼저 후보 카드, 선택한 후보의 상세 영역, 이름 입력, 직업 선택으로 나눴다. 현재 화면에는 후보 3칸이 표시되며 카드에서 HP&amp;middot;MP와 특성 칩을 확인할 수 있다. 후보를 선택하면 이름과 직업을 정해 고용 요청을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면이 고용 가능 여부와 명단 변경을 직접 결정하지 않도록 했다. 모집 페이지는 후보 ID&amp;middot;이름&amp;middot;직업을 전달하고, AdventurerManager가 후보 존재 여부와 직업 선택을 검사한다. 성공하면 후보 목록에서 제거하고 고용 명단에 추가한 뒤 두 목록의 변경을 알린다. 후보와 명단은 순회용 배열과 ID 조회용 맵을 함께 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화 테스트에서는 고용 요청 후 명단 추가와 후보 카드 갱신을 확인했다. 다만 주간보고용 최종 화면 촬영에서 고용 버튼을 클릭한 뒤에는 후보 제거가 확인되지 않았다. 입력 도구가 클릭을 전달했다는 결과와 게임 로직이 고용을 처리했다는 결과는 구분해 다시 확인할 예정이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특성은 식별자와 표시 데이터를 분리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후보의 특성은 GameplayTag로 식별하고, 표시 이름&amp;middot;설명&amp;middot;칩 색상과 스탯 변화 값은 DataTable에서 찾도록 구성했다. 카드 위젯은 특성 규칙을 계산하지 않고 전달받은 정의를 표시한다. 같은 태그를 다른 화면에서 사용하더라도 표시 데이터를 한곳에서 조회할 수 있도록 하기 위한 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 후보 생성 코드는 특성을 1~2개 선택한다. 이는 현재 구현 값이며 최종 밸런스나 저장 후 재실행까지 검증했다는 의미는 아니다. 실제 화면에서는 서로 다른 특성 칩을 확인했지만, 상세 설명 문구는 이번 촬영에서 보이지 않아 재점검 항목으로 남겼다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고정 후보를 보존하며 새로고침하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 날짜와 게임 시드만으로 후보 난수를 만들었다. 이 방식으로는 같은 날 여러 번 새로고침하는 경우를 구분하기 어려웠다. 그래서 게임 시드&amp;middot;날짜&amp;middot;슬롯 번호&amp;middot;수동 새로고침 차수를 각각 생성 입력에 포함했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790513916594&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FRandomStream Stream(HashCombine(RefreshSerial, Day, GameSeed, SlotIdx));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬롯 번호를 넣은 이유는 고정 후보 때문이다. 앞 슬롯을 고정해 생성을 건너뛰어도 뒤 슬롯의 난수 소비 순서가 달라지지 않도록 각 슬롯의 스트림을 분리했다. 고정된 후보는 기존 구조체를 보존하고 비고정 슬롯만 다시 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로고침 차수는 성공한 수동 생성 때만 증가한다. 현재는 한 턴에 최대 3회까지 허용하고 남은 횟수를 3/3처럼 표시한다. 0/3이 되면 버튼을 비활성화하지만, 실제 제한 판정도 AdventurerManager에서 수행한다. 버튼 외의 경로로 요청해도 같은 규칙을 적용하기 위해서다. 서로 다른 시드를 사용해도 결과 스탯이 반드시 서로 달라지는 것은 아니며, 후보 ID도 별도로 생성한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UIManager로 화면 수명과 요청 연결 모으기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 GuildHall이 모집 페이지의 요청을 매니저에 전달했다. 화면을 여닫는 위치와 입력 모드 전환까지 여러 클래스에 나뉘자 요청 경로를 따라가기 어려웠다. 현재는 Subsystem이 TownManager와 UIManager를 같은 계층에서 보관하고, UIManager가 마을&amp;middot;시설 화면의 생성과 종료, 입력 모드 전환, 턴&amp;middot;고용&amp;middot;새로고침 요청 연결을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후보 생성과 고용 판정은 AdventurerManager에 남겼다. 모집 페이지의 `OnRefreshRequested`는 사용자의 요청이고, `OnCandidatesChanged`는 후보 상태가 실제로 바뀐 뒤의 알림이다. 성공 후 화면이 후보 목록을 중복 주입하지 않고 변경 알림을 받아 다시 읽게 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2920&quot; data-origin-height=&quot;1600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BVP8B/dJMcahTrnhh/Pkgfzq9FZqBUUbaahc96P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BVP8B/dJMcahTrnhh/Pkgfzq9FZqBUUbaahc96P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BVP8B/dJMcahTrnhh/Pkgfzq9FZqBUUbaahc96P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBVP8B%2FdJMcahTrnhh%2FPkgfzq9FZqBUUbaahc96P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2920&quot; height=&quot;1600&quot; data-origin-width=&quot;2920&quot; data-origin-height=&quot;1600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1790513986748&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;모집 페이지의 새로고침 요청
&amp;rarr; UIManager &amp;rarr; TownManager &amp;rarr; AdventurerManager
&amp;rarr; 후보 변경 알림 &amp;rarr; UIManager &amp;rarr; 모집 페이지 갱신&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시설을 열 때는 UI 입력으로 전환하고 마을 화면을 비활성화한다. 닫을 때는 마을 화면과 입력&amp;middot;커서 상태를 복원한다. 버튼 내부 연결과 화면 구독의 수명을 구분해 등록&amp;middot;해제하도록 정리했다. 이 변경의 목적은 호출 위치와 화면 수명의 일관성이며, 성능 개선을 측정한 것은 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고용 버튼의 최종 화면 동작과 특성 설명 출력을 다시 확인하고, 고정&amp;middot;해제 UI와 모험가 명단 화면을 이어서 구현할 계획이다. 이후 편성과 파견으로 모집 결과가 실제 게임 진행에 연결되는지 확인하겠다. 세이브&amp;middot;로드는 아직 구현하지 않았으므로 게임 시드&amp;middot;후보&amp;middot;새로고침 차수의 복원도 후속 검증 항목으로 남겨둔다.&lt;/p&gt;</description>
      <category>UnrealEngine</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/14</guid>
      <comments>https://baeksa01.tistory.com/entry/Unreal5-C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-3%EC%A3%BC%EC%B0%A8#entry14comment</comments>
      <pubDate>Sun, 27 Sep 2026 22:02:14 +0900</pubDate>
    </item>
    <item>
      <title>[Unreal5, C++] 길드 경영 + 3인칭 전투 게임 개발 2주차</title>
      <link>https://baeksa01.tistory.com/entry/Unreal5-C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-2%EC%A3%BC%EC%B0%A8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;상호작용 HUD 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 고민 방식&amp;rarr;컨트롤러에 HUD를 달아두고 캐릭터 빙의시 이관하여 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;책임소재가 꼬이고 로직이 복잡해짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해결 방식&amp;rarr; GMBaseCharacter에 위젯컴포넌트와 HUD WBP를 할당해둠
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐릭터마다 WBP가 생긴다는 단점이 있지만 한번에 생성되어있는 캐릭터의 수도 적고 WBP의 리소스 소모량도 크지 않아서 해결하고 다음 스텝으로 넘어감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ZoomInOut에 따라서 HUD의 위치를 가변적으로 조절해서 캐릭터를 가리지 않도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1789958019646&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (FollowCamera)
{
	// 최대확대&amp;rarr;1, 최대축소&amp;rarr;0 (범위는 컨트롤러에서 전달)
    const float Alpha = FMath::Clamp((MinZoom - FollowCamera-&amp;gt;OrthoWidth) / (MinZoom - MaxZoom),0.0f, 1.0f);
    ZoomExtra = FMath::Lerp(InteractionHUDZoomExtra, 0.0f, Alpha);
}
InteractionHUDComp-&amp;gt;SetRelativeLocation(FVector(0.0f, 0.0f, CapsuleTop + InteractionHUDOffset + ZoomExtra));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;턴 시스템 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SubSystem에서 게임 전체 턴 진행을 관리&lt;/li&gt;
&lt;li&gt;턴 진행 결과 내용을 구조체화&lt;/li&gt;
&lt;li&gt;턴 실패시 실패 사유 전달&lt;/li&gt;
&lt;li&gt;턴 종료 클릭시 전투,작업,유지비 소모 등의 작업이 동기적으로 이어지도록 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 시스템 구현&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;작업큐 관리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업을 실행하는데 필요한 내용, 작업 결과 반영용 내용들을 구조체화 하여 관리&lt;/li&gt;
&lt;li&gt;각 작업을 식별하기 위한 방법으로 Fguid를 통해서 식별정보를 부여.&lt;/li&gt;
&lt;li&gt;작업 큐 자료구조&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1789957638855&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TMap&amp;lt;FGuid,FTownTaskType&amp;gt; TaskMap;//실제 작업을 저장할 Map
TArray&amp;lt;FTaskBucket&amp;gt; TaskMapTurnIndex;//각 턴별 실행할 작업 id목록Array(이중컨테이너)
int32 WheelHead = 0;//Array를 원형리스트로 사용하기 위한 Pointer&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원형 큐의 개념을 사용하여 고정된 사이즈의 TArray를 사용하면서 현재 Head 포인터가 가리키는 위치를 항상 0번째로 인식 할 수 있도록 구현.&lt;/li&gt;
&lt;li&gt;턴 추가시 계산 : 현재 포인터의 값+1을 전체 사이즈로 나눈 나머지를 인덱스로 취급 ex) (98+1)%99=0으로, 마지막 위치에서 +1해도 정상적으로 0번째 인덱스로 연결됨&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #191a1c; color: #d0d0d0;&quot;&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;WheelHead = (WheelHead + 1) % TaskWheelCapacity;  &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 작업이 처리되어야 할 시점의 턴을 계산: 현재 포인터값+소모턴을 그대로 쓸 경우 최대 사이즈를 넘어가는 경우 발생, 이때 전체사이즈로 나눈 나머지를 적용하여 원형 큐 인덱싱 적용. 다만 SpendTurn이 전체사이즈-1보다 크면 안됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1789958892169&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(WheelHead+SpendTurn)%TaskWheelCapacity&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;작업 처리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SubSystem에서 TownManager(마을 작업의 전체 관리자)에 구현된 작업처리함수를 호출.&lt;/li&gt;
&lt;li&gt;TownManager에서 각 작업별 Manager를 호출해서 작업 처리 후 결과를 구조체에 담아서 반환.&lt;/li&gt;
&lt;li&gt;SubSystem은 반환받은 결과 구조체 목록을 기반으로 게임 데이터를 갱신.&lt;/li&gt;
&lt;li&gt;게임데이터 자체에 관여하는 주체는 SubSystem으로 한정하되, 하위의 작업 코드를 전부 SubSystem에 집중시키지 않도록 구현&lt;/li&gt;
&lt;li&gt;SubSystem이 TownManager를 UPROPERTY로 선언하면서 Transient로 선언하여 GC가 추적할 수 있으면서도 별도로 불필요하게 데이터를 남기지 않도록 함&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공부한 내용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DataTable Row LiveCoding 충돌&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터테이블의 구조로 사용중인 CPP 클래스의 USTRUCT를 수정할때, 에디터를 켠 상태로 수정 후 빌드하게 될 경우 LiveCoding과 충돌이 일어나면서 내가 수정한 행이 적용되지 않고 기존의 값이 임시 캐시값으로 고정되어버린다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언구조 변경시에는 반드시 에디터를 종료 후 빌드를 해야 할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FGuid&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 구조상 작업큐 객체나 모험가 객체 등 객체를 식별해야 할 문제가 많았는데 조사중 FGuid를 알게 되었다.&lt;br /&gt;4바이트 4개의 영역을 가지는 16바이트짜리 구조체 형태로, 고유성을 보장해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>UnrealEngine</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/13</guid>
      <comments>https://baeksa01.tistory.com/entry/Unreal5-C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-2%EC%A3%BC%EC%B0%A8#entry13comment</comments>
      <pubDate>Tue, 22 Sep 2026 09:19:40 +0900</pubDate>
    </item>
    <item>
      <title>[Unreal5,C++] 길드 경영 + 3인칭 전투 게임 개발 1주차</title>
      <link>https://baeksa01.tistory.com/entry/Unreal5C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-1%EC%A3%BC%EC%B0%A8</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;게임 기획&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 구상한 게임은 다키스트던전의 시스템을 차용한 모험가 길드 경영 게임이였다.&lt;br /&gt;그러나 최근 명일방주:엔드필드라는 게임을 즐기면서 전투에서는 최근 서브컬처 오픈월드게임의 방식을 차용하면 재밌을것 같아서 전투 방식을 변경했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 게임기획은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;lt; 기획 요약 &amp;gt;&lt;br /&gt;GuildMaster는 중세 판타지 기반의 길드 경영과 실시간 파티 전투를 결합한 RPG다.&lt;br /&gt;플레이어는 길드 마스터가 되어 모험가를 고용하고 직업&amp;middot;장비&amp;middot;치료 상태를 관리한다.&lt;br /&gt;마을의 시설과 자원을 운영하고 최대 4명의 파티를 의뢰와 몬스터 토벌에 파견한다.&lt;br /&gt;전투에서는 모험가들을 직접 조작하며 숫자키로 조작 대상을 전환하고, 나머지 동료는 자율적으로 행동한다.&lt;br /&gt;전투 보상을 길드와 마을의 성장에 다시 투자하는 반복 구조가 현재 핵심 플레이 범위다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;진행 내용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;UnrealMCP&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 게임엔진들이 LLM을 게임 엔진에 활용할 수 있는 MCP 기능들을 시험적으로 업데이트해주었다. 언리얼엔진 또한 5.8버전부터 MCP를 통해 LLM으로 제어가 가능해졌다.&lt;br /&gt;해당 내용을 적용해보기 위해 언리얼MCP와 라이더를 연동하여 제어가능하도록 세팅하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.09.02 - [UnrealEngine] - [Unreal, MCP] Codex에 Unreal MCP와 Rider MCP 연결하기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1788942322002&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Unreal, MCP] Codex에 Unreal MCP와 Rider MCP 연결하기&quot; data-og-description=&quot;Unreal Engine 프로젝트에서 Codex를 사용하면 C++ 코드를 작성하는 것뿐만 아니라, Unreal Editor 안의 Actor나 에셋을 조사하고 자동화 테스트를 실행하는 작업까지 맡길 수 있다.이를 위해서는 성격이 다&quot; data-og-host=&quot;baeksa01.tistory.com&quot; data-og-source-url=&quot;https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tL2o6/dJMb9aKYzs4/KKhKj5iDuLrkTQllxCGMfK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/n9Or7/dJMb9c9Rqn1/A4S7Vz139KkB7QSZAXRGK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/vPWO8/dJMb82e6HOu/XPuViMrCIfezThkZK6Yj41/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tL2o6/dJMb9aKYzs4/KKhKj5iDuLrkTQllxCGMfK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/n9Or7/dJMb9c9Rqn1/A4S7Vz139KkB7QSZAXRGK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/vPWO8/dJMb82e6HOu/XPuViMrCIfezThkZK6Yj41/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Unreal, MCP] Codex에 Unreal MCP와 Rider MCP 연결하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unreal Engine 프로젝트에서 Codex를 사용하면 C++ 코드를 작성하는 것뿐만 아니라, Unreal Editor 안의 Actor나 에셋을 조사하고 자동화 테스트를 실행하는 작업까지 맡길 수 있다.이를 위해서는 성격이 다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;baeksa01.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;LLM을 활용한 게임기획&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임의 아이디어 자체는 금방 떠오르고 정리가 되었지만, 막상 머리속에 있는 내용을 게임 개발을 위한 명세서로 작성하려니 두서없이 작성되고, 어떤내용을 포함하고, 어떤내용을 빠트렸는지 관리가 어려웠다.&lt;br /&gt;그래서 Codex를 통해서 내 게임 기획 및 설정, 추구하는 플레이 스타일,장르등을 상세하게 작성하여 설명하고, 해당 게임에 대한 부족한 세부 명세 내용을 정리하여 목록화 한 후 한개의 목록씩 질문하도록 시켰다.&lt;br /&gt;또한 해당 내용들을 Notion에 작성하도록 하여 훨씬 수월하게 문서화를 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기획 문서 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex를 통해서 데스크톱에 설치한 Drawio를 제어해서 각종 플로우차트와 다이어그램을 작성할 수 있었다.&lt;br /&gt;그러나 기존의 기획내용을 참고해서 전부 작성을 맡겼더니 지나치게 장황하고 복잡한 차트를 그려주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차트및 다이어그램의 목적이 현 기획의 내용을 누구나 이해할 수 있도록 단순하게 전달하기 위함이기 때문에 결국 상세한 지시사항을 통해 다시 작성하였다.&lt;br /&gt;시퀀스다이어그램은 해당 페이지를 통해 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sequencediagram.org/&quot;&gt;https://sequencediagram.org&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1789022401822&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SequenceDiagram.org - UML Sequence Diagram Online Tool&quot; data-og-description=&quot;Free sequence diagram online tool. Create sequence diagrams using textual notation or draw quickly via Drag and Drop with an easy to use interface.&quot; data-og-host=&quot;sequencediagram.org&quot; data-og-source-url=&quot;https://sequencediagram.org/&quot; data-og-url=&quot;https://sequencediagram.org&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eezC7Z/dJMb9llqI23/KQJl9vsAvPMCyoe7orW3yk/img.png?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128&quot;&gt;&lt;a href=&quot;https://sequencediagram.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sequencediagram.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eezC7Z/dJMb9llqI23/KQJl9vsAvPMCyoe7orW3yk/img.png?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SequenceDiagram.org - UML Sequence Diagram Online Tool&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Free sequence diagram online tool. Create sequence diagrams using textual notation or draw quickly via Drag and Drop with an easy to use interface.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sequencediagram.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플로우차트는 게임의 핵심인 마을 관리와 전투에 대해서만 작성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_PresentationFlowcharts-01 마을 관리.drawio.png&quot; data-origin-width=&quot;6300&quot; data-origin-height=&quot;3508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/unF0C/dJMcahZ3JaL/pSLKd6PeP6kRQWlVkeFRx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/unF0C/dJMcahZ3JaL/pSLKd6PeP6kRQWlVkeFRx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/unF0C/dJMcahZ3JaL/pSLKd6PeP6kRQWlVkeFRx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FunF0C%2FdJMcahZ3JaL%2FpSLKd6PeP6kRQWlVkeFRx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6300&quot; height=&quot;3508&quot; data-filename=&quot;GM_PresentationFlowcharts-01 마을 관리.drawio.png&quot; data-origin-width=&quot;6300&quot; data-origin-height=&quot;3508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_PresentationFlowcharts-02 일반 전투.drawio.png&quot; data-origin-width=&quot;6216&quot; data-origin-height=&quot;3488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFYPUQ/dJMcadpQ9LG/C41L7XnKgcYzczPgxdbCg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFYPUQ/dJMcadpQ9LG/C41L7XnKgcYzczPgxdbCg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFYPUQ/dJMcadpQ9LG/C41L7XnKgcYzczPgxdbCg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFYPUQ%2FdJMcadpQ9LG%2FC41L7XnKgcYzczPgxdbCg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6216&quot; height=&quot;3488&quot; data-filename=&quot;GM_PresentationFlowcharts-02 일반 전투.drawio.png&quot; data-origin-width=&quot;6216&quot; data-origin-height=&quot;3488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스다이어그램은 각 역할별로 나눠서 작성하였다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-01 전체 실행&amp;amp;middot;상태 소유.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ZgXT/dJMcaatemcA/o7CnmBIkUmzjf2IcEsqwZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ZgXT/dJMcaatemcA/o7CnmBIkUmzjf2IcEsqwZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ZgXT/dJMcaatemcA/o7CnmBIkUmzjf2IcEsqwZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ZgXT%2FdJMcaatemcA%2Fo7CnmBIkUmzjf2IcEsqwZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6088&quot; height=&quot;3092&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-01 전체 실행&amp;middot;상태 소유.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3092&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-02 마을 운영&amp;amp;middot;시설.drawio.png&quot; data-origin-width=&quot;6212&quot; data-origin-height=&quot;3256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pruS4/dJMcahlDprG/VVKkittN0k1JWt3dVW7cHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pruS4/dJMcahlDprG/VVKkittN0k1JWt3dVW7cHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pruS4/dJMcahlDprG/VVKkittN0k1JWt3dVW7cHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpruS4%2FdJMcahlDprG%2FVVKkittN0k1JWt3dVW7cHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6212&quot; height=&quot;3256&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-02 마을 운영&amp;middot;시설.drawio.png&quot; data-origin-width=&quot;6212&quot; data-origin-height=&quot;3256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-03 모험가 관리.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mVmIG/dJMcackaVYg/PBjEZv0xg5sKnLWGjj3Va0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mVmIG/dJMcackaVYg/PBjEZv0xg5sKnLWGjj3Va0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mVmIG/dJMcackaVYg/PBjEZv0xg5sKnLWGjj3Va0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmVmIG%2FdJMcackaVYg%2FPBjEZv0xg5sKnLWGjj3Va0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6088&quot; height=&quot;3324&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-03 모험가 관리.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-04 파티&amp;amp;middot;파견.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR8pYI/dJMcadpQ8NW/Ypi5TuAqqH0smxZ7BkkAQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR8pYI/dJMcadpQ8NW/Ypi5TuAqqH0smxZ7BkkAQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR8pYI/dJMcadpQ8NW/Ypi5TuAqqH0smxZ7BkkAQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR8pYI%2FdJMcadpQ8NW%2FYpi5TuAqqH0smxZ7BkkAQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6088&quot; height=&quot;3172&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-04 파티&amp;middot;파견.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-05 일반 전투.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgKT3y/dJMcackaVYh/lL08hc6iL4kDa2ZP3KE7W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgKT3y/dJMcackaVYh/lL08hc6iL4kDa2ZP3KE7W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgKT3y/dJMcackaVYh/lL08hc6iL4kDa2ZP3KE7W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgKT3y%2FdJMcackaVYh%2FlL08hc6iL4kDa2ZP3KE7W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6088&quot; height=&quot;3452&quot; data-filename=&quot;GM_ClassDiagram_ByFeature-05 일반 전투.drawio.png&quot; data-origin-width=&quot;6088&quot; data-origin-height=&quot;3452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시퀀스다이어그램역시 마을관리와 전투의 흐름에 대해서만 작성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ManageGuild.png&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;873&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csUePh/dJMcagG2dG5/zaO89ekjungTTXT85MKMiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csUePh/dJMcagG2dG5/zaO89ekjungTTXT85MKMiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csUePh/dJMcagG2dG5/zaO89ekjungTTXT85MKMiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsUePh%2FdJMcagG2dG5%2FzaO89ekjungTTXT85MKMiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1180&quot; height=&quot;873&quot; data-filename=&quot;ManageGuild.png&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;873&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;combat.png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blgem2/dJMcacLpf1g/WW7Yp9quxIXVHZXYUTIHI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blgem2/dJMcacLpf1g/WW7Yp9quxIXVHZXYUTIHI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blgem2/dJMcacLpf1g/WW7Yp9quxIXVHZXYUTIHI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblgem2%2FdJMcacLpf1g%2FWW7Yp9quxIXVHZXYUTIHI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;971&quot; height=&quot;751&quot; data-filename=&quot;combat.png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기획 및 설계, 일정관리.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 작성한 기획을 기반으로 codex와 opencode에서 현재 일시적으로 무료로 제공중인 MuseSpark1.3을 통해서 Notion에 문서화하여 일정 및 프로젝트를 관리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1789115288073&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;서휘도 [언리얼11기] | Notion&quot; data-og-description=&quot;  프로젝트 로드맵&quot; data-og-host=&quot;midnight-pea-dd1.notion.site&quot; data-og-source-url=&quot;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&quot; data-og-url=&quot;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://midnight-pea-dd1.notion.site/11-ca359bc360aa8391becb011eddaf16c8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;서휘도 [언리얼11기] | Notion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  프로젝트 로드맵&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;midnight-pea-dd1.notion.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 기획및 일정대로 진행한 구현 내용&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;BaseCharacter 및 TopviewCharacter 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 동작만 테스트하기 위한 TopviewCharacter클래스가 필요했다. 그러나 현재 기획상 Topview뿐만 아니라 ThirdPerson까지 필요하기 때문에 기본적인 카메라와 스프링암 설정을 공유할 BaseCharacter까지 구현해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Topview구현시 원근감이 없는 모습도 테스트하고싶어서 카메라의 투영 방식을 직교방식으로 변경해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1789115552794&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AGMTopViewCharacter::AGMTopViewCharacter()
{
	if (FollowCamera)
	{
		// 1. 원근감 제거 (직교 투영 모드로 변경)
		FollowCamera-&amp;gt;ProjectionMode = ECameraProjectionMode::Orthographic;

		// 2. 카메라 시야 범위(가로 크기) 설정 (기본값은 보통 512.f)
		// 값이 커질수록 화면이 축소(줌아웃)되고, 작아질수록 확대(줌인)됩니다.
		FollowCamera-&amp;gt;OrthoWidth = 2048.0f; 

		// 3. (선택사항) 종횡비 유지 설정
		FollowCamera-&amp;gt;bConstrainAspectRatio = true;
		FollowCamera-&amp;gt;AspectRatio = 1.777778f; // 16:9 비율
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;범용PlayerController 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구조상, 캐릭터가 아닌 컨트롤러가 플레이어의 주체라고 볼수 있다. 그렇기때문에 컨트롤러가 특정 캐릭터에 빙의시 조작 방식을 해당 캐릭터에 맞게 조절할 수 있도록 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 조작 방식은 언리얼5부터 도입된 EnhancedInput을 사용하여 인풋 방식에 구애받지 않도록 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;InputAction을 통해&lt;span style=&quot;color: #000000;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Axis2D와 같은 설정된 입력 타입은&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;nbsp;키를 직접 판별하지 않고, Input Mapping Context가 합성한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Vector2D&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 사용한다. 같은 액션에 패드 스틱을 연결해도 이동 코드는 바꾸지 않아도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시점에 따라 이동 기준 회전값을 다르게 설정하는 과정에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;const&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수와 람다식의 사용법을 새롭게 익혔다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 변수를 선언한 뒤&lt;span&gt;&amp;nbsp;&lt;/span&gt;switch문에서 값을 대입하려 했지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;const&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수는 선언과 동시에 초기화해야 하므로 이 구조를 그대로 사용할 수 없었다. 이를 해결하기 위해 즉시 실행 람다식을 사용했다.&lt;/p&gt;
&lt;pre class=&quot;php&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;const FRotator YawRotation = [&amp;amp;]() -&amp;gt; FRotator
{
    switch (ViewType)
    {
    case EGMViewType::ThirdPerson:
        return FRotator(0.0f, GetControlRotation().Yaw, 0.0f);

    case EGMViewType::TopView:
        return FRotator(
            0.0f,
            PlayerCameraManager-&amp;gt;GetCameraRotation().Yaw,
            0.0f
        );

    default:
        return FRotator::ZeroRotator;
    }
}();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만&lt;span&gt;&amp;nbsp;&lt;/span&gt;[&amp;amp;]로 캡처한 람다를 다른 곳에 저장하거나 나중에 실행하면 참조한 객체의 수명이 끝났을 가능성을 고려해야 한다. 이번처럼 선언 직후 실행하는 람다에서는 해당 위험이 작지만, 장기간 보관하는 람다에서는 필요한 값만 명시적으로 캡처하는 편이 안전하다는 점도 알게 되었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;빙의용 OnPossess함수를 오버라이딩하면서 컨트롤러에 캐싱하기 위해 인풋으로 들어온 APawn을 처음에는 Acharcter로 캐스팅할 계획이였지만, 해당 프로젝트에서는 모든 캐릭터의 부모클래스로 ACharacter를 상속받아 작성한 AGMBaseCharacter를 사용하여 캐릭터의 함수 사용 용이성을 챙겼다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;회고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 개발 프로젝트를 준비하면서 LLM AI의 발달로 점차 기획자와 개발자 사이의 경계가 허물어져간다는 생각이 들었다.&lt;br /&gt;개발자도 AI를 활용하여 상상했던 게임의 세부 기획을 정하고, 기획과 설계에 따라 마일스톤과 로드맵을 구축하고, 더 나아가 타인에게 설명하기 위한 다이어그램들또한 매우 짧은 시간 안에 완성할 수 있었다. 아마 기획자또한 본인의 기획에 맞게 AI를 통해 코딩 뿐만아니라 엔진을 통해 레벨 디자인과 액터 배치까지 빠른 시간안에 해낼 수 있을것이다. 앞으로의 프로젝트 진행에 있어서도 단순히 나는 프로그래밍만 하고 엔진만 다룰줄 알면 된다는 생각이 아닌 프로젝트 전체에 대한 이해와 기획에 대한 관점을 함께 고려해가면서 진행해야겠다.&lt;/p&gt;</description>
      <category>UnrealEngine</category>
      <category>unreal5</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/12</guid>
      <comments>https://baeksa01.tistory.com/entry/Unreal5C-%EA%B8%B8%EB%93%9C-%EA%B2%BD%EC%98%81-3%EC%9D%B8%EC%B9%AD-%EC%A0%84%ED%88%AC-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C-1%EC%A3%BC%EC%B0%A8#entry12comment</comments>
      <pubDate>Sun, 13 Sep 2026 12:21:21 +0900</pubDate>
    </item>
    <item>
      <title>[Unreal, MCP] Codex에 Unreal MCP와 Rider MCP 연결하기</title>
      <link>https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Engine 프로젝트에서 Codex를 사용하면 C++ 코드를 작성하는 것뿐만 아니라, Unreal Editor 안의 Actor나 에셋을 조사하고 자동화 테스트를 실행하는 작업까지 맡길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 성격이 다른 두 개의 MCP 서버를 연결해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Rider MCP Server&lt;/b&gt;: 코드 분석, 심볼 검색, 빌드와 실행 담당&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Unreal MCP Server&lt;/b&gt;: Unreal Editor, Actor, 에셋과 자동화 테스트 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 Rider와 Unreal Editor를 직접 연결하는 &lt;b&gt;UnrealLink/RiderLink&lt;/b&gt;가 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 비슷해 혼동하기 쉽지만, RiderLink와 Rider MCP는 서로 다른 기술이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 연결 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조를 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;                         ┌─ Rider MCP Server ─&amp;rarr; 코드 분석&amp;middot;검색&amp;middot;빌드&amp;middot;실행
Codex(MCP Client) ───────┤
                         └─ Unreal MCP Server &amp;rarr; Editor&amp;middot;Actor&amp;middot;Asset&amp;middot;Test 조작

Rider(UnrealLink) &amp;harr; RiderLink(Unreal Editor Plugin)
        └─ Rider와 Unreal Editor 사이의 개발 편의 기능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 MCP Client 역할을 수행한다. Rider와 Unreal Editor는 각각 MCP Server가 되어 자신이 가진 기능을 Codex에 도구로 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UnrealLink와 RiderLink는 Codex를 위한 연결이 아니다. 이 둘은 Rider와 Unreal Editor가 Blueprint 정보, 로그, 실행 상태 등을 주고받도록 연결한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구성요소별 역할&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;구성요소위치역할
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;Codex 앱 또는 CLI&lt;/td&gt;
&lt;td&gt;요청을 해석하고 MCP 도구를 선택해 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rider&lt;/td&gt;
&lt;td&gt;IDE&lt;/td&gt;
&lt;td&gt;Unreal C++ 인덱싱, 코드 분석, 빌드&amp;middot;실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UnrealLink&lt;/td&gt;
&lt;td&gt;Rider 내부&lt;/td&gt;
&lt;td&gt;Rider 쪽 Unreal Engine 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RiderLink&lt;/td&gt;
&lt;td&gt;Unreal Editor 플러그인&lt;/td&gt;
&lt;td&gt;Blueprint 정보와 Editor 상태를 Rider에 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rider MCP Server&lt;/td&gt;
&lt;td&gt;Rider 내부&lt;/td&gt;
&lt;td&gt;Rider의 IDE 기능을 Codex에 노출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unreal MCP&lt;/td&gt;
&lt;td&gt;Unreal Editor 내부&lt;/td&gt;
&lt;td&gt;Editor 기능을 Codex에 노출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toolset Registry&lt;/td&gt;
&lt;td&gt;Unreal 내부&lt;/td&gt;
&lt;td&gt;Unreal 도구를 Toolset 단위로 등록&amp;middot;검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All Toolsets&lt;/td&gt;
&lt;td&gt;Unreal 플러그인&lt;/td&gt;
&lt;td&gt;Epic이 제공하는 기본 Toolset 로드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Rider와 Unreal Editor 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 일반적인 Rider 기반 Unreal 개발 환경을 구성한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rider에서 프로젝트 열기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider는 .uproject 파일을 직접 열 수 있다. 별도의 Visual Studio 솔루션을 반드시 생성할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서는 다음 파일을 Rider로 연다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;C:\shd\Unreal\GuildMaster\GuildMaster\GuildMaster.uproject&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RiderLink 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider에서 Unreal 프로젝트를 처음 열면 RiderLink가 없다는 알림이 표시될 수 있다. 알림을 통해 다음 두 위치 중 하나에 설치할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Engine 설치&lt;/b&gt;: 현재 UE 버전을 사용하는 모든 프로젝트에서 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Game 설치&lt;/b&gt;: 현재 프로젝트에서만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트별 재현성과 플러그인 버전 고정을 중요하게 본다면 Game 설치가 유리하다. 여러 테스트 프로젝트에서 동일한 RiderLink를 사용할 목적이라면 Engine 설치가 편리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알림을 놓쳤다면 Rider에서 다음 메뉴로 이동한다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;Settings
&amp;rarr; Languages &amp;amp; Frameworks
&amp;rarr; Unreal Engine&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RiderLink는 Rider가 설치를 관리하므로 일반적으로 직접 플러그인 파일을 내려받을 필요는 없다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UnrealLink와 RiderLink의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 플러그인은 하나의 통합 기능을 양쪽에서 나눠 담당한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UnrealLink&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rider에 설치되는 플러그인&lt;/li&gt;
&lt;li&gt;Unreal 프로젝트 모델과 Blueprint 정보를 IDE에서 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RiderLink&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unreal Editor에 설치되는 플러그인&lt;/li&gt;
&lt;li&gt;Editor 상태와 Blueprint 정보를 Rider에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연결로 다음 기능을 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ 코드가 어떤 Blueprint에서 사용되는지 확인&lt;/li&gt;
&lt;li&gt;C++ 코드에서 관련 Blueprint를 Unreal Editor로 열기&lt;/li&gt;
&lt;li&gt;Rider에서 Unreal Editor 로그 확인&lt;/li&gt;
&lt;li&gt;Rider에서 게임 실행 모드 관리&lt;/li&gt;
&lt;li&gt;Unreal 관련 코드 탐색과 분석 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RiderLink가 없더라도 Rider MCP Server가 반드시 중단되는 것은 아니다. 다만 Rider와 Unreal Editor 사이의 Blueprint, 로그, 실행 통합 기능이 제한된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 &lt;a href=&quot;https://www.jetbrains.com/help/rider/Unreal_Engine__UnrealLink_RiderLink.html&quot;&gt;&lt;span&gt;&lt;span&gt;JetBrains UnrealLink와 RiderLink 공식 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Codex와 Rider MCP 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Rider가 가진 코드 분석 기능을 Codex에서 사용할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JetBrains Rider 2025.2 이상에는 외부 AI Client가 IDE 기능을 사용할 수 있도록 하는 MCP Server가 통합되어 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rider MCP Server 활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider에서 다음 메뉴로 이동한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Settings
&amp;rarr; Tools
&amp;rarr; MCP Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 아래 순서로 설정한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Enable MCP Server를 활성화한다.&lt;/li&gt;
&lt;li&gt;외부 애플리케이션이 접근할 수 있는 권한을 확인한다.&lt;/li&gt;
&lt;li&gt;Clients Auto-Configuration에서 Codex를 찾는다.&lt;/li&gt;
&lt;li&gt;Auto-Configure를 실행한다.&lt;/li&gt;
&lt;li&gt;Codex를 다시 시작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP Server 메뉴가 보이지 않는다면 다음 위치에서 플러그인을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Settings
&amp;rarr; Plugins
&amp;rarr; Installed
&amp;rarr; MCP Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트마다 MCP 노출 범위를 다르게 관리하려면 전역 설정 대신 Project Clients Auto-Configuration을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JetBrains 공식 문서에는 Codex를 포함한 여러 외부 Client에 대한 자동 설정 기능이 안내되어 있다. &lt;a href=&quot;https://www.jetbrains.com/help/rider/mcp-server.html&quot;&gt;&lt;span&gt;&lt;span&gt;JetBrains Rider MCP Server 공식 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rider MCP가 제공하는 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider MCP는 단순히 파일을 읽는 것보다 한 단계 높은 IDE 인덱스 기반 정보를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 도구는 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;도구용도
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;search_symbol&lt;/td&gt;
&lt;td&gt;클래스, 함수, 필드 등 코드 심볼 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_symbol_info&lt;/td&gt;
&lt;td&gt;특정 심볼의 선언, 타입, 문서 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;analyze_calls&lt;/td&gt;
&lt;td&gt;함수 호출자와 호출 대상 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_file_problems&lt;/td&gt;
&lt;td&gt;특정 파일의 오류와 경고 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lint_files&lt;/td&gt;
&lt;td&gt;변경된 여러 파일을 Rider 검사기로 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_project_modules&lt;/td&gt;
&lt;td&gt;프로젝트 모듈 구조 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_project_dependencies&lt;/td&gt;
&lt;td&gt;프로젝트 의존성 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build_project&lt;/td&gt;
&lt;td&gt;Rider를 통해 프로젝트 빌드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_run_configurations&lt;/td&gt;
&lt;td&gt;실행&amp;middot;테스트 구성 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;execute_run_configuration&lt;/td&gt;
&lt;td&gt;선택한 실행 또는 테스트 구성 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list_directory_tree&lt;/td&gt;
&lt;td&gt;Rider 프로젝트 디렉터리 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;open_file_in_editor&lt;/td&gt;
&lt;td&gt;특정 파일을 Rider에서 열기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reformat_file&lt;/td&gt;
&lt;td&gt;Rider 코드 스타일로 파일 포맷팅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Codex가 특정 Unreal C++ 함수를 수정할 때 다음 흐름을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;search_symbol
&amp;rarr; get_symbol_info
&amp;rarr; analyze_calls
&amp;rarr; 코드 수정
&amp;rarr; lint_files
&amp;rarr; build_project&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;analyze_calls는 일반 텍스트 검색보다 실제 코드 호출 관계를 정확하게 보여준다. 오버로드가 있거나 이름이 비슷한 함수가 많은 Unreal C++ 프로젝트에서 특히 유용하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;초기 권한 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider MCP에는 터미널 명령이나 Run Configuration을 사용자 확인 없이 실행할 수 있는 옵션도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 연결할 때는 다음 구성을 권장한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 조회 및 분석 도구 활성화&lt;/li&gt;
&lt;li&gt;빌드와 실행 도구는 사용자 확인 유지&lt;/li&gt;
&lt;li&gt;파일 수정 도구는 필요한 것만 노출&lt;/li&gt;
&lt;li&gt;brave mode 비활성화&lt;/li&gt;
&lt;li&gt;연결과 권한 범위가 검증된 후 단계적으로 확대&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Codex와 Unreal MCP 연결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rider MCP가 코드 영역을 담당한다면 Unreal MCP는 Editor 영역을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Engine 5.8의 Unreal MCP는 Unreal Editor 프로세스 안에서 MCP Server를 실행한다. Codex는 HTTP로 이 서버에 연결하여 Editor 기능을 호출한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unreal 플러그인 활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Editor에서 다음 메뉴로 이동한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Edit
&amp;rarr; Plugins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 플러그인을 활성화한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unreal MCP&lt;/li&gt;
&lt;li&gt;All Toolsets&lt;/li&gt;
&lt;li&gt;Toolset Registry&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal MCP의 내부 플러그인 식별자는 ModelContextProtocol이다. Toolset Registry는 의존성으로 자동 활성화될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인을 활성화한 후에는 Unreal Editor를 재시작한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MCP Server 자동 시작 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Editor에서 다음 메뉴로 이동한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Edit
&amp;rarr; Editor Preferences
&amp;rarr; General
&amp;rarr; Model Context Protocol&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 설정은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Auto Start Server: Enabled
Server Port Number: 8000
Server URL Path: /mcp
Enable Tool Search: Enabled&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 주소는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/mcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 서버를 시작하려면 Unreal 콘솔에서 다음 명령을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ModelContextProtocol.StartServer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트를 명시할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ModelContextProtocol.StartServer 8000&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Codex 설정 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal 콘솔에서 다음 명령을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ModelContextProtocol.GenerateClientConfig Codex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치형 Unreal Engine에서는 일반적으로 프로젝트 루트에 다음 파일이 만들어진다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;.codex/config.toml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성되는 설정은 개념적으로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[mcp_servers.unreal-mcp]
url = &quot;http://127.0.0.1:8000/mcp&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 전역 ~/.codex/config.toml뿐만 아니라 신뢰된 프로젝트의 .codex/config.toml도 읽을 수 있다. Codex 앱, CLI, IDE 확장은 동일한 Codex 호스트의 MCP 설정을 공유한다. &lt;a href=&quot;https://learn.chatgpt.com/docs/extend/mcp?surface=cli&quot;&gt;&lt;span&gt;&lt;span&gt;OpenAI Codex MCP 공식 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 생성 후에는 해당 프로젝트 루트에서 Codex를 다시 시작하거나 프로젝트를 다시 열어야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CLI로 직접 등록하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex CLI를 사용한다면 Unreal MCP를 다음처럼 등록할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;codex mcp add unreal-mcp --url http://127.0.0.1:8000/mcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록된 서버는 다음 명령으로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;codex mcp list
codex mcp get unreal-mcp&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Unreal MCP의 Tool Search 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal MCP에서 가장 중요한 개념은 Toolset이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal의 모든 기능을 개별 MCP Tool로 한꺼번에 노출하면 스키마가 지나치게 커진다. UE 5.8은 이를 피하기 위해 기본적으로 Tool Search 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex에 처음 노출되는 핵심 도구는 세 개다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;도구역할
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;list_toolsets&lt;/td&gt;
&lt;td&gt;사용 가능한 Toolset 이름과 설명 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;describe_toolset&lt;/td&gt;
&lt;td&gt;특정 Toolset의 도구와 입력 스키마 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;call_tool&lt;/td&gt;
&lt;td&gt;선택한 실제 Unreal 도구 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 호출 순서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;list_toolsets
&amp;rarr; describe_toolset
&amp;rarr; call_tool&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Actor 관련 작업은 다음과 같은 흐름으로 처리한다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;list_toolsets
&amp;rarr; describe_toolset(&quot;ActorTools&quot;)
&amp;rarr; call_tool(&quot;ActorTools&quot;, ...)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대표적인 Unreal Toolset&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활성화한 플러그인에 따라 정확한 목록은 달라지지만 일반적으로 다음 영역의 Toolset을 사용할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;Toolset용도
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SceneTools&lt;/td&gt;
&lt;td&gt;현재 레벨과 Scene 조사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ActorTools&lt;/td&gt;
&lt;td&gt;Actor 조회, 선택, 생성, 이동과 컴포넌트 조작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ObjectTools&lt;/td&gt;
&lt;td&gt;UObject와 에셋 정보 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MaterialInstanceTools&lt;/td&gt;
&lt;td&gt;Material Instance 생성과 파라미터 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor Toolset&lt;/td&gt;
&lt;td&gt;선택 상태와 Editor 컨텍스트 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automation Test Toolset&lt;/td&gt;
&lt;td&gt;Unreal 자동화 테스트 조회와 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slate Toolset&lt;/td&gt;
&lt;td&gt;Slate&amp;middot;UMG 위젯 구조 조사&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 도구 이름을 기억해서 바로 호출하기보다는 항상 list_toolsets와 describe_toolset으로 현재 Editor가 제공하는 스키마를 확인하는 편이 안전하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unreal MCP 관리 명령&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ModelContextProtocol.StartServer
ModelContextProtocol.StopServer
ModelContextProtocol.RefreshTools
ModelContextProtocol.GenerateClientConfig Codex&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StartServer: MCP Server 시작&lt;/li&gt;
&lt;li&gt;StopServer: 서버와 연결된 세션 종료&lt;/li&gt;
&lt;li&gt;RefreshTools: Toolset Registry를 다시 조사&lt;/li&gt;
&lt;li&gt;GenerateClientConfig: 지원되는 AI Client 설정 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 Toolset을 작성하거나 기존 Toolset을 변경한 경우 RefreshTools를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 새로운 C++ UFUNCTION을 추가한 경우에는 Live Coding만으로 반영되지 않을 수 있으므로 Unreal Editor를 완전히 재시작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세 설정과 제약사항은 &lt;a href=&quot;https://dev.epicgames.com/documentation/unreal-engine/unreal-mcp-in-unreal-editor&quot;&gt;&lt;span&gt;&lt;span&gt;Epic Games UE 5.8 Unreal MCP 공식 문서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;에 정리되어 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Rider MCP와 Unreal MCP를 함께 등록하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 Codex 설정에는 두 MCP Server가 모두 들어가야 한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[mcp_servers.unreal-mcp]
url = &quot;http://127.0.0.1:8000/mcp&quot;

[mcp_servers.rider]
# Rider의 Auto-Configure 또는 Copy Config가 제공한 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UE 5.8의 GenerateClientConfig Codex 명령은 .codex/config.toml이 이미 존재하면 덮어쓰지 않는다. 따라서 두 서버를 설정할 때는 다음 순서를 권장한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Unreal에서 GenerateClientConfig Codex 실행&lt;/li&gt;
&lt;li&gt;생성된 .codex/config.toml 확인&lt;/li&gt;
&lt;li&gt;Rider에서 Copy Config 실행&lt;/li&gt;
&lt;li&gt;Rider 설정을 기존 TOML에 병합&lt;/li&gt;
&lt;li&gt;Codex 재시작&lt;/li&gt;
&lt;li&gt;두 MCP Server가 모두 노출되는지 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Rider가 먼저 설정 파일을 만들었다면 Unreal 명령이 실패할 수 있다. 이때 기존 설정 파일을 바로 삭제하기보다는 Unreal MCP 항목을 수동으로 추가하는 편이 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 실제 작업에서의 역할 분담&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 요청을 Codex에 전달한다고 가정해 보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티 편성 C++ 로직을 수정하고 Unreal Editor에서 자동화 테스트를 실행해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 작업을 다음처럼 나눌 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rider MCP가 담당하는 부분&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;관련 클래스와 함수 검색&lt;/li&gt;
&lt;li&gt;함수 호출 관계 분석&lt;/li&gt;
&lt;li&gt;C++ 코드 수정&lt;/li&gt;
&lt;li&gt;Rider Inspection 실행&lt;/li&gt;
&lt;li&gt;Unreal Editor Target 빌드&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unreal MCP가 담당하는 부분&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 Editor와 프로젝트 상태 확인&lt;/li&gt;
&lt;li&gt;필요한 테스트 에셋이나 Actor 조사&lt;/li&gt;
&lt;li&gt;Automation Test 조회&lt;/li&gt;
&lt;li&gt;테스트 실행&lt;/li&gt;
&lt;li&gt;Editor 로그와 결과 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RiderLink가 담당하는 부분&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;수정한 C++ 클래스의 Blueprint 사용 위치 제공&lt;/li&gt;
&lt;li&gt;Unreal Editor 로그를 Rider에 표시&lt;/li&gt;
&lt;li&gt;Rider와 Editor 사이의 탐색 지원&lt;/li&gt;
&lt;li&gt;실행 구성과 Editor 상태 연동&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 문장으로 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Rider MCP는 코드 쪽의 손과 눈,
Unreal MCP는 Editor와 에셋 쪽의 손과 눈,
RiderLink는 Rider와 Unreal Editor 사이의 다리다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 안전하게 사용할 때 주의할 점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unreal MCP는 로컬 주소만 사용한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UE 5.8 Unreal MCP는 기본적으로 다음 주소에 바인딩된다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal MCP에는 기본 인증 계층이 없기 때문에 외부 네트워크에 공개하면 안 된다. 포트 포워딩이나 외부 IP 바인딩도 피해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unreal 도구 호출은 병렬 실행하지 않는다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal MCP의 Editor 도구 호출은 Game Thread에서 직렬로 실행된다. Actor 생성이나 에셋 수정 같은 Editor 변경 요청을 동시에 여러 개 호출하면 충돌하거나 예측하기 어려운 결과가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Unreal Editor 변경은 다음처럼 순차적으로 실행해야 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;대상 조회
&amp;rarr; 변경 계획 확인
&amp;rarr; 단일 도구 실행
&amp;rarr; 결과 검증
&amp;rarr; 다음 도구 실행&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;변경 도구와 조회 도구를 구분한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 검증은 읽기 전용 작업부터 시작하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 선택된 Actor 확인&lt;/li&gt;
&lt;li&gt;사용 가능한 Toolset 조회&lt;/li&gt;
&lt;li&gt;Rider 프로젝트 모듈 조회&lt;/li&gt;
&lt;li&gt;C++ 파일의 오류와 경고 조회&lt;/li&gt;
&lt;li&gt;Automation Test 목록 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결이 정상임을 확인한 뒤 Actor 생성, 파일 수정, 빌드, 테스트 실행 도구를 순서대로 허용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex와 Unreal Engine 개발 환경을 제대로 연결하려면 하나의 플러그인만 설치해서는 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 구성요소가 담당하는 범위를 구분해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Rider MCP Server&lt;/b&gt;는 코드 분석과 빌드를 담당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Unreal MCP Server&lt;/b&gt;는 Unreal Editor와 에셋 조작을 담당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UnrealLink/RiderLink&lt;/b&gt;는 Rider와 Unreal Editor의 개발 경험을 연결한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Codex&lt;/b&gt;는 두 MCP Server의 도구를 선택하고 전체 작업 순서를 조율한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 MCP를 함께 사용하면 Codex가 단순한 코드 생성 도구를 넘어, 코드 분석부터 Unreal Editor 검증까지 이어지는 개발 루프에 참여할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 자동화 범위가 넓어지는 만큼 로컬 연결, 명시적인 도구 권한, 순차적인 Editor 변경, 빌드와 테스트를 통한 검증이 반드시 함께 따라야 한다.&lt;/p&gt;</description>
      <category>UnrealEngine</category>
      <category>codex</category>
      <category>jetbrain</category>
      <category>MCP</category>
      <category>rider</category>
      <category>unreal</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/11</guid>
      <comments>https://baeksa01.tistory.com/entry/Unreal-MCP-Codex%EC%97%90-Unreal-MCP%EC%99%80-Rider-MCP-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0#entry11comment</comments>
      <pubDate>Wed, 2 Sep 2026 17:18:24 +0900</pubDate>
    </item>
    <item>
      <title>[C++/SFML] 던그리드 모작 개발 (6) - 씬 생명주기, 스켈 보스 전투와 데이터 기반 시스템 구현</title>
      <link>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-6-%EC%94%AC-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%8A%A4%EC%BC%88-%EB%B3%B4%EC%8A%A4-%EC%A0%84%ED%88%AC%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B8%B0%EB%B0%98-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;지난 5회차까지는 전투 매니저와 오브젝트 풀링, 카메라, UI, 보상 상자처럼 던전 플레이를 구성하는 개별 시스템을 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번 개발에서는 이 시스템들을 실제 게임의 시작부터 종료까지 이어지는 하나의 플레이 흐름으로 확장했다. 타이틀&amp;middot;마을&amp;middot;던전&amp;middot;결과 화면을 분리하고, 스켈레톤 보스의 전투 패턴과 사망 연출을 구현했다. 이와 함께 게임 데이터를 JSON으로 통합하고 오디오&amp;middot;로그&amp;middot;디버그 기능을 보강하여 반복 테스트가 가능한 구조를 만드는 데 집중했다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;1. SceneManager를 이용한 게임 흐름 분리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존에는 &lt;/span&gt;&lt;span&gt;main.cpp&lt;/span&gt;&lt;span&gt;가 게임 초기화부터 업데이트, 렌더링까지 대부분의 흐름을 직접 처리하고 있었다. 기능이 늘어나면서 타이틀 화면과 던전 로직이 같은 실행 흐름에 섞였고, 장면을 다시 진입할 때 이전 객체의 상태가 남는 문제도 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 해결하기 위해 화면별 책임을 다음과 같이 분리했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;TitleScene&lt;/span&gt;&lt;span&gt;: 타이틀 배경과 구름 애니메이션, 시작 입력 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;VillageScene&lt;/span&gt;&lt;span&gt;: 던전 입장 전 대기 공간과 플레이어 상태 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DungeonScene&lt;/span&gt;&lt;span&gt;: 맵&amp;middot;몬스터&amp;middot;보스&amp;middot;전투&amp;middot;보상 업데이트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DeathScene&lt;/span&gt;&lt;span&gt;: 실패 및 클리어 결과 화면 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;SceneManager&lt;/span&gt;&lt;span&gt;: 현재 장면 전환과 장면별 입력&amp;middot;업데이트&amp;middot;렌더링 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;GameplayContext&lt;/span&gt;&lt;span&gt;: 여러 장면이 공유하는 플레이어와 카메라 참조 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각 장면은 자신의 기능만 갱신하고 렌더링한다. 따라서 결과 화면이 표시된 동안 던전의 몬스터나 투사체가 계속 움직이거나, 입력 한 번으로 장면 전환이 중복 실행되는 문제를 방지할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SceneFlow.svg&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL8GsZ/dJMcafAW1vl/0SdXZYYfe4mxwe0UxKsgjK/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL8GsZ/dJMcafAW1vl/0SdXZYYfe4mxwe0UxKsgjK/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL8GsZ/dJMcafAW1vl/0SdXZYYfe4mxwe0UxKsgjK/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL8GsZ%2FdJMcafAW1vl%2F0SdXZYYfe4mxwe0UxKsgjK%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1181&quot; height=&quot;281&quot; data-filename=&quot;SceneFlow.svg&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2. 결과 화면과 장면 재진입 오류 해결&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;플레이어가 사망하거나 보스를 처치하면 현재 전투 화면을 캡처한 뒤 결과 화면의 배경으로 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실패 시에는 캡처 화면을 어둡고 무채색에 가깝게 변환하고, 성공 시에는 황금색 오버레이를 적용한다. 그 위에 UI 아틀라스의 &lt;/span&gt;&lt;span&gt;ResultFail&lt;/span&gt;&lt;span&gt; 또는 &lt;/span&gt;&lt;span&gt;ResultSuccess&lt;/span&gt;&lt;span&gt; 이미지를 중앙에 배치했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음에는 사망이나 보스 처치가 감지된 즉시 결과 화면으로 전환했다. 하지만 이 방식은 플레이어의 사망 애니메이션과 보스의 파편 연출을 결과 화면이 덮어버리는 문제가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재는 다음 조건을 만족한 뒤 결과 장면으로 전환한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;플레이어 사망: &lt;/span&gt;&lt;span&gt;Player_Die&lt;/span&gt;&lt;span&gt; 애니메이션 재생 완료&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보스 처치: 사망 파편과 양손 낙하 연출 완료&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또 다른 문제는 보스방에서 사망한 뒤 마을로 돌아갔다가 던전에 재진입할 때 발생한 읽기 접근 위반이었다. 이전 던전의 객체가 풀&amp;middot;UI&amp;middot;타일 리소스 참조를 유지한 상태에서 새로운 던전이 초기화되는 것이 원인이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 해결하기 위해 &lt;/span&gt;&lt;span&gt;DungeonScene::leave()&lt;/span&gt;&lt;span&gt;에서 다음 상태를 한 번에 정리하도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;활성 몬스터&amp;middot;투사체&amp;middot;이펙트 풀 슬롯&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보상 상자 진행 상태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;활성 보스 포인터&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;전투방 및 보스 처치 플래그&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;풀에서 재사용될 몬스터의 Animator 상태&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;장면 진입과 이탈의 책임이 명확해지면서 반복해서 던전에 입장해도 이전 플레이의 메모리나 전투 상태가 남지 않게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;3. 스켈레톤 보스와 LRU 기반 패턴 선택&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일반 몬스터와 다른 전투 흐름을 구현하기 위해 &lt;/span&gt;&lt;span&gt;Boss&lt;/span&gt;&lt;span&gt; 기반 클래스와 &lt;/span&gt;&lt;span&gt;SkelBoss&lt;/span&gt;&lt;span&gt;를 추가했다. 보스가 생성되면 전용 체력 UI가 연결되며, 보스 객체의 수명에 따라 UI도 함께 표시되거나 해제된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;스켈 보스는 크게 세 가지 공격 후보를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;양손 사이에 플레이어가 있을 때 사용하는 레이저&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;플레이어가 바닥 영역에 있을 때 사용하는 검 소환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;항상 사용할 수 있는 회전 탄막&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;초기 구현에서는 직전에 사용한 패턴만 피하는 고정 fallback 방식을 사용했다. 이 경우 레이저 조건이 먼저 참이 되면 다른 패턴이 후보에서 제외되고, 특정 상황에서 같은 공격 흐름이 반복됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 개선하기 위해 각 패턴의 마지막 사용 순서를 기록하는 LRU 방식으로 변경했다. 현재 상황에서 사용할 수 있는 패턴을 모두 후보에 넣은 뒤, 가장 오래 사용하지 않은 패턴을 선택한다. 패턴별 발동 조건을 유지하면서도 전투가 한쪽 공격에 치우치지 않도록 만들 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[이미지 : 레이저&amp;middot;검 소환&amp;middot;회전 탄막 비교]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;탄막은 보스의 입 위치에서 네 방향으로 생성되며, 매 프레임 낮은 회전량을 누적해 믹서 칼날처럼 회전하는 형태를 만든다. 검 소환 패턴은 대기 이펙트를 먼저 표시한 뒤 플레이어를 조준해 실제 검을 발사한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;보스가 소환한 검에는 공용 투사체와 다른 충돌 규칙이 필요했다. 일반 투사체처럼 처리하면 중간 벽이나 일방향 플랫폼에 막혀 의도한 연출이 나오지 않았다. 따라서 보스 검의 갱신을 &lt;/span&gt;&lt;span&gt;SkelBoss&lt;/span&gt;&lt;span&gt;가 직접 담당하도록 분리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;검은 플레이어에게 한 번 적중하더라도 사라지지 않고 계속 진행하며, &lt;/span&gt;&lt;span&gt;Solid&lt;/span&gt;&lt;span&gt; 바닥의 윗면에 도달했을 때만 꽂힌다. 바닥에 꽂힌 검은 타일 뒤쪽 레이어에 렌더링하여 실제 지형에 박힌 것처럼 보이도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;보스 사망 시에는 남아 있는 레이저&amp;middot;탄막&amp;middot;검을 정리하고, 사망 프레임을 이용한 파편과 양손에 중력을 적용해 낙하시키는 연출을 추가했다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;4. 액터와 보스 데이터를 JSON으로 통합&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존 코드에는 플레이어 능력치와 보스 패턴에 필요한 장비 키가 임시 상수로 남아 있었다. 이 방식은 밸런스를 조정할 때마다 C++ 코드를 수정하고 다시 빌드해야 한다는 문제가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;플레이어와 보스의 설정을 &lt;/span&gt;&lt;span&gt;actor_data.json&lt;/span&gt;&lt;span&gt;으로 분리해 다음 항목을 데이터에서 불러오도록 변경했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;플레이어 기본 능력치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;디버그용 easy 능력치 프리셋&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;플레이어 기본 장비&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;스켈 보스의 체력과 표시 이름&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보스 패턴에서 사용할 무기 및 투사체 참조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로그램 시작 시에는 방&amp;middot;몬스터&amp;middot;무기&amp;middot;액터 데이터를 먼저 검증한다. 필수 데이터나 장비 참조가 누락되면 잘못된 상태로 게임을 시작하지 않고, 정확한 원인을 로그에 남긴 뒤 초기화를 중단한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;투사체 종류와 몬스터 공격 패턴을 공통 enum으로 변환하던 중간 계층도 줄였다. 투사체는 아틀라스 애니메이션 키와 공격 대상 설정을 기준으로 동작하며, 미노타우르의 돌진처럼 특정 몬스터만 사용하는 기술은 해당 몬스터의 선택 데이터로 분리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 구조에서는 새로운 몬스터 전용 기술을 추가할 때 공통 enum과 여러 분기문을 함께 수정할 필요가 줄어든다. 실제로 기본 무기인 &lt;/span&gt;&lt;span&gt;ShortSword&lt;/span&gt;&lt;span&gt;의 피해량도 JSON 값만 변경하여 100에서 20으로 조정할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;5. AudioManager와 객체 소유 기반 효과음 관리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;효과음과 BGM을 담당하는 &lt;/span&gt;&lt;span&gt;AudioManager&lt;/span&gt;&lt;span&gt;도 새로 구축했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;게임 시작 시 &lt;/span&gt;&lt;span&gt;resources/Audios/SFX&lt;/span&gt;&lt;span&gt;와 &lt;/span&gt;&lt;span&gt;BGM&lt;/span&gt;&lt;span&gt; 폴더를 스캔하고, 파일 경로와 사운드 버퍼를 미리 등록한다. 코드에서는 확장자를 제외한 파일명을 호출 키로 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어 &lt;/span&gt;&lt;span&gt;Minotaurs_Charge.wav&lt;/span&gt;&lt;span&gt;는 별도의 오디오 enum이나 JSON 경로를 거치지 않고 &lt;/span&gt;&lt;span&gt;Minotaurs_Charge&lt;/span&gt;&lt;span&gt;라는 키로 재생할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;같은 행동에 여러 효과음이 존재하는 경우에는 다음과 같은 파일명을 하나의 그룹으로 묶는다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Owner_Action-00.wav&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Owner_Action-01.wav&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;등록된 변형 효과음은 순서대로 재생되어 동일한 타격음이 계속 반복되는 느낌을 줄였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여러 공격과 보스 탄막이 동시에 발생하면 재생 중인 소리가 잘리는 현상이 있어, 미리 생성하는 SFX voice 풀을 8개에서 32개로 확장했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;각 액터에는 재사용 수명 동안 유지되는 &lt;/span&gt;&lt;span&gt;EntityId&lt;/span&gt;&lt;span&gt;를 부여했다. 몬스터가 오브젝트 풀로 반환될 때 해당 ID가 소유한 효과음만 중지하므로, 다른 액터의 소리까지 함께 끊지 않으면서 풀 객체의 오디오 상태를 안전하게 정리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;BGM은 장면에 따라 다음과 같이 전환된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;마을: &lt;/span&gt;&lt;span&gt;Village_BGM&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;일반 던전: &lt;/span&gt;&lt;span&gt;Dungeon_BGM&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보스방: &lt;/span&gt;&lt;span&gt;Boss_BGM&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현재 재생 중인 BGM과 같은 키가 반복 요청되면 다시 시작하지 않도록 처리해, 매 프레임 호출되더라도 음악이 처음부터 재생되는 문제를 방지했다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;6. 구조화된 로그와 디버그 도구 개선&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;JSON과 아틀라스 데이터가 많아지면서 단순한 표준 오류 출력만으로는 초기화 실패 원인을 찾기 어려워졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 해결하기 위해 리소스&amp;middot;맵&amp;middot;방&amp;middot;타일&amp;middot;UI&amp;middot;보스 패턴의 실패 경로를 &lt;/span&gt;&lt;span&gt;LogManager&lt;/span&gt;&lt;span&gt;로 통합했다. 현재는 다음 정보를 로그에서 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;누락된 JSON 또는 리소스 파일&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;존재하지 않는 방&amp;middot;무기&amp;middot;투사체 키&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;방 레퍼런스와 통로 선택 실패&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;타일맵 생성 및 아틀라스 프레임 조회 실패&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;보스 패턴 진입과 투사체 생성 결과&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;F6 디버그 메뉴에서는 지정한 층과 방을 바로 생성하거나 전투 판정 범위를 표시할 수 있다. 플레이어와 몬스터의 피격 박스, 감지 범위, 공격 범위, 근접 공격 판정, 투사체 충돌 영역을 서로 다른 색으로 확인할 수 있도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;방 ID에 &lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;을 입력하면 해당 층에 실제로 생성된 모든 방의 타일맵을 미리 보기 형태로 출력한다. 각 방의 크기를 비교해 화면 안에 모두 들어오는 열 개수와 축척을 자동으로 계산하므로, JSON 방 데이터를 수정한 뒤 직접 모든 방을 이동하며 확인할 필요가 줄었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[이미지 삽입: F6 디버그 메뉴의 전체 방 프리뷰]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Debug 빌드에서는 F7을 눌러 &lt;/span&gt;&lt;span&gt;actor_data.json&lt;/span&gt;&lt;span&gt;의 easy 프리셋을 적용할 수 있다. easy 모드가 활성화되면 화면 왼쪽 아래에 빨간색 표시를 출력해 일반 테스트와 혼동하지 않도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오브젝트 선생성 시점도 프로그램 시작 단계에서 &lt;/span&gt;&lt;span&gt;DungeonScene::enter()&lt;/span&gt;&lt;span&gt;로 옮겼다. 실제 던전 데이터가 준비된 시점에 몬스터 풀을 구성하고, 보스 전투에 필요한 투사체 슬롯 384개를 미리 확보한다. 덕분에 타이틀과 마을에서는 불필요한 전투 객체를 준비하지 않으면서 던전 진입 이후의 동적 할당을 줄일 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;7. 방 통로 데이터와 보스방 잠금 처리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;방의 외곽선 두께와 통로 크기는 기존에 코드 상수로 고정되어 있었다. 이를 &lt;/span&gt;&lt;span&gt;room_data.json&lt;/span&gt;&lt;span&gt;의 레이아웃 설정으로 이동하여 상&amp;middot;하 통로 너비와 좌&amp;middot;우 통로 높이를 방 데이터에 맞춰 변경할 수 있도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;타일맵 생성, 문 감지 영역, 배경 타일 배치가 모두 같은 통로 규격을 사용하므로 시각적인 문 위치와 실제 이동 판정이 어긋나는 문제도 줄었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;보스방에서는 조우가 시작되는 프레임에 문 충돌체를 먼저 활성화한다. 플레이어 이동보다 문 잠금 상태를 먼저 확정하기 때문에, 보스가 생성되는 순간 입구로 되돌아가 전투를 벗어나는 문제를 방지할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;최종 완성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://youtu.be/4aZb9-7kNUE&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/4aZb9-7kNUE&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=4aZb9-7kNUE&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/b4rz2O/dJMb9dHGNVK/ZKKMtiHLX9luqG9e1M7jzk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/cINoPJ/dJMb9lMtW51/v1dQiw31l1Uv5gmG4gSjS1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;던그리드 모작 시연 영상&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/4aZb9-7kNUE&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;마치며&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번 개발의 핵심은 개별 기능을 추가하는 것보다 게임의 전체 생명주기를 완성하는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;장면별 책임을 분리하고 진입&amp;middot;이탈 시 상태를 정리하면서 타이틀부터 보스 처치와 결과 화면까지 하나의 플레이 사이클이 연결되었다. 스켈 보스에는 상황 조건과 최근 사용 이력을 함께 고려하는 패턴 선택을 적용했고, 액터&amp;middot;무기&amp;middot;보스 설정을 JSON으로 통합해 밸런스와 콘텐츠를 코드 밖에서 조정할 수 있게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오디오, 구조화된 로그, 전체 방 프리뷰와 같은 개발 지원 기능도 함께 보강했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>SFML</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/10</guid>
      <comments>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-6-%EC%94%AC-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%8A%A4%EC%BC%88-%EB%B3%B4%EC%8A%A4-%EC%A0%84%ED%88%AC%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B8%B0%EB%B0%98-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84#entry10comment</comments>
      <pubDate>Wed, 2 Sep 2026 13:01:14 +0900</pubDate>
    </item>
    <item>
      <title>[Unreal,Blueprint] Arrow a row 모작 개발기</title>
      <link>https://baeksa01.tistory.com/entry/Unreal57Blueprint-Arrow-a-row-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Arrow a Row&lt;/code&gt;의 핵심 흐름을 참고하되, 게임 규칙과 플레이 흐름만 재해석해 만든 Windows용 개인 프로젝트입니다. 원작의 코드&amp;middot;리소스&amp;middot;문구는 사용하지 않았으며, Unreal Engine 5.7.4의 Blueprint만으로 이동, 타일 재활용, 적&amp;middot;투사체 오브젝트 풀링, 전투, 강화, HUD와 메뉴 흐름을 구성했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 한눈에 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://git.seohwido.site/shd/Runner_Demo.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://git.seohwido.site/shd/Runner_Demo.git&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1788321423210&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Runner_Demo&quot; data-og-description=&quot;Config [mod]프로젝타일 속도 미적용 버그 수정 2026-09-02 10:21:37 +09:00 Content [mod]프로젝타일 속도 미적용 버그 수정 2026-09-02 10:21:37 +09:00 Docs [del]미사용 에셋 제거 2026-09-02 00:07:58 +09:00 .gitattributes Initial&quot; data-og-host=&quot;git.seohwido.site&quot; data-og-source-url=&quot;https://git.seohwido.site/shd/Runner_Demo.git&quot; data-og-url=&quot;https://git.seohwido.site/shd/Runner_Demo&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cWRQNO/dJMb8TCsMGS/aLkdwHMOGSkn8I9T8Lj7nk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://git.seohwido.site/shd/Runner_Demo.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://git.seohwido.site/shd/Runner_Demo.git&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cWRQNO/dJMb8TCsMGS/aLkdwHMOGSkn8I9T8Lj7nk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Runner_Demo&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Config [mod]프로젝타일 속도 미적용 버그 수정 2026-09-02 10:21:37 +09:00 Content [mod]프로젝타일 속도 미적용 버그 수정 2026-09-02 10:21:37 +09:00 Docs [del]미사용 에셋 제거 2026-09-02 00:07:58 +09:00 .gitattributes Initial&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;git.seohwido.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;내용&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트명&lt;/td&gt;
&lt;td&gt;Runner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장르&lt;/td&gt;
&lt;td&gt;자동 진행형 러너&amp;middot;슈팅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개발 기간&lt;/td&gt;
&lt;td&gt;2026.08.24 ~ 2026.09.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개발 형태&lt;/td&gt;
&lt;td&gt;개인 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;엔진&lt;/td&gt;
&lt;td&gt;Unreal Engine 5.7.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 방식&lt;/td&gt;
&lt;td&gt;Blueprint-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대상 플랫폼&lt;/td&gt;
&lt;td&gt;Windows 64-bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 기술&lt;/td&gt;
&lt;td&gt;Enhanced Input, 타일&amp;middot;적&amp;middot;투사체 오브젝트 풀링, Blueprint Interface, Data Asset, UMG, Event Dispatcher, GameInstance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;형상 관리&lt;/td&gt;
&lt;td&gt;Git, Git LFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트의 조작은 단순하다. 플레이어는 좌우 이동만 직접 조작하고, 전투는 일정 주기로 자동 진행된다. 플레이 도중 두 개의 강화 표지판 중 하나를 통과해 능력치를 올리고, 앞으로 이어지는 타일과 적을 상대한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 조작과 달리 내부에서는 장시간 플레이 중 액터가 계속 생성&amp;middot;파괴되는 문제, 여러 종류의 투사체와 피해 처리의 결합, 타일 재사용 시 적과 강화 오브젝트 상태가 남는 문제를 해결해야 했다. 그래서 이번 프로젝트는 콘텐츠 수보다 반복 실행 가능한 구조를 만드는 데 초점을 맞췄다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시연 영상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/HeW1g0u7sFc&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/HeW1g0u7sFc&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 삽입 후보: 메인 메뉴, 인게임 전체 화면, 강화 표지판 선택 장면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처음 세운 목표와 범위 조정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 목표는 자동 전진, 자동 사격, 여러 종류의 적과 보스, 런 중 강화, 영구 성장, 상점, 저장&amp;middot;불러오기까지 하나의 세션으로 연결하는 것이었다. 하지만 제한된 일정 안에서 모든 콘텐츠를 얕게 구현하기보다 다음 핵심 구조가 실제로 동작하도록 범위를 줄였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌우 이동과 고정형 인게임 카메라&lt;/li&gt;
&lt;li&gt;재사용되는 맵 타일&lt;/li&gt;
&lt;li&gt;적 풀과 투사체 풀&lt;/li&gt;
&lt;li&gt;투사체 충돌, 진영 판별, 피해와 사망&lt;/li&gt;
&lt;li&gt;런 중 능력치 강화와 현재 능력치 재계산&lt;/li&gt;
&lt;li&gt;골드와 &lt;code&gt;GameInstance&lt;/code&gt; 기반 메타 스탯 강화&lt;/li&gt;
&lt;li&gt;메인 메뉴, 상점, 인게임 HUD, 일시정지&amp;middot;사망 메뉴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에 검토했던 펫 구매&amp;middot;장착, 프로세스 재실행 후 복원되는 &lt;code&gt;SaveGame&lt;/code&gt;, 다수의 보스와 빌드 조합은 최종 구현 범위에서 제외했다. 포트폴리오와 이 글에서도 설계 문서에만 남아 있는 기능을 완료 기능처럼 적지 않았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Blueprint-only 프로젝트 기반 구성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에는 C++ 모듈을 추가하지 않았다. 게임 규칙을 하나의 거대한 Event Graph에 몰아넣지 않고 역할별 Blueprint로 나눴다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;BP_TileManager              타일 풀 생성&amp;middot;재배치
BP_LevelManager             스테이지 진행&amp;middot;적 배치&amp;middot;강화 표지판 데이터
BP_EnemySpawnManager        적 풀 생성&amp;middot;대여&amp;middot;반환
BP_ProjectileManager       투사체 풀 생성&amp;middot;대여&amp;middot;반환
BP_RunnerPlayer            플레이어 상태&amp;middot;자동 사격&amp;middot;피해&amp;middot;런 강화
BP_RunnerGameInstance      골드&amp;middot;메타 강화 데이터
BP_RunnerPlayerController  입력&amp;middot;HUD&amp;middot;일시정지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 &lt;code&gt;Enum&lt;/code&gt;, &lt;code&gt;Struct&lt;/code&gt;, &lt;code&gt;Data Asset&lt;/code&gt;으로 분리했다. 예를 들어 적 정의에는 ID, 타입, 클래스, 초기 풀 크기, 등장 가능 스테이지, 가중치, 기본 체력&amp;middot;공격력과 공격 방식을 담았다. 투사체 발사 요청도 발사자, 진영, Transform, 피해량, 속도, 최대 이동 거리, Mesh, Scale을 하나의 구조체로 묶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 역할과 데이터를 먼저 나눈 덕분에 Player와 Enemy가 같은 투사체 시스템을 사용하고, 적 종류가 늘어나더라도 Manager의 핵심 흐름을 복제하지 않을 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Enhanced Input과 카메라 분리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌우 이동은 &lt;code&gt;IA_HorizontalMove&lt;/code&gt;와 &lt;code&gt;IMC_Gameplay&lt;/code&gt;로 구성했다. A/D 입력을 1차원 축으로 받아 &lt;code&gt;PlayerController&lt;/code&gt;에서 플레이어의 오른쪽 벡터 방향으로 &lt;code&gt;Add Movement Input&lt;/code&gt;을 전달했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 자동 전진과 좌우 이동을 모두 &lt;code&gt;AddMovementInput&lt;/code&gt;으로 처리하는 방법도 검토했다. 그러나 &lt;code&gt;CharacterMovement&lt;/code&gt;는 두 입력을 하나의 벡터로 합친 뒤 &lt;code&gt;MaxWalkSpeed&lt;/code&gt;로 제한하기 때문에, 대각선 입력 시 전진 속도와 횡이동 속도를 완전히 독립시키기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서는 플레이어 조작과 월드 진행 책임을 분리하는 방향으로 정리했다. 플레이어는 횡이동에 집중하고, 맵 타일은 재배치를 통해 계속 이어진다. 카메라는 &lt;code&gt;BP_InGameCamera&lt;/code&gt; Actor로 분리하고 Spring Arm을 적용해 플레이어와 맵을 안정적으로 바라보도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일시정지는 별도의 &lt;code&gt;IA_Pause&lt;/code&gt; 입력으로 처리한다. 메뉴가 열리면 게임을 정지하고 UI 입력 모드와 마우스 커서를 활성화하며, 복귀 시 Gameplay Mapping Context와 Game Only 입력 상태를 복원한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 끊김 없는 길을 위한 타일 오브젝트 풀링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;러너 게임의 길을 매번 Spawn&amp;middot;Destroy하면 장시간 플레이에서 생성 비용과 가비지 컬렉션 부담이 반복된다. 그래서 시작 시 정해진 수의 &lt;code&gt;BP_BaseTile&lt;/code&gt;을 만들고, 뒤로 지나간 타일을 가장 앞쪽으로 옮기는 구조를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타일 크기를 상수로 중복 입력하지 않고 첫 타일의 &lt;code&gt;RecycleCheck.GetScaledBoxExtent&lt;/code&gt;에서 가져온 것이 핵심이다. &lt;code&gt;PoolSize&lt;/code&gt;가 N일 때 Manager의 Box Collision 안에는 N-1개의 타일이 들어가고, 마지막 한 개는 재배치를 위한 여유 타일로 남긴다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ContainedTileCount = PoolSize - 1
CollisionExtent.X  = TileExtent.X * ContainedTileCount
FirstTileCenter.X  = BoxCenter.X - CollisionExtent.X + TileExtent.X&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 타일을 배치한 뒤에는 길이를 다시 계산하지 않는다. 각 타일의 &lt;code&gt;NextSpawnPoint&lt;/code&gt; 월드 위치를 다음 타일의 기준점으로 사용해 연속 배치한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;초기화
&amp;rarr; PoolSize만큼 BP_BaseTile 생성
&amp;rarr; 첫 타일 Bounds로 Manager Box 크기 설정
&amp;rarr; NextSpawnPoint를 따라 나머지 타일 연결

재사용
&amp;rarr; 해당 타일 소유 Enemy 반환
&amp;rarr; 강화 선택 상태와 표지판 초기화
&amp;rarr; 마지막 타일 NextSpawnPoint 앞으로 이동
&amp;rarr; 새 Stage&amp;middot;Enemy 데이터 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타일이 재사용될 때는 위치만 바꾸면 안 된다. 이전 타일의 적 배열, 강화 표지판 표시&amp;middot;Collision, 이미 선택했다는 Boolean이 남을 수 있기 때문이다. &lt;code&gt;BP_BaseTile&lt;/code&gt;에 초기화 함수를 두고 재사용 시 관련 상태를 함께 되돌렸다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 삽입 후보: &lt;code&gt;BP_TileManager&lt;/code&gt; 초기 풀 생성 그래프, 타일 9개 + 여유 타일 1개를 보여 주는 에디터 화면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 타입별 적 풀과 타일 소유권&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적은 &lt;code&gt;BP_EnemySpawnManager&lt;/code&gt;가 관리한다. &lt;code&gt;DA_EnemyCatalog&lt;/code&gt;의 적 정의를 읽어 게임 시작 시 필요한 수만큼 미리 생성하고 비활성화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀 데이터에는 Enemy ID, 타입, 클래스, 풀 크기와 대기 중인 Enemy 배열이 들어간다. 조회 비용과 역할을 줄이기 위해 다음 인덱스를 함께 관리했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enemy ID &amp;rarr; 해당 풀 인덱스&lt;/li&gt;
&lt;li&gt;Enemy Type &amp;rarr; 해당 타입에 속한 풀 인덱스 목록&lt;/li&gt;
&lt;li&gt;활성 Enemy Set&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 API는 &lt;code&gt;InitEnemyPool&lt;/code&gt;, &lt;code&gt;AcquireEnemy&lt;/code&gt;, &lt;code&gt;ReturnEnemy&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;AcquireEnemy
&amp;rarr; 요청 타입&amp;middot;ID의 Pool 탐색
&amp;rarr; Available Enemy 존재 여부 확인
&amp;rarr; 하나를 꺼내 Active Set에 등록
&amp;rarr; ActivateFromPool(Transform, OwningTile)

ReturnEnemy
&amp;rarr; Active Set 포함 여부 확인
&amp;rarr; DeactivateFromPool
&amp;rarr; 원래 Pool의 Available 배열에 복귀&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 비었을 때 런타임 Spawn으로 크기를 늘리지 않았다. 프레임 비용이 갑자기 증가하는 것보다 해당 배치나 발사를 생략하는 쪽을 선택했다. 반환할 때도 Active Set 포함 여부를 확인해 같은 Enemy가 중복 반환되는 상황을 막았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BP_LevelManager&lt;/code&gt;는 타일과 적 풀 사이를 중개한다. Early, Mid, Boss Phase와 타일 순번을 계산하고, Normal&amp;middot;Elite&amp;middot;Boss 타입에 맞는 배치점을 선택한다. 실제 최종 콘텐츠는 Normal과 Elite 중심의 최소 구성으로 제한했지만, 데이터와 Enum에는 단계 확장을 위한 경계를 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 &lt;code&gt;BP_BaseTile&lt;/code&gt;은 자신에게 배치된 &lt;code&gt;TileEnemys&lt;/code&gt; 배열을 가진다. 타일을 재사용하기 전에 이 배열을 기준으로 Enemy를 풀에 반환한다. &lt;code&gt;Get All Actors Of Class&lt;/code&gt;로 월드 전체를 검색하지 않아도 되며, 타일 소유권이 반환 범위를 명확하게 만든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 적 활성화&amp;middot;비활성화의 상태 대칭&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀링 Actor는 숨기기만 해서는 충분하지 않다. Collision, Tick, 이동, 공격 Timer가 남으면 화면에 보이지 않는 Enemy가 피해를 주거나 연산을 계속할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BP_EnemyBase&lt;/code&gt;는 활성화와 비활성화 작업을 대칭적으로 구성했다.&lt;/p&gt;
&lt;pre class=&quot;julia&quot;&gt;&lt;code&gt;ActivateFromPool
&amp;rarr; 위치와 소유 타일 설정
&amp;rarr; 상태 초기화
&amp;rarr; 표시 활성화
&amp;rarr; Collision&amp;middot;Overlap 활성화
&amp;rarr; 필요한 Tick&amp;middot;공격 Timer 활성화

DeactivateFromPool
&amp;rarr; 공격 Timer 정리
&amp;rarr; Collision&amp;middot;Overlap 비활성화
&amp;rarr; Tick 비활성화
&amp;rarr; Actor 숨김
&amp;rarr; 풀 반환 가능한 상태로 전환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피해를 받아 HP가 0 이하가 되면 &lt;code&gt;CheckDie&lt;/code&gt;에서 중복 사망을 막고, 보상 골드를 &lt;code&gt;GameInstance&lt;/code&gt;에 전달한 뒤 Enemy를 풀에 반환한다. 적 Actor를 Destroy하지 않기 때문에 다음 배치에서 같은 인스턴스를 재사용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Player와 Enemy가 공유하는 범용 투사체 풀&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Player 전용 투사체 관리도 생각했지만, Player와 Enemy가 각자 별도 시스템을 가지면 발사&amp;middot;반환&amp;middot;충돌 코드를 반복하게 된다. 최종 구조는 월드에 하나의 &lt;code&gt;BP_ProjectileManager&lt;/code&gt;를 두고 양쪽이 같은 풀을 사용하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발사자는 &lt;code&gt;ST_ProjectileRequest&lt;/code&gt;를 만들어 Manager에 전달한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shooter&lt;/td&gt;
&lt;td&gt;자기 자신과의 충돌 제외, 피해 출처 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team&lt;/td&gt;
&lt;td&gt;Player&amp;middot;Enemy 진영 판별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SpawnTransform&lt;/td&gt;
&lt;td&gt;시작 위치와 방향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Damage&lt;/td&gt;
&lt;td&gt;이번 발사의 피해량&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;투사체 이동 속도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MaxTravelDistance&lt;/td&gt;
&lt;td&gt;자동 반환 거리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProjectileMesh / MeshScale&lt;/td&gt;
&lt;td&gt;같은 Actor의 외형 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;RequestProjectile&lt;/code&gt;은 대기 배열에서 하나를 꺼내 활성 배열로 옮기고 &lt;code&gt;ActivateProjectile&lt;/code&gt;을 호출한다. 활성화 시 Transform, Mesh, Scale, 속도, Collision과 Tick을 설정한다. 반환 시에는 이동을 즉시 멈추고 Collision&amp;middot;Tick을 끈 뒤 숨겨 대기 배열에 돌려놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투사체는 &lt;code&gt;Initial Life Span&lt;/code&gt;이나 Destroy를 사용하지 않는다. 명중, 벽 충돌 또는 최대 이동 거리 도달 시 Manager로 반환한다. 최대 이동 거리 계산은 활성 투사체의 Tick에서만 실행하고, 비활성화 시 Tick도 함께 끈다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;DistanceSquared(CurrentLocation, StartLocation)
&amp;gt;= MaxTravelDistance&amp;sup2;
&amp;rarr; ReturnProjectile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제곱 거리 비교를 사용해 불필요한 제곱근 계산을 피하고, 곡선 이동이 없는 현재 직선 투사체에 맞는 최소 로직으로 구성했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. Blueprint Interface로 결합도를 낮춘 피해 처리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투사체가 &lt;code&gt;Cast To BP_RunnerPlayer&lt;/code&gt;, &lt;code&gt;Cast To BP_EnemyBase&lt;/code&gt;를 반복하지 않도록 두 개의 Interface를 만들었다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;BPI_CombatUnit
└─ GetCombatTeam &amp;rarr; Player / Enemy

BPI_Damageable
└─ ReceiveDamage(ST_DamageInfo)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Overlap이 발생하면 발사자 본인인지 확인하고, 상대가 &lt;code&gt;BPI_CombatUnit&lt;/code&gt;을 구현했는지 검사한다. 다른 진영이면서 &lt;code&gt;BPI_Damageable&lt;/code&gt;을 구현한 대상에만 피해 정보를 전달한 뒤 투사체를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 투사체는 맞은 대상이 Player인지 Enemy인지 알 필요가 없다. 이후 새로운 전투 Actor를 추가해도 Interface만 구현하면 같은 투사체 흐름에 참여할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 통합 단계에서는 &lt;code&gt;ST_ProjectileRequest.Speed&lt;/code&gt;가 &lt;code&gt;ProjectileMovement&lt;/code&gt;에 제대로 적용되지 않아 요청 속도와 실제 속도가 달라지는 문제가 있었다. 활성화 시 &lt;code&gt;Initial Speed&lt;/code&gt;, &lt;code&gt;Max Speed&lt;/code&gt;, Local Velocity에 요청 속도를 모두 반영하도록 수정했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 삽입 후보: &lt;code&gt;ST_ProjectileRequest&lt;/code&gt;, &lt;code&gt;BP_Projectile.ActivateProjectile&lt;/code&gt;, Interface 기반 Overlap 그래프&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Map 기반 스탯 합산과 런 중 강화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어 스탯은 &lt;code&gt;E_Stats &amp;rarr; Float&lt;/code&gt; Map으로 관리했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ArrowDamage&lt;/li&gt;
&lt;li&gt;ArrowRange&lt;/li&gt;
&lt;li&gt;ArrowSpeed&lt;/li&gt;
&lt;li&gt;ArrowCount&lt;/li&gt;
&lt;li&gt;HorizonSpeed&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 능력치는 세 계층을 합산한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;CurrentStats = BaseStats + RunBonusStats + PlayerPermanentBonusStats&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BaseStats&lt;/code&gt;는 캐릭터 기본값, &lt;code&gt;RunBonusStats&lt;/code&gt;는 현재 플레이에서 표지판으로 얻은 값, &lt;code&gt;PlayerPermanentBonusStats&lt;/code&gt;는 &lt;code&gt;GameInstance&lt;/code&gt;의 메타 강화 값이다. &lt;code&gt;UpdateStats&lt;/code&gt;는 &lt;code&gt;StatsList&lt;/code&gt;를 순회하며 세 Map에서 같은 Key를 찾고 합산한 결과를 &lt;code&gt;CurrentStats&lt;/code&gt;에 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강화 표지판은 왼쪽과 오른쪽에 서로 다른 스탯과 값을 표시한다. 플레이어가 Collision에 들어오면 &lt;code&gt;TryApplySignUpgrade&lt;/code&gt;가 해당 능력치를 &lt;code&gt;RunBonusStats&lt;/code&gt;에 누적하고 &lt;code&gt;UpdateStats&lt;/code&gt;를 다시 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 표지판의 Collision이 같은 프레임에 겹칠 가능성을 막기 위해 타일 단위 &lt;code&gt;bUpgradeSelected&lt;/code&gt; Guard를 사용했다. 적용을 시작하기 전에 먼저 true로 설정하고, 타일을 재사용할 때만 false로 초기화한다. 통합 과정에서 표지판을 활성화하면서 이 값을 반대로 초기화하던 버그가 발견되어 초기화 시점을 타일 재사용 경로로 옮겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스탯이 갱신되면 &lt;code&gt;OnStatUpdate&lt;/code&gt; Event Dispatcher가 호출되고, PlayerController와 HUD가 현재 값을 다시 표시한다. 화살 피해&amp;middot;사거리&amp;middot;속도&amp;middot;수량과 횡이동 속도 역시 이 최종 스탯을 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. HUD, 메뉴, 상점 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 시작 맵은 &lt;code&gt;Main&lt;/code&gt;이고, 인게임은 &lt;code&gt;Ingame&lt;/code&gt; 맵으로 분리했다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Main
├─ 게임 시작 &amp;rarr; Ingame 열기
└─ 상점 &amp;rarr; WidgetSwitcher로 Shop 표시

Ingame
├─ Player HUD
├─ 일시정지 메뉴
├─ 사망 상태 표시
└─ 메인 메뉴 복귀&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인게임 HUD의 스탯 행은 &lt;code&gt;WBP_HUDStatRow&lt;/code&gt;를 동적으로 생성한다. 표시할 Enum 목록을 순회하고 &lt;code&gt;CurrentStats&lt;/code&gt; Map의 값을 찾아 Row에 전달한다. 갱신 때 기존 자식을 Clear한 뒤 다시 만들어 중복 Row가 쌓이지 않게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;골 지점 진행도는 매 Tick 계산하지 않는다. &lt;code&gt;BP_LevelManager.ReportTilePassed&lt;/code&gt;가 통과 타일 수를 변경할 때 &lt;code&gt;OnGoalProgressChanged&lt;/code&gt; Event Dispatcher를 호출하고, PlayerController가 HUD의 &lt;code&gt;UpdateGoalProgress&lt;/code&gt;를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;GoalPercent = Clamp(PassedTileCount / Max(GoalTileCount, 1), 0, 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어 머리 위 체력바는 현재 HP와 최대 HP의 비율을 Progress Bar에 전달한다. 초기 구현에서 현재 HP를 그대로 0~1로 Clamp해 거의 항상 100%로 보이는 문제가 있어 &lt;code&gt;CurrentHP / MaxHP&lt;/code&gt; 비율로 수정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상점은 현재 골드와 &lt;code&gt;E_Stats&lt;/code&gt;별 강화 레벨&amp;middot;비용&amp;middot;증가량을 보여 준다. 구매 요청은 &lt;code&gt;GameInstance.TryBuyUpgrade&lt;/code&gt;로 모으고, 골드가 비용 이상일 때만 차감과 보너스 Map 갱신을 수행한다. 이 데이터는 레벨 전환 동안 유지되지만, 별도 &lt;code&gt;SaveGame&lt;/code&gt;이 완성되지 않았으므로 프로그램 재실행 후 복원되는 구조는 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 데이터와 이벤트 중심으로 Tick 줄이기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 Tick 사용 기준을 명확히 하려고 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속 이동이 필요한 타일&amp;middot;활성 투사체만 Tick 사용&lt;/li&gt;
&lt;li&gt;비활성 풀 Actor는 Tick과 Collision을 모두 비활성화&lt;/li&gt;
&lt;li&gt;자동 사격과 적 공격은 Timer 사용&lt;/li&gt;
&lt;li&gt;스탯&amp;middot;HUD&amp;middot;진행도는 Event Dispatcher로 변경 시점에만 갱신&lt;/li&gt;
&lt;li&gt;타일 소유 Enemy 배열을 사용해 월드 전체 Actor 검색 제거&lt;/li&gt;
&lt;li&gt;풀 고갈 시 런타임 확장 대신 요청 생략&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blueprint-only 프로젝트에서도 Actor 수와 이벤트 호출 시점을 관리하면 불필요한 프레임 연산과 생성 비용을 줄일 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 통합 과정에서 수정한 문제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스탯 이벤트 시그니처 불일치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;OnStatUpdate&lt;/code&gt;의 입력 시그니처를 변경한 뒤 PlayerController의 기존 Event 노드에 사라진 Pin이 남아 전체 컴파일이 실패했다. 관련 Event를 새 시그니처에 맞춰 갱신하고 연결을 다시 구성했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;표지판 선택 Guard 초기화 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표지판을 활성화할 때 &lt;code&gt;bUpgradeSelected&lt;/code&gt;를 잘못된 값으로 초기화해 선택이 막히거나 중복 처리될 수 있었다. Guard는 타일 초기화 시 false, 선택 적용 직전 true가 되도록 생명주기를 정리했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;투사체 속도 미적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 구조체에는 속도가 있었지만 실제 &lt;code&gt;ProjectileMovement&lt;/code&gt;의 속도와 Local Velocity에 모두 반영되지 않았다. 활성화 경로에서 세 값을 같은 Request Speed로 맞췄다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Git LFS 접근 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal의 &lt;code&gt;.uasset&lt;/code&gt;, &lt;code&gt;.umap&lt;/code&gt;을 Git LFS로 관리하던 중 로컬 LFS 객체 권한 문제로 Push가 막혔다. 손상 범위를 확인하고 ACL을 백업한 뒤 필요한 객체 권한을 복구했다. 미사용 외부 에셋과 불필요한 BuiltData가 원격 이력에 포함되지 않도록 커밋 이력도 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 회고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블루프린트만으로 게임을 개발하는 과정은 불편한 점이 많았다. 단순한 데이터관리도 사전작업도 많고, 관리 체계도 한눈에 들어오지않아서 프로젝트를 추적하는데 어려움을 느꼈다. 또한 코딩을 몰라도 할수 있다라는 장점이 무색하게, 개발 시작전 완전하게 개발 구조와 모델을 확정짓고, 이해도가 높지 않은 이상 중간중간 디버깅이나 버그 추적이 굉장히 어려웠다.&lt;br /&gt;다만, 간단한 게임이라면 어떤 순서로 액터들이 활성화되고, 작동하는지 라이프 사이클을 관찰하기에는 좋았다.&lt;/p&gt;</description>
      <category>UnrealEngine</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/9</guid>
      <comments>https://baeksa01.tistory.com/entry/Unreal57Blueprint-Arrow-a-row-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C%EA%B8%B0#entry9comment</comments>
      <pubDate>Wed, 2 Sep 2026 12:50:12 +0900</pubDate>
    </item>
    <item>
      <title>[C++/SFML] 던그리드 모작 개발 (5) - 카메라 경계 제어, UI 픽셀 렌더링 및 보상 상호작용 구현</title>
      <link>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-5-%EC%B9%B4%EB%A9%94%EB%9D%BC-%EA%B2%BD%EA%B3%84-%EC%A0%9C%EC%96%B4-UI-%ED%94%BD%EC%85%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%8F-%EB%B3%B4%EC%83%81-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번 5회차 개발에서는 이전 단계에서 개별적으로 구축한 물리 연산, 객체 풀링, 전투 매니저들을 통합하는 메인 게임 루프를 구성했다. 또한 플레이어의 시각을 담당하는 카메라 제어 시스템, 해상도 번짐 현상을 해결한 UI 시스템, 그리고 방 클리어 이후의 보상 상호작용 로직을 신규 구현했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;1. 단일 게임 루프 통합 및 파이프라인 동기화 (main.cpp)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;설계 의도: 다수의 매니저 클래스가 동시에 동작하면서 발생할 수 있는 물리 연산 간섭과 프레임 지연 이슈를 방지하기 위해, main.cpp 내부의 실행 흐름을 엄격하게 통제했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;방 전환과 같은 화면 이동이 발생할 경우 기존 방의 몬스터와 투사체를 즉시 풀링 매니저에 회수시키며, 모든 물리 보정이 완료된 최종 좌표를 기준으로 카메라와 UI를 렌더링하도록 실행 파이프라인의 순서를 강제했다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjL06LwoZaWAxUAAAAAHQAAAAAQhwI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;main_flow.drawio.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;1425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0sGgP/dJMcahFoezp/epJ9D5mYHDgynRffFeOWR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0sGgP/dJMcahFoezp/epJ9D5mYHDgynRffFeOWR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0sGgP/dJMcahFoezp/epJ9D5mYHDgynRffFeOWR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0sGgP%2FdJMcahFoezp%2FepJ9D5mYHDgynRffFeOWR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;919&quot; height=&quot;1425&quot; data-filename=&quot;main_flow.drawio.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;1425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;2. 카메라 추적 및 경계 제한 로직 (Camera)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;설계 의도: 플레이어의 이동에 맞춰 2D 뷰포트를 실시간으로 추적하는 카메라 클래스를 설계했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;트러블슈팅: 단순히 플레이어의 좌표를 카메라 중심으로 고정하면, 맵의 가장자리로 이동했을 때 타일맵 바깥의 검은색 여백이 화면에 노출되는 문제가 발생한다. 이를 방지하기 위해 clampCenter 함수를 구현하여 카메라의 중심 좌표가 맵의 물리적 경계(Map Bounds) 내부에서만 움직이도록 제한했다. 만약 특정 방의 크기가 화면의 뷰포트보다 작을 경우에는, 화면의 중앙을 타일맵의 정중앙에 고정하여 시야가 흔들리지 않고 안정적으로 렌더링 되도록 예외를 처리했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;3. 커스텀 텍스처 스케일링을 통한 UI 렌더링 최적화 (UIManager)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;설계 의도: 체력바, 대시 횟수, 마우스 조준 커서 등을 관리하는 UI 매니저를 추가했다. 월드 카메라의 이동과 무관하게 화면 상단에 고정으로 렌더링하기 위해, UI를 그릴 때는 윈도우의 뷰를 기본 뷰(Default View)로 전환하는 방식을 사용한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;트러블슈팅: 현재 체력 비율에 따라 너비가 유동적으로 변하는 픽셀 아트 기반의 체력바 UI를 내장 함수인 setScale로 확대하면, 픽셀 경계선이 번지거나 안티앨리어싱이 강제 적용되어 도트 그래픽의 외곽선이 흐려지는 현상이 발생했다. 이를 해결하기 위해 원본 아틀라스 텍스처의 픽셀 데이터를 sf::Image 형태로 메모리에 읽어 들인 후, 반복문을 통해 픽셀 단위를 수동으로 직접 복사 및 배수 확장하여 새로운 텍스처 이미지를 재구성하는 createScaledLifeBarTexture 함수를 구현했다. 이후 텍스처 스무딩 비활성화(setSmooth(false))를 거쳐 도트 그래픽 특유의 선명함을 보존했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;4. 보상 상자 상호작용 및 동적 배치 알고리즘 (RewardChestManager)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;설계 의도: 전투방의 몬스터를 모두 처치했을 때 보물상자와 보상 요정이 등장하는 로직을 전담한다. 키보드의 특정 키(F)를 일정 시간 유지(Hold)해야 상자가 열리도록 상태 머신을 구성했으며, 상자가 열린 후 요정과 플레이어의 충돌 박스가 겹치면 영구적인 스탯(최대 체력, 기본 공격력) 증가 보상을 적용한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;트러블슈팅 및 배치 제어: 타일맵의 구조가 방마다 다르기 때문에 상자가 공중에 생성되거나 벽 타일에 파묻히는 문제가 있었다. 이를 방지하기 위해 타일맵 내 바닥 지점의 후보군을 모두 순회하고, 방의 중앙 X 좌표와 가장 깊은 Y 좌표를 기준으로 오차 거리가 가장 적은 최적의 타일 위치를 std::min_element와 수학적 절대값 계산을 통해 도출하도록 알고리즘을 최적화했다. 이를 통해 어느 방에서든 가장 자연스러운 바닥 중앙에 상자가 스폰된다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size26&quot;&gt;5. 일회성 시각 효과 관리 (EffectManager)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;설계 의도: 무기 궤적(Swing), 피격 이펙트(Slash), 마법진 등 수명이 짧은 일회성 시각 효과의 재생을 관리한다. 이펙트 역시 매번 동적으로 생성하지 않고 ObjectPoolingManager로부터 비활성화된 객체를 대여하여 재생 위치와 회전각만 덮어씌우는 방식으로 작동하여 힙 메모리 오버헤드를 차단했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;이번 업데이트를 통해 개별적으로 분리되어 있던 시스템들을 단일 루프 파이프라인 안에서 안전하게 동기화했다. 2D 게임 특유의 렌더링 제약(픽셀 번짐, 화면 밖 여백 노출)과 동적 객체 배치 알고리즘의 문제들을 수학적 텍스처 복사와 좌표 클램핑 연산을 통해 효과적으로 해결하여 시각적인 완성도를 높였다.&lt;/p&gt;</description>
      <category>SFML</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/8</guid>
      <comments>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-5-%EC%B9%B4%EB%A9%94%EB%9D%BC-%EA%B2%BD%EA%B3%84-%EC%A0%9C%EC%96%B4-UI-%ED%94%BD%EC%85%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%8F-%EB%B3%B4%EC%83%81-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9-%EA%B5%AC%ED%98%84#entry8comment</comments>
      <pubDate>Mon, 10 Aug 2026 23:31:33 +0900</pubDate>
    </item>
    <item>
      <title>[C++/SFML] 던그리드 모작 개발 (4)  - 전투 판정 파이프라인 및 객체 순서도 설계</title>
      <link>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-4-%EC%A0%84%ED%88%AC-%ED%8C%90%EC%A0%95-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%8F-%EA%B0%9D%EC%B2%B4-%EC%88%9C%EC%84%9C%EB%8F%84-%EC%84%A4%EA%B3%84</link>
      <description>&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이번 4회차 개발에서는 매니저 클래스의 과도한 책임을 분리하고, 전투 판정과 객체 생명주기 관리를 독립적인 모듈로 재구성했다. 핵심 로직인 CombatManager와 ObjectPoolingManager를 중심으로 전체 프레임의 흐름과 세부 설계 의도를 정리한다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;1. 프레임 전체 처리 순서도 (Game Frame Flowchart)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;1프레임 동안 실행되는 게임 전체의 업데이트 및 렌더링 파이프라인 흐름이다. 각 모듈은 자신에게 할당된 단일 책임만을 수행하며 다음 단계로 연산을 넘긴다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwitlo7FpZGWAxUAAAAAHQAAAAAQsgQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;frameFlow.drawio.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRb39/dJMcacc1rA7/f2gbMB1o0X7YdyPvk1j3vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRb39/dJMcacc1rA7/f2gbMB1o0X7YdyPvk1j3vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRb39/dJMcacc1rA7/f2gbMB1o0X7YdyPvk1j3vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRb39%2FdJMcacc1rA7%2Ff2gbMB1o0X7YdyPvk1j3vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;1164&quot; data-filename=&quot;frameFlow.drawio.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;1164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size26&quot;&gt;2. 전투 판정 및 객체 처리 순서도 (Combat &amp;amp; Object Flowchart)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;CombatManager가 ObjectPoolingManager와 연동하여 근접 및 원거리 공격, 투사체 충돌을 처리하는 세부 순서도이다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwitlo7FpZGWAxUAAAAAHQAAAAAQswQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;&lt;/span&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oCNy9/dJMcahyE1gN/lIyIeXsWarBLMJ4B8xPTqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oCNy9/dJMcahyE1gN/lIyIeXsWarBLMJ4B8xPTqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oCNy9/dJMcahyE1gN/lIyIeXsWarBLMJ4B8xPTqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoCNy9%2FdJMcahyE1gN%2FlIyIeXsWarBLMJ4B8xPTqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1424&quot; height=&quot;1733&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1733&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;3. 핵심 전투 판정 파이프라인 (CombatManager)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;CombatManager는 객체의 생명주기에 직접 관여하지 않는다. 오직 ObjectPoolingManager로부터 현재 활성화된 객체들의 참조만 전달받아 독립적인 전투 연산을 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;플레이어 공격 처리&lt;/b&gt;: 장착한 무기의 종류에 따라 연산 방식이 분기된다. 근접 무기일 경우, 현재 화면에 존재하는 모든 활성 몬스터를 순회하며 무기의 공격 영역과 몬스터의 피격 영역이 겹치는지 검사한다. 충돌이 확인되면 데미지 연산과 함께 타격 위치를 기반으로 한 넉백 처리를 즉시 수행한다. 원거리 무기일 경우, 직접 타격을 입히지 않고 투사체 발사 요청만을 생성하여 풀링 매니저로 전달한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;투사체 업데이트 및 충돌 회수&lt;/b&gt;: 화면에 존재하는 모든 투사체의 위치를 델타타임에 맞춰 갱신한다. 갱신 직후 타일맵의 벽면과 충돌했는지, 혹은 지정된 타겟(플레이어 또는 몬스터)의 충돌 박스와 일치하는지 판별한다. 충돌이 발생한 투사체는 삭제되지 않고 회수 대기열에 임시 등록되며, 모든 투사체의 순회가 끝난 후 일괄적으로 풀에 반환된다. 이를 통해 순회 중 발생하는 물리 연산 간섭과 반복자 무효화 문제를 방지했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size26&quot;&gt;4. 객체 생명주기 관리 (ObjectPoolingManager)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;게임 플레이 중 끊임없이 생성되고 사라지는 몬스터와 투사체의 동적 할당 오버헤드를 원천적으로 차단하기 위해 비활성 객체를 큐(Queue) 구조로 관리한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,0,0&quot;&gt;객체 할당 및 재사용&lt;/b&gt;: 투사체나 몬스터의 생성 요청이 들어오면 먼저 내부의 비활성 큐를 검사한다. 대기 중인 빈 슬롯이 존재한다면, 해당 객체의 메모리를 새로 할당하는 대신 내부 스탯과 위치 값만 초기화하여 즉시 재사용한다. 비활성 큐가 완전히 비어있어 재사용할 객체가 없을 때에 한해서만 제한적으로 메모리를 동적 할당하여 풀의 크기를 확장한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0&quot;&gt;객체 회수&lt;/b&gt;: 체력이 0이 된 몬스터나 벽에 부딪힌 투사체는 메모리에서 파괴되지 않는다. 화면에서 렌더링되지 않도록 활성화 상태만 해제한 뒤, 다시 비활성 큐에 삽입되어 다음 호출을 대기한다. 이 구조를 통해 가비지 컬렉션이나 힙 메모리 단편화로 인한 프레임 저하를 억제했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;이번 업데이트를 통해 프레임별 처리 순서와 객체의 생명주기 관리가 명확하게 정립되었다. 전투 판정을 CombatManager에 집중시키고, 객체 할당 오버헤드를 ObjectPoolingManager로 제어함으로써 시스템 전체의 결합도를 낮추고 렌더링 안정성을 확보했다. 논리적 구조가 튼튼하게 자리 잡은 만큼, 이를 토대로 본격적인 룸 스폰 로직 및 스테이지 진행 구현을 이어나갈 예정이다.&lt;/p&gt;</description>
      <category>SFML</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/7</guid>
      <comments>https://baeksa01.tistory.com/entry/CSFML-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-4-%EC%A0%84%ED%88%AC-%ED%8C%90%EC%A0%95-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EB%B0%8F-%EA%B0%9D%EC%B2%B4-%EC%88%9C%EC%84%9C%EB%8F%84-%EC%84%A4%EA%B3%84#entry7comment</comments>
      <pubDate>Sun, 9 Aug 2026 01:07:07 +0900</pubDate>
    </item>
    <item>
      <title>[SFML/C++] 던그리드 모작 개발(3) - 타일맵 충돌, 몬스터 FSM, 장비 및 피격 시스템 구현</title>
      <link>https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C3-%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%B6%A9%EB%8F%8C-%EB%AA%AC%EC%8A%A4%ED%84%B0-FSM-%EC%9E%A5%EB%B9%84-%EB%B0%8F-%ED%94%BC%EA%B2%A9-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</link>
      <description>&lt;h1 data-path-to-node=&quot;3&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size26&quot;&gt;오늘 한 일 (Today I Learned)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 액터(Actor), 몬스터, 타일맵, 충돌, 장비 등 게임의 핵심 요소들을 전반적으로 정리하고 서로 연동하는 대규모 업데이트를 진행했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;특히, 기존에 덩치가 커지고 복잡해진 MapManager, MonsterManager와 관련 실행 코드들을 과감히 덜어내고, 핵심 로직들의 작동을 명확하게 검증하기 위해 &lt;b data-index-in-node=&quot;95&quot; data-path-to-node=&quot;6&quot;&gt;NewMapManager 브랜치에서 구조를 단순화한 테스트 환경&lt;/b&gt;을 재구축했다. 이를 통해 타일맵 물리 충돌과 몬스터 AI, 장비 조준 시스템이 완벽하게 맞물려 돌아가는 것을 확인했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;핵심 개발 내용&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;1. 정점 배열(VertexArray) 기반 타일맵과 Collision 분리&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;맵의 렌더링 최적화와 물리 충돌 처리를 위해 타일맵 시스템을 새롭게 설계했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;타일맵(Tilemap) 클래스 추가&lt;/b&gt;: 타일 정보를 바탕으로 SFML의 정점 배열(VertexArray)을 구성해 렌더링 성능을 높였다. 렌더링용 그래픽 데이터와 물리 연산용 충돌 타일 데이터를 분리하되, 함께 생성되도록 묶어서 관리한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;충돌(Collision) 전담 클래스 분리&lt;/b&gt;: 액터와 타일맵 간의 충돌 연산을 독립시켰다. 액터의 Hitbox와 타일의 교집합(Intersection) 영역을 계산하여 좌우&amp;middot;상하 겹침 위치를 밀어내어 보정하고, 액터가 땅을 밟고 있는지(지면 상태)를 갱신하도록 처리했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;테스트 환경 구축&lt;/b&gt;: main 실행 코드에서 40x20 크기의 타일맵과 바닥 타일을 직접 하드코딩으로 깔아두고, 플레이어와 몬스터가 정상적으로 맵과 상호작용(중력, 충돌 보정 등) 하는지 테스트를 완료했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size23&quot;&gt;2. 몬스터 유한 상태 머신 (FSM) 구축&lt;/h3&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;몬스터가 플레이어를 인식하고 행동할 수 있도록 FSM(Finite State Machine) 패턴을 적용했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;5가지 상태 정의&lt;/b&gt;: Idle(대기), Patrol(순찰), Chase(추적), Attack(공격), Dead(사망) 상태를 추가했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;거리 기반 AI&lt;/b&gt;: 플레이어와 몬스터 사이의 거리를 계산하여 대기/순찰/추적 상태를 동적으로 전환한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;충돌 및 공격 처리&lt;/b&gt;: 추적(Chase) 중 플레이어와 물리적으로 충돌하면, 두 객체를 수평으로 분리(밀어냄)한 뒤 즉시 Attack 상태로 전환하여 공격 애니메이션을 재생하도록 구현했다.&lt;/li&gt;
&lt;li&gt;추가로, 리소스 낭비를 막기 위해 몬스터 타입(이름)에 맞는 애니메이션 데이터만 선별하여 등록하도록 최적화했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;3. 장비(Equipment) 시스템과 마우스 에임 조준&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;드디어 플레이어가 무기를 들고 조준할 수 있게 되었다! 장비 시스템 역시 리소스 매니저의 아틀라스를 통해 이미지를 관리하도록 변경했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;기본 장비 장착&lt;/b&gt;: 플레이어 생성 시 기본 무기로 ShortSword를 장착하도록 구성했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;동적 트랜스폼(Transform)&lt;/b&gt;: 마우스 커서의 조준 방향을 실시간으로 추적하여, 무기의 위치(Position), 회전(Rotation), 좌우 반전(Flip) 상태를 자연스럽게 갱신한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0&quot;&gt;공격 메커니즘&lt;/b&gt;: 마우스 클릭 시 조준 방향을 기준으로 무기 스프라이트를 1회전 시키며 베는 모션을 연출하고, 장비에 설정된 '공격 속도' 스탯을 바탕으로 쿨타임(공격 시간)을 계산하도록 구현했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;4. 액터(Actor) 구조 정리 및 피격 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;액터 통합 관리&lt;/b&gt;: 액터 클래스 내부에서 상태(Status), 이동, 충돌, 장비 정보를 종합적으로 관리하도록 데이터를 응집시켰다. 스프라이트는 빈 상태가 아니라 텍스처를 직접 전달받아 생성하도록 안전하게 수정했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;애니메이션 개선&lt;/b&gt;: 애니메이터 내부의 함수 명칭들을 통일성 있게 리팩토링하고, 현재 재생 중인 애니메이션이 중복으로 재생(Play) 호출되지 않도록 방어 로직을 추가했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;피격(Hit) 시스템 추가&lt;/b&gt;: 액터가 피해를 입으면 체력을 감소시키는 것은 물론, 타격자의 위치를 역추산하여 반대 방향으로 밀려나는 수평 넉백(Knockback)을 적용했다. 또한 타격감을 살리기 위해 피격 시 붉은색 색상 필터(Tint)가 깜빡이도록 시각적 효과를 더했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;21&quot; data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;시스템의 규모가 커지다 보니 매니저 클래스들이 너무 많은 책임을 지게 되어 코드가 꼬이기 시작했었다. 이를 해결하기 위해 NewMapManager 브랜치를 따로 파서, 거추장스러운 매니저들을 치워버리고 타일맵, 액터, 장비, 충돌 처리를 main에서 직접 조립해 보며 테스트했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;아직 넉백 기능이 제대로 작동하지 않아서 해당 내용 점검과 추가적으로 맵 로직을 다시 구현해야겠다.&lt;/p&gt;
&lt;blockquote data-path-to-node=&quot;24&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;24,0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>SFML</category>
      <category>#C++ #SFML #로그라이크 #던그리드모작 #타일맵 #충돌처리 #FSM #게임AI #디벨로그 #리팩토링</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/6</guid>
      <comments>https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C3-%ED%83%80%EC%9D%BC%EB%A7%B5-%EC%B6%A9%EB%8F%8C-%EB%AA%AC%EC%8A%A4%ED%84%B0-FSM-%EC%9E%A5%EB%B9%84-%EB%B0%8F-%ED%94%BC%EA%B2%A9-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84#entry6comment</comments>
      <pubDate>Thu, 6 Aug 2026 23:29:39 +0900</pubDate>
    </item>
    <item>
      <title>[SFML/C++] 던그리드 모작 개발일지(2)-맵생성,액터,컨트롤러,애니메이션 구현</title>
      <link>https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%802-%EB%A7%B5%EC%83%9D%EC%84%B1%EC%95%A1%ED%84%B0%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EB%B0%8F-%ED%85%8D%EC%8A%A4%EC%B2%98-%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A7%A4%EB%8B%88%EC%A0%80-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.08.04 - [SFML] - [SFML/C++] 던그리드 모작 개발 (1) - 클래스 아키텍처 설계 및 텍스처 아틀라스 리소스 매니저 구현&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1785938548177&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[SFML/C++] 던그리드 모작 개발 (1) - 클래스 아키텍처 설계 및 텍스처 아틀라스 리소스 매니저 구현&quot; data-og-description=&quot;Git: https://github.com/SeoHwiDo/Dungreed_SFML.git GitHub - SeoHwiDo/Dungreed_SFML: sfml을 활용한 던그리드 게임 모작sfml을 활용한 던그리드 게임 모작. Contribute to SeoHwiDo/Dungreed_SFML development by creating an account on GitHub.&quot; data-og-host=&quot;baeksa01.tistory.com&quot; data-og-source-url=&quot;https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EB%B0%8F-%ED%85%8D%EC%8A%A4%EC%B2%98-%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A7%A4%EB%8B%88%EC%A0%80-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EB%B0%8F-%ED%85%8D%EC%8A%A4%EC%B2%98-%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A7%A4%EB%8B%88%EC%A0%80-%EA%B5%AC%ED%98%84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wqGe3/dJMb8XSk6Wf/YumcEzNUefXXPWibkezMAK/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718,https://scrap.kakaocdn.net/dn/ypnP9/dJMb9lll2LZ/eKp3OkdX0fvDo2vtwPLT90/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718,https://scrap.kakaocdn.net/dn/S5Tom/dJMb8QeBnnp/LSCpzuMSapFBPQHOTnbJu0/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718&quot;&gt;&lt;a href=&quot;https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EB%B0%8F-%ED%85%8D%EC%8A%A4%EC%B2%98-%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A7%A4%EB%8B%88%EC%A0%80-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C-1-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%84%A4%EA%B3%84-%EB%B0%8F-%ED%85%8D%EC%8A%A4%EC%B2%98-%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A7%A4%EB%8B%88%EC%A0%80-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wqGe3/dJMb8XSk6Wf/YumcEzNUefXXPWibkezMAK/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718,https://scrap.kakaocdn.net/dn/ypnP9/dJMb9lll2LZ/eKp3OkdX0fvDo2vtwPLT90/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718,https://scrap.kakaocdn.net/dn/S5Tom/dJMb8QeBnnp/LSCpzuMSapFBPQHOTnbJu0/img.png?width=732&amp;amp;height=718&amp;amp;face=0_0_732_718');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[SFML/C++] 던그리드 모작 개발 (1) - 클래스 아키텍처 설계 및 텍스처 아틀라스 리소스 매니저 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Git: https://github.com/SeoHwiDo/Dungreed_SFML.git GitHub - SeoHwiDo/Dungreed_SFML: sfml을 활용한 던그리드 게임 모작sfml을 활용한 던그리드 게임 모작. Contribute to SeoHwiDo/Dungreed_SFML development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;baeksa01.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/SeoHwiDo/Dungreed_SFML.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/SeoHwiDo/Dungreed_SFML.git&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1785938580491&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - SeoHwiDo/Dungreed_SFML: sfml을 활용한 던그리드 게임 모작&quot; data-og-description=&quot;sfml을 활용한 던그리드 게임 모작. Contribute to SeoHwiDo/Dungreed_SFML development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SeoHwiDo/Dungreed_SFML.git&quot; data-og-url=&quot;https://github.com/SeoHwiDo/Dungreed_SFML&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/N2qta/dJMb81f7Ldy/QgJkhsdacqU6n30Zmt6Mt0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bifQA0/dJMb8QMroMX/GQdaey4KSRK901Ozauuu80/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Wavpn/dJMb9cBWVxN/XAtEYZELOdHKsiVVCMKDj1/img.png?width=692&amp;amp;height=936&amp;amp;face=0_0_692_936&quot;&gt;&lt;a href=&quot;https://github.com/SeoHwiDo/Dungreed_SFML.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SeoHwiDo/Dungreed_SFML.git&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/N2qta/dJMb81f7Ldy/QgJkhsdacqU6n30Zmt6Mt0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bifQA0/dJMb8QMroMX/GQdaey4KSRK901Ozauuu80/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Wavpn/dJMb9cBWVxN/XAtEYZELOdHKsiVVCMKDj1/img.png?width=692&amp;amp;height=936&amp;amp;face=0_0_692_936');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - SeoHwiDo/Dungreed_SFML: sfml을 활용한 던그리드 게임 모작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;sfml을 활용한 던그리드 게임 모작. Contribute to SeoHwiDo/Dungreed_SFML development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-path-to-node=&quot;10&quot;&gt;전체 시스템 아키텍처 설계(수정)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;class (1).png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WUalr/dJMcadCPV2w/XNijxf0RU44RtkXkWQlGG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WUalr/dJMcadCPV2w/XNijxf0RU44RtkXkWQlGG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WUalr/dJMcadCPV2w/XNijxf0RU44RtkXkWQlGG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWUalr%2FdJMcadCPV2w%2FXNijxf0RU44RtkXkWQlGG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;936&quot; data-filename=&quot;class (1).png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;오늘 한 일&lt;/h2&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 애니메이션 적용을 위한 클래스 구현, 랜덤한 그래프 형태의 던전 맵 생성 클래스, 게임 내 몬스터, 플레이어를 위한 액터와 플레이어를 제어하기 위한 컨트롤러 클래스를 구현하였다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;그중 던그리드의 정체성중 하나인 로그라이크를 위해 하드코딩된 맵이 아니라 매번 다르게 생성되는 던전을 만들기 위해 간단한 절차적 맵 생성 기법을 도입했고, 캐릭터의 복잡한 상태(점프, 공격, 대시 등)를 효율적으로 다루기 위해 비트마스크 기반의 상태 머신을 적용했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;더불어 지난번 구현한 리소스 매니저를 통해 모든 애니메이션이 정상적으로 로드되고 화면에 뿌려지는 것까지 테스트를 완료했다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;핵심 개발 내용&lt;/h2&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;1. BFS를 활용한 절차적 맵 생성&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;로그라이크의 핵심 재미 요소인 '매번 다르게 생성되는 미지의 던전'을 구현하기 위해 MapManager에 무작위 맵 생성 알고리즘을 작성했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;무작위 연결과 간선 추가&lt;/b&gt;: 생성된 방(Room)들을 배열해 두고 랜덤하게 연결(linkRoom)한 뒤, 전체 방 개수의 절반만큼 무작위 간선(extra edges)을 추가로 덧붙여 미로와 같은 복잡한 맵 구조를 완성했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;시작 방과 보스 방 배치 (BFS 활용)&lt;/b&gt;: 단순히 맵을 엮는 것에 그치지 않고, &lt;b data-index-in-node=&quot;45&quot; data-path-to-node=&quot;12,1,0&quot;&gt;BFS&lt;/b&gt;알고리즘을 사용해 맵 내에서 가장 거리가 먼 두 방을 찾아냈다. 이 두 방을 각각 시작(Start) 방과 보스(Boss) 방으로 자동 지정하도록 설계해 로그라이크 특유의 던전 동선을 만들어냈다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,2,0&quot;&gt;방 넓이 비례 몬스터 스폰&lt;/b&gt;: 방의 타일맵 넓이(bounds)를 계산하여 설정된 몬스터 밀도(monstersPerSquareUnit)에 따라 생성될 몬스터의 수를 유동적으로 조절하는 디테일을 살렸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;2. 비트마스크(Bitmask) 기반 플레이어 상태 머신 적용&lt;/h3&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;캐릭터는 이동하면서 점프할 수도 있고, 점프 중에 공격이나 대시를 할 수도 있다. 이러한 다중 상태를 일반적인 Enum이나 boolean 변수 여러 개로 관리하면 코드가 스파게티처럼 꼬이기 쉽다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwiF-t6L0YmWAxUAAAAAHQAAAAAQkQI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;C++&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;enum PlayerState : uint32_t {
    Idle   = 1 &amp;lt;&amp;lt; 0, // 1
    Run    = 1 &amp;lt;&amp;lt; 1, // 2
    Jump   = 1 &amp;lt;&amp;lt; 2, // 4
    Dash   = 1 &amp;lt;&amp;lt; 3, // 8
    Attack = 1 &amp;lt;&amp;lt; 4, // 16
    Dead   = 1 &amp;lt;&amp;lt; 5  // 32
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;비트 연산 활용&lt;/b&gt;: 플레이어의 상태(PlayerState)를 비트마스크 기법으로 구성했다. 이를 통해 state |= PlayerState::Jump;처럼 비트를 누적시켜 Run | Jump, Dash | Attack 등 &lt;b data-index-in-node=&quot;123&quot; data-path-to-node=&quot;16,0,0&quot;&gt;두 가지 이상의 복합적인 상태를 하나의 정수형 변수로 가볍고 강력하게 제어&lt;/b&gt;할 수 있게 되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;3. 수학을 활용한 마우스 조준 시스템&lt;/h3&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;키보드 방향키 이동 외에도 마우스를 향해 무기를 조준하는 로직을 Controller 클래스로 분리하여 구현했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;아크탄젠트(atan2) 활용&lt;/b&gt;: 캐릭터의 중심점과 마우스 포인터의 월드 좌표 차이(dx, dy)를 구한 뒤, std::atan2(dy, dx) 함수를 사용하여 360도 전방위 라디안(aimRadian) 값을 구했다.&lt;/li&gt;
&lt;li&gt;0으로 나누는 오류를 방지함과 동시에, 이 값을 통해 단위 벡터(aimDir)를 도출해 내어 플레이어 스프라이트가 항상 마우스를 바라보도록 좌우 반전 로직을 적용했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size23&quot;&gt;4. Actor 물리 연산과 리소스 매니저 고도화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;21&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,0,0&quot;&gt;중력과 이동 (Physics)&lt;/b&gt;: Actor 기본 클래스에 std::optional&amp;lt;sf::Sprite&amp;gt;를 적용해 객체의 텍스처 생성 시점을 안전하게 관리하도록 했다. 또한 updatePhysics 함수를 통해 매 프레임마다 중력(gravity)이 y축 속도(velocity.y)에 누적되도록 하여 자연스러운 추락과 점프 로직을 완성했다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,1,0&quot;&gt;리소스 파싱 리팩토링&lt;/b&gt;: 기존 JSON 파싱 로직에서 파일 경로에 슬래시(/)가 포함되어 있으면(예: Walk/Player_Walk-00.png) 애니메이션 이름이 잘못 분리되는 문제가 있었다. extractAnimationName 내부에서 마지막 슬래시의 위치를 탐색해 경로를 먼저 잘라낸 후 하이픈(-)을 파싱하도록 구조를 개선하여 버그를 잡았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;23&quot; data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;24&quot; data-ke-size=&quot;size16&quot;&gt;오늘 구현한 BFS 기반 맵 탐색, 아크탄젠트를 이용한 조준 방향 계산, 비트마스크 상태 제어 등은 모두 상용 2D 게임 개발에서 필수적으로 쓰이는 기법들이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;main.cpp에서 테스트 환경을 띄워보니 수많은 캐릭터들이 각각의 애니메이션을 버그 없이 완벽하게 재생하는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;오늘 구현한 맵과 플레이어 컨트롤 기능등을 테스트 할 수 있도록 내일은 몬스터 객체와 공격을 위한 무기, 몬스터 및 캐릭터간 상호작용을 위한 콜리젼 기능을 고도화시켜야겠다.&lt;/p&gt;</description>
      <category>SFML</category>
      <category>C++ #SFML #로그라이크 #던그리드모작 #절차적맵생성 #BFS알고리즘 #비트마스크 #FSM #게임물리 #디벨로그</category>
      <author>SeoHwiDo</author>
      <guid isPermaLink="true">https://baeksa01.tistory.com/5</guid>
      <comments>https://baeksa01.tistory.com/entry/SFMLC-%EB%8D%98%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%AA%A8%EC%9E%91-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%802-%EB%A7%B5%EC%83%9D%EC%84%B1%EC%95%A1%ED%84%B0%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EA%B5%AC%ED%98%84#entry5comment</comments>
      <pubDate>Wed, 5 Aug 2026 23:04:35 +0900</pubDate>
    </item>
  </channel>
</rss>