<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>xodn246 님의 블로그</title>
    <link>https://xodn246.tistory.com/</link>
    <description>xodn246 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 13 May 2026 23:35:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>xodn246</managingEditor>
    <item>
      <title>[내배캠 TIL 260430] C++ 숫자 짝꿍 (빈도수 배열 최적화)</title>
      <link>https://xodn246.tistory.com/61</link>
      <description>&lt;h2 data-end=&quot;134&quot; data-start=&quot;123&quot; data-section-id=&quot;g9vwef&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 핵심&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;201&quot; data-start=&quot;136&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;172&quot; data-start=&quot;136&quot; data-section-id=&quot;1ss91gj&quot;&gt;두 문자열 X, Y에서 공통 숫자로 만들 수 있는 가장 큰 수&lt;/li&gt;
&lt;li data-end=&quot;201&quot; data-start=&quot;173&quot; data-section-id=&quot;14ji0b8&quot;&gt;길이 최대 3,000,000 &amp;rarr; 시간복잡도 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;206&quot; data-start=&quot;203&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;222&quot; data-start=&quot;208&quot; data-section-id=&quot;1flj4r1&quot; data-ke-size=&quot;size26&quot;&gt;2. 기존 방식 문제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;239&quot; data-start=&quot;224&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;239&quot; data-start=&quot;224&quot; data-section-id=&quot;c1v8mh&quot;&gt;sort + 이중 반복문&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;정렬: O(N log N)
탐색: O(N^2) (최악)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;301&quot; data-start=&quot;281&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;281&quot; data-section-id=&quot;icgp8e&quot;&gt;중복 비교 많아서 시간 초과 위험&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;306&quot; data-start=&quot;303&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;319&quot; data-start=&quot;308&quot; data-section-id=&quot;wnyj9c&quot; data-ke-size=&quot;size26&quot;&gt;3. 해결 방법&lt;/h2&gt;
&lt;h3 data-end=&quot;332&quot; data-start=&quot;321&quot; data-section-id=&quot;1qb7f3j&quot; data-ke-size=&quot;size23&quot;&gt;핵심 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;363&quot; data-start=&quot;334&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;353&quot; data-start=&quot;334&quot; data-section-id=&quot;kl198i&quot;&gt;숫자는 0~9 &amp;rarr; 종류가 고정됨&lt;/li&gt;
&lt;li data-end=&quot;363&quot; data-start=&quot;354&quot; data-section-id=&quot;127j26t&quot;&gt;배열로 카운팅&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;368&quot; data-start=&quot;365&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;379&quot; data-start=&quot;370&quot; data-section-id=&quot;wul5pq&quot; data-ke-size=&quot;size23&quot;&gt;시간복잡도&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;O(N)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;413&quot; data-start=&quot;395&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;402&quot; data-start=&quot;395&quot; data-section-id=&quot;1mc23sr&quot;&gt;정렬 없음&lt;/li&gt;
&lt;li data-end=&quot;413&quot; data-start=&quot;403&quot; data-section-id=&quot;jutfgq&quot;&gt;이중 반복 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;418&quot; data-start=&quot;415&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;428&quot; data-start=&quot;420&quot; data-section-id=&quot;zifblj&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;string solution(string X, string Y) {
    string answer = &quot;&quot;;
    int countX[10] = {0};
    int countY[10] = {0};

    for (char c : X) countX[c - '0']++;
    for (char c : Y) countY[c - '0']++;

    for (int i = 9; i &amp;gt;= 0; i--) {
        int common = min(countX[i], countY[i]);
        for (int j = 0; j &amp;lt; common; j++) {
            answer += to_string(i);
        }
    }

    if (answer == &quot;&quot;) return &quot;-1&quot;;
    if (answer[0] == '0') return &quot;0&quot;;

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;915&quot; data-start=&quot;912&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;929&quot; data-start=&quot;917&quot; data-section-id=&quot;fpshzf&quot; data-ke-size=&quot;size26&quot;&gt;5. 핵심 포인트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1008&quot; data-start=&quot;931&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;956&quot; data-start=&quot;931&quot; data-section-id=&quot;9s1awh&quot;&gt;값의 범위가 작으면 &amp;rarr; 배열 카운팅이 최적&lt;/li&gt;
&lt;li data-end=&quot;981&quot; data-start=&quot;957&quot; data-section-id=&quot;1xhg0q0&quot;&gt;정렬 대신 순회 순서로 정렬 효과 만들기&lt;/li&gt;
&lt;li data-end=&quot;1008&quot; data-start=&quot;982&quot; data-section-id=&quot;110zv9g&quot;&gt;문자열 문제라도 연산 전에 복잡도 먼저 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1013&quot; data-start=&quot;1010&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1027&quot; data-start=&quot;1015&quot; data-section-id=&quot;57g3c1&quot; data-ke-size=&quot;size26&quot;&gt;6. 오늘의 교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1092&quot; data-start=&quot;1029&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1045&quot; data-start=&quot;1029&quot; data-section-id=&quot;1wl2eh4&quot;&gt;정렬부터 쓰는 습관 버리기&lt;/li&gt;
&lt;li data-end=&quot;1069&quot; data-start=&quot;1046&quot; data-section-id=&quot;12g9if9&quot;&gt;범위 제한 보이면 카운팅 먼저 떠올리기&lt;/li&gt;
&lt;li data-end=&quot;1092&quot; data-start=&quot;1070&quot; data-section-id=&quot;6ehl2k&quot;&gt;성능은 결국 자료구조 선택에서 갈린다&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/61</guid>
      <comments>https://xodn246.tistory.com/61#entry61comment</comments>
      <pubDate>Thu, 30 Apr 2026 10:48:31 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 TIL 260428] Unreal Grid 기반 Fog of War</title>
      <link>https://xodn246.tistory.com/60</link>
      <description>&lt;h3 data-end=&quot;120&quot; data-start=&quot;108&quot; data-section-id=&quot;q79q0x&quot; data-ke-size=&quot;size23&quot;&gt;1. 문제 상황&lt;/h3&gt;
&lt;p data-end=&quot;136&quot; data-start=&quot;122&quot; data-ke-size=&quot;size16&quot;&gt;기존 방식 (Radial)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;170&quot; data-start=&quot;137&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;149&quot; data-start=&quot;137&quot; data-section-id=&quot;1pyisml&quot;&gt;원형으로 시야 표시&lt;/li&gt;
&lt;li data-end=&quot;170&quot; data-start=&quot;150&quot; data-section-id=&quot;164l6rq&quot;&gt;실제 타일 기반 로직이랑 안 맞음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;204&quot; data-start=&quot;172&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결 : Grid 기준으로 Fog를 직접 그림&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;209&quot; data-start=&quot;206&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;223&quot; data-start=&quot;211&quot; data-section-id=&quot;1mqtmzb&quot; data-ke-size=&quot;size23&quot;&gt;2. 핵심 흐름&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777377679237&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;World &amp;rarr; Grid &amp;rarr; UV&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;309&quot; data-start=&quot;252&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;267&quot; data-start=&quot;252&quot; data-section-id=&quot;e2tgy9&quot;&gt;유닛 위치 &amp;rarr; 타일 좌표&lt;/li&gt;
&lt;li data-end=&quot;294&quot; data-start=&quot;268&quot; data-section-id=&quot;f8wyi1&quot;&gt;타일 &amp;rarr; RenderTarget 좌표로 변환&lt;/li&gt;
&lt;li data-end=&quot;309&quot; data-start=&quot;295&quot; data-section-id=&quot;1qu1ef2&quot;&gt;그 위치에 Fog 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;314&quot; data-start=&quot;311&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;328&quot; data-start=&quot;316&quot; data-section-id=&quot;1syj2gj&quot; data-ke-size=&quot;size23&quot;&gt;3. 성능 개선&lt;/h3&gt;
&lt;p data-end=&quot;332&quot; data-start=&quot;330&quot; data-ke-size=&quot;size16&quot;&gt;기존&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;367&quot; data-start=&quot;333&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;347&quot; data-start=&quot;333&quot; data-section-id=&quot;1lvr9jw&quot;&gt;타일마다 Draw 호출&lt;/li&gt;
&lt;li data-end=&quot;367&quot; data-start=&quot;348&quot; data-section-id=&quot;12ggc7g&quot;&gt;Draw Call 과다 &amp;rarr; 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;371&quot; data-start=&quot;369&quot; data-ke-size=&quot;size16&quot;&gt;개선&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;400&quot; data-start=&quot;372&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;383&quot; data-start=&quot;372&quot; data-section-id=&quot;1d0mn39&quot;&gt;Canvas 사용&lt;/li&gt;
&lt;li data-end=&quot;400&quot; data-start=&quot;384&quot; data-section-id=&quot;138oq28&quot;&gt;한 프레임에 한 번만 그림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;426&quot; data-start=&quot;402&quot; data-ke-size=&quot;size16&quot;&gt;결론:&lt;br /&gt;&lt;b&gt;N번 그리기 &amp;rarr; 1번으로 줄임&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;431&quot; data-start=&quot;428&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;445&quot; data-start=&quot;433&quot; data-section-id=&quot;b1doct&quot; data-ke-size=&quot;size23&quot;&gt;4. 핵심 코드&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1777377671741&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(...);

Canvas-&amp;gt;DrawTile(...); // Clear

for (Vision)
{
    for (Tiles)
    {
        Canvas-&amp;gt;K2_DrawMaterial(...);
    }
}

UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(...);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;700&quot; data-start=&quot;697&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;715&quot; data-start=&quot;702&quot; data-section-id=&quot;gg5o94&quot; data-ke-size=&quot;size23&quot;&gt;5. 핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;759&quot; data-start=&quot;717&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;737&quot; data-start=&quot;717&quot; data-section-id=&quot;ibs553&quot;&gt;RenderTarget = 화면용&lt;/li&gt;
&lt;li data-end=&quot;759&quot; data-start=&quot;738&quot; data-section-id=&quot;1eisj3t&quot;&gt;실제 시야 판단 = Grid 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;775&quot; data-start=&quot;761&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 둘을 분리해야 안 꼬임&lt;/p&gt;
&lt;hr data-end=&quot;780&quot; data-start=&quot;777&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;795&quot; data-start=&quot;782&quot; data-section-id=&quot;7bakxr&quot; data-ke-size=&quot;size23&quot;&gt;6. 한 줄 정리&lt;/h3&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;831&quot; data-start=&quot;797&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;Fog는 원이 아니라 타일 기반으로 그려야 정확하다&amp;rdquo;&lt;/b&gt;&lt;/p&gt;</description>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/60</guid>
      <comments>https://xodn246.tistory.com/60#entry60comment</comments>
      <pubDate>Tue, 28 Apr 2026 21:02:12 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 TIL 260417] Unreal RTS 영웅 스킬 구조 설계 (다형성 + 타이머 기반 판정)</title>
      <link>https://xodn246.tistory.com/59</link>
      <description>&lt;h2 data-end=&quot;199&quot; data-start=&quot;172&quot; data-section-id=&quot;4l9bfm&quot; data-ke-size=&quot;size26&quot;&gt;1. 유닛 &amp;amp; 컨트롤러 구조 (다형성 설계)&lt;/h2&gt;
&lt;p data-end=&quot;236&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;컨트롤러는 유닛의 &quot;종류&quot;를 몰라도 된다&lt;/b&gt;는 것.&lt;/p&gt;
&lt;h3 data-end=&quot;244&quot; data-start=&quot;238&quot; data-section-id=&quot;1hrk1ga&quot; data-ke-size=&quot;size23&quot;&gt;구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;295&quot; data-start=&quot;245&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;272&quot; data-start=&quot;245&quot; data-section-id=&quot;blm5ar&quot;&gt;부모 클래스: ATWHeroUnitBase&lt;/li&gt;
&lt;li data-end=&quot;295&quot; data-start=&quot;273&quot; data-section-id=&quot;1jdyr65&quot;&gt;자식 클래스: 투사체형 / 범위형 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;306&quot; data-start=&quot;297&quot; data-section-id=&quot;v0w95e&quot; data-ke-size=&quot;size23&quot;&gt;설계 방식&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776427984853&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ATWHeroUnitBase : public AActor
{
public:
    virtual void UseSkill(FVector TargetLocation);
    virtual bool IsIndicatorRequired() const;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1776427996214&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ATWHeroUnitBase* Hero = Cast&amp;lt;ATWHeroUnitBase&amp;gt;(SelectedUnit);
Hero-&amp;gt;UseSkill(ClickLocation);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;593&quot; data-start=&quot;586&quot; data-section-id=&quot;1xkb0ru&quot; data-ke-size=&quot;size23&quot;&gt;포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;660&quot; data-start=&quot;594&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;622&quot; data-start=&quot;594&quot; data-section-id=&quot;1gpv0gx&quot;&gt;컨트롤러에 분기 없음 (if/switch 제거)&lt;/li&gt;
&lt;li data-end=&quot;647&quot; data-start=&quot;623&quot; data-section-id=&quot;7q845x&quot;&gt;새로운 영웅 추가 &amp;rarr; 컨트롤러 수정 없음&lt;/li&gt;
&lt;li data-end=&quot;660&quot; data-start=&quot;648&quot; data-section-id=&quot;7f5kso&quot;&gt;완전한 다형성 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;665&quot; data-start=&quot;662&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;695&quot; data-start=&quot;667&quot; data-section-id=&quot;1vujotj&quot; data-ke-size=&quot;size23&quot;&gt;신호등 함수 (Indicator 분기 제거)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776428009593&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (Hero-&amp;gt;IsIndicatorRequired())
{
    ShowIndicator();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;812&quot; data-start=&quot;767&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;792&quot; data-start=&quot;767&quot; data-section-id=&quot;r7eqlb&quot;&gt;유닛이 &amp;ldquo;내가 타게팅 필요한지&amp;rdquo; 직접 판단&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;793&quot; data-section-id=&quot;1ralkle&quot;&gt;컨트롤러 책임 &amp;darr;&lt;/li&gt;
&lt;li data-end=&quot;812&quot; data-start=&quot;805&quot; data-section-id=&quot;32es3c&quot;&gt;확장성 &amp;uarr;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;817&quot; data-start=&quot;814&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;839&quot; data-start=&quot;819&quot; data-section-id=&quot;1pv4sct&quot; data-ke-size=&quot;size26&quot;&gt;2. 스킬 로직 vs 연출 분리&lt;/h2&gt;
&lt;h3 data-end=&quot;847&quot; data-start=&quot;841&quot; data-section-id=&quot;1hrhjdu&quot; data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;877&quot; data-start=&quot;848&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;877&quot; data-start=&quot;848&quot; data-section-id=&quot;1i0g4ta&quot;&gt;매번 액터 Spawn &amp;rarr; 비용 큼 / 관리 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;885&quot; data-start=&quot;879&quot; data-section-id=&quot;1hrnhqa&quot; data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;917&quot; data-start=&quot;886&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;895&quot; data-start=&quot;886&quot; data-section-id=&quot;1x1ewcj&quot;&gt;로직: 타이머&lt;/li&gt;
&lt;li data-end=&quot;917&quot; data-start=&quot;896&quot; data-section-id=&quot;1tp7c7q&quot;&gt;연출: Debug Draw (임시)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;922&quot; data-start=&quot;919&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;936&quot; data-start=&quot;924&quot; data-section-id=&quot;15zeq2h&quot; data-ke-size=&quot;size26&quot;&gt;데이터 기반 설계&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776428020425&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;USTRUCT()
struct FHeroData
{
    TSubclassOf&amp;lt;AActor&amp;gt; SkillAreaClass;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1062&quot; data-start=&quot;1022&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1045&quot; data-start=&quot;1022&quot; data-section-id=&quot;igb7nf&quot;&gt;스킬 정보는 DataTable에서 관리&lt;/li&gt;
&lt;li data-end=&quot;1062&quot; data-start=&quot;1046&quot; data-section-id=&quot;nu6i16&quot;&gt;코드 수정 없이 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1067&quot; data-start=&quot;1064&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1090&quot; data-start=&quot;1069&quot; data-section-id=&quot;zmgs4x&quot; data-ke-size=&quot;size26&quot;&gt;3. 시간차 스킬 구조 (타이머)&lt;/h2&gt;
&lt;h3 data-end=&quot;1098&quot; data-start=&quot;1092&quot; data-section-id=&quot;1hrk1ga&quot; data-ke-size=&quot;size23&quot;&gt;구조&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1132&quot; data-start=&quot;1099&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1109&quot; data-start=&quot;1099&quot; data-section-id=&quot;l4byym&quot;&gt;예고 (충전)&lt;/li&gt;
&lt;li data-end=&quot;1120&quot; data-start=&quot;1110&quot; data-section-id=&quot;1rp42oc&quot;&gt;폭발 (판정)&lt;/li&gt;
&lt;li data-end=&quot;1132&quot; data-start=&quot;1121&quot; data-section-id=&quot;1pamjuc&quot;&gt;여운 (이펙트)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;1140&quot; data-start=&quot;1134&quot; data-section-id=&quot;1hrs3bi&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776428049774&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 예고
DrawDebugCircle(GetWorld(), Loc, Radius, 50, FColor::Green, false, 3.0f);

// 2. 폭발 예약
GetWorldTimerManager().SetTimer(TimerHandle, [this, Loc]()
{
    // 실제 판정 위치 유지됨 (람다 캡처)
    
    // TODO: 데미지 처리
    DrawDebugCircle(GetWorld(), Loc, Radius, 50, FColor::Red, false, 1.0f);

}, 3.0f, false);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1467&quot; data-start=&quot;1457&quot; data-section-id=&quot;vxvelv&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1544&quot; data-start=&quot;1468&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1500&quot; data-start=&quot;1468&quot; data-section-id=&quot;al20f&quot;&gt;[this, Loc] 캡처 &amp;rarr; 클릭 당시 위치 유지&lt;/li&gt;
&lt;li data-end=&quot;1521&quot; data-start=&quot;1501&quot; data-section-id=&quot;fcf6qm&quot;&gt;액터 없이도 스킬 흐름 구현 가능&lt;/li&gt;
&lt;li data-end=&quot;1544&quot; data-start=&quot;1522&quot; data-section-id=&quot;1nydjwi&quot;&gt;상태 머신 없이 간단하게 시퀀스 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1549&quot; data-start=&quot;1546&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1575&quot; data-start=&quot;1551&quot; data-section-id=&quot;11vloqt&quot; data-ke-size=&quot;size26&quot;&gt;4. DrawDebugCircle 핵심&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776428058496&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DrawDebugCircle(
    GetWorld(),
    Loc,
    Radius,
    50,
    FColor::Green,
    false,
    3.0f,
    0,
    2.0f,
    FVector(0, 0, 1) // 중요
);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1749&quot; data-start=&quot;1738&quot; data-section-id=&quot;1wdlxy3&quot; data-ke-size=&quot;size23&quot;&gt;중요 파라미터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1853&quot; data-start=&quot;1750&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1781&quot; data-start=&quot;1750&quot; data-section-id=&quot;4vz0k2&quot;&gt;Normal: (0,0,1) &amp;rarr; 바닥 기준 원&lt;/li&gt;
&lt;li data-end=&quot;1810&quot; data-start=&quot;1782&quot; data-section-id=&quot;f3ihwq&quot;&gt;Segments: 32~50 &amp;rarr; 원 부드러움&lt;/li&gt;
&lt;li data-end=&quot;1853&quot; data-start=&quot;1811&quot; data-section-id=&quot;61pa1e&quot;&gt;LifeTime:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1853&quot; data-start=&quot;1827&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1838&quot; data-start=&quot;1827&quot; data-section-id=&quot;1cdk7z3&quot;&gt;-1 &amp;rarr; 1프레임&lt;/li&gt;
&lt;li data-end=&quot;1853&quot; data-start=&quot;1841&quot; data-section-id=&quot;15nepkc&quot;&gt;0 &amp;rarr; 유지 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1864&quot; data-start=&quot;1855&quot; data-section-id=&quot;s6g217&quot; data-ke-size=&quot;size23&quot;&gt;트러블슈팅&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1899&quot; data-start=&quot;1865&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1899&quot; data-start=&quot;1865&quot; data-section-id=&quot;nfkpp7&quot;&gt;원이 선처럼 보임 &amp;rarr; &lt;b&gt;Normal 값 문제 100%&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1904&quot; data-start=&quot;1901&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1921&quot; data-start=&quot;1906&quot; data-section-id=&quot;1cosqtj&quot; data-ke-size=&quot;size26&quot;&gt;5. 현재 구조의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2013&quot; data-start=&quot;1923&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1944&quot; data-start=&quot;1923&quot; data-section-id=&quot;gm1vne&quot;&gt;컨트롤러 수정 없이 영웅 추가 가능&lt;/li&gt;
&lt;li data-end=&quot;1962&quot; data-start=&quot;1945&quot; data-section-id=&quot;7ntw4g&quot;&gt;스킬 흐름을 타이머로 단순화&lt;/li&gt;
&lt;li data-end=&quot;1981&quot; data-start=&quot;1963&quot; data-section-id=&quot;okpvao&quot;&gt;초기 단계에서 빠른 검증 가능&lt;/li&gt;
&lt;li data-end=&quot;2013&quot; data-start=&quot;1982&quot; data-section-id=&quot;1nks2ke&quot;&gt;불필요한 Spawn 제거 &amp;rarr; 성능 + 개발 속도 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2018&quot; data-start=&quot;2015&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2039&quot; data-start=&quot;2020&quot; data-section-id=&quot;1ej4yp1&quot; data-ke-size=&quot;size26&quot;&gt;6. 다음 단계 (실전 로직)&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776428071000&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;TArray&amp;lt;AActor*&amp;gt; HitActors;

UKismetSystemLibrary::SphereOverlapActors(
    GetWorld(),
    Loc,
    Radius,
    ObjectTypes,
    AMonster::StaticClass(),
    IgnoreActors,
    HitActors
);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2278&quot; data-start=&quot;2242&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2253&quot; data-start=&quot;2242&quot; data-section-id=&quot;1yga3v5&quot;&gt;태그 기반 필터링&lt;/li&gt;
&lt;li data-end=&quot;2265&quot; data-start=&quot;2254&quot; data-section-id=&quot;90mwpc&quot;&gt;실제 데미지 적용&lt;/li&gt;
&lt;li data-end=&quot;2278&quot; data-start=&quot;2266&quot; data-section-id=&quot;14k1jyh&quot;&gt;팀 판정 추가 필요&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/59</guid>
      <comments>https://xodn246.tistory.com/59#entry59comment</comments>
      <pubDate>Fri, 17 Apr 2026 21:17:32 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 TIL 260415]  Unreal  미니맵 카메라 프러스텀 투영 및 클릭 이동 구현</title>
      <link>https://xodn246.tistory.com/58</link>
      <description>&lt;h2 data-end=&quot;113&quot; data-start=&quot;105&quot; data-section-id=&quot;zhgto2&quot; data-ke-size=&quot;size26&quot;&gt;1. 개요&lt;/h2&gt;
&lt;p data-end=&quot;220&quot; data-start=&quot;115&quot; data-ke-size=&quot;size16&quot;&gt;Tiny Dominion 프로젝트에서&lt;br /&gt;현재 카메라가 보고 있는 영역(Frustum)을 미니맵에 실시간으로 표시하고,&lt;br /&gt;미니맵 클릭 시 해당 위치로 카메라를 이동시키는 기능을 구현했다.&lt;/p&gt;
&lt;p data-end=&quot;235&quot; data-start=&quot;222&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 다음 두 가지다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;286&quot; data-start=&quot;237&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;261&quot; data-start=&quot;237&quot; data-section-id=&quot;1ndzzze&quot;&gt;화면 &amp;rarr; 월드 투영 (Deproject)&lt;/li&gt;
&lt;li data-end=&quot;286&quot; data-start=&quot;262&quot; data-section-id=&quot;4re4zs&quot;&gt;월드 &amp;rarr; 미니맵 좌표 변환 (UV 매핑)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;291&quot; data-start=&quot;288&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;311&quot; data-start=&quot;293&quot; data-section-id=&quot;1nv7q0g&quot; data-ke-size=&quot;size26&quot;&gt;2. 카메라 프러스텀 렌더링&lt;/h2&gt;
&lt;h3 data-end=&quot;324&quot; data-start=&quot;313&quot; data-section-id=&quot;1qb7f3j&quot; data-ke-size=&quot;size23&quot;&gt;핵심 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;408&quot; data-start=&quot;326&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;346&quot; data-start=&quot;326&quot; data-section-id=&quot;g4q4pc&quot;&gt;화면의 4개 모서리를 월드로 변환&lt;/li&gt;
&lt;li data-end=&quot;379&quot; data-start=&quot;347&quot; data-section-id=&quot;i1dx39&quot;&gt;해당 Ray를 지면(Z=0)과 교차시켜 실제 위치 계산&lt;/li&gt;
&lt;li data-end=&quot;408&quot; data-start=&quot;380&quot; data-section-id=&quot;c3t7u&quot;&gt;월드 좌표를 미니맵 UV로 변환 후 선으로 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;413&quot; data-start=&quot;410&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;424&quot; data-start=&quot;415&quot; data-section-id=&quot;1kulbk6&quot; data-ke-size=&quot;size23&quot;&gt;구현 코드&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776254947714&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ATWMiniMap::DrawCameraFrustum(UCanvas* Canvas, int32 Width, int32 Height)
{
    if (!Canvas || !GetWorld()) return;

    APlayerController* PC = GetWorld()-&amp;gt;GetFirstPlayerController();
    if (!PC) return;

    FVector2D ScreenCorners[4] = {
        FVector2D(0, 0),
        FVector2D(Width, 0),
        FVector2D(Width, Height),
        FVector2D(0, Height)
    };

    TArray&amp;lt;FVector2D&amp;gt; MinimapPoints;

    for (int32 i = 0; i &amp;lt; 4; ++i)
    {
        FVector WorldLoc, WorldDir;

        if (PC-&amp;gt;DeprojectScreenPositionToWorld(
            ScreenCorners[i].X,
            ScreenCorners[i].Y,
            WorldLoc,
            WorldDir))
        {
            if (FMath::IsNearlyZero(WorldDir.Z)) continue;

            float t = -WorldLoc.Z / WorldDir.Z;
            FVector GroundPos = WorldLoc + (WorldDir * t);

            FVector RelativePos = GroundPos - GetActorLocation();

            float U = (RelativePos.Y / CaptureWidth) + 0.5f;
            float V = (RelativePos.X / CaptureWidth) + 0.5f;

            MinimapPoints.Add(FVector2D(U * Width, V * Height));
        }
    }

    if (MinimapPoints.Num() == 4)
    {
        for (int32 i = 0; i &amp;lt; 4; ++i)
        {
            Canvas-&amp;gt;K2_DrawLine(
                MinimapPoints[i],
                MinimapPoints[(i + 1) % 4],
                2.0f,
                FLinearColor::White
            );
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1825&quot; data-start=&quot;1822&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1837&quot; data-start=&quot;1827&quot; data-section-id=&quot;vxvelv&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1954&quot; data-start=&quot;1839&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1885&quot; data-start=&quot;1839&quot; data-section-id=&quot;1it1nus&quot;&gt;DeprojectScreenPositionToWorld 사용 (함수명 중요)&lt;/li&gt;
&lt;li data-end=&quot;1908&quot; data-start=&quot;1886&quot; data-section-id=&quot;1qdkzsb&quot;&gt;Ray와 평면 교차로 실제 위치 계산&lt;/li&gt;
&lt;li data-end=&quot;1934&quot; data-start=&quot;1909&quot; data-section-id=&quot;1klphzw&quot;&gt;WorldDir.Z == 0 방어 필요&lt;/li&gt;
&lt;li data-end=&quot;1954&quot; data-start=&quot;1935&quot; data-section-id=&quot;1pqc68t&quot;&gt;미니맵 기준 좌표계로 변환 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1959&quot; data-start=&quot;1956&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1984&quot; data-start=&quot;1961&quot; data-section-id=&quot;40wxo6&quot; data-ke-size=&quot;size26&quot;&gt;3. 미니맵 클릭 &amp;rarr; 월드 좌표 변환&lt;/h2&gt;
&lt;h3 data-end=&quot;1997&quot; data-start=&quot;1986&quot; data-section-id=&quot;1qb7f3j&quot; data-ke-size=&quot;size23&quot;&gt;핵심 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2052&quot; data-start=&quot;1999&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2016&quot; data-start=&quot;1999&quot; data-section-id=&quot;eqszqj&quot;&gt;UI 좌표 &amp;rarr; UV(0~1)&lt;/li&gt;
&lt;li data-end=&quot;2032&quot; data-start=&quot;2017&quot; data-section-id=&quot;1gm5jvk&quot;&gt;UV &amp;rarr; 월드 상대 좌표&lt;/li&gt;
&lt;li data-end=&quot;2052&quot; data-start=&quot;2033&quot; data-section-id=&quot;7yuk59&quot;&gt;회전된 미니맵 기준으로 축 보정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2057&quot; data-start=&quot;2054&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2068&quot; data-start=&quot;2059&quot; data-section-id=&quot;1kulbk6&quot; data-ke-size=&quot;size23&quot;&gt;구현 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1776254962166&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FVector ATWMiniMap::GetWorldLocationFromTouch(
    FVector2D TouchPos,
    FVector2D WidgetSize)
{
    float U = TouchPos.X / WidgetSize.X;
    float V = TouchPos.Y / WidgetSize.Y;

    float RelativeX = (U - 0.5f) * CaptureWidth;
    float RelativeY = (V - 0.5f) * CaptureWidth;

    // -90도 회전 보정
    float WorldX = -RelativeY;
    float WorldY = RelativeX;

    FVector MapLocation = GetActorLocation();

    return FVector(
        MapLocation.X + WorldX,
        MapLocation.Y + WorldY,
        0.0f
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2599&quot; data-start=&quot;2596&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2611&quot; data-start=&quot;2601&quot; data-section-id=&quot;vxvelv&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2689&quot; data-start=&quot;2613&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2639&quot; data-start=&quot;2613&quot; data-section-id=&quot;xtobk7&quot;&gt;UV 기준은 항상 (0.5, 0.5)가 중심&lt;/li&gt;
&lt;li data-end=&quot;2666&quot; data-start=&quot;2640&quot; data-section-id=&quot;1pqkp07&quot;&gt;CaptureWidth = 월드 범위 스케일&lt;/li&gt;
&lt;li data-end=&quot;2689&quot; data-start=&quot;2667&quot; data-section-id=&quot;oevcw5&quot;&gt;미니맵 회전 시 반드시 축 보정 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2694&quot; data-start=&quot;2691&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2709&quot; data-start=&quot;2696&quot; data-section-id=&quot;1mep01b&quot; data-ke-size=&quot;size26&quot;&gt;4. 머테리얼 처리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uW6Ug/dJMcabcLk2s/mW5nhfv9jLRbT7EwOf0Xc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uW6Ug/dJMcabcLk2s/mW5nhfv9jLRbT7EwOf0Xc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uW6Ug/dJMcabcLk2s/mW5nhfv9jLRbT7EwOf0Xc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuW6Ug%2FdJMcabcLk2s%2FmW5nhfv9jLRbT7EwOf0Xc0%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;910&quot; height=&quot;441&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-end=&quot;2717&quot; data-start=&quot;2711&quot; data-section-id=&quot;1hrk2sb&quot; data-ke-size=&quot;size23&quot;&gt;구성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2772&quot; data-start=&quot;2719&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2750&quot; data-start=&quot;2719&quot; data-section-id=&quot;1ir85pk&quot;&gt;지형 텍스처 + 프러스텀 RenderTarget 합성&lt;/li&gt;
&lt;li data-end=&quot;2772&quot; data-start=&quot;2751&quot; data-section-id=&quot;eb9pjf&quot;&gt;Add 연산으로 외곽선만 덧씌움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2784&quot; data-start=&quot;2774&quot; data-section-id=&quot;vxvelv&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2879&quot; data-start=&quot;2786&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2824&quot; data-start=&quot;2786&quot; data-section-id=&quot;d3qtw6&quot;&gt;CustomRotator(-90&amp;deg;)를 &lt;b&gt;둘 다 동일하게 적용&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2855&quot; data-start=&quot;2825&quot; data-section-id=&quot;zug78m&quot;&gt;Multiply 사용 시 배경이 어두워짐 &amp;rarr; 부적합&lt;/li&gt;
&lt;li data-end=&quot;2879&quot; data-start=&quot;2856&quot; data-section-id=&quot;1mvwnkp&quot;&gt;Add 사용 시 선만 자연스럽게 합성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2884&quot; data-start=&quot;2881&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2897&quot; data-start=&quot;2886&quot; data-section-id=&quot;17030pt&quot; data-ke-size=&quot;size26&quot;&gt;5. 위젯 처리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnp6W0/dJMcadBAWMW/spFAx80IyUzRzLxQOjxrjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnp6W0/dJMcadBAWMW/spFAx80IyUzRzLxQOjxrjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnp6W0/dJMcadBAWMW/spFAx80IyUzRzLxQOjxrjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnp6W0%2FdJMcadBAWMW%2FspFAx80IyUzRzLxQOjxrjk%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;1306&quot; height=&quot;369&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-end=&quot;2905&quot; data-start=&quot;2899&quot; data-section-id=&quot;1hrny1e&quot; data-ke-size=&quot;size23&quot;&gt;흐름&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2976&quot; data-start=&quot;2907&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2927&quot; data-start=&quot;2907&quot; data-section-id=&quot;3wls5a&quot;&gt;OnMouseButtonDown&lt;/li&gt;
&lt;li data-end=&quot;2953&quot; data-start=&quot;2928&quot; data-section-id=&quot;14ycmih&quot;&gt;Absolute &amp;rarr; Local 좌표 변환&lt;/li&gt;
&lt;li data-end=&quot;2966&quot; data-start=&quot;2954&quot; data-section-id=&quot;70yay7&quot;&gt;C++ 함수 호출&lt;/li&gt;
&lt;li data-end=&quot;2976&quot; data-start=&quot;2967&quot; data-section-id=&quot;n361ou&quot;&gt;카메라 이동&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;2981&quot; data-start=&quot;2978&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2991&quot; data-start=&quot;2983&quot; data-section-id=&quot;qc9ijn&quot; data-ke-size=&quot;size23&quot;&gt;주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3044&quot; data-start=&quot;2993&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3015&quot; data-start=&quot;2993&quot; data-section-id=&quot;1vgb13k&quot;&gt;Screen 좌표 그대로 쓰면 틀어짐&lt;/li&gt;
&lt;li data-end=&quot;3044&quot; data-start=&quot;3016&quot; data-section-id=&quot;12kbtmd&quot;&gt;반드시 Widget Local 좌표로 변환 필요&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/58</guid>
      <comments>https://xodn246.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 15 Apr 2026 21:12:27 +0900</pubDate>
    </item>
    <item>
      <title>TIL - Unreal Engine 미니맵 구현</title>
      <link>https://xodn246.tistory.com/57</link>
      <description>&lt;h3 data-end=&quot;120&quot; data-start=&quot;108&quot; data-section-id=&quot;1qnith8&quot; data-ke-size=&quot;size23&quot;&gt;1. 핵심 구조&lt;/h3&gt;
&lt;p data-end=&quot;149&quot; data-start=&quot;122&quot; data-ke-size=&quot;size16&quot;&gt;미니맵과 안개는 &lt;b&gt;완전히 분리된 시스템&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;216&quot; data-start=&quot;151&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;183&quot; data-start=&quot;151&quot; data-section-id=&quot;1xt4z3u&quot;&gt;미니맵 &amp;rarr; SceneCapture로 지형을 RT에 그림&lt;/li&gt;
&lt;li data-end=&quot;216&quot; data-start=&quot;184&quot; data-section-id=&quot;14uu4d2&quot;&gt;안개 &amp;rarr; 별도의 Render Target 2장으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;270&quot; data-start=&quot;222&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;270&quot; data-start=&quot;224&quot; data-ke-size=&quot;size16&quot;&gt;&quot;지형 / 현재 시야 / 탐색 기록&quot;을 각각 따로 만들고 마지막에 머티리얼에서 합성&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;275&quot; data-start=&quot;272&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;307&quot; data-start=&quot;277&quot; data-section-id=&quot;2q5e7f&quot; data-ke-size=&quot;size23&quot;&gt;2. 미니맵 캡처 (SceneCapture2D)&lt;/h3&gt;
&lt;h4 data-end=&quot;320&quot; data-start=&quot;309&quot; data-ke-size=&quot;size20&quot;&gt;핵심 포인트&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;389&quot; data-start=&quot;321&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;342&quot; data-start=&quot;321&quot; data-section-id=&quot;ir0bmj&quot;&gt;매 프레임 X &amp;rarr; 타이머 기반 갱신&lt;/li&gt;
&lt;li data-end=&quot;366&quot; data-start=&quot;343&quot; data-section-id=&quot;4phnit&quot;&gt;OrthoWidth로 월드 크기 동기화&lt;/li&gt;
&lt;li data-end=&quot;389&quot; data-start=&quot;367&quot; data-section-id=&quot;uj1f2v&quot;&gt;Render Target에 직접 그림&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1776082814221&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ATWMiniMap::BeginPlay()
{
    Super::BeginPlay();

    if (CaptureComp &amp;amp;&amp;amp; MinimapRT)
    {
        CaptureComp-&amp;gt;TextureTarget = MinimapRT;
        CaptureComp-&amp;gt;OrthoWidth = CaptureWidth;

        CaptureComp-&amp;gt;bCaptureEveryFrame = false;
        CaptureComp-&amp;gt;bCaptureOnMovement = false;

        GetWorldTimerManager().SetTimer(
            CaptureTimerHandle,
            this,
            &amp;amp;ATWMiniMap::UpdateCapture,
            0.1f,
            true
        );
    }
}

void ATWMiniMap::UpdateCapture()
{
    CaptureComp-&amp;gt;CaptureScene();
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;957&quot; data-start=&quot;951&quot; data-ke-size=&quot;size16&quot;&gt;핵심 요약:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1024&quot; data-start=&quot;958&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;996&quot; data-start=&quot;958&quot; data-section-id=&quot;1nr8r71&quot;&gt;SceneCapture는 &quot;카메라&quot;가 아니라 &quot;RT에 찍는 도구&quot;&lt;/li&gt;
&lt;li data-end=&quot;1024&quot; data-start=&quot;997&quot; data-section-id=&quot;1m89bqe&quot;&gt;빈번한 업데이트는 성능 낭비 &amp;rarr; 타이머로 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1029&quot; data-start=&quot;1026&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1054&quot; data-start=&quot;1031&quot; data-section-id=&quot;t2a9hj&quot; data-ke-size=&quot;size23&quot;&gt;3. 2중 Fog of War 구조&lt;/h3&gt;
&lt;h4 data-end=&quot;1073&quot; data-start=&quot;1056&quot; data-ke-size=&quot;size20&quot;&gt;분리된 RT 2개 사용&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1185&quot; data-start=&quot;1075&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1123&quot; data-start=&quot;1075&quot; data-section-id=&quot;1wcaa37&quot;&gt;CurrentRT&lt;br /&gt;&amp;rarr; &quot;지금 보이는 영역&quot;&lt;br /&gt;&amp;rarr; 매 프레임 덮어씀&lt;/li&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1125&quot; data-section-id=&quot;m7sluq&quot;&gt;ExploredRT&lt;br /&gt;&amp;rarr; &quot;지나간 기록&quot;&lt;br /&gt;&amp;rarr; CurrentRT를 누적(Additive)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;1190&quot; data-start=&quot;1187&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;1202&quot; data-start=&quot;1192&quot; data-ke-size=&quot;size20&quot;&gt;동작 흐름&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1776082874321&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;플레이어 이동
	&amp;darr;
CurrentRT에 원 그리기
	&amp;darr;
ExploredRT에 누적&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1306&quot; data-start=&quot;1303&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1333&quot; data-start=&quot;1308&quot; data-section-id=&quot;855d0x&quot; data-ke-size=&quot;size23&quot;&gt;4. 머티리얼 합성 로직 (UI 전용)&lt;/h3&gt;
&lt;h4 data-end=&quot;1346&quot; data-start=&quot;1335&quot; data-ke-size=&quot;size20&quot;&gt;머테리얼 노드&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRatZR/dJMcacirEsB/tZZXyhXkADHgJDGetqfBx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRatZR/dJMcacirEsB/tZZXyhXkADHgJDGetqfBx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRatZR/dJMcacirEsB/tZZXyhXkADHgJDGetqfBx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRatZR%2FdJMcacirEsB%2FtZZXyhXkADHgJDGetqfBx1%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;1210&quot; height=&quot;444&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-end=&quot;1423&quot; data-start=&quot;1416&quot; data-ke-size=&quot;size20&quot;&gt;의미&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1513&quot; data-start=&quot;1425&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1453&quot; data-start=&quot;1425&quot; data-section-id=&quot;172k1m2&quot;&gt;Explored = 0 &amp;rarr; 아예 안보임 (검정)&lt;/li&gt;
&lt;li data-end=&quot;1491&quot; data-start=&quot;1454&quot; data-section-id=&quot;7ttd4x&quot;&gt;Explored = 1 + Current = 0 &amp;rarr; 어두운 지형&lt;/li&gt;
&lt;li data-end=&quot;1513&quot; data-start=&quot;1492&quot; data-section-id=&quot;v7rw26&quot;&gt;Current = 1 &amp;rarr; 완전 밝음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1524&quot; data-start=&quot;1515&quot; data-ke-size=&quot;size20&quot;&gt;값 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1553&quot; data-start=&quot;1526&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1538&quot; data-start=&quot;1526&quot; data-section-id=&quot;3xvkp2&quot;&gt;Dark = 0.1&lt;/li&gt;
&lt;li data-end=&quot;1553&quot; data-start=&quot;1539&quot; data-section-id=&quot;1lnt5yw&quot;&gt;Bright = 1.0&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1558&quot; data-start=&quot;1555&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1580&quot; data-start=&quot;1560&quot; data-section-id=&quot;17gctth&quot; data-ke-size=&quot;size23&quot;&gt;5. 방향 문제 해결 (중요)&lt;/h3&gt;
&lt;h4 data-end=&quot;1589&quot; data-start=&quot;1582&quot; data-ke-size=&quot;size20&quot;&gt;문제&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1616&quot; data-start=&quot;1590&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1602&quot; data-start=&quot;1590&quot; data-section-id=&quot;1vc2ncl&quot;&gt;월드: X+가 북쪽&lt;/li&gt;
&lt;li data-end=&quot;1616&quot; data-start=&quot;1603&quot; data-section-id=&quot;1y26z5r&quot;&gt;UI: 오른쪽이 동쪽&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1634&quot; data-start=&quot;1618&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그대로 쓰면 90도 틀어짐&lt;/p&gt;
&lt;h4 data-end=&quot;1643&quot; data-start=&quot;1636&quot; data-ke-size=&quot;size20&quot;&gt;해결&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1686&quot; data-start=&quot;1644&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1686&quot; data-start=&quot;1663&quot; data-section-id=&quot;1sf5rll&quot;&gt;머테리얼이 들어갈 UI Image를 -90만큼 회전&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1719&quot; data-start=&quot;1716&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1736&quot; data-start=&quot;1721&quot; data-section-id=&quot;cvog2m&quot; data-ke-size=&quot;size23&quot;&gt;6. UI 연결 구조&lt;/h3&gt;
&lt;h4 data-end=&quot;1745&quot; data-start=&quot;1738&quot; data-ke-size=&quot;size20&quot;&gt;흐름&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1776083003683&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C++
&amp;rarr; Dynamic Material 생성
&amp;rarr; RenderTarget 연결
&amp;rarr; UMG Image에 적용&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776082997847&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AMyPlayerController::SetupMinimapUI()
{
    if (MinimapWidgetClass)
    {
        UUserWidget* Widget = CreateWidget&amp;lt;UUserWidget&amp;gt;(this, MinimapWidgetClass);
        Widget-&amp;gt;AddToViewport();

        // Dynamic Material 생성
        // BaseMap / CurrentFog / ExploredFog 연결
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2118&quot; data-start=&quot;2115&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2132&quot; data-start=&quot;2120&quot; data-section-id=&quot;e3cfo2&quot; data-ke-size=&quot;size23&quot;&gt;7. 트러블슈팅&lt;/h3&gt;
&lt;h4 data-end=&quot;2154&quot; data-start=&quot;2134&quot; data-ke-size=&quot;size20&quot;&gt;1. 안개가 빨갛게 나올 때&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2182&quot; data-start=&quot;2155&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2182&quot; data-start=&quot;2155&quot; data-section-id=&quot;18mnuz0&quot;&gt;Texture Sample &amp;rarr; R 채널만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2198&quot; data-start=&quot;2184&quot; data-ke-size=&quot;size20&quot;&gt;2. 방향 틀어짐&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2221&quot; data-start=&quot;2199&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2221&quot; data-start=&quot;2199&quot; data-section-id=&quot;11aeqbi&quot;&gt;CustomRotator = 0.25&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2236&quot; data-start=&quot;2223&quot; data-ke-size=&quot;size20&quot;&gt;3. 성능 문제&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2291&quot; data-start=&quot;2237&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2259&quot; data-start=&quot;2237&quot; data-section-id=&quot;qrjbz6&quot;&gt;SceneCapture 타이머로 제한&lt;/li&gt;
&lt;li data-end=&quot;2291&quot; data-start=&quot;2260&quot; data-section-id=&quot;kdt4ee&quot;&gt;Fog 계산은 전부 GPU(Material)에서 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2296&quot; data-start=&quot;2293&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2313&quot; data-start=&quot;2298&quot; data-section-id=&quot;11xlfh4&quot; data-ke-size=&quot;size23&quot;&gt;8. 최종 결과 상태&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;상태값결과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2415&quot; data-start=&quot;2315&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2415&quot; data-start=&quot;2352&quot;&gt;
&lt;tr data-end=&quot;2368&quot; data-start=&quot;2352&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2358&quot; data-start=&quot;2352&quot;&gt;미탐색&lt;/td&gt;
&lt;td data-end=&quot;2362&quot; data-start=&quot;2358&quot; data-col-size=&quot;sm&quot;&gt;0&lt;/td&gt;
&lt;td data-end=&quot;2368&quot; data-start=&quot;2362&quot; data-col-size=&quot;sm&quot;&gt;검정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2391&quot; data-start=&quot;2369&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2375&quot; data-start=&quot;2369&quot;&gt;탐색됨&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2381&quot; data-start=&quot;2375&quot;&gt;0.1&lt;/td&gt;
&lt;td data-end=&quot;2391&quot; data-start=&quot;2381&quot; data-col-size=&quot;sm&quot;&gt;어두운 지형&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2415&quot; data-start=&quot;2392&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2400&quot; data-start=&quot;2392&quot;&gt;현재 시야&lt;/td&gt;
&lt;td data-end=&quot;2406&quot; data-start=&quot;2400&quot; data-col-size=&quot;sm&quot;&gt;1.0&lt;/td&gt;
&lt;td data-end=&quot;2415&quot; data-start=&quot;2406&quot; data-col-size=&quot;sm&quot;&gt;밝은 지형&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/57</guid>
      <comments>https://xodn246.tistory.com/57#entry57comment</comments>
      <pubDate>Mon, 13 Apr 2026 21:27:20 +0900</pubDate>
    </item>
    <item>
      <title>[내배캠 TIL 260406] Unreal Fog of War 서버연동 정리</title>
      <link>https://xodn246.tistory.com/56</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 핵심 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fog of War에서 네트워크의 역할은 단순하다.&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;b&gt;서버는 &quot;위치 정보만 동기화&quot;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트는 &quot;시야를 직접 그린다&quot;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링을 서버에서 처리하려고 하면 비용이 폭발한다.&lt;br /&gt;따라서 시야 계산과 렌더링은 &lt;b&gt;완전히 클라이언트 책임&lt;/b&gt;으로 분리한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 데이터 흐름 구조&lt;/h2&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;유닛 위치, 상태를 authoritative하게 관리&lt;/li&gt;
&lt;li&gt;Pawn / Actor Transform 복제 (Replication)&lt;/li&gt;
&lt;/ul&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;복제된 유닛 정보 수신&lt;/li&gt;
&lt;li&gt;로컬 기준으로 시야 계산&lt;/li&gt;
&lt;li&gt;Render Target에 Fog 직접 렌더링&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;[Server] 위치 동기화 &amp;rarr; [Client] 로컬 유닛 필터링 &amp;rarr; Fog 렌더링
&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;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 로컬 소유권 기반 필터링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 클라이언트는 &lt;b&gt;모든 유닛을 받는다.&lt;/b&gt;&lt;br /&gt;따라서 반드시 &amp;ldquo;내 유닛&amp;rdquo;만 골라내는 과정이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;APawn* OwnerPawn = Cast&amp;lt;APawn&amp;gt;(VisionComp-&amp;gt;GetOwner());

if (OwnerPawn &amp;amp;&amp;amp; OwnerPawn-&amp;gt;IsLocallyControlled())
{
    // 이 클라이언트 기준 '내 유닛'만 시야 반영
}
&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;Replication 자체는 &amp;ldquo;전체 공유&amp;rdquo;&lt;/li&gt;
&lt;li&gt;시야는 &amp;ldquo;로컬 필터링&amp;rdquo;&lt;/li&gt;
&lt;li&gt;서버는 시야를 절대 계산하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 왜 IsLocallyControlled()인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티플레이에서 Pawn은 다음과 같이 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Authority (서버)&lt;/li&gt;
&lt;li&gt;AutonomousProxy (내 캐릭터)&lt;/li&gt;
&lt;li&gt;SimulatedProxy (다른 플레이어)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IsLocallyControlled()는&lt;br /&gt;&lt;b&gt;AutonomousProxy만 true&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 즉, &amp;ldquo;내가 조작 중인 Pawn&amp;rdquo;만 정확히 식별 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 네트워크 안정성 고려&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fog 시스템 자체는 클라이언트 로직이지만&lt;br /&gt;입력 데이터는 네트워크에 의존한다.&lt;/p&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;Replication 지연 &amp;rarr; 시야 반영 지연&lt;/li&gt;
&lt;li&gt;Pawn 소유권 변경 시 필터링 영향&lt;/li&gt;
&lt;li&gt;Destroy된 Actor 참조 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방어 코드&lt;/h3&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;if (!IsValid(VisionComp)) continue;

APawn* OwnerPawn = Cast&amp;lt;APawn&amp;gt;(VisionComp-&amp;gt;GetOwner());
if (!IsValid(OwnerPawn)) continue;
&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;&amp;nbsp;&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;현재 구조는 &amp;ldquo;개인전 기준&amp;rdquo;이다.&lt;br /&gt;팀 기반으로 확장하려면 Ownership 기준만 바꾸면 된다.&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;// 개인전
OwnerPawn-&amp;gt;IsLocallyControlled()

// 팀전
OwnerPawn-&amp;gt;TeamID == MyTeamID
&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;아군 유닛 시야 공유&lt;/li&gt;
&lt;li&gt;거점 시야 연동 가능&lt;/li&gt;
&lt;li&gt;RTS 전형 구조 완성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 네트워크 설계 핵심 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 시야를 모른다 (렌더링 X)&lt;/li&gt;
&lt;li&gt;클라이언트는 시야를 직접 계산한다&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;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.&amp;nbsp;설계 인사이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티플레이에서 중요한 건&lt;br /&gt;&amp;ldquo;데이터를 어디서 계산할 것인가&amp;rdquo;다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 &amp;rarr; 상태 관리&lt;/li&gt;
&lt;li&gt;클라이언트 &amp;rarr; 시각 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경계를 명확히 나누는 순간&lt;br /&gt;Fog of War는 네트워크 문제가 아니라&lt;br /&gt;단순한 로컬 렌더링 문제로 바뀐다.&lt;/p&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/56</guid>
      <comments>https://xodn246.tistory.com/56#entry56comment</comments>
      <pubDate>Mon, 6 Apr 2026 21:15:24 +0900</pubDate>
    </item>
    <item>
      <title>[ 내배캠 TIL 260403] Unreal RenderTarget 기반 Fog of War 구현</title>
      <link>https://xodn246.tistory.com/55</link>
      <description>&lt;h2 data-end=&quot;114&quot; data-start=&quot;86&quot; data-section-id=&quot;1wvk8q7&quot; data-ke-size=&quot;size26&quot;&gt;1. FogManager.cpp (핵심 로직)&lt;/h2&gt;
&lt;p data-end=&quot;169&quot; data-start=&quot;116&quot; data-ke-size=&quot;size16&quot;&gt;안개 시스템의 중심 역할&lt;br /&gt;RenderTarget 초기화 + 유닛 시야를 기반으로 안개를 제거&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1775218678092&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Kismet/KismetRenderingLibrary.h&quot;
#include &quot;Kismet/KismetMaterialLibrary.h&quot;

void AFogManager::BeginPlay()
{
    Super::BeginPlay();

    // RenderTarget 초기화 (검정 = 안개 상태)
    if (CurrentFogRT)
        UKismetRenderingLibrary::ClearRenderTarget2D(GetWorld(), CurrentFogRT, FLinearColor::Black);
    
    if (ExploredFogRT)
        UKismetRenderingLibrary::ClearRenderTarget2D(GetWorld(), ExploredFogRT, FLinearColor::Black);

    // MPC에 맵 크기 전달 (머티리얼에서 좌표 계산용)
    UKismetMaterialLibrary::SetVectorParameterValue(
        GetWorld(),
        FogMPC,
        TEXT(&quot;MapSize&quot;),
        FLinearColor(MapSize.X, MapSize.Y, 0, 0)
    );
}

void AFogManager::UpdateFog(float DeltaTime)
{
    // 등록된 모든 VisionComponent 순회
    for (UVisionComponent* VisionComp : RegisteredUnits)
    {
        FVector ActorLoc = VisionComp-&amp;gt;GetOwner()-&amp;gt;GetActorLocation();
        
        // World &amp;rarr; UV 변환 (0~1 범위)
        FVector2D UV;
        UV.X = (ActorLoc.X - MapOrigin.X) / MapSize.X;
        UV.Y = (ActorLoc.Y - MapOrigin.Y) / MapSize.Y;

        // 시야 반경 보정
        float FinalRadius = (VisionComp-&amp;gt;VisionRadius / MapSize.X) * 2.5f;

        // 머티리얼 파라미터 전달
        DrawMID-&amp;gt;SetVectorParameterValue(TEXT(&quot;VisionPos&quot;), FLinearColor(UV.X, UV.Y, 0, 0));
        DrawMID-&amp;gt;SetScalarParameterValue(TEXT(&quot;Radius&quot;), FinalRadius);

        // RenderTarget에 시야 그리기
        UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(), CurrentFogRT, DrawMID);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1639&quot; data-start=&quot;1637&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1728&quot; data-start=&quot;1641&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1672&quot; data-start=&quot;1641&quot; data-section-id=&quot;1ftq8ye&quot;&gt;RenderTarget은 UV(0~1) 기준으로 동작&lt;/li&gt;
&lt;li data-end=&quot;1691&quot; data-start=&quot;1673&quot; data-section-id=&quot;v9gbfp&quot;&gt;월드 좌표 &amp;rarr; UV 변환 필수&lt;/li&gt;
&lt;li data-end=&quot;1728&quot; data-start=&quot;1692&quot; data-section-id=&quot;zs8u1y&quot;&gt;DrawMaterialToRenderTarget으로 시야 누적&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1733&quot; data-start=&quot;1730&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1767&quot; data-start=&quot;1735&quot; data-section-id=&quot;p5xzo2&quot; data-ke-size=&quot;size26&quot;&gt;2. M_DrawVision (시야 그리기 머티리얼)&lt;/h2&gt;
&lt;p data-end=&quot;1797&quot; data-start=&quot;1769&quot; data-ke-size=&quot;size16&quot;&gt;RenderTarget에 흰색 원을 그리는 머티리얼&lt;/p&gt;
&lt;p data-end=&quot;1797&quot; data-start=&quot;1769&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1801&quot; data-start=&quot;1799&quot; data-ke-size=&quot;size16&quot;&gt;설정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1861&quot; data-start=&quot;1803&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1838&quot; data-start=&quot;1803&quot; data-section-id=&quot;rxma9&quot;&gt;Domain: Surface (또는 Post Process)&lt;/li&gt;
&lt;li data-end=&quot;1861&quot; data-start=&quot;1839&quot; data-section-id=&quot;1yuf56e&quot;&gt;Blend Mode: Additive&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1868&quot; data-start=&quot;1863&quot; data-ke-size=&quot;size16&quot;&gt;노드 구조&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SnoTQ/dJMcadVNobE/o4luidL8zqalupZpVdX4zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SnoTQ/dJMcadVNobE/o4luidL8zqalupZpVdX4zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SnoTQ/dJMcadVNobE/o4luidL8zqalupZpVdX4zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSnoTQ%2FdJMcadVNobE%2Fo4luidL8zqalupZpVdX4zk%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;945&quot; height=&quot;365&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1985&quot; data-start=&quot;1983&quot; data-ke-size=&quot;size16&quot;&gt;핵심&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2068&quot; data-start=&quot;1987&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2005&quot; data-start=&quot;1987&quot; data-section-id=&quot;153f4hr&quot;&gt;Distance 기반 원 생성&lt;/li&gt;
&lt;li data-end=&quot;2029&quot; data-start=&quot;2006&quot; data-section-id=&quot;1p2inb4&quot;&gt;Saturate로 값 안정화 (0~1)&lt;/li&gt;
&lt;li data-end=&quot;2049&quot; data-start=&quot;2030&quot; data-section-id=&quot;1jqgj63&quot;&gt;Power로 경계 부드럽게 처리&lt;/li&gt;
&lt;li data-end=&quot;2068&quot; data-start=&quot;2050&quot; data-section-id=&quot;yq67jl&quot;&gt;RGB 동일 값으로 흰색 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2073&quot; data-start=&quot;2070&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2110&quot; data-start=&quot;2075&quot; data-section-id=&quot;1j9onui&quot; data-ke-size=&quot;size26&quot;&gt;3. M_FinalFogPostProcess (최종 출력)&lt;/h2&gt;
&lt;p data-end=&quot;2142&quot; data-start=&quot;2112&quot; data-ke-size=&quot;size16&quot;&gt;게임 화면 위에 Fog를 덮는 포스트 프로세스 머티리얼&lt;/p&gt;
&lt;p data-end=&quot;2146&quot; data-start=&quot;2144&quot; data-ke-size=&quot;size16&quot;&gt;설정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2170&quot; data-start=&quot;2148&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2170&quot; data-start=&quot;2148&quot; data-section-id=&quot;rogyn5&quot;&gt;Domain: Post Process&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2177&quot; data-start=&quot;2172&quot; data-ke-size=&quot;size16&quot;&gt;노드 구조&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1607&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckXaVa/dJMcabKwuuJ/FnZQJxMeaBXUDgZsIhhHp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckXaVa/dJMcabKwuuJ/FnZQJxMeaBXUDgZsIhhHp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckXaVa/dJMcabKwuuJ/FnZQJxMeaBXUDgZsIhhHp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckXaVa%2FdJMcabKwuuJ%2FFnZQJxMeaBXUDgZsIhhHp0%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;1607&quot; height=&quot;800&quot; data-origin-width=&quot;1607&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2317&quot; data-start=&quot;2315&quot; data-ke-size=&quot;size16&quot;&gt;핵심&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2373&quot; data-start=&quot;2319&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2335&quot; data-start=&quot;2319&quot; data-section-id=&quot;137tgtq&quot;&gt;Alpha = Fog 정보&lt;/li&gt;
&lt;li data-end=&quot;2353&quot; data-start=&quot;2336&quot; data-section-id=&quot;4x24h6&quot;&gt;1 &amp;rarr; 보임 / 0 &amp;rarr; 가림&lt;/li&gt;
&lt;li data-end=&quot;2373&quot; data-start=&quot;2354&quot; data-section-id=&quot;16y9d7f&quot;&gt;R 채널만 사용해서 마스크 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2378&quot; data-start=&quot;2375&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2405&quot; data-start=&quot;2380&quot; data-section-id=&quot;1nkc4gp&quot; data-ke-size=&quot;size26&quot;&gt;4. VisionComponent.cpp&lt;/h2&gt;
&lt;p data-end=&quot;2430&quot; data-start=&quot;2407&quot; data-ke-size=&quot;size16&quot;&gt;유닛에 붙여 시야 정보를 제공하는 컴포넌트&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1775218717681&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void UVisionComponent::BeginPlay()
{
    Super::BeginPlay();

    AActor* FoundActor = UGameplayStatics::GetActorOfClass(GetWorld(), AFogManager::StaticClass());
    AFogManager* FogMgr = Cast&amp;lt;AFogManager&amp;gt;(FoundActor);
    
    if (FogMgr)
    {
        FogMgr-&amp;gt;RegisterUnit(this);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2736&quot; data-start=&quot;2734&quot; data-ke-size=&quot;size16&quot;&gt;핵심&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2798&quot; data-start=&quot;2738&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2769&quot; data-start=&quot;2738&quot; data-section-id=&quot;s80sly&quot;&gt;BeginPlay에서 FogManager에 자신 등록&lt;/li&gt;
&lt;li data-end=&quot;2798&quot; data-start=&quot;2770&quot; data-section-id=&quot;1nin1ux&quot;&gt;FogManager가 모든 시야를 중앙에서 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2803&quot; data-start=&quot;2800&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2810&quot; data-start=&quot;2805&quot; data-section-id=&quot;1melx8&quot; data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2899&quot; data-start=&quot;2812&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2837&quot; data-start=&quot;2812&quot; data-section-id=&quot;1wonfyw&quot;&gt;VisionComponent &amp;rarr; 시야 제공&lt;/li&gt;
&lt;li data-end=&quot;2872&quot; data-start=&quot;2838&quot; data-section-id=&quot;gv6jr5&quot;&gt;FogManager &amp;rarr; RenderTarget에 시야 기록&lt;/li&gt;
&lt;li data-end=&quot;2899&quot; data-start=&quot;2873&quot; data-section-id=&quot;plw3xb&quot;&gt;PostProcess &amp;rarr; 화면에 Fog 적용&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/55</guid>
      <comments>https://xodn246.tistory.com/55#entry55comment</comments>
      <pubDate>Fri, 3 Apr 2026 21:21:38 +0900</pubDate>
    </item>
    <item>
      <title>[ 내배캠 TIL 260402] Unreal Grid &amp;amp; Fog of War 시스템 정리</title>
      <link>https://xodn246.tistory.com/54</link>
      <description>&lt;h2 data-end=&quot;126&quot; data-start=&quot;111&quot; data-section-id=&quot;3o39de&quot; data-ke-size=&quot;size26&quot;&gt;1. 데이터 구조 설계&lt;/h2&gt;
&lt;p data-end=&quot;197&quot; data-start=&quot;128&quot; data-ke-size=&quot;size16&quot;&gt;RTS에서 &lt;b&gt;지형(Grid)&lt;/b&gt;과 &lt;b&gt;시야(Fog)&lt;/b&gt;는 역할이 완전히 다르기 때문에 분리해서 관리하는 것이 핵심이다.&lt;/p&gt;
&lt;h3 data-end=&quot;209&quot; data-start=&quot;199&quot; data-section-id=&quot;vxvelv&quot; data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;265&quot; data-start=&quot;210&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;227&quot; data-start=&quot;210&quot; data-section-id=&quot;1hgbzt5&quot;&gt;Grid &amp;rarr; 물리/지형 정보&lt;/li&gt;
&lt;li data-end=&quot;241&quot; data-start=&quot;228&quot; data-section-id=&quot;1ewm66t&quot;&gt;Fog &amp;rarr; 시야 정보&lt;/li&gt;
&lt;li data-end=&quot;265&quot; data-start=&quot;242&quot; data-section-id=&quot;1k0f0oy&quot;&gt;메모리 절약을 위해 uint8 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1775059359349&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// GridManager.h
USTRUCT(BlueprintType)
struct FGridCell 
{
    GENERATED_BODY()

    uint8 WalkableState; // 0: 이동불가, 1: 이동가능
    uint8 TerrainType;   // 0: 평지, 1: 숲, 2: 물 등

    FGridCell() : WalkableState(1), TerrainType(0) {}
};

// FogManager.h
USTRUCT(BlueprintType)
struct FFogCell 
{
    GENERATED_BODY()

    uint8 FogState; // 0: 미탐색, 1: 안개, 2: 현재시야

    FFogCell() : FogState(0) {}
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;679&quot; data-start=&quot;673&quot; data-section-id=&quot;1hrqjbz&quot; data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;764&quot; data-start=&quot;680&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;702&quot; data-start=&quot;680&quot; data-section-id=&quot;1n22qa8&quot;&gt;역할 분리를 통해 &lt;b&gt;확장성 확보&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;735&quot; data-start=&quot;703&quot; data-section-id=&quot;1ww9ap2&quot;&gt;uint8 사용으로 &lt;b&gt;대규모 배열 메모리 절약&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;764&quot; data-start=&quot;736&quot; data-section-id=&quot;1604loy&quot;&gt;Fog는 단순 상태값만 필요 &amp;rarr; 더 가볍게 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;769&quot; data-start=&quot;766&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;790&quot; data-start=&quot;771&quot; data-section-id=&quot;1jxh8z8&quot; data-ke-size=&quot;size26&quot;&gt;2. 좌표 변환 (핵심 로직)&lt;/h2&gt;
&lt;p data-end=&quot;838&quot; data-start=&quot;792&quot; data-ke-size=&quot;size16&quot;&gt;RTS에서 가장 중요한 부분 중 하나는&lt;br /&gt;&lt;b&gt;월드 좌표 &amp;harr; 그리드 인덱스 변환&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;843&quot; data-start=&quot;840&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;867&quot; data-start=&quot;845&quot; data-section-id=&quot;129o7v4&quot; data-ke-size=&quot;size23&quot;&gt;World &amp;rarr; Grid Index&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1775059384656&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int32 AGridManager::WorldToGridIndex(FVector WorldLocation) const 
{
    FVector ActorLoc = GetActorLocation();

    float HalfWidth = (GridCountX * CellSize) * 0.5f;
    float HalfHeight = (GridCountY * CellSize) * 0.5f;

    FVector GridStartLoc = ActorLoc - FVector(HalfWidth, HalfHeight, 0.f);
    FVector RelativePos = WorldLocation - GridStartLoc;

    int32 X = FMath::FloorToInt(RelativePos.X / CellSize);
    int32 Y = FMath::FloorToInt(RelativePos.Y / CellSize);

    if (X &amp;lt; 0 || X &amp;gt;= GridCountX || Y &amp;lt; 0 || Y &amp;gt;= GridCountY)
        return -1;

    return X + (Y * GridCountX);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1493&quot; data-start=&quot;1471&quot; data-section-id=&quot;ibty34&quot; data-ke-size=&quot;size23&quot;&gt;Grid Index &amp;rarr; World&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1775059427279&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FVector AGridManager::GridIndexToWorld(int32 Index) const 
{
    if (!GridData.IsValidIndex(Index))
        return FVector::ZeroVector;

    int32 GridX = Index % GridCountX;
    int32 GridY = Index / GridCountX;

    FVector ActorLoc = GetActorLocation();

    FVector GridStartLoc = ActorLoc - FVector(
        (GridCountX * CellSize) * 0.5f,
        (GridCountY * CellSize) * 0.5f,
        0.f
    );

    float WorldX = GridStartLoc.X + (GridX * CellSize) + (CellSize * 0.5f);
    float WorldY = GridStartLoc.Y + (GridY * CellSize) + (CellSize * 0.5f);

    return FVector(WorldX, WorldY, ActorLoc.Z);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;2120&quot; data-start=&quot;2114&quot; data-section-id=&quot;1hrqjbz&quot; data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2230&quot; data-start=&quot;2121&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2151&quot; data-start=&quot;2121&quot; data-section-id=&quot;1nrm4t2&quot;&gt;Grid 기준점 = Actor 중심 기준 좌측 하단&lt;/li&gt;
&lt;li data-end=&quot;2189&quot; data-start=&quot;2152&quot; data-section-id=&quot;ojefh3&quot;&gt;핵심 수식
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2189&quot; data-start=&quot;2164&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2189&quot; data-start=&quot;2164&quot; data-section-id=&quot;1wjf8do&quot;&gt;Index = X + (Y * Width)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2230&quot; data-start=&quot;2190&quot; data-section-id=&quot;n9dd20&quot;&gt;Cell 중심 좌표 계산 시 + CellSize * 0.5f 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2235&quot; data-start=&quot;2232&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2253&quot; data-start=&quot;2237&quot; data-section-id=&quot;6vbz21&quot; data-ke-size=&quot;size26&quot;&gt;3. 배열 초기화 최적화&lt;/h2&gt;
&lt;p data-end=&quot;2304&quot; data-start=&quot;2255&quot; data-ke-size=&quot;size16&quot;&gt;대규모 Grid/Fog 데이터를 다루기 때문에&lt;br /&gt;초기화 방식이 성능에 큰 영향을 준다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1775059439106&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// GridData
GridData.SetNumUninitialized(TotalCells);
for (int32 i = 0; i &amp;lt; TotalCells; ++i) {
    GridData[i].WalkableState = 1;
}

// FogData
FogData.SetNumZeroed(TotalCells);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;2502&quot; data-start=&quot;2496&quot; data-section-id=&quot;1hrqjbz&quot; data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;방식특징사용 이유
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2637&quot; data-start=&quot;2504&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2637&quot; data-start=&quot;2551&quot;&gt;
&lt;tr data-end=&quot;2598&quot; data-start=&quot;2551&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2573&quot; data-start=&quot;2551&quot;&gt;SetNumUninitialized&lt;/td&gt;
&lt;td data-end=&quot;2582&quot; data-start=&quot;2573&quot; data-col-size=&quot;sm&quot;&gt;초기화 생략&lt;/td&gt;
&lt;td data-end=&quot;2598&quot; data-start=&quot;2582&quot; data-col-size=&quot;sm&quot;&gt;기본값이 0이 아닐 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2637&quot; data-start=&quot;2599&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2614&quot; data-start=&quot;2599&quot;&gt;SetNumZeroed&lt;/td&gt;
&lt;td data-end=&quot;2624&quot; data-start=&quot;2614&quot; data-col-size=&quot;sm&quot;&gt;0으로 초기화&lt;/td&gt;
&lt;td data-end=&quot;2637&quot; data-start=&quot;2624&quot; data-col-size=&quot;sm&quot;&gt;기본값이 0일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2664&quot; data-start=&quot;2639&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 불필요한 초기화 비용 제거 = 성능 최적화&lt;/p&gt;
&lt;hr data-end=&quot;2669&quot; data-start=&quot;2666&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2695&quot; data-start=&quot;2671&quot; data-section-id=&quot;1v7wuew&quot; data-ke-size=&quot;size26&quot;&gt;4. Fog of War 업데이트 로직&lt;/h2&gt;
&lt;p data-end=&quot;2719&quot; data-start=&quot;2697&quot; data-ke-size=&quot;size16&quot;&gt;기본 흐름은 매우 단순한 상태 머신이다.&lt;/p&gt;
&lt;h3 data-end=&quot;2730&quot; data-start=&quot;2721&quot; data-section-id=&quot;87ta4y&quot; data-ke-size=&quot;size23&quot;&gt;처리 순서&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2777&quot; data-start=&quot;2731&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2754&quot; data-start=&quot;2731&quot; data-section-id=&quot;294lli&quot;&gt;현재 시야(2) &amp;rarr; 안개(1)로 변경&lt;/li&gt;
&lt;li data-end=&quot;2777&quot; data-start=&quot;2755&quot; data-section-id=&quot;1f7e258&quot;&gt;유닛 주변을 다시 시야(2)로 갱신&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1775059471421&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AFogManager::UpdateFog(const TArray&amp;lt;FVector&amp;gt;&amp;amp; ViewerLocations, float SightRadius) 
{
    // 1. 기존 시야를 안개로 변경
    for (auto&amp;amp; Cell : FogData) 
    {
        if (Cell.FogState == 2)
            Cell.FogState = 1;
    }

    // 2. 시야 제공자 기준 갱신
    for (const FVector&amp;amp; Loc : ViewerLocations) 
    {
        int32 CenterIndex = TargetGridManager-&amp;gt;WorldToGridIndex(Loc);

        int32 RadiusInCells = FMath::RoundToInt(
            SightRadius / TargetGridManager-&amp;gt;CellSize
        );

        // 중첩 for문 + 거리 체크
        // (x^2 + y^2 &amp;lt;= r^2)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px;&quot;&gt;핵심 개념&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3440&quot; data-start=&quot;3340&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3394&quot; data-start=&quot;3340&quot; data-section-id=&quot;i1zn4q&quot;&gt;Fog 상태 3단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3394&quot; data-start=&quot;3355&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3363&quot; data-start=&quot;3355&quot; data-section-id=&quot;djbt2b&quot;&gt;0: 미탐색&lt;/li&gt;
&lt;li data-end=&quot;3381&quot; data-start=&quot;3366&quot; data-section-id=&quot;ab14vu&quot;&gt;1: 안개 (과거 시야)&lt;/li&gt;
&lt;li data-end=&quot;3394&quot; data-start=&quot;3384&quot; data-section-id=&quot;disru0&quot;&gt;2: 현재 시야&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3440&quot; data-start=&quot;3395&quot; data-section-id=&quot;z4wpdv&quot;&gt;원형 범위 체크&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1775059513789&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(dx * dx + dy * dy &amp;lt;= r * r)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;3445&quot; data-start=&quot;3442&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3461&quot; data-start=&quot;3447&quot; data-section-id=&quot;1bt7uvf&quot; data-ke-size=&quot;size26&quot;&gt;5. 전체 구조 정리&lt;/h2&gt;
&lt;h3 data-end=&quot;3476&quot; data-start=&quot;3463&quot; data-section-id=&quot;andl7a&quot; data-ke-size=&quot;size23&quot;&gt;시스템 분리 구조&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1775059526613&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GridManager
├── 지형 데이터
├── 좌표 변환
└── Cell 정보 관리

FogManager
├── 시야 데이터
├── 유닛 기반 시야 갱신
└── GridManager 참조&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/54</guid>
      <comments>https://xodn246.tistory.com/54#entry54comment</comments>
      <pubDate>Thu, 2 Apr 2026 01:08:42 +0900</pubDate>
    </item>
    <item>
      <title>RTS 주요 점령 기믹 기획</title>
      <link>https://xodn246.tistory.com/53</link>
      <description>&lt;h1 data-end=&quot;69&quot; data-start=&quot;55&quot; data-section-id=&quot;1w4vkgy&quot;&gt;전장 시스템 기획서&lt;/h1&gt;
&lt;h2 data-end=&quot;98&quot; data-start=&quot;70&quot; data-section-id=&quot;1s1rbir&quot; data-ke-size=&quot;size26&quot;&gt;Control Line Nexus Battle&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;131&quot; data-start=&quot;105&quot; data-section-id=&quot;xplc6b&quot; data-ke-size=&quot;size26&quot;&gt;1. 전장 구조 (Map Topology)&lt;/h2&gt;
&lt;h3 data-end=&quot;146&quot; data-start=&quot;133&quot; data-section-id=&quot;he98vw&quot; data-ke-size=&quot;size23&quot;&gt;1.1 본진 배치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;221&quot; data-start=&quot;147&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;184&quot; data-start=&quot;147&quot; data-section-id=&quot;b1ydzh&quot;&gt;Player A (Blue Team): 맵 좌하단 (7시 방향)&lt;/li&gt;
&lt;li data-end=&quot;221&quot; data-start=&quot;185&quot; data-section-id=&quot;1iwu7y8&quot;&gt;Player B (Red Team): 맵 우상단 (1시 방향)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;264&quot; data-start=&quot;223&quot; data-ke-size=&quot;size16&quot;&gt;각 플레이어는 본진(Nexus)을 중심으로 유닛 생산 및 방어를 수행한다.&lt;/p&gt;
&lt;h3 data-end=&quot;307&quot; data-start=&quot;271&quot; data-section-id=&quot;y790qw&quot; data-ke-size=&quot;size23&quot;&gt;1.2 거점 배치 (Central Control Line)&lt;/h3&gt;
&lt;p data-end=&quot;372&quot; data-start=&quot;309&quot; data-ke-size=&quot;size16&quot;&gt;맵에는 총 3개의 주요 거점이 존재하며,&lt;br /&gt;좌상단(11시)에서 우하단(5시)을 잇는 대각선 축을 따라 배치된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;거점위치전략적 특징
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;556&quot; data-start=&quot;374&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;556&quot; data-start=&quot;424&quot;&gt;
&lt;tr data-end=&quot;473&quot; data-start=&quot;424&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;444&quot; data-start=&quot;424&quot;&gt;거점 1 (North-West)&lt;/td&gt;
&lt;td data-end=&quot;456&quot; data-start=&quot;444&quot; data-col-size=&quot;sm&quot;&gt;좌상단 (11시)&lt;/td&gt;
&lt;td data-end=&quot;473&quot; data-start=&quot;456&quot; data-col-size=&quot;sm&quot;&gt;Player B에게 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;507&quot; data-start=&quot;474&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;490&quot; data-start=&quot;474&quot;&gt;거점 2 (Center)&lt;/td&gt;
&lt;td data-end=&quot;495&quot; data-start=&quot;490&quot; data-col-size=&quot;sm&quot;&gt;중앙&lt;/td&gt;
&lt;td data-end=&quot;507&quot; data-start=&quot;495&quot; data-col-size=&quot;sm&quot;&gt;핵심 교전 지역&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;556&quot; data-start=&quot;508&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;528&quot; data-start=&quot;508&quot;&gt;거점 3 (South-East)&lt;/td&gt;
&lt;td data-end=&quot;539&quot; data-start=&quot;528&quot; data-col-size=&quot;sm&quot;&gt;우하단 (5시)&lt;/td&gt;
&lt;td data-end=&quot;556&quot; data-start=&quot;539&quot; data-col-size=&quot;sm&quot;&gt;Player A에게 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;561&quot; data-start=&quot;558&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;575&quot; data-start=&quot;563&quot; data-section-id=&quot;1ocstdw&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 시스템&lt;/h2&gt;
&lt;h3 data-end=&quot;610&quot; data-start=&quot;582&quot; data-section-id=&quot;p3hr5q&quot; data-ke-size=&quot;size23&quot;&gt;2.1 점령 메커니즘 (51:49 Rule)&lt;/h3&gt;
&lt;h4 data-end=&quot;622&quot; data-start=&quot;612&quot; data-ke-size=&quot;size20&quot;&gt;초기 상태&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;668&quot; data-start=&quot;623&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;647&quot; data-start=&quot;623&quot; data-section-id=&quot;mrx6zl&quot;&gt;모든 거점은 중립 상태 (50%)로 시작&lt;/li&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;648&quot; data-section-id=&quot;cgft3t&quot;&gt;각 거점에는 중립 몬스터가 배치됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;697&quot; data-start=&quot;675&quot; data-ke-size=&quot;size20&quot;&gt;점령 시작 (중립 몬스터 처치)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;761&quot; data-start=&quot;698&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;761&quot; data-start=&quot;698&quot; data-section-id=&quot;10frz4p&quot;&gt;중립 몬스터 처치 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;761&quot; data-start=&quot;715&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;738&quot; data-start=&quot;715&quot; data-section-id=&quot;2clzkc&quot;&gt;막타 팀에게 +20% 점령 보너스 부여&lt;/li&gt;
&lt;li data-end=&quot;761&quot; data-start=&quot;741&quot; data-section-id=&quot;1db5erz&quot;&gt;점령 게이지 즉시 70%에서 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;781&quot; data-start=&quot;768&quot; data-ke-size=&quot;size20&quot;&gt;점령 진행 규칙&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;상황동작
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;889&quot; data-start=&quot;783&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;889&quot; data-start=&quot;811&quot;&gt;
&lt;tr data-end=&quot;833&quot; data-start=&quot;811&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;820&quot; data-start=&quot;811&quot;&gt;아군만 존재&lt;/td&gt;
&lt;td data-end=&quot;833&quot; data-start=&quot;820&quot; data-col-size=&quot;sm&quot;&gt;점령 게이지 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;856&quot; data-start=&quot;834&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;843&quot; data-start=&quot;834&quot;&gt;적군과 충돌&lt;/td&gt;
&lt;td data-end=&quot;856&quot; data-start=&quot;843&quot; data-col-size=&quot;sm&quot;&gt;점령 게이지 정지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;889&quot; data-start=&quot;857&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;866&quot; data-start=&quot;857&quot;&gt;아무도 없음&lt;/td&gt;
&lt;td data-end=&quot;889&quot; data-start=&quot;866&quot; data-col-size=&quot;sm&quot;&gt;점령 게이지가 50%로 서서히 복귀&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-end=&quot;907&quot; data-start=&quot;896&quot; data-ke-size=&quot;size20&quot;&gt;소유권 판정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;965&quot; data-start=&quot;908&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;937&quot; data-start=&quot;908&quot; data-section-id=&quot;1ngl0ax&quot;&gt;점령 게이지가 51% 이상일 경우 해당 팀이 소유&lt;/li&gt;
&lt;li data-end=&quot;965&quot; data-start=&quot;938&quot; data-section-id=&quot;r3qt74&quot;&gt;50%를 기준으로 지속적인 줄다리기 구조 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1004&quot; data-start=&quot;972&quot; data-section-id=&quot;5mjygm&quot; data-ke-size=&quot;size23&quot;&gt;2.2 점령 보상 (Control Benefits)&lt;/h3&gt;
&lt;h4 data-end=&quot;1016&quot; data-start=&quot;1006&quot; data-ke-size=&quot;size20&quot;&gt;자원 수급&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1051&quot; data-start=&quot;1017&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1051&quot; data-start=&quot;1017&quot; data-section-id=&quot;k6pspv&quot;&gt;점령한 거점 수에 비례하여 고급 유닛 생산 자원 자동 획득&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1089&quot; data-start=&quot;1058&quot; data-ke-size=&quot;size20&quot;&gt;넥서스 방어력 시스템 (Nexus Shield)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;점령 거점 수상대 넥서스 피해 감소
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1207&quot; data-start=&quot;1091&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1207&quot; data-start=&quot;1157&quot;&gt;
&lt;tr data-end=&quot;1169&quot; data-start=&quot;1157&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1162&quot; data-start=&quot;1157&quot;&gt;0개&lt;/td&gt;
&lt;td data-end=&quot;1169&quot; data-start=&quot;1162&quot; data-col-size=&quot;sm&quot;&gt;90%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1182&quot; data-start=&quot;1170&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1175&quot; data-start=&quot;1170&quot;&gt;1개&lt;/td&gt;
&lt;td data-end=&quot;1182&quot; data-start=&quot;1175&quot; data-col-size=&quot;sm&quot;&gt;60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1195&quot; data-start=&quot;1183&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1188&quot; data-start=&quot;1183&quot;&gt;2개&lt;/td&gt;
&lt;td data-end=&quot;1195&quot; data-start=&quot;1188&quot; data-col-size=&quot;sm&quot;&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1207&quot; data-start=&quot;1196&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1201&quot; data-start=&quot;1196&quot;&gt;3개&lt;/td&gt;
&lt;td data-end=&quot;1207&quot; data-start=&quot;1201&quot; data-col-size=&quot;sm&quot;&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1234&quot; data-start=&quot;1209&quot; data-ke-size=&quot;size16&quot;&gt;거점을 점령해야만 본진 공격이 의미를 갖는다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1264&quot; data-start=&quot;1241&quot; data-section-id=&quot;me517k&quot; data-ke-size=&quot;size26&quot;&gt;3. 전략 흐름 (Game Flow)&lt;/h2&gt;
&lt;h3 data-end=&quot;1297&quot; data-start=&quot;1271&quot; data-section-id=&quot;17650sg&quot; data-ke-size=&quot;size23&quot;&gt;3.1 초반 (Opening Phase)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1394&quot; data-start=&quot;1298&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1370&quot; data-start=&quot;1298&quot; data-section-id=&quot;m7teom&quot;&gt;각 플레이어는 자신의 위치와 가까운 거점을 우선 확보
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1370&quot; data-start=&quot;1332&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1349&quot; data-start=&quot;1332&quot; data-section-id=&quot;165943t&quot;&gt;Player A: 5시 거점&lt;/li&gt;
&lt;li data-end=&quot;1370&quot; data-start=&quot;1352&quot; data-section-id=&quot;1t68t3z&quot;&gt;Player B: 11시 거점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1394&quot; data-start=&quot;1371&quot; data-section-id=&quot;o5fx3f&quot;&gt;초기 자원 확보 및 안정적인 기반 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1422&quot; data-start=&quot;1401&quot; data-section-id=&quot;1m1mqmn&quot; data-ke-size=&quot;size23&quot;&gt;3.2 중반 (Mid Game)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1446&quot; data-start=&quot;1423&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1446&quot; data-start=&quot;1423&quot; data-section-id=&quot;1bshahd&quot;&gt;중앙 거점을 둘러싼 본격적인 교전 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1454&quot; data-start=&quot;1448&quot; data-ke-size=&quot;size16&quot;&gt;주요 전략:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1506&quot; data-start=&quot;1455&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1462&quot; data-start=&quot;1455&quot; data-section-id=&quot;1lyvrb1&quot;&gt;정면 교전&lt;/li&gt;
&lt;li data-end=&quot;1481&quot; data-start=&quot;1463&quot; data-section-id=&quot;1kn6hxu&quot;&gt;기동 유닛을 활용한 측면 교란&lt;/li&gt;
&lt;li data-end=&quot;1506&quot; data-start=&quot;1482&quot; data-section-id=&quot;21kzfr&quot;&gt;적 거점을 49%까지 낮추는 견제 플레이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1538&quot; data-start=&quot;1508&quot; data-ke-size=&quot;size16&quot;&gt;핵심 개념:&lt;br /&gt;이기지 못하면, 상대의 균형을 무너뜨린다.&lt;/p&gt;
&lt;h3 data-end=&quot;1566&quot; data-start=&quot;1545&quot; data-section-id=&quot;19x8oac&quot; data-ke-size=&quot;size23&quot;&gt;3.3 후반 (End Game)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1611&quot; data-start=&quot;1567&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1594&quot; data-start=&quot;1567&quot; data-section-id=&quot;tveaje&quot;&gt;2개 이상의 거점 확보 시 넥서스 방어력 약화&lt;/li&gt;
&lt;li data-end=&quot;1611&quot; data-start=&quot;1595&quot; data-section-id=&quot;xmobo1&quot;&gt;본진 공격 가능 상태 진입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1621&quot; data-start=&quot;1613&quot; data-ke-size=&quot;size16&quot;&gt;수비 측 전략:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1650&quot; data-start=&quot;1622&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1650&quot; data-start=&quot;1622&quot; data-section-id=&quot;ynwdmd&quot;&gt;하나의 거점만 탈환해도 넥서스 방어력 즉시 복구&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1669&quot; data-start=&quot;1652&quot; data-ke-size=&quot;size16&quot;&gt;끝까지 역전 가능성을 유지한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1691&quot; data-start=&quot;1676&quot; data-section-id=&quot;e9yfr&quot; data-ke-size=&quot;size26&quot;&gt;4. 시스템 연계 요소&lt;/h2&gt;
&lt;h3 data-end=&quot;1725&quot; data-start=&quot;1698&quot; data-section-id=&quot;l9ytfv&quot; data-ke-size=&quot;size23&quot;&gt;4.1 시야 시스템 (Fog of War)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1751&quot; data-start=&quot;1727&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1751&quot; data-start=&quot;1727&quot; data-section-id=&quot;302j4w&quot;&gt;거점 점령 시 해당 지역 주변 시야 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1756&quot; data-start=&quot;1753&quot; data-ke-size=&quot;size16&quot;&gt;효과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1788&quot; data-start=&quot;1757&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1774&quot; data-start=&quot;1757&quot; data-section-id=&quot;1vvtztw&quot;&gt;점령이 정보 장악으로 이어짐&lt;/li&gt;
&lt;li data-end=&quot;1788&quot; data-start=&quot;1775&quot; data-section-id=&quot;qpbmu0&quot;&gt;전략 판단 요소 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1825&quot; data-start=&quot;1795&quot; data-section-id=&quot;116frr3&quot; data-ke-size=&quot;size23&quot;&gt;4.2 지형 설계 (Terrain Design)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1870&quot; data-start=&quot;1827&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1870&quot; data-start=&quot;1827&quot; data-section-id=&quot;sjqbsr&quot;&gt;거점 사이에 이동 제한 요소 배치
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1870&quot; data-start=&quot;1850&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1854&quot; data-start=&quot;1850&quot; data-section-id=&quot;yikq3h&quot;&gt;절벽&lt;/li&gt;
&lt;li data-end=&quot;1860&quot; data-start=&quot;1857&quot; data-section-id=&quot;374gpg&quot;&gt;물&lt;/li&gt;
&lt;li data-end=&quot;1870&quot; data-start=&quot;1863&quot; data-section-id=&quot;1ech934&quot;&gt;좁은 통로&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1875&quot; data-start=&quot;1872&quot; data-ke-size=&quot;size16&quot;&gt;목적:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1909&quot; data-start=&quot;1876&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1886&quot; data-start=&quot;1876&quot; data-section-id=&quot;yk1p9g&quot;&gt;이동 경로 제한&lt;/li&gt;
&lt;li data-end=&quot;1898&quot; data-start=&quot;1887&quot; data-section-id=&quot;racx3p&quot;&gt;전술 다양성 증가&lt;/li&gt;
&lt;li data-end=&quot;1909&quot; data-start=&quot;1899&quot; data-section-id=&quot;jgdqxw&quot;&gt;병목 지점 형성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1934&quot; data-start=&quot;1916&quot; data-section-id=&quot;1pe78kx&quot; data-ke-size=&quot;size23&quot;&gt;4.3 UI / UX 설계&lt;/h3&gt;
&lt;h4 data-end=&quot;1949&quot; data-start=&quot;1936&quot; data-ke-size=&quot;size20&quot;&gt;상단 상태 UI&lt;/h4&gt;
&lt;p data-end=&quot;1968&quot; data-start=&quot;1951&quot; data-ke-size=&quot;size16&quot;&gt;화면 상단에 거점 상태를 표시:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1774594545951&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 상단에 3개의 구형 UI
● ● ●&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&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-end=&quot;2075&quot; data-start=&quot;1989&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2075&quot; data-start=&quot;2017&quot;&gt;
&lt;tr data-end=&quot;2039&quot; data-start=&quot;2017&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2024&quot; data-start=&quot;2017&quot;&gt;Blue&lt;/td&gt;
&lt;td data-end=&quot;2039&quot; data-start=&quot;2024&quot; data-col-size=&quot;sm&quot;&gt;Player A 점령&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2061&quot; data-start=&quot;2040&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2046&quot; data-start=&quot;2040&quot;&gt;Red&lt;/td&gt;
&lt;td data-end=&quot;2061&quot; data-start=&quot;2046&quot; data-col-size=&quot;sm&quot;&gt;Player B 점령&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2075&quot; data-start=&quot;2062&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2069&quot; data-start=&quot;2062&quot;&gt;Gray&lt;/td&gt;
&lt;td data-end=&quot;2075&quot; data-start=&quot;2069&quot; data-col-size=&quot;sm&quot;&gt;중립&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2118&quot; data-start=&quot;2077&quot; data-ke-size=&quot;size16&quot;&gt;목표:&lt;br /&gt;플레이어가 짧은 시간 내 전황을 직관적으로 파악할 수 있도록 한다.&lt;/p&gt;
&lt;hr data-end=&quot;2123&quot; data-start=&quot;2120&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2140&quot; data-start=&quot;2125&quot; data-section-id=&quot;2rc2ea&quot; data-ke-size=&quot;size26&quot;&gt;5. 핵심 디자인 철학&lt;/h2&gt;
&lt;h3 data-end=&quot;2181&quot; data-start=&quot;2147&quot; data-section-id=&quot;33kzd1&quot; data-ke-size=&quot;size23&quot;&gt;선택과 집중 (Choice and Commitment)&lt;/h3&gt;
&lt;p data-end=&quot;2201&quot; data-start=&quot;2182&quot; data-ke-size=&quot;size16&quot;&gt;플레이어는 매 순간 선택해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2236&quot; data-start=&quot;2202&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2217&quot; data-start=&quot;2202&quot; data-section-id=&quot;oejjf3&quot;&gt;안정적으로 유지할 것인가&lt;/li&gt;
&lt;li data-end=&quot;2236&quot; data-start=&quot;2218&quot; data-section-id=&quot;xcxhez&quot;&gt;위험을 감수하고 확장할 것인가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2267&quot; data-start=&quot;2243&quot; data-section-id=&quot;1sqbfv0&quot; data-ke-size=&quot;size23&quot;&gt;줄다리기 구조 (Tug-of-War)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2293&quot; data-start=&quot;2268&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2293&quot; data-start=&quot;2268&quot; data-section-id=&quot;rjt31d&quot;&gt;거점은 고정되지 않고 지속적으로 변화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2310&quot; data-start=&quot;2300&quot; data-section-id=&quot;14gfspz&quot; data-ke-size=&quot;size23&quot;&gt;정보의 가치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2345&quot; data-start=&quot;2311&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2345&quot; data-start=&quot;2311&quot; data-section-id=&quot;17hitt2&quot;&gt;거점 점령은 자원뿐 아니라 시야와 전략적 우위를 제공한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/53</guid>
      <comments>https://xodn246.tistory.com/53#entry53comment</comments>
      <pubDate>Fri, 27 Mar 2026 15:57:52 +0900</pubDate>
    </item>
    <item>
      <title>[ 내배캠 TIL 260325] Unreal Engine 네트워크 복제 시스템 정리</title>
      <link>https://xodn246.tistory.com/52</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 액터 복제 설정 (bReplicates)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;&lt;br /&gt;해당 액터를 네트워크 복제 대상으로 포함할지 결정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설정 위치&lt;/b&gt;&lt;br /&gt;생성자에서 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;bReplicates = true;
&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;&lt;b&gt;핵심 포인트&lt;/b&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 변수 복제 등록 (GetLifetimeReplicatedProps)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;&lt;br /&gt;액터 내부 변수 중 어떤 것을 복제할지 선택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구현 방법&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;#include &quot;Net/UnrealNetwork.h&quot;

void AMyActor::GetLifetimeReplicatedProps(TArray&amp;lt;FLifetimeProperty&amp;gt;&amp;amp; OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(AMyActor, Health);
}
&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;&lt;b&gt;핵심 포인트&lt;/b&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;선언만 하고 구현 안 하면 링커 에러(LNK2001) 발생&lt;/li&gt;
&lt;li&gt;헤더 포함 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. OnRep 함수 (ReplicatedUsing)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;&lt;br /&gt;복제된 값이 클라이언트에 도착했을 때 실행되는 콜백 함수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설정 방법&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health;

UFUNCTION()
void OnRep_Health();
&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;&lt;b&gt;구현 예시&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void AMyActor::OnRep_Health()
{
    // 체력 변경 시 실행할 로직 (UI 갱신 등)
}
&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;&lt;b&gt;핵심 포인트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 변경될 때만 실행 (Tick 필요 없음)&lt;/li&gt;
&lt;li&gt;기본적으로 클라이언트에서만 자동 호출&lt;/li&gt;
&lt;li&gt;서버에서는 필요 시 수동 호출&lt;/li&gt;
&lt;li&gt;이전 값 비교도 가능 (OldValue 활용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;C++ OnRep vs Blueprint RepNotify&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공통 개념 (Replication Notify)&lt;/h3&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;C++ : OnRep_&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health;
&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;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 이름은 보통 OnRep_변수명 규칙 사용&lt;/li&gt;
&lt;li&gt;C++에서 직접 구현&lt;/li&gt;
&lt;li&gt;필요 시 OldValue를 받아 이전 값 비교 가능&lt;/li&gt;
&lt;li&gt;서버에서는 자동 호출되지 않음 (직접 호출 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blueprint : RepNotify&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;설정 방식&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 옵션에서 RepNotify 체크&lt;/li&gt;
&lt;li&gt;자동으로 Notify 함수 생성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&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;내부적으로는 C++의 OnRep과 동일한 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;C++ &amp;rarr; 직접 함수 구현 (유연성 높음)&lt;/li&gt;
&lt;li&gt;Blueprint &amp;rarr; 자동 이벤트 생성 (사용 편의성 높음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한줄 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &quot;OnRep과 RepNotify는 동일한 개념이며, C++은 직접 구현, 블루프린트는 자동 이벤트 형태로 제공된다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;bReplicates = true&lt;br /&gt;&amp;rarr; 액터 자체를 네트워크에 등장시킴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOREPLIFETIME&lt;br /&gt;&amp;rarr; 복제할 변수 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OnRep_ / RepNotify&lt;br /&gt;&amp;rarr; 값 변경 시 클라이언트에서 후처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bReplicates &amp;rarr; 공연장 입장권&lt;/li&gt;
&lt;li&gt;GetLifetimeReplicatedProps &amp;rarr; 반입 물품 목록&lt;/li&gt;
&lt;li&gt;ReplicatedUsing / RepNotify &amp;rarr; 알림 벨&lt;/li&gt;
&lt;li&gt;OnRep 함수 &amp;rarr; 벨 울리면 하는 행동&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>내배캠 TIL</category>
      <author>xodn246</author>
      <guid isPermaLink="true">https://xodn246.tistory.com/52</guid>
      <comments>https://xodn246.tistory.com/52#entry52comment</comments>
      <pubDate>Wed, 25 Mar 2026 19:49:54 +0900</pubDate>
    </item>
  </channel>
</rss>