<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>평생 배우는 개발자</title>
    <link>https://learn-forever.tistory.com/</link>
    <description>코딩 테스트, 개발자 면접을 위해 평생 배우는 개발자의 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 12 Aug 2026 12:21:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발지식 블로그</managingEditor>
    <image>
      <title>평생 배우는 개발자</title>
      <url>https://tistory1.daumcdn.net/tistory/7514965/attach/dc4196527f184fa8ab8e140325d7e2a6</url>
      <link>https://learn-forever.tistory.com</link>
    </image>
    <item>
      <title>4차 프로젝트 회고</title>
      <link>https://learn-forever.tistory.com/35</link>
      <description>&lt;h1 data-heading=&quot;MonsterWorld 프로젝트 회고&quot;&gt;MonsterWorld 프로젝트 회고&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원티드 4기 언리얼 팀 프로젝트&lt;br /&gt;&lt;b&gt;개발 기간:&lt;/b&gt; 2026.04.30 ~ 2026.05.30&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-heading=&quot;1. 프로젝트 개요&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-heading=&quot;1. 프로젝트 개요&quot; data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 개요&lt;/h2&gt;
&lt;h3 data-heading=&quot;게임 소개&quot; data-ke-size=&quot;size23&quot;&gt;게임 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MonsterWorld&lt;/b&gt;는 &lt;b&gt;Unreal Engine 5.6&lt;/b&gt;으로 제작한 3인칭 액션 RPG입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어는 &lt;b&gt;대검, 한손검, 활&lt;/b&gt; 중 하나의 무기를 선택해 두꺼비, 전갈, 드래곤 등의 몬스터를 처치하며 퀘스트를 수행하고, 최종 보스를 쓰러뜨리는 것을 목표로 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  전투 플레이 전체 흐름&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FightWithToadWithGS.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brTMnP/dJMcaftgnS5/GeKsGOpYykWFMpnPivXULk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brTMnP/dJMcaftgnS5/GeKsGOpYykWFMpnPivXULk/img.gif&quot; data-alt=&quot;대검&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brTMnP/dJMcaftgnS5/GeKsGOpYykWFMpnPivXULk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/brTMnP/dJMcaftgnS5/GeKsGOpYykWFMpnPivXULk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;FightWithToadWithGS.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대검&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Bow.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMfF7g/dJMcaffMGtZ/4IFWfWj3g1NUos5rYhLKm0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMfF7g/dJMcaffMGtZ/4IFWfWj3g1NUos5rYhLKm0/img.gif&quot; data-alt=&quot;활&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMfF7g/dJMcaffMGtZ/4IFWfWj3g1NUos5rYhLKm0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bMfF7g/dJMcaffMGtZ/4IFWfWj3g1NUos5rYhLKm0/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;Bow.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;활&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FightWithToadWithSS_First.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/COJmz/dJMcabRTRED/OKY5mtmprzvKH2xKhLTxsk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/COJmz/dJMcabRTRED/OKY5mtmprzvKH2xKhLTxsk/img.gif&quot; data-alt=&quot;검과 방패&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/COJmz/dJMcabRTRED/OKY5mtmprzvKH2xKhLTxsk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/COJmz/dJMcabRTRED/OKY5mtmprzvKH2xKhLTxsk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;FightWithToadWithSS_First.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;검과 방패&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-heading=&quot;개발 정보&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-heading=&quot;개발 정보&quot; data-ke-size=&quot;size23&quot;&gt;개발 정보&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;개발 기간&lt;/td&gt;
&lt;td&gt;2026년 4월 30일 ~ 2026년 5월 30일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개발 기간 요약&lt;/td&gt;
&lt;td&gt;약 4주&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참여 인원&lt;/td&gt;
&lt;td&gt;5명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트 형태&lt;/td&gt;
&lt;td&gt;원티드 4기 게임개발 언리얼 팀 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-heading=&quot;개발 환경&quot; data-ke-size=&quot;size23&quot;&gt;개발 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 사용 기술&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;엔진&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;Unreal Engine 5.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;C++&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;버전 관리&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;Perforce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;IDE&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;Rider / Visual Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;AI&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;Chat Gpt, Claude&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;정리 및 협업&lt;/td&gt;
&lt;td style=&quot;width: 76.1628%;&quot;&gt;Obsidian, Trello, Figma&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-heading=&quot;현황 및 결과&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-heading=&quot;현황 및 결과&quot; 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;두꺼비&amp;middot;전갈 몬스터 전투 루프 완성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;피격 / 패링 / 사망&lt;/li&gt;
&lt;li&gt;VFX / SFX&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;퀘스트 수주 &amp;rarr; 몬스터 처치 &amp;rarr; 보스 포탈 진입 흐름 구축&lt;/li&gt;
&lt;li&gt;팀 최종 발표 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-heading=&quot;주요 기능&quot; 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;Behavior Tree 기반 몬스터 AI
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;감지&lt;/li&gt;
&lt;li&gt;추적&lt;/li&gt;
&lt;li&gt;공격&lt;/li&gt;
&lt;li&gt;귀환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;패링 / 스턴 / 피격 / 사망 상태 처리&lt;/li&gt;
&lt;li&gt;아이템 드랍, 데미지 폰트, HP 경고 연출&lt;/li&gt;
&lt;li&gt;Dissolve 사망 이펙트 + 피격 플래시&lt;/li&gt;
&lt;li&gt;전투 BGM 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조우&lt;/li&gt;
&lt;li&gt;사망&lt;/li&gt;
&lt;li&gt;복귀&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Niagara 사전 로드를 통한 성능 최적화&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;2. 기획 목표 및 협업 방식&quot; data-ke-size=&quot;size26&quot;&gt;2. 기획 목표 및 협업 방식&lt;/h2&gt;
&lt;h3 data-heading=&quot;기획 의도 및 목표&quot; data-ke-size=&quot;size23&quot;&gt;기획 의도 및 목표&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;실제로 몬스터와 싸우는 느낌이 드는 몬스터를 만들자.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 몬스터를 배치하는 것이 아니라, 전투 흐름이 자연스럽게 읽히도록 만드는 것이 목표였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터가 플레이어를 인식하고, 추적하고, 공격하고, 맞으면 반응하고, 죽으면 녹아 사라지는 하나의 사이클이 플레이어에게 납득되는 수준이 되어야 한다고 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 목표는 &lt;b&gt;&amp;ldquo;버그 없는 전투 루프&amp;rdquo;&lt;/b&gt;였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈에 보이는 기능보다 끊기지 않는 흐름이 더 중요하다고 판단했고, 그 기준으로 작업했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;협업 방식&quot; 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;Perforce로 파일 단위 충돌 관리 ( &lt;a href=&quot;https://kaput-joggers-46b.notion.site/Perforce_ChangeList-3775423766728084986bdb38fd873132&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Perforce_ChangeList&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Obsidian으로 현재 나의 작업 부분을 기록 (&lt;a title=&quot;Obsidian 정리&quot; href=&quot;https://drive.google.com/file/d/1xFXZEFlBn_3LDbfoYzk4kDHe8fTEaxyY/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Obsidian Log&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;두꺼비&amp;middot;전갈 몬스터 AI와 전투 시스템 전담&lt;/li&gt;
&lt;li&gt;보스 드래곤, 플레이어 전투, UI, 맵은 다른 팀원이 담당&lt;/li&gt;
&lt;li&gt;ICombatInterface 등 공통 인터페이스를 팀이 함께 정의&lt;/li&gt;
&lt;li&gt;각자의 영역은 독립적으로 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;3. 아키텍처 및 워크플로우&quot; data-ke-size=&quot;size26&quot;&gt;3. 아키텍처 및 워크플로우&lt;/h2&gt;
&lt;h3 data-heading=&quot;아키텍처 특징 및 설계 의도&quot; data-ke-size=&quot;size23&quot;&gt;아키텍처 특징 및 설계 의도&lt;/h3&gt;
&lt;h4 data-heading=&quot;Behavior Tree + Blackboard&quot; data-ke-size=&quot;size20&quot;&gt;Behavior Tree + Blackboard&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 상태를 코드가 아닌 &lt;b&gt;Behavior Tree 그래프&lt;/b&gt;로 관리했습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Idle &amp;rarr; Detect &amp;rarr; Chase &amp;rarr; Attack &amp;rarr; Beaten / Death
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 흐름이 노드 단위로 분리되어 있어, 상태 추가나 수정 시 코드 전체를 건드리지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 좌측에 최우선순위 행동을 두고 우측에는 흔한 행동을 두어서 우선순위를 관리하였습니다. 예를 들어, 나머지 행동을 하다가도 Death 조건이 만족되면 현재 행동을 그만두고 바로 Death 상태로 들어가게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ToadBT.png&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2sbw3/dJMcageEtFb/A7VNFvO2aldMDdhAd44Da0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2sbw3/dJMcageEtFb/A7VNFvO2aldMDdhAd44Da0/img.png&quot; data-alt=&quot;Monster Behavior Tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2sbw3/dJMcageEtFb/A7VNFvO2aldMDdhAd44Da0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2sbw3%2FdJMcageEtFb%2FA7VNFvO2aldMDdhAd44Da0%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;1734&quot; height=&quot;806&quot; data-filename=&quot;ToadBT.png&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Monster Behavior Tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-heading=&quot;AnimNotifyState로 히트 윈도우 제어&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-heading=&quot;AnimNotifyState로 히트 윈도우 제어&quot; data-ke-size=&quot;size20&quot;&gt;AnimNotifyState로 히트 윈도우 제어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격 판정과 패리 윈도우를 AnimNotifyState로 구현했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TriggerHitCheck&lt;/li&gt;
&lt;li&gt;ParryWindow&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션 타임라인에 직접 판정 구간을 붙이는 방식이라, 타이밍 조정 시 애니메이션 편집만으로 해결할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZI9f/dJMcafGP880/yULusqPN4zgVXh0Sp2Ofyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZI9f/dJMcafGP880/yULusqPN4zgVXh0Sp2Ofyk/img.png&quot; data-alt=&quot;Trigger Check 프레임과 Parrying 가능 return 범위&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZI9f/dJMcafGP880/yULusqPN4zgVXh0Sp2Ofyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZI9f%2FdJMcafGP880%2FyULusqPN4zgVXh0Sp2Ofyk%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;1095&quot; height=&quot;397&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Trigger Check 프레임과 Parrying 가능 return 범위&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-heading=&quot;ICombatInterface로 데미지 처리 표준화&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-heading=&quot;ICombatInterface로 데미지 처리 표준화&quot; data-ke-size=&quot;size20&quot;&gt;ICombatInterface로 데미지 처리 표준화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어든 몬스터든 피격 처리는 모두 ICombatInterface를 경유합니다. 이 인터페이스 클래스를 상속받는 클래스에서 AttackDamageCheck 함수와 RequestParry를 반드시 구현하게 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 다음 처리가 한 곳에서 일관되게 이루어집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방어력 계산&lt;/li&gt;
&lt;li&gt;피격 연출&lt;/li&gt;
&lt;li&gt;스턴 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-heading=&quot;사용자 워크플로우&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-heading=&quot;사용자 워크플로우&quot; data-ke-size=&quot;size23&quot;&gt;게임 플레이 워크플로우&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;메인 메뉴
└─ 캐릭터 선택
   ├─ 대검
   ├─ 한손검
   └─ 활
      └─ 마을 진입
         └─ NPC 퀘스트 수주
            └─ 두꺼비 구역
               └─ 전갈 구역
                  └─ 보스 포탈 진입
                     └─ 보스 드래곤 3페이즈
                        └─ 엔딩
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;4. 주요 작업&quot; data-ke-size=&quot;size26&quot;&gt;4. 주요 작업&lt;/h2&gt;
&lt;h3 data-heading=&quot;핵심 루프 구현 &amp;mdash; 두꺼비 &amp;amp; 전갈 AI&quot; data-ke-size=&quot;size23&quot;&gt;핵심 루프 구현 &amp;mdash; 두꺼비 &amp;amp; 전갈 AI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 몬스터 모두 같은 Behavior Tree 구조를 베이스로 두고, 각자의 패턴을 얹는 방식으로 설계했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;공통 구조&quot; data-ke-size=&quot;size23&quot;&gt;공통 구조&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Idle 순찰
└─ Detect 감지 범위 내 플레이어 탐지
   └─ Chase 추적
      └─ Attack 공격 패턴 실행
         └─ Beaten / Stun / Death
            └─ 홈 범위 이탈 시 귀환
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;두꺼비 특이 패턴&quot; 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;점프 범위 공격&lt;/li&gt;
&lt;li&gt;세 가지 패턴 랜덤 실행&lt;/li&gt;
&lt;li&gt;점프 공격 시 AnimNotify로 이동 시점 제어&lt;/li&gt;
&lt;li&gt;점프 공격 중 충돌 비활성화로 플레이어 끼임 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FightWithToadWithSS_poison.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baZfTF/dJMcaaFx4HS/yKIbMhkYz7cYDhGdVnIL91/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baZfTF/dJMcaaFx4HS/yKIbMhkYz7cYDhGdVnIL91/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baZfTF/dJMcaaFx4HS/yKIbMhkYz7cYDhGdVnIL91/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/baZfTF/dJMcaaFx4HS/yKIbMhkYz7cYDhGdVnIL91/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;FightWithToadWithSS_poison.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FightWithToadWithSS_Jump.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UqLT7/dJMcaaFx4HR/Ik4ShirysZ3l4TRrmMHpRK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UqLT7/dJMcaaFx4HR/Ik4ShirysZ3l4TRrmMHpRK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UqLT7/dJMcaaFx4HR/Ik4ShirysZ3l4TRrmMHpRK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/UqLT7/dJMcaaFx4HR/Ik4ShirysZ3l4TRrmMHpRK/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;FightWithToadWithSS_Jump.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;전갈 특이 패턴&quot; 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;집게 / 꼬리 / 독 범위 / 내려찍기 4가지 공격 랜덤 실행&lt;/li&gt;
&lt;li&gt;독 공격
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범위 내 5초간 유지&lt;/li&gt;
&lt;li&gt;1초마다 틱 데미지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;집게 충돌은 제거하되 데미지 판정은 유지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;플레이어 끼임 방지 목적&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Scorpion_fight.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTJJ04/dJMcabdhZo3/mzWPlwk0o2xNTGHHHqIeR0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTJJ04/dJMcabdhZo3/mzWPlwk0o2xNTGHHHqIeR0/img.gif&quot; data-alt=&quot;두꺼비 AI 전투&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTJJ04/dJMcabdhZo3/mzWPlwk0o2xNTGHHHqIeR0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cTJJ04/dJMcabdhZo3/mzWPlwk0o2xNTGHHHqIeR0/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;Scorpion_fight.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두꺼비 AI 전투&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;5. 피드백 및 HUD 구현&quot; data-ke-size=&quot;size26&quot;&gt;5. 피드백 및 HUD 구현&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전투에서 피드백이 없으면 공격이 실제로 닿았는지 알기 어렵습니다.&lt;br /&gt;그래서 시각&amp;middot;청각 피드백을 단계적으로 붙였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;데미지 폰트&lt;/td&gt;
&lt;td&gt;피격 시 피해량 수치가 3D 공간에 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;피격 플래시&lt;/td&gt;
&lt;td&gt;몬스터가 맞을 때 머티리얼 전체가 순간적으로 하얗게 번쩍임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HP 50% 경고&lt;/td&gt;
&lt;td&gt;체력이 절반 이하로 내려가면 빨간 플래시 3회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;아이템 드랍&lt;/td&gt;
&lt;td&gt;사망 애니메이션 완료 후 아이템 스폰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;충돌 시 카메라 Shake&lt;/td&gt;
&lt;td&gt;몬스터와 무기가 충돌 할 때 카메라의 흔들림을 주어서 타격감 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;무기 궤적 표시&lt;/td&gt;
&lt;td&gt;무기를 휘두른 자리에 trail 효과를 주어서 궤적을 알아보기 쉽게 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;사망 Dissolve 연출&quot; data-ke-size=&quot;size23&quot;&gt;사망 Dissolve 연출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10초 후 단순히 Destroy 하는 방식 대신, 머티리얼 파라미터를 조절해 몸체가 서서히 녹아 사라지도록 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두꺼비와 전갈 모두 해당 연출을 적용했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;FightWithToadWithSS_dead.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jfgwy/dJMcabRTRrk/4OZX4J6faT1Aqi104ZSh00/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jfgwy/dJMcabRTRrk/4OZX4J6faT1Aqi104ZSh00/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jfgwy/dJMcabRTRrk/4OZX4J6faT1Aqi104ZSh00/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Jfgwy/dJMcabRTRrk/4OZX4J6faT1Aqi104ZSh00/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;FightWithToadWithSS_dead.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-heading=&quot;전투 BGM 시스템&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-heading=&quot;전투 BGM 시스템&quot; data-ke-size=&quot;size23&quot;&gt;전투 BGM 시스템&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몬스터가 플레이어를 처음 발견하고 Roar 하는 순간부터 전투 BGM 시작&lt;/li&gt;
&lt;li&gt;사망 애니메이션이 끝난 뒤 마을 BGM으로 자연스럽게 전환&lt;/li&gt;
&lt;li&gt;Roar 재생 중에는 카메라 셰이크와 피격음 비활성화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;7. 문제 해결 및 기술적 도전&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;6. 문제 해결 및 기술적 도전&lt;/h2&gt;
&lt;h3 data-heading=&quot;이슈 1. 몬스터 추적이 버벅이는 문제&quot; data-ke-size=&quot;size23&quot;&gt;이슈 1. 몬스터 추적이 버벅거리는 문제&lt;/h3&gt;
&lt;h4 data-heading=&quot;상황&quot; data-ke-size=&quot;size20&quot;&gt;상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몬스터가 플레이어를 따라올 때 뚝뚝 끊기며 이동했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-heading=&quot;원인&quot; data-ke-size=&quot;size20&quot;&gt;원인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MoveTo 태스크에서 Blackboard Key를 Object(Target Actor)로 지정했더니, 매 틱마다 Actor 위치를 다시 계산하는 오버헤드가 발생했습니다.&lt;/p&gt;
&lt;h4 data-heading=&quot;해결&quot; data-ke-size=&quot;size20&quot;&gt;해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blackboard Key를 Vector(Target Location)로 변경하고, NavAcceleration과 OrientRotation 설정을 추가로 조정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Orient Rotation To movement는 true&lt;br /&gt;Use Controller Rotation Yaw는 false 해서 뚝뚝 끊기는 현상 해결&lt;br /&gt;=&amp;gt; &lt;span&gt;Use Controller Rotation Yaw를 끄면서&lt;/span&gt;&lt;br /&gt;&lt;span&gt;캐릭터가 Controller의 순간적인 방향 변경을 따라가지 않게 되었고,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;Orient Rotation To Movement를 켜면서&lt;/span&gt;&lt;br /&gt;&lt;span&gt;실제 이동 방향 기준으로 MovementComponent가 부드럽게 회전하게 되었기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-heading=&quot;결과&quot; data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추적 움직임이 부드러워졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추적 버벅거림 수정 전/후&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MonsterMove_Before.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crMFLY/dJMcahq7IxA/QFvEZkeVtzAkVf6IYFT3n1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crMFLY/dJMcahq7IxA/QFvEZkeVtzAkVf6IYFT3n1/img.gif&quot; data-alt=&quot;뚝뚝 끊기며 Chase&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crMFLY/dJMcahq7IxA/QFvEZkeVtzAkVf6IYFT3n1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/crMFLY/dJMcahq7IxA/QFvEZkeVtzAkVf6IYFT3n1/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;MonsterMove_Before.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;뚝뚝 끊기며 Chase&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MonsterMove_After.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dA5HZF/dJMcacwzg5n/9UULKQZAn9Sy22Qce5jt01/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dA5HZF/dJMcacwzg5n/9UULKQZAn9Sy22Qce5jt01/img.gif&quot; data-alt=&quot;부드럽게 Chase&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dA5HZF/dJMcacwzg5n/9UULKQZAn9Sy22Qce5jt01/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dA5HZF/dJMcacwzg5n/9UULKQZAn9Sy22Qce5jt01/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;MonsterMove_After.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;부드럽게 Chase&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;8. 기술적 의사결정 기록&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;7. 기술적 의사결정 기록&lt;/h2&gt;
&lt;h3 data-heading=&quot;데미지 처리를 UGameplayStatics::ApplyDamage로 직접 하면 안 되나?&quot; data-ke-size=&quot;size23&quot;&gt;데미지 처리를 UGameplayStatics::ApplyDamage로 직접 하면 안 되나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 구현에서는 몬스터가 플레이어에게 직접 ApplyDamage를 호출했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 짧고 빠르다는 장점은 있었지만, 다음 문제가 있었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;플레이어의 방어력이 반영되지 않음&lt;/li&gt;
&lt;li&gt;피격 연출이 실행되지 않음&lt;/li&gt;
&lt;li&gt;스턴 판정이 일관되게 처리되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 ICombatInterface를 경유하도록 수정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 경로가 한 단계 늘어나지만, 플레이어 전투 규칙이 모두 일관되게 동작합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방어력 적용&lt;/li&gt;
&lt;li&gt;상태 변화&lt;/li&gt;
&lt;li&gt;연출 트리거&lt;/li&gt;
&lt;li&gt;스턴 판정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 전체가 같은 인터페이스를 사용하므로, 나중에 새 몬스터를 추가할 때도 별도 예외 처리가 필요 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-heading=&quot;9. 결과 및 회고&quot; data-ke-size=&quot;size26&quot;&gt;8. 결과 및 회고&lt;/h2&gt;
&lt;h3 data-heading=&quot;결과&quot; 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;두꺼비&amp;middot;전갈 몬스터 전투 루프 완성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;피격 / 패링 / 사망&lt;/li&gt;
&lt;li&gt;VFX / SFX&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;퀘스트 수주 &amp;rarr; 몬스터 처치 &amp;rarr; 보스 포탈 진입까지 이어지는 흐름 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;잘 된 점&quot; data-ke-size=&quot;size23&quot;&gt;잘 된 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능을 만드는 것보다 &lt;b&gt;&amp;ldquo;끊기지 않는 전투 루프&amp;rdquo;&lt;/b&gt;를 목표로 설정한 점이 좋았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추적 버벅거림, 몰림 현상, Collision 잔류처럼 눈에 잘 안 띄지만 플레이 감각을 망치는 문제를 계속 잡아낼 수 있었던 이유입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-heading=&quot;아쉬운 점&quot; 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;Behavior Tree가 복잡해질수록 디버깅이 어려웠습니다. 어떤 노드에서 어떤 조건으로 분기가 일어났는지 추적하는 데 시간이 많이 걸렸고, 초반에 Blackboard 설계를 더 꼼꼼히 했다면 중간에 Key 타입을 갈아엎는 일은 없었을 것 같습니다.&lt;/li&gt;
&lt;li&gt;협업 측면에서는 공동 코드를 수정한 뒤 변경 내용을 충분히 기록하고 공유하지 못했던 점이 아쉬웠습니다. 같은 코드를 사용하는 팀원에게 영향을 줄 수 있는 부분이었지만, 수정 이유와 영향 범위를 명확히 전달하지 못해 오류가 발생한 경험이 있었습니다. 이 과정을 통해 팀 프로젝트에서 코드는 개인 작업물이 아니라 함께 관리하는 자산이며, 변경 사항을 꾸준히 공유하는 것도 개발자의 중요한 역량이라는 점을 배웠습니다. 또한 팀원 간 진행 상황 공유가 충분하지 않아 전체 일정이나 막힌 부분을 늦게 파악하는 경우도 있었습니다. 당시에는 제 작업을 마무리하는 데 집중했지만, 다음에는 팀원들의 진행 상황까지 더 적극적으로 확인하고 조율하는 태도가 필요하다고 느꼈습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-heading=&quot;다음에 한다면&quot; 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;BT 작업 전에 상태도를 먼저 그리고 시작할 것 같습니다. 코드보다 상태 전이 설계가 먼저여야 나중에 BT 수정이 쉬워진다는 것을 이번 프로젝트를 통해 직접 느꼈습니다.&lt;/li&gt;
&lt;li&gt;사실 상 Trello나 Figma 같은 협업 도구를 처음에 사용하자고 논의는 되었지만 프로젝트를 진행 할 때 거의 사용되지 않았던 점이 아쉽습니다. 다음 프로젝트에서는 실제로 쓰일 수 있는 기능을 더 공부해서 협업에 용이하게 사용하고 싶습니다.&lt;/li&gt;
&lt;li&gt;협업 방식에서는 Trello 같은 도구를 초반에만 사용하는 것이 아니라, 프로젝트가 끝날 때까지 꾸준히 활용하고 싶습니다. 해야 할 작업, 진행 중인 작업, 완료된 작업, 막힌 부분, 공동 코드 수정 사항을 매일 기록하면 팀원들이 서로의 상황을 더 쉽게 파악할 수 있을 것 같습니다. 다음 프로젝트에서는 단순히 &amp;ldquo;내가 어디까지 했는지&amp;rdquo;만 공유하는 것이 아니라, &amp;ldquo;어디서 막혔는지&amp;rdquo;, &amp;ldquo;누구의 도움이 필요한지&amp;rdquo;, &amp;ldquo;어떤 코드가 변경되었는지&amp;rdquo;까지 함께 공유하는 문화를 만들고 싶습니다. 이를 통해 불필요한 오해나 지연을 줄이고, 팀 전체가 더 안정적으로 움직일 수 있는 개발자가 되고 싶습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>게임개발</category>
      <category>언리얼</category>
      <category>원티드 4기</category>
      <category>팀 프로젝트</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/35</guid>
      <comments>https://learn-forever.tistory.com/35#entry35comment</comments>
      <pubDate>Mon, 1 Jun 2026 22:02:26 +0900</pubDate>
    </item>
    <item>
      <title>4차 프로젝트 후기</title>
      <link>https://learn-forever.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;협업 과정에서 배운 소통과 기록의 중요성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트를 마무리하면서 단순히 어떤 기능을 만들었는지만 돌아보는 것이 아니라, 제가 맡은 역할을 어떻게 수행했는지, 협업 과정에서 어떤 부족함이 있었는지, 그리고 다음 프로젝트에서 무엇을 개선하고 싶은지 정리해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 기능 구현뿐만 아니라 팀원들과의 소통, 진행 상황 공유, 공동 코드 관리의 중요성을 많이 느낄 수 있었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 이번 프로젝트에서 내가 특히 잘한 부분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 제가 특히 잘했다고 생각하는 부분은 &lt;b&gt;제가 맡은 포지션에서 해야 할 일을 책임감 있게 처리한 점&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하면서 제가 맡은 기능이나 역할을 미루지 않고 하나씩 처리하려고 노력했습니다. 단순히 제 작업만 하는 것이 아니라, 현재 제가 어떤 부분을 진행하고 있는지, 어느 정도 완료되었는지 팀원들에게 공유하려고 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 프로젝트에서는 각자 맡은 일이 따로 있어도 결국 하나의 결과물로 합쳐져야 하기 때문에, 개인 작업의 진행 상황을 공유하는 것이 중요하다고 느꼈습니다. 제가 맡은 부분을 끝까지 책임지고 처리하면서 팀 전체 일정이 크게 흔들리지 않도록 기여했다는 점에서 잘한 부분이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 작업 상황을 공유하면서 팀원들이 제 진행 상태를 파악할 수 있었고, 필요한 부분에서는 서로 맞춰가며 프로젝트를 이어갈 수 있었습니다.&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;p data-ke-size=&quot;size16&quot;&gt;부족했던 점은 &lt;b&gt;프로젝트 초반 공동 코드를 수정한 뒤 충분히 공유하지 못했던 부분&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 같은 코드를 함께 사용하는 개발자와 협업할 때, 제가 공동 코드를 수정한 뒤 그 내용을 제대로 공유하지 않아 오류가 발생한 적이 있었습니다. 코드 수정을 해놓고 기록을 해두지 않아서 시간이 지난 후 제대로 공유가 안되었던게 원인이었습니다. 이런 행동이 같은 코드를 사용하는 팀원에게는 큰 영향을 줄 수 있다는 것을 그때 알게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 이 경험을 통해 팀 프로젝트에서 코드는 개인 작업물이 아니라, 팀 전체가 함께 사용하는 자산이라는 점을 배웠습니다. 특히 공동으로 사용하는 코드일수록 수정 내용, 변경 이유, 영향 범위를 확실히 공유해야 한다고 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 같은 실수를 반복하지 않기 위해 해당 경험을 기록해두었고, 공동 코드나 다른 팀원의 작업에 영향을 줄 수 있는 부분을 수정할 때는 더 신경 써서 공유하려고 노력했습니다. 이 과정에서 단순히 코드를 잘 작성하는 것뿐만 아니라, &lt;b&gt;변경 내용을 명확히 전달하는 능력도 개발자의 중요한 역량&lt;/b&gt;이라는 것을 배웠습니다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원 중에는 자신이 어디까지 작업했는지, 어떤 부분에서 막혀 있는지, 왜 일정이 늦어지고 있는지를 잘 공유하지 않는 경우가 있었습니다. 그러다 보니 팀 전체가 현재 상황을 정확히 파악하기 어려웠고, 어떤 부분을 도와야 하는지도 늦게 알게 되는 경우가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에는 각자 맡은 작업을 진행하는 데 집중하다 보니, 제가 먼저 더 적극적으로 다가가서 상황을 물어보거나 소통을 이끌어내지 못했던 점이 아쉽게 남았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 발생한 뒤에는 진행 상황을 더 확인하려고 했지만, 처음부터 더 자주 소통했다면 일정 관리나 문제 해결이 더 수월했을 것이라고 생각합니다. 이 경험을 통해 팀 프로젝트에서는 단순히 내 일만 잘하는 것에서 끝나는 것이 아니라, &lt;b&gt;팀원들의 진행 상황을 함께 확인하고 필요한 부분을 조율하는 태도&lt;/b&gt;도 중요하다는 것을 배웠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 다음 프로젝트에서 개선하거나 새롭게 시도하고 싶은 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로젝트에서는 &lt;b&gt;Trello 같은 협업 도구를 더 체계적으로 활용해보고 싶습니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서도 초반에는 Trello를 사용했지만, 시간이 지나면서 점점 사용 빈도가 줄어들었습니다. 처음에는 작업을 정리하고 진행 상황을 공유하기 위해 사용했지만, 프로젝트가 바빠질수록 구두 공유나 개인 작업에 의존하게 되었고, 결국 협업 도구가 충분히 활용되지 못했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로젝트에서는 Trello를 단순히 초반에만 사용하는 것이 아니라, 매일매일 작업 상황을 정리하고 공유하는 방식으로 꾸준히 사용해보고 싶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 방식으로 활용하면 더 좋을 것 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해야 할 작업 정리하기&lt;/li&gt;
&lt;li&gt;현재 진행 중인 작업 누가 하고 있는지 표시하기&lt;/li&gt;
&lt;li&gt;완료된 작업 구분하기&lt;/li&gt;
&lt;li&gt;막힌 부분이나 도움이 필요한 부분 기록하기&lt;/li&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;이렇게 체계적으로 관리하면 팀원들이 각자의 진행 상황을 더 쉽게 파악할 수 있고, 문제가 생겼을 때도 더 빠르게 대응할 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 프로젝트에서는 &amp;ldquo;내가 어디까지 했는지&amp;rdquo;뿐만 아니라 &amp;ldquo;어디서 막혔는지&amp;rdquo;, &amp;ldquo;누구의 도움이 필요한지&amp;rdquo;까지 공유하는 문화를 만들고 싶습니다. 그래야 팀 전체가 더 안정적으로 움직일 수 있고, 협업 과정에서 생기는 불필요한 오해나 지연을 줄일 수 있다고 생각합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트를 통해 가장 크게 배운 것은 &lt;b&gt;협업에서 중요한 것은 단순히 각자 맡은 일을 끝내는 것만이 아니라, 진행 상황과 변경 사항을 꾸준히 공유하는 것&lt;/b&gt;이라는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 제가 맡은 역할을 책임감 있게 수행하려고 노력했고, 진행 상황도 공유하려고 했습니다. 하지만 공동 코드를 수정한 뒤 충분히 공유하지 않아 오류가 발생했던 경험도 있었고, 팀원과의 소통이 부족했던 부분도 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 경험을 통해 다음 프로젝트에서는 더 적극적으로 소통하고, Trello 같은 협업 도구를 꾸준히 활용하며, 팀 전체가 현재 상황을 명확히 알 수 있는 환경을 만들고 싶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트는 결과물뿐만 아니라 협업 방식에 대해서도 많이 배울 수 있었던 경험이었습니다. 앞으로는 이번에 배운 점을 바탕으로 더 체계적으로 기록하고 공유하며, 팀에 더 도움이 되는 개발자로 성장하고 싶습니다.&lt;/p&gt;</description>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>4차</category>
      <category>언리얼</category>
      <category>원티드</category>
      <category>팀프로젝트</category>
      <category>프로젝트</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/34</guid>
      <comments>https://learn-forever.tistory.com/34#entry34comment</comments>
      <pubDate>Sun, 31 May 2026 21:32:59 +0900</pubDate>
    </item>
    <item>
      <title>3차 프로젝트 회고</title>
      <link>https://learn-forever.tistory.com/33</link>
      <description>&lt;h1 data-heading=&quot;Dice Rogue&quot;&gt;Dice Rogue&lt;/h1&gt;
&lt;h2 data-heading=&quot;1. 프로젝트 개요&quot; data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 개요&lt;/h2&gt;
&lt;h3 data-heading=&quot;게임 소개&quot; data-ke-size=&quot;size23&quot;&gt;게임 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dice Rogue는 허브에서 주사위를 굴려 이동한 뒤, 도착한 테마 필드에서 전투를 진행하고 보상을 얻으며 성장하는 3D 싱글 액션 게임입니다. 주사위 결과에 따라 다음 전투와 성장 방향이 달라지도록 설계했고, 허브-이동-전투-보상-복귀가 하나의 루프로 이어지도록 구현했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;개발 기간&quot; data-ke-size=&quot;size23&quot;&gt;개발 기간&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026.03.24 ~ 2026.04.06&lt;/p&gt;
&lt;h3 data-heading=&quot;참여 인원&quot; data-ke-size=&quot;size23&quot;&gt;참여 인원&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 프로젝트&lt;/p&gt;
&lt;h3 data-heading=&quot;개발 환경&quot; data-ke-size=&quot;size23&quot;&gt;개발 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unreal Engine Blueprint 기반으로 제작했습니다. 초기 기획 단계에서는 AI를 활용해 게임 루프와 스코프를 빠르게 정리했고, 구현과 디버깅은 직접 반복하며 진행했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;현황(결과)&quot; data-ke-size=&quot;size23&quot;&gt;현황(결과)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브에서 주사위를 굴리고, 결과에 따라 필드를 이동한 뒤, 포탈을 통해 전투 필드로 진입하고, 전투 클리어 후 보상을 적용한 뒤 다시 허브로 복귀하는 핵심 루프를 구현했습니다. 또한 HUD, 타이틀 시퀀스, 주사위 판정과 이동 관련 주요 버그 수정까지 마무리했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;주요 기능&quot; 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;전투 클리어 후 스탯 및 스킬 보상 지급&lt;/li&gt;
&lt;li&gt;HUD를 통한 HP 및 주요 스탯 표시&lt;/li&gt;
&lt;li&gt;허브 복귀 후 반복 플레이 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-heading=&quot;2. 기획 목표 및 협업 방식&quot; data-ke-size=&quot;size26&quot;&gt;2. 기획 목표 및 협업 방식&lt;/h2&gt;
&lt;h3 data-heading=&quot;기획의도 및 목표&quot; data-ke-size=&quot;size23&quot;&gt;기획의도 및 목표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 개발 기간 안에 주사위 기반 이동, 테마 전투, 반복 성장으로 이어지는 핵심 게임 루프를 완성하는 것을 목표로 했습니다. 테마별로 공격, 방어, 속도 보상을 나누고, 같은 테마를 반복 방문할수록 스탯 상승과 스킬 해금 및 강화가 이어지도록 설계해 반복 플레이의 의미를 만들고자 했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;협업 방식&quot; data-ke-size=&quot;size23&quot;&gt;협업 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 프로젝트로 진행했으며, 구현 과정과 버그 해결 과정을 개발일지 형태로 기록하며 작업했습니다. 기능 추가보다 핵심 루프 완성과 흐름 정리에 우선순위를 두고 진행했습니다.&lt;/p&gt;
&lt;h2 data-heading=&quot;3. 아키텍처 및 워크플로우&quot; data-ke-size=&quot;size26&quot;&gt;3. 아키텍처 및 워크플로우&lt;/h2&gt;
&lt;h3 data-heading=&quot;아키텍처 특징 및 설계 의도&quot; data-ke-size=&quot;size23&quot;&gt;아키텍처 특징 및 설계 의도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 구조는 허브를 중심으로 설계했습니다. UI에서 주사위 입력이 발생하면 주사위 결과를 처리하고, 이동이 끝나면 포탈이 열리며, 전투 클리어 후 보상이 적용된 뒤 다시 허브로 돌아오는 흐름입니다. 기능별 역할을 나누어 게임 흐름이 끊기지 않도록 구성했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;사용자 워크플로우&quot; data-ke-size=&quot;size23&quot;&gt;사용자 워크플로우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 시작 &amp;rarr; 주사위 굴리기 &amp;rarr; 필드 이동 &amp;rarr; 포탈 진입 &amp;rarr; 전투 클리어 &amp;rarr; 보상 획득 &amp;rarr; 허브 복귀 &amp;rarr; 반복 &amp;rarr; 보스 진입 구조로 설계했습니다.&lt;/p&gt;
&lt;h2 data-heading=&quot;4. 주요 작업&quot; data-ke-size=&quot;size26&quot;&gt;4. 주요 작업&lt;/h2&gt;
&lt;h3 data-heading=&quot;핵심 루프 구현&quot; data-ke-size=&quot;size23&quot;&gt;핵심 루프 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브, 주사위, 포탈, 전투 필드를 연결해 플레이 가능한 기본 루프를 먼저 완성했습니다. 이동, 전투 진입, 클리어 후 복귀까지의 흐름을 우선 구현한 뒤 세부 기능을 보강했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;테마 보상 및 HUD 구현&quot; data-ke-size=&quot;size23&quot;&gt;테마 보상 및 HUD 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테마별로 공격(물 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;테마&lt;/span&gt;), 체력(마그마 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;테마&lt;/span&gt; ), 속도(잔디 테마) 보상을 나누고, 방문 횟수에 따라 스탯 증가와 스킬 해금이 이루어지도록 계획하였으나 일정을 맞추지 못해 스탯 증가만 구현했습니다. 또한 HUD에 HP와 주요 스탯을 표시해 전투 결과와 보상 변화가 바로 보이도록 했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;전환 연출 정리&quot; data-ke-size=&quot;size23&quot;&gt;전환 연출 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 게임 시작, 포탈 이동, 복귀 위치, 카메라 방향, UI 상태를 함께 정리해 허브와 전투 필드 전환이 자연스럽게 이어지도록 수정했습니다.&lt;/p&gt;
&lt;h2 data-heading=&quot;5. 문제 해결 및 기술적 도전&quot; data-ke-size=&quot;size26&quot;&gt;5. 문제 해결 및 기술적 도전&lt;/h2&gt;
&lt;h3 data-heading=&quot;이슈 1. 주사위 결과 판정 문제&quot; data-ke-size=&quot;size23&quot;&gt;이슈 1. 주사위 결과 판정 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3D 물리 주사위의 윗면 판정이 안정적이지 않아, 주사위가 충분히 멈춘 뒤 각 면의 방향을 비교해 결과를 판정하는 방식으로 개선했습니다. 이를 통해 물리 기반 주사위 연출은 유지하면서도 결과 판정의 안정성을 높였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bduZfN/dJMcadVUpnJ/BffxniKNbKArrzTw5WvxMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bduZfN/dJMcadVUpnJ/BffxniKNbKArrzTw5WvxMK/img.png&quot; data-alt=&quot;주사위가 멈춰도 Rotation 값이 안정적이지 않은 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bduZfN/dJMcadVUpnJ/BffxniKNbKArrzTw5WvxMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbduZfN%2FdJMcadVUpnJ%2FBffxniKNbKArrzTw5WvxMK%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;240&quot; height=&quot;364&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주사위가 멈춰도 Rotation 값이 안정적이지 않은 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-heading=&quot;이슈 2. 이동 및 경로 관련 버그&quot; data-ke-size=&quot;size23&quot;&gt;이슈 2. 이동 및 경로 관련 버그&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이어가 움직이지 않거나 목적지까지 비정상적으로 이동하는 문제가 있었습니다. 원인을 점검한 결과 NavMesh 배치와 이동 경로 구성이 핵심 원인이었고, 레벨 구조와 이동 흐름을 함께 정리해 해결했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;이슈 3. 전투 필드 복귀 후 상태 꼬임&quot; data-ke-size=&quot;size23&quot;&gt;이슈 3. 전투 필드 복귀 후 상태 꼬임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전투 필드 이동 후 허브로 돌아왔을 때 이전 목적지를 향해 계속 이동하거나, 카메라와 UI 상태가 어긋나는 문제가 발생했습니다. 이동 종료 처리, 입력 상태, 복귀 위치와 회전값, UI 재활성화 흐름을 다시 정리해 해결했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;이슈 4. 이벤트 바인드 및 UI 갱신 문제&quot; data-ke-size=&quot;size23&quot;&gt;이슈 4. 이벤트 바인드 및 UI 갱신 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blueprint 이벤트 연결 과정에서 바인드 함수 실행 위치에 따라 의도한 함수가 호출되지 않는 문제가 있었습니다. 이벤트 흐름을 다시 정리하고, 상태 변화 시점에 맞춰 UI가 직접 갱신되도록 수정했습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;기술적 의사결정 기록&quot; data-ke-size=&quot;size23&quot;&gt;기술적 의사결정 기록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서는 복잡한 시스템 확장보다 핵심 루프 완성과 안정성을 우선했습니다. 구현 난도가 높은 요소는 완성 가능한 수준으로 조정하고, 게임이 실제로 끊기지 않고 돌아가도록 만드는 데 집중했습니다.&lt;/p&gt;
&lt;h2 data-heading=&quot;6. 결과 및 회고&quot; data-ke-size=&quot;size26&quot;&gt;6. 결과 및 회고&lt;/h2&gt;
&lt;h3 data-heading=&quot;결과&quot; data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dice Rogue는 허브, 주사위, 이동, 포탈, 전투, 보상, 복귀라는 핵심 게임 루프를 실제 플레이 가능한 형태로 연결했다는 점에서 의미가 있었습니다. 또한 테마별 보상 구조와 HUD 반영, 주요 버그 수정을 통해 단순한 기능 구현을 넘어 하나의 게임 흐름을 갖춘 프로토타입을 완성할 수 있었습니다.&lt;/p&gt;
&lt;h3 data-heading=&quot;회고&quot; data-ke-size=&quot;size23&quot;&gt;회고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트를 통해 핵심 루프를 먼저 완성하는 것의 중요성을 다시 확인했습니다. 구현 과정에서는 주사위 판정, 이동, 복귀, 이벤트 연결처럼 겉보기보다 복잡한 문제들을 많이 겪었고, 이를 해결하면서 Blueprint 구조와 상태 전환 흐름에 대한 이해를 넓힐 수 있었습니다.&lt;/p&gt;
&lt;h2 data-heading=&quot;7. 시각자료&quot; data-ke-size=&quot;size26&quot;&gt;7. 시각자료&lt;/h2&gt;
&lt;h3 data-heading=&quot;주요 기능 화면&quot; data-ke-size=&quot;size23&quot;&gt;주요 기능 화면&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;StartGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7jZmN/dJMcaiQr7fb/Wm93gkQWRjNaGxgFtT22Jk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7jZmN/dJMcaiQr7fb/Wm93gkQWRjNaGxgFtT22Jk/img.gif&quot; data-alt=&quot;시작화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7jZmN/dJMcaiQr7fb/Wm93gkQWRjNaGxgFtT22Jk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/7jZmN/dJMcaiQr7fb/Wm93gkQWRjNaGxgFtT22Jk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;StartGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시작화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RollGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nvksp/dJMb99TxG9P/FJDvT4v5kh7bYpNUGKp5Fk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nvksp/dJMb99TxG9P/FJDvT4v5kh7bYpNUGKp5Fk/img.gif&quot; data-alt=&quot;주사위 굴리는 장면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nvksp/dJMb99TxG9P/FJDvT4v5kh7bYpNUGKp5Fk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Nvksp/dJMb99TxG9P/FJDvT4v5kh7bYpNUGKp5Fk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;RollGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주사위 굴리는 장면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-heading=&quot;플레잉 화면&quot; data-ke-size=&quot;size23&quot;&gt;플레잉 화면&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;BattleGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCEkV3/dJMb9965wJ8/AJaGEYa2MdntqDI36Xd9Gk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCEkV3/dJMb9965wJ8/AJaGEYa2MdntqDI36Xd9Gk/img.gif&quot; data-alt=&quot;물 테마&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCEkV3/dJMb9965wJ8/AJaGEYa2MdntqDI36Xd9Gk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dCEkV3/dJMb9965wJ8/AJaGEYa2MdntqDI36Xd9Gk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;BattleGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;물 테마&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GrassGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvB8JU/dJMcac3MG3s/7gHyQqvWYUJUPftNqgNZpK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvB8JU/dJMcac3MG3s/7gHyQqvWYUJUPftNqgNZpK/img.gif&quot; data-alt=&quot;잔디 테마&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvB8JU/dJMcac3MG3s/7gHyQqvWYUJUPftNqgNZpK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bvB8JU/dJMcac3MG3s/7gHyQqvWYUJUPftNqgNZpK/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;GrassGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잔디 테마&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MagmaGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZdike/dJMcac3MG3A/bfELqb2cJ1q2X3LjN6bTjk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZdike/dJMcac3MG3A/bfELqb2cJ1q2X3LjN6bTjk/img.gif&quot; data-alt=&quot;마그마 테마&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZdike/dJMcac3MG3A/bfELqb2cJ1q2X3LjN6bTjk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bZdike/dJMcac3MG3A/bfELqb2cJ1q2X3LjN6bTjk/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;MagmaGIF.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;마그마 테마&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-heading=&quot;8. 프로젝트 총평&quot; data-ke-size=&quot;size26&quot;&gt;8. 프로젝트 총평&lt;/h2&gt;
&lt;h3 data-heading=&quot;1차 프로젝트를 되돌아보며&quot; data-ke-size=&quot;size23&quot;&gt;3차 프로젝트를 되돌아보며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 시간 안에 주사위 기반 성장형 액션 게임의 핵심 구조를 직접 구현한 프로젝트였습니다. 다양한 요소를 무리하게 확장하기보다 먼저 플레이 가능한 루프를 완성하고, 그 위에 필요한 기능을 보강하는 방식으로 진행했다는 점에서 의미가 있었습니다. 특히 핵심 기능의 우선순위를 빠르게 정하고 실제로 돌아가는 흐름을 중심으로 구현했으며, 구현 과정에서 발생한 버그도 단순히 넘어가지 않고 원인을 추적하며 전체 흐름을 함께 정리한 경험이 큰 수확이었습니다. 다만 상태 초기화와 이벤트 흐름을 더 이른 시점에 체계적으로 정리했다면 반복되는 문제를 줄일 수 있었을 것이고, 구현 난도가 높은 요소에 대해서도 범위를 더 빠르게 판단했다면 전체 완성도를 한층 더 높일 수 있었을 것이라 생각합니다.&lt;/p&gt;</description>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>3차</category>
      <category>UE</category>
      <category>UE 5</category>
      <category>UnReal</category>
      <category>Unreal Engine</category>
      <category>게임개발</category>
      <category>원티드</category>
      <category>프로젝트</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/33</guid>
      <comments>https://learn-forever.tistory.com/33#entry33comment</comments>
      <pubDate>Mon, 13 Apr 2026 22:02:39 +0900</pubDate>
    </item>
    <item>
      <title>2차 프로젝트 회고</title>
      <link>https://learn-forever.tistory.com/31</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;소감&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원티드 포텐업 게임개발 4기 2차 프로젝트를 클리어했다! 1차 프로젝트에서 완성했던 QuadTree 시각화에 더해 A star Algorithm까지 완성시켜서 의미가 있었던 프로젝트였다. 이번 프로젝트는 QuadTree와 A* Algorithm을 시각화하는 게 목표였고 잘 이루어 냈다고 생각한다. 다만, 아쉬운 점은 맵을 크게 정해서 여러 actor들을 이동시킬 때 FPS가 하락하는 현상이 남아있고, actor가 이동 시에 다른 actor를 선택하면 이전 actor의 Path가 화면에 계속 남아있는 버그 등이 발견되었다는 것이다. 다음 프로젝트는 마지막 점검을 깐깐하게 해서 최적화 및 버그를 잘 잡아내야겠다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에도 1차 프로젝트와 같이 정말 창의적인 아이디어로 만든 게임이 즐비했고, 배워야 할 점이 많다는 것을 여실히 깨달았다. 다음 프로젝트 부터는 언리얼로 진행을 하는데 어떤 프로젝트가 나올지 긴장도 되고 기대도 된다. 지금까지 배운 점을 바탕으로 재미있는 프로젝트를 진행하고 싶은 마음이 생겨났다.&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  프로젝트 이름&lt;/h2&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 콘솔 뱀서라이크 V2&lt;/b&gt;&lt;/h3&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;게임 소개:&lt;br /&gt;콘솔 환경에서 Quadtree와 A* Algorithm 기술을 시각화하는 데모입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;개발 기간:&lt;br /&gt;&amp;nbsp;총 7일(2026-03-04 ~ 2026-03-10 + 발표 준비 2026-03-11 )&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;참여 인원:&lt;br /&gt;개인&amp;nbsp;프로젝트&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;현황(결과):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;GitHub:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/jaw-choi/Wanted4_ShootingGame#&quot;&gt;https://github.com/jaw-choi/Wanted4_ShootingGame#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기술 스택&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;Skill Set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Core Tech&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;Custom Game Loop(GameLevel Tick), Actor Update Pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Gameplay Systems&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;Stat/Interface(IStatHolder), LevelUp + Pause, Hit Invincibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Collision&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;Quadtree&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Optimization&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;점유 유닛 그리드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Trouble Shoot&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;double delete,mouse input 강제 초기화, 파싱 오류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; width: 17.907%;&quot;&gt;Environment&lt;/td&gt;
&lt;td style=&quot;color: #333333; width: 81.9767%;&quot;&gt;Windows Console, Visual Studio, Git, Obsidian, Trello&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 104px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;Player 이동 및 조작&lt;/td&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;마우스 드래그 기능, 마우스 우클릭으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;충돌 처리&lt;/td&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;Quadtree 공간 분할로 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;color: #333333; height: 16px;&quot;&gt;A* Algorithm&lt;/td&gt;
&lt;td style=&quot;color: #333333; height: 16px;&quot;&gt;Actor의 이동에 최적의 이동 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;디버그 모드&lt;/td&gt;
&lt;td style=&quot;color: #333333; height: 22px;&quot;&gt;화면에 Quadtree 디버그 라인 표시, A* algorithm, closed path, best path 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 기획 목표&amp;nbsp;&lt;/h2&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; 기획의도 및 목표&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;게임 분석:&lt;br /&gt;QuadTree 기술과 A* Algorithm 계산을 안정적으로 실행하고 시각화하는 것.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;구현 목표:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;안정적인 QuadTree계산과 시각화&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;안정적인 A* Algorithm 계산과 시각화&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;마우스 인풋을 활용한 드래그 기능 및 클릭 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;성장 목표:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;엔진 루프(GameLevel Tick) 구조 이해와 제어(dt/pause)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;자료구조 기반 최적화(Quadtree, A* Algorithm, 유닛 점유 그리드)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;문서화 습관(일지, 이슈 분류, 재발 방지 기록) - Obsidian, Trello, Git&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; 기록 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;문서화:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;솔로 프로젝트였기 때문에 기록에 집중&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Obsidian, Trello 기반 개발일지/체크리스트/이슈 로그로 문서화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Git 전략:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;개발 과정에서 예기치 못한 버그에 대비해 빠른 롤백이 가능하도록 자주 커밋&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;솔로 프로젝트였기 때문에 branch는 만들지 않았고, 원활한 작은 기능(Git 사용 여부 및 브랜치 전략)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;이슈 트래킹 방식:&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Obsidian 체크리스트 + 날짜별 로그 + 이슈 분류(억까 버그/내 실력 버그/배우기 좋은 버그)로 관리&lt;br /&gt;Trello : 칸반, 카드 활용 즉시 Todo 생성 및 Doing, Testing, Done카드로 옮겨가며 프로젝트 팔로우 빠르게 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;3. 핵심기능 구현&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;3-1. A* 길 찾기 알고리즘 구현&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드래그로 선택된 유닛에게 마우스 우클릭으로 이동 명령을 내리면, 해당 유닛은 현재 위치에서 목표 지점까지의 최적 경로를 A* 알고리즘으로 계산하고 그 경로를 따라 이동한다. 경로 위에 벽이 있거나 다른 유닛이 길을 막고 있으면 이를 회피하고, 이동 중에 막히면 즉시 새 경로를 다시 계산한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A* 알고리즘의 기본 구조는 Open List와 Closed List를 관리하면서 f(n) = g(n) + h(n) 공식으로 다음에 탐색할 노드를 결정하는 방식이다. 여기서 g(n)은 출발지부터 현재 노드까지의 실제 비용이고, h(n)은 현재 노드에서 목적지까지의 추정 비용인 휴리스틱이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴리스틱으로는 맨해튼 거리(abs(dx) + abs(dy))를 선택했다. 상하좌우 4방향 이동만 지원하기 때문에 맨해튼 거리가 실제 이동 비용을 가장 정확하게 추정한다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술적 도전 &amp;mdash; 유닛 크기 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 좌표 한 칸만 보는 게 아니라 유닛의 전체 크기 영역을 고려해야 했다. 예를 들어 유닛이 3 &amp;times;3 크기라면, 경로의 특정 노드에 도달했을 때 유닛의 몸체가 차지하는 3&amp;times;3 영역 전체에 대해 벽이나 다른 유닛과의 충돌 여부를 확인해야 한다. 단순히 현재 좌표 한 점만 검사하면 이동 중에 벽이나 다른 유닛과 겹치는 현상이 발생한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이중 장애물 회피 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애물 확인은 두 가지 경로로 분리했다. 벽은 IsBlockedByMap() 함수로 타일 배열을 직접 조회하고, 다른 유닛의 위치는 &lt;b&gt;유닛 점유 그리드&lt;/b&gt;를 통해 O(1)로 조회한다. 이 두 가지 확인을 동시에 통과해야 해당 칸으로 이동 가능하다고 판단한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;재탐색 로직&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로를 계산한 이후에도 다른 유닛이 이동해 길을 막는 상황이 발생할 수 있다. 이를 대비해 이동 중에 막히면 즉시 A*를 재실행하는 재탐색 로직을 구현했다. 단, 재탐색을 무제한으로 허용하면 여러 유닛이 동시에 재탐색을 반복할 때 FPS가 급격히 떨어진다. 실험을 통해 재탐색 횟수를 최대 3회로 제한하는 것이 경로 안정성과 성능 사이의 균형점임을 확인했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 이론으로 이해하는 것과 실제로 게임에 통합해 돌아가게 만드는 것은 완전히 다른 문제였다. 특히 유닛 크기, 동적으로 변하는 장애물, 성능 제한 같은 현실적인 제약 조건들이 알고리즘 구현을 훨씬 복잡하게 만든다. 이론 공부와 구현 연습을 병행하는 것의 중요성을 실감했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3-2. 유닛 점유 그리드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵의 각 타일 칸에 현재 그 칸을 점유하고 있는 유닛이 무엇인지를 기록하는 2차원 배열 구조다. A* 탐색 과정에서 특정 좌표를 이동 가능 여부를 판단할 때 이 배열을 조회한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 과정과 도입 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 A* 탐색 중에 유닛과의 충돌 확인을 QuadTree로 처리하려 했다. QuadTree가 공간 탐색을 빠르게 해 준다고 생각했기 때문이다. 그런데 실제로 구현해 보니 A*는 경로 하나를 계산하는 동안 수십에서 수백 번의 충돌 확인을 반복한다. 게다가 유닛 수가 많아질수록 이 반복 횟수는 더 늘어난다. QuadTree 쿼리는 그 자체로도 동적 갱신 비용이 있어서, 이렇게 고빈도로 호출하기에는 너무 무거웠다. FPS가 눈에 띄게 떨어지는 것을 확인하고 다른 방법을 찾았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결책은 단순했다. 맵 전체를 2차원 배열로 만들고, 유닛이 이동할 때마다 이전 위치를 null로, 새 위치를 해당 유닛 참조로 업데이트하는 방식이다. A* 탐색 중에는 이 배열을 &lt;u&gt;좌표로 직접 인덱싱&lt;/u&gt;하는 O(1) 조회 한 번으로 유닛 존재 여부를 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과&lt;/h3&gt;
&lt;div&gt;방식유닛 확인 비용특이사항
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QuadTree (이전)&lt;/td&gt;
&lt;td&gt;O(log n) ~ O(n)&lt;/td&gt;
&lt;td&gt;동적 갱신 비용 추가 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;점유 그리드 (현재)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;배열 인덱스 조회 1회&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유닛 수가 늘어날수록 성능 차이는 더욱 극명해진다. QuadTree는 데이터가 많을수록 탐색 비용이 늘어나지만, 점유 그리드는 유닛 수와 무관하게 항상 O(1)을 유지한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;공간 탐색이 필요하면 무조건 공간 분할 자료구조&quot;라는 선입견이 얼마나 위험한지 배웠다. 해결하려는 문제의 접근 패턴을 먼저 분석하는 것이 중요하다. 고빈도 반복 조회가 필요한 상황에서는 복잡한 자료구조보다 단순한 배열이 훨씬 나은 선택일 수 있다. 자료구조는 용도에 맞게 선택해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3-3. QuadTree 공간 분할 충돌 최적화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵 공간을 재귀적으로 4등분(좌상, 우상, 좌하, 우하)해서 그 구역 안의 Actor를 저장하는 트리 자료구조다. 팀 간 충돌 판정 시 전체 유닛을 순회하는 대신, 대상 유닛 주변 영역과 겹치는 후보만 빠르게 추출하는 데 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 프레임 모든 활성 Actor를 위치 기반으로 QuadTree에 삽입한다. Team A 유닛의 충돌 판정이 필요할 때 Query()를 호출하면 해당 유닛의 영역과 겹치는 후보 목록만 반환된다. 반환된 목록에서 Team B 유닛만 필터링한 뒤, TestIntersect()로 AABB 충돌과 layer/mask 조건을 동시에 확인해 최종 충돌 여부를 결정한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;점유 그리드와의 역할 분리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QuadTree와 점유 그리드를 함께 쓰는 것이 중복처럼 보일 수 있다. 하지만 두 구조는 해결하는 문제가 완전히 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점유 그리드는 A* &lt;b&gt;탐색 중&lt;/b&gt; 특정 타일 칸에 유닛이 있는지를 확인하는 O(1) 룩업 테이블이다. 그리드 단위의 이산적 공간에서 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QuadTree는 A*와 독립적으로, 팀 간 &lt;b&gt;실시간 충돌 판정&lt;/b&gt; 시 근거리 후보를 추려내는 연속 공간 인덱스다. 유닛의 실제 픽셀/좌표 영역 기반으로 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 구조가 각자의 역할을 맡아야 시스템 전체가 성능과 정확성을 함께 갖출 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QuadTree 없이 단순 순회로 충돌을 판정하면 O(n&amp;sup2;) 비용이 발생한다. 유닛 수가 10개면 100번, 30개면 900번의 비교가 필요하다. QuadTree를 도입하면 공간적으로 멀리 있는 유닛은 후보에서 처음부터 제외되므로 실제 비교 횟수가 대폭 줄어든다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 문제를 하나의 완벽한 자료구조로 해결하려는 생각보다, 각 구조의 강점을 파악하고 문제의 특성에 맞게 조합하는 것이 더 현실적이고 효과적인 설계라는 것을 배웠다. QuadTree, 점유 그리드, A*는 서로 다른 레이어에서 서로 다른 문제를 해결하며 시스템을 완성한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3-4. 마우스 입력 &amp;amp; 드래그 UI&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 좌클릭 드래그로 범위 안의 유닛을 다중 선택하고, 우클릭으로 선택된 유닛 전체에 이동 명령을 일괄 전달하는 UI 시스템이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔 환경에서 마우스 입력을 처리하기 위해 Windows API 기반의 MOUSE_EVENT_RECORD를 활용했다. 드래그 선택은 마우스 버튼을 누른 시작 좌표와 현재 좌표로 사각형 영역을 계산하고, 해당 영역 안에 포함된 Actor를 선택 상태로 전환하는 방식으로 구현했다. 선택된 유닛은 시각적으로 구분되도록 표시 처리도 함께 했다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;트러블 슈팅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨이 바뀔 때 system(&quot;cls&quot;)를 사용했는데 이 함수가 mouse inputsystem mode가 초기화되던 문제를 발견하고, 최초에만 설정하던 mode를 매 level이 변경될 때마다 변경해 주어서 해결하였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 프로젝트 기록 &amp;amp; 추적 시스템&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작업 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Obsidian, Git, Trello 세 가지 도구를 각각의 역할에 맞게 운영했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Obsidian&lt;/b&gt;은 개발 과정에서 만든 모든 기술 지식의 허브였다. A*와 QuadTree를 설계하는 과정에서 떠오른 아이디어, 구조 결정의 근거, 트러블슈팅 원인과 해결 과정을 모두 마크다운 문서로 정리했다. 특히 알고리즘 설계 노트는 막힐 때마다 다시 읽으면서 현재 상태를 점검하는 기준점이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Git&lt;/b&gt;은 기능 단위로 커밋해 변경 이력을 관리했다. 버그가 발생했을 때 어느 커밋 이후부터 문제가 생겼는지 이력을 추적하고, 필요하면 롤백해 문제 지점을 복원할 수 있었다. 이는 디버깅 시간을 크게 단축시켜 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Trello&lt;/b&gt;는 할 일 관리 보드로 운영했다. TODO/DOING/DONE의 칸반 방식으로 기능 구현 상태를 시각화했고, 버그가 발생하면 즉시 카드를 만들어 이슈를 추적했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구를 쓰는 것 자체가 목적이 되어선 안 된다. 각 도구가 해결하는 문제를 명확히 이해하고, 그 도구가 가장 잘 맞는 용도에만 쓰는 것이 중요하다. Obsidian은 지식 축적, Git은 이력 관리, Trello는 작업 흐름 관리로 역할을 분리했기 때문에 세 도구가 서로 충돌하지 않고 보완하는 구조가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;5. 트러블 슈팅&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISSUE 1. 마우스 입력 모드 초기화 문제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 생성 후 마우스 우클릭으로 이동 명령을 내리면 정상적으로 동작해야 하는데, 특정 시점 이후부터 마우스 클릭이 전혀 반응하지 않거나 엉뚱한 동작을 했다. 처음에는 이동 명령 로직 자체의 문제라고 생각하고 해당 부분을 집중적으로 들여다봤지만 코드에는 문제가 없었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범위를 넓혀 추적하던 중 system(&quot;cls&quot;)를 호출하는 시점과 문제가 발생하는 시점이 일치한다는 것을 발견했다. 레벨이 바뀌는 시점에 콘솔 화면을 지우기 위해 system(&quot;cls&quot;)를 실행하면 콘솔의 입력 모드 설정이 초기화되는 부작용이 있었다. 마우스 입력을 받으려면 SetConsoleMode()로 ENABLE_MOUSE_INPUT 플래그를 활성화해야 하는데, system(&quot;cls&quot;) 호출 이후 이 설정이 기본값으로 되돌아가면서 마우스 입력이 무시되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 핵심이 system(&quot;cls&quot;)의 부작용임을 파악한 뒤, 레벨이 바뀌거나 화면을 지울 때마다 ProcessInput()에서 명시적으로 마우스 입력 모드를 다시 설정하는 것으로 해결하였다. 레벨 전환 시마다 SetConsoleMode()를 호출해 마우스 모드를 명시적으로 복원하는 코드를 추가했다. 이후 마우스 입력이 안정적으로 동작했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;system()류 함수는 운영체제 수준에서 프로세스를 실행하기 때문에 현재 프로그램의 상태에 예상치 못한 부작용을 줄 수 있다. 특히 콘솔 모드 설정처럼 프로세스별로 관리되는 상태는 외부 명령 실행 후 변경될 수 있다는 점을 알아두어야 한다. 중요한 시스템 설정은 외부 호출 전후로 명시적으로 복원하는 방어적 코딩이 필요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISSUE 2. Actor 삭제 시 메모리 충돌&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨을 종료하거나 전환할 때 게임이 갑자기 크래시 되는 현상이 반복적으로 발생했다. 항상 같은 시점, 즉 레벨 삭제 과정에서 터졌기 때문에 해당 흐름을 집중적으로 살펴봤다. 디버거로 확인하니 이미 해제된 메모리 주소에 다시 delete를 시도하는 이중 해제(double free) 오류였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor 객체의 포인터가 여러 곳에서 관리되고 있었다. Actor는 씬의 Actor 목록에도 등록되어 있고, 팀의 유닛 목록에도 같은 포인터가 들어 있었다. 레벨을 삭제하는 과정에서 씬이 먼저 Actor를 delete 하고, 이후 팀도 동일한 Actor를 delete 하려 시도하면서 이미 해제된 메모리를 다시 해제하는 문제가 발생했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SafeDelete() 매크로 혹은 함수를 도입했다. 이 함수는 포인터가 nullptr인지 먼저 확인하고, nullptr가 아닌 경우에만 delete를 수행한 뒤 포인터를 nullptr로 설정하는 구조다. 삭제 후 포인터를 nullptr로 만들기 때문에 같은 포인터로 두 번 delete를 시도해도 두 번째 호출에서는 아무런 동작도 하지 않는다. 크래시가 완전히 사라졌다.&lt;/p&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;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;cpp&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;// SafeDelete 예시
template &amp;lt;typename T&amp;gt;
void SafeDelete(T*&amp;amp; ptr) {
    if (ptr != nullptr) {
        delete ptr;
        ptr = nullptr;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 객체를 여러 컨테이너에서 동시에 소유권을 갖는 구조는 생명 주기 관리가 복잡해진다. 이 문제를 근본적으로 해결하려면 소유권을 명확히 한 곳에만 두고 다른 곳은 약한 참조만 유지하거나, 현대 C++의 shared_ptr/weak_ptr 같은 스마트 포인터를 활용하는 것이 좋다. 이번에는 SafeDelete로 방어적으로 처리했지만, 소유권 설계 자체를 처음부터 명확히 하는 것이 더 본질적인 해결책이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISSUE 3. 다수 유닛 이동 명령 시 FPS 급락 (핵심 이슈)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유닛 수가 적을 때는 괜찮았지만 10개 이상의 유닛에 동시에 이동 명령을 내리면 게임이 버벅거리기 시작했다. 유닛 수가 늘어날수록 FPS 하락이 두드러졌고, 30개 이상이 되면 거의 슬라이드쇼 수준이 되었다. 처음에는 A* 알고리즘 자체의 탐색 복잡도 문제라고 생각했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인 분석 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로파일링과 코드 검토를 통해 병목이 A* 알고리즘의 탐색 자체가 아니라, &lt;b&gt;탐색 중 매 노드마다 호출하는 충돌 확인 로직&lt;/b&gt;에 있다는 것을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 구조는 A* 탐색 중에 특정 좌표가 다른 유닛에 의해 막혀 있는지 확인하기 위해 매번 QuadTree에 쿼리를 날리고 있었다. 경로 하나를 계산하는 동안 수십~수백 번의 QuadTree 쿼리가 발생하고, 여러 유닛이 동시에 A*를 실행하면 이 쿼리 횟수가 곱해진다. 여기에 QuadTree는 매 프레임 전체를 재구성해야 하는 갱신 비용도 있었다. 결과적으로 O(n&amp;sup2;)에 가까운 연산이 매 프레임 발생하고 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 &lt;b&gt;A* 탐색 중 사용하는 유닛 확인 자료구조를 QuadTree에서 점유 그리드로 교체&lt;/b&gt;하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점유 그리드는 맵과 동일한 크기의 2차원 배열로, 각 칸에 그 칸을 점유한 유닛의 포인터(또는 null)를 저장한다. 유닛이 이동할 때마다 이전 위치를 null로, 새 위치를 해당 유닛으로 갱신한다. A* 탐색 중에는 grid [y][x]!= null이면 이동 불가 칸으로 판단한다. 배열 인덱스 조회 한 번이므로 O(1)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 재탐색 횟수를 최대 3회로 제한했다. 여러 유닛이 동시에 재탐색을 무한히 반복하면 프레임당 처리량이 폭증하기 때문이다. 3회 안에 경로를 찾지 못하면 현 위치에서 대기하도록 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수치 비교&lt;/h3&gt;
&lt;div&gt;유닛 수QuadTree 방식 (이전)점유 그리드 방식 (이후)
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10개&lt;/td&gt;
&lt;td&gt;버벅거림 시작&lt;/td&gt;
&lt;td&gt;원활&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30개&lt;/td&gt;
&lt;td&gt;슬라이드쇼 수준&lt;/td&gt;
&lt;td&gt;정상 범위 FPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;확인 비용&lt;/td&gt;
&lt;td&gt;O(log n) + 갱신 비용&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 문제를 만났을 때 &quot;이 알고리즘이 느린 것&quot;이라고 바로 단정하지 말고, &lt;b&gt;어느 구체적인 연산이 얼마나 자주 실행되는가&lt;/b&gt;를 먼저 파악해야 한다. 이번 경우 A* 자체는 문제가 아니었다. 문제는 A* 내부에서 고빈도로 반복 호출되는 확인 로직이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;시각자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&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 style=&quot;list-style-type: disc;&quot;&gt;A* Algorithm으로 구현 한 Title&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;랜덤맵 생성(미로맵)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;TeamA (파란 Actor)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;TeamB (빨간 Actor)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;보라색 pixel(Closed Path)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;노란색 pixel(Best Path)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;화면상 Quad로 나눠지는 Debug Line&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TitleVideo.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmmc9c/dJMcagEMXaW/Hpw2GTkWVKvWugSBJKFJrK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmmc9c/dJMcagEMXaW/Hpw2GTkWVKvWugSBJKFJrK/img.gif&quot; data-alt=&quot;A* Algorithm으로 구현 한 Title&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmmc9c/dJMcagEMXaW/Hpw2GTkWVKvWugSBJKFJrK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cmmc9c/dJMcagEMXaW/Hpw2GTkWVKvWugSBJKFJrK/img.gif&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;426&quot; height=&quot;240&quot; data-filename=&quot;TitleVideo.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A* Algorithm으로 구현 한 Title&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2739&quot; data-origin-height=&quot;1368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RBNsc/dJMcac3pdNu/fD8lh3Q7TBo9RV3gDMy8q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RBNsc/dJMcac3pdNu/fD8lh3Q7TBo9RV3gDMy8q0/img.png&quot; data-alt=&quot;랜덤맵 생성(미로맵)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RBNsc/dJMcac3pdNu/fD8lh3Q7TBo9RV3gDMy8q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRBNsc%2FdJMcac3pdNu%2FfD8lh3Q7TBo9RV3gDMy8q0%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;606&quot; height=&quot;303&quot; data-origin-width=&quot;2739&quot; data-origin-height=&quot;1368&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;랜덤맵 생성(미로맵)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pSj0Y/dJMcahDEpV4/PKh7iQwF2xkVwxArayf2M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pSj0Y/dJMcahDEpV4/PKh7iQwF2xkVwxArayf2M1/img.png&quot; data-alt=&quot;TeamA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pSj0Y/dJMcahDEpV4/PKh7iQwF2xkVwxArayf2M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpSj0Y%2FdJMcahDEpV4%2FPKh7iQwF2xkVwxArayf2M1%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;557&quot; height=&quot;454&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TeamA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JGrgE/dJMcagrdRhP/wzccZFNokEw9gvqogG0NbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JGrgE/dJMcagrdRhP/wzccZFNokEw9gvqogG0NbK/img.png&quot; data-alt=&quot;보라색 pixel(Closed Path)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JGrgE/dJMcagrdRhP/wzccZFNokEw9gvqogG0NbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJGrgE%2FdJMcagrdRhP%2FwzccZFNokEw9gvqogG0NbK%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;535&quot; height=&quot;280&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보라색 pixel(Closed Path)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs9tIa/dJMcacbijnY/iIGZDnUykFPKxEdQFiwSh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs9tIa/dJMcacbijnY/iIGZDnUykFPKxEdQFiwSh0/img.png&quot; data-alt=&quot;TeamA와 TeamB&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs9tIa/dJMcacbijnY/iIGZDnUykFPKxEdQFiwSh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs9tIa%2FdJMcacbijnY%2FiIGZDnUykFPKxEdQFiwSh0%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;362&quot; height=&quot;393&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TeamA와 TeamB&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Quadtree.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovedE/dJMcab4v97W/9aPkHWGnXPkmCvINeZ7C90/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovedE/dJMcab4v97W/9aPkHWGnXPkmCvINeZ7C90/img.gif&quot; data-alt=&quot;Quadtree 시각화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovedE/dJMcab4v97W/9aPkHWGnXPkmCvINeZ7C90/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ovedE/dJMcab4v97W/9aPkHWGnXPkmCvINeZ7C90/img.gif&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;731&quot; height=&quot;412&quot; data-filename=&quot;Quadtree.gif&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Quadtree 시각화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>2차 프로젝트</category>
      <category>게임개발</category>
      <category>게임개발4기</category>
      <category>원티드</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/31</guid>
      <comments>https://learn-forever.tistory.com/31#entry31comment</comments>
      <pubDate>Sun, 15 Mar 2026 21:30:01 +0900</pubDate>
    </item>
    <item>
      <title>베오메오 게임개발일지 - 2화</title>
      <link>https://learn-forever.tistory.com/30</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UWa9K/dJMcacPTzus/VN1wJ8g906tMQxx0AKDvMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UWa9K/dJMcacPTzus/VN1wJ8g906tMQxx0AKDvMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UWa9K/dJMcacPTzus/VN1wJ8g906tMQxx0AKDvMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUWa9K%2FdJMcacPTzus%2FVN1wJ8g906tMQxx0AKDvMk%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8AwoY/dJMcabXKQ1S/PaVxfYMBKYToKdjBBEPcYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8AwoY/dJMcabXKQ1S/PaVxfYMBKYToKdjBBEPcYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8AwoY/dJMcabXKQ1S/PaVxfYMBKYToKdjBBEPcYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8AwoY%2FdJMcabXKQ1S%2FPaVxfYMBKYToKdjBBEPcYK%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1QYrb/dJMcaaEy0zz/8K53KKnPzIUYB4hI7k3lCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1QYrb/dJMcaaEy0zz/8K53KKnPzIUYB4hI7k3lCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1QYrb/dJMcaaEy0zz/8K53KKnPzIUYB4hI7k3lCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1QYrb%2FdJMcaaEy0zz%2F8K53KKnPzIUYB4hI7k3lCK%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eytlk2/dJMcajam2S0/OnGAZf0gYNwyrCR50C1ax1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eytlk2/dJMcajam2S0/OnGAZf0gYNwyrCR50C1ax1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eytlk2/dJMcajam2S0/OnGAZf0gYNwyrCR50C1ax1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feytlk2%2FdJMcajam2S0%2FOnGAZf0gYNwyrCR50C1ax1%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KGtO8/dJMcajam2SZ/voZPaWmFaRNg3iKUOhwBA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KGtO8/dJMcajam2SZ/voZPaWmFaRNg3iKUOhwBA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KGtO8/dJMcajam2SZ/voZPaWmFaRNg3iKUOhwBA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKGtO8%2FdJMcajam2SZ%2FvoZPaWmFaRNg3iKUOhwBA1%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260314_193128728_05.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cil3gn/dJMcabXKQ2b/ZkmmzJ3pV5gsUW1DAtRkTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cil3gn/dJMcabXKQ2b/ZkmmzJ3pV5gsUW1DAtRkTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cil3gn/dJMcabXKQ2b/ZkmmzJ3pV5gsUW1DAtRkTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcil3gn%2FdJMcabXKQ2b%2FZkmmzJ3pV5gsUW1DAtRkTK%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260314_193128728_05.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구독하고 다음 화에서 만나요! 더 많은 이야기가 기다리고 있어요!&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;팔로우하고 새로운 작품을 만나보세요!&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;https://www.instagram.com/beomeo_studio/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773484513216&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 &amp;bull; Instagram&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.instagram.com&quot; data-og-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot; data-og-url=&quot;https://www.instagram.com/beomeo_studio/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 &amp;bull; Instagram&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.instagram.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>베오메오 개발일지 만화</category>
      <category>게임개발</category>
      <category>베오메오</category>
      <category>인스타툰</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/30</guid>
      <comments>https://learn-forever.tistory.com/30#entry30comment</comments>
      <pubDate>Sat, 14 Mar 2026 20:40:08 +0900</pubDate>
    </item>
    <item>
      <title>베오메오 게임개발일지 - 1화</title>
      <link>https://learn-forever.tistory.com/29</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232701594.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I0ElV/dJMcahKpF0N/bKleRslxzVWwxVguGwD0H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I0ElV/dJMcahKpF0N/bKleRslxzVWwxVguGwD0H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I0ElV/dJMcahKpF0N/bKleRslxzVWwxVguGwD0H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI0ElV%2FdJMcahKpF0N%2FbKleRslxzVWwxVguGwD0H1%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232701594.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232701594_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wcsMN/dJMcaduoGtp/GKctmgUISo8x741HVpzX3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wcsMN/dJMcaduoGtp/GKctmgUISo8x741HVpzX3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wcsMN/dJMcaduoGtp/GKctmgUISo8x741HVpzX3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwcsMN%2FdJMcaduoGtp%2FGKctmgUISo8x741HVpzX3k%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232701594_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232701594_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CjTby/dJMcafy3NEk/r8JDGCZcvdEwbYbReq1bk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CjTby/dJMcafy3NEk/r8JDGCZcvdEwbYbReq1bk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CjTby/dJMcafy3NEk/r8JDGCZcvdEwbYbReq1bk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCjTby%2FdJMcafy3NEk%2Fr8JDGCZcvdEwbYbReq1bk1%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232701594_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232701594_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bt5WI3/dJMcafy3NEl/AghIpF6MMqQMDFeMNk1ar0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bt5WI3/dJMcafy3NEl/AghIpF6MMqQMDFeMNk1ar0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bt5WI3/dJMcafy3NEl/AghIpF6MMqQMDFeMNk1ar0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbt5WI3%2FdJMcafy3NEl%2FAghIpF6MMqQMDFeMNk1ar0%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232701594_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232701594_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xdvBB/dJMcaakcKb1/9hU8RA4tS5RwJOzEJIvQok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xdvBB/dJMcaakcKb1/9hU8RA4tS5RwJOzEJIvQok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xdvBB/dJMcaakcKb1/9hU8RA4tS5RwJOzEJIvQok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxdvBB%2FdJMcaakcKb1%2F9hU8RA4tS5RwJOzEJIvQok%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232701594_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구독하고 다음 화에서 만나요! 더 많은 이야기가 기다리고 있어요!&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;팔로우하고 새로운 작품을 만나보세요!&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;https://www.instagram.com/beomeo_studio/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773239685353&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 &amp;bull; Instagram&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.instagram.com&quot; data-og-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot; data-og-url=&quot;https://www.instagram.com/accounts/login/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 &amp;bull; Instagram&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.instagram.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>베오메오 개발일지 만화</category>
      <category>게임개발</category>
      <category>베오메오</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/29</guid>
      <comments>https://learn-forever.tistory.com/29#entry29comment</comments>
      <pubDate>Thu, 12 Mar 2026 06:00:53 +0900</pubDate>
    </item>
    <item>
      <title>베오메오 게임개발일지 - 프롤로그</title>
      <link>https://learn-forever.tistory.com/28</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bulibi/dJMcadA9sGO/DJPGlklyL9Ehyo3UCnphbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bulibi/dJMcadA9sGO/DJPGlklyL9Ehyo3UCnphbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bulibi/dJMcadA9sGO/DJPGlklyL9Ehyo3UCnphbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbulibi%2FdJMcadA9sGO%2FDJPGlklyL9Ehyo3UCnphbk%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pdAK4/dJMcafy3Nvs/1Uq2PL7jttmIKedSxTzX31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pdAK4/dJMcafy3Nvs/1Uq2PL7jttmIKedSxTzX31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pdAK4/dJMcafy3Nvs/1Uq2PL7jttmIKedSxTzX31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpdAK4%2FdJMcafy3Nvs%2F1Uq2PL7jttmIKedSxTzX31%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116_01.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u49QH/dJMcadA9sGP/fCx2xWSxg7IQWRtijPBHe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u49QH/dJMcadA9sGP/fCx2xWSxg7IQWRtijPBHe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u49QH/dJMcadA9sGP/fCx2xWSxg7IQWRtijPBHe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu49QH%2FdJMcadA9sGP%2FfCx2xWSxg7IQWRtijPBHe0%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116_02.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZJgla/dJMcadA9sGQ/kLoCCglovkYwxFFUWm7oik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZJgla/dJMcadA9sGQ/kLoCCglovkYwxFFUWm7oik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZJgla/dJMcadA9sGQ/kLoCCglovkYwxFFUWm7oik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZJgla%2FdJMcadA9sGQ%2FkLoCCglovkYwxFFUWm7oik%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116_03.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjy3zf/dJMcafy3Nvr/lnbCmAlljjIMxAA8tgp2e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjy3zf/dJMcafy3Nvr/lnbCmAlljjIMxAA8tgp2e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjy3zf/dJMcafy3Nvr/lnbCmAlljjIMxAA8tgp2e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjy3zf%2FdJMcafy3Nvr%2FlnbCmAlljjIMxAA8tgp2e0%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116_04.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260311_232334116_05.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJcei/dJMcafy3Nvq/F9Ijpia4oHMZIb0QbraK00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJcei/dJMcafy3Nvq/F9Ijpia4oHMZIb0QbraK00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJcei/dJMcafy3Nvq/F9Ijpia4oHMZIb0QbraK00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJcei%2FdJMcafy3Nvq%2FF9Ijpia4oHMZIb0QbraK00%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;2160&quot; height=&quot;2700&quot; data-filename=&quot;KakaoTalk_20260311_232334116_05.png&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;2700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구독하고 다음 화에서 만나요! 더 많은 이야기가 기다리고 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팔로우하고 새로운 작품을 만나보세요! &lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;https://www.instagram.com/beomeo_studio/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773239287716&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;로그인 &amp;bull; Instagram&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.instagram.com&quot; data-og-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot; data-og-url=&quot;https://www.instagram.com/accounts/login/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/beomeo_studio/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.instagram.com/beomeo_studio/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;로그인 &amp;bull; Instagram&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.instagram.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>베오메오 개발일지 만화</category>
      <category>베오메오#개발일지</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/28</guid>
      <comments>https://learn-forever.tistory.com/28#entry28comment</comments>
      <pubDate>Wed, 11 Mar 2026 23:30:37 +0900</pubDate>
    </item>
    <item>
      <title>1차 프로젝트 회고</title>
      <link>https://learn-forever.tistory.com/27</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;소감&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원티드 포텐업 게임개발 4기 1차 프로젝트를 클리어했다! 주말이 포함된 일정이었지만 안 그래도 짧은 기간 중, 딱 주말에 열이 나고 어지러워서 정말 아무것도 못했다는 점에 아쉬움이 남는다. 물론 처음 계획 했던 QuadTree 시각화는 완성했지만, 비주얼로 보았을 때는 거의 기본 버전 그대로라서 게임성이 떨어진다는 것에 아쉬움이 남는다. 이를 양분 삼아서, 다음 2차 프로젝트에는 어느 정도 게임성을 갖추면서도 내가 원하는 기능을 보여줄 수 있게 기획하고 디버그 기능도 확실하게 만들고 싶다. 1차 프로젝트를 진행하면서 게임 엔진의 큰 그림을 이해하고 활용할 수 있는 기회였어서 좋았고 구현해보고 싶었던 기능과 만들고 싶었던 게임을 제작하면서 정말 재미있었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히, 프로젝트 발표 다음날 만들었던 게임을 서로서로 해보는 시간을 가졌는데, 발표로만 보던 게임을 직접 해보니 정말 재미있었고, 다음 프로젝트에는 더 재미있는 게임을 만들고 싶은 마음이 생겨났다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  프로젝트 이름&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 콘솔 뱀서라이크&lt;/b&gt;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임 소개:&lt;br /&gt;콘솔 환경에서 뱀서류(서바이버) 핵심 루프를 구현한 프로토타입입니다. 자체 엔진 구조를 기반으로 Player/Enemy/Bullet, EXP&amp;middot;레벨업&amp;middot;스탯, 카메라 스크롤 월드, 대량 객체 처리(Object Pooling), 충돌 최적화(AABB &amp;rarr; Quadtree), 디버그 모드를 통합하여 구현-통합-디버깅-최적화의 전 과정을 경험하는 것을 목표로 했습니다.&lt;/li&gt;
&lt;li&gt;개발 기간:&lt;br /&gt;&amp;nbsp;총&amp;nbsp;7일(2026-02-04&amp;nbsp;~&amp;nbsp;2026-02-10&amp;nbsp;+&amp;nbsp;발표&amp;nbsp;준비&amp;nbsp;2026-02-11&amp;nbsp;)&lt;/li&gt;
&lt;li&gt;참여 인원:&lt;br /&gt;개인&amp;nbsp;프로젝트&lt;/li&gt;
&lt;li&gt;현황(결과):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GitHub: &lt;a href=&quot;https://github.com/jaw-choi/Wanted4_ShootingGame#&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/jaw-choi/Wanted4_ShootingGame#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기술 스택&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;Skill Set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core Tech&lt;/td&gt;
&lt;td&gt;Custom Game Loop(GameLevel Tick), Actor Update Pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gameplay Systems&lt;/td&gt;
&lt;td&gt;Stat/Interface(IStatHolder), LevelUp + Pause, Hit Invincibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI / Behavior&lt;/td&gt;
&lt;td&gt;Vector-based Chasing(normalize 사용), (A* algorithm 검토 후 미적용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collision&lt;/td&gt;
&lt;td&gt;AABB &amp;rarr; Quadtree 전환, Query 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimization&lt;/td&gt;
&lt;td&gt;Object Pooling, swap-pop(vector에서 erase할 때), Debug Mode tools(FPS, Actor position, Quadtree 정보)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trouble Shoot&lt;/td&gt;
&lt;td&gt;dllexport와 dllimport 자동화 매크로 사용시 판단 미스 버그 해결, 오브젝트 풀링 BeginPlay()에서 prewarm() 시 프로세스 메모리 무한 증가 해결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;Windows Console, Visual Studio, Git, Obsidian&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 286px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;Player 이동 및 조작&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;키보드 이동, 좌표 출력(디버그 모드 포함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;Enemy 스폰 및 추격&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;랜덤 방향 스폰, Player 추격(방향 벡터 계산, 정규화로 속도 일정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;자동 공격 시스템&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;가장 가까운 적 탐색 후 일정 시간 마다 자동 발사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;Bullet(투사체)&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;방향 벡터 기반 이동, 속도/형변환 이슈 해결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;스탯 시스템&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;Stat struct + IStatHolder 인터페이스 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;HP/피격/무적&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;데미지/게임오버/피격 플래시/무적 시간 알아 볼 수 있는 UI 인터페이스 제작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;EXP/레벨업&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;EXP 드랍, FullExp 20% 증가, 레벨 표시 UI 인터페이스 제작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;레벨업 UI + Pause&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;게임 일시정지 후 중앙 UI에서 선택(방향키)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;충돌 처리&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;AABB 구현 후 Quadtree 공간 분할로 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;오브젝트 풀링&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;Exp Gem/Enemy/Bullet 메모리 공간 미리 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;카메라/월드 확장&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;스크롤링 카메라( worldPos - cameraPos에 Draw), 마진 설정, 잔디 타일링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;디버그 모드&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;화면에 Quadtree 디버그 라인 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;게임 분석:&lt;br /&gt;뱀서류 장르는 시간이 지날수록 적/탄/아이템 오브젝트가 폭증하기 때문에, &amp;ldquo;대량 객체 처리&amp;rdquo;와 &amp;ldquo;충돌 최적화&amp;rdquo;가 구조적으로 중요합니다. 콘솔 환경에서도 동일한 부담이 발생하므로, 풀링과 공간 분할을 중심으로 성능과 안정성을 설계 과제로 삼았습니다.&lt;/li&gt;
&lt;li&gt;구현 목표:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Player/Enemy/Bullet 등 핵심 class 구조 완성&lt;/li&gt;
&lt;li&gt;HP/EXP/레벨업/스탯 적용 및 UI 통합&lt;/li&gt;
&lt;li&gt;대량 객체 대응(Object Pooling)&lt;/li&gt;
&lt;li&gt;충돌 최적화(AABB &amp;rarr; Quadtree 전환)&lt;/li&gt;
&lt;li&gt;카메라 스크롤 월드 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;성장 목표:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 루프(GameLevel Tick) 구조 이해와 제어(dt/pause)&lt;/li&gt;
&lt;li&gt;자료구조 기반 최적화(Quadtree, swap-pop)&lt;/li&gt;
&lt;li&gt;메모리/라이프사이클 문제(누수, 반복 호출, 이미 제거된 객체 접근) 디버깅 역량 강화&lt;/li&gt;
&lt;li&gt;문서화 습관(일지, 이슈 분류, 재발 방지 기록) - Obsidian 사용&lt;/li&gt;
&lt;/ul&gt;
&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;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;Obsidian 기반 개발일지/체크리스트/이슈 로그로 문서화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Git 전략:
&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;솔로 프로젝트였기 때문에 branch는 만들지 않았고, 원활한 작은 기능(Git 사용 여부 및 브랜치 전략)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이슈 트래킹 방식:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Obsidian 체크리스트 + 날짜별 로그 + 이슈 분류(억까 버그/내 실력 버그/배우기 좋은 버그)로 관리&lt;br /&gt;다음 계획: 만약 팀 프로젝트를 진행한다면 Trello로 전환 예정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 아키텍처 및 워크플로우&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;구조적 특징:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GameLevel 중심 게임 루프에서 Actor 리스트를 순회하며 Tick/Render 수행&lt;/li&gt;
&lt;li&gt;레벨업 UI 노출 시 dt 제어로 게임 진행을 일시정지하는 방식 적용&lt;/li&gt;
&lt;li&gt;IStatHolder 인터페이스 + Stat struct로 Player/Enemy 스탯 접근을 공통화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;게임 흐름:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enemy는 Player 포인터(또는 Actor 참조)를 전달받아 target 기반 추격&lt;/li&gt;
&lt;li&gt;Enemy 사망 시 EXP 드롭 &amp;rarr; Player가 수집 &amp;rarr; FullExp 도달 시 레벨업&lt;/li&gt;
&lt;li&gt;레벨업 UI에서 선택된 업그레이드가 Player Stat에 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;확장성/안정성 관점 포인트:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object Pooling으로 런타임 생성/삭제 비용과 단편화 위험을 줄이려는 의도&lt;/li&gt;
&lt;li&gt;충돌은 AABB(베이스) 유지 + Quadtree(확장)로 단계적 최적화&lt;/li&gt;
&lt;li&gt;디버그 모드(좌표/쿼드트리 테스트 기능)로 통합 과정 검증 가능하도록 구성&lt;/li&gt;
&lt;/ul&gt;
&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;게임 시작&lt;/li&gt;
&lt;li&gt;Player 이동&lt;/li&gt;
&lt;li&gt;Enemy 사방 스폰 및 추격&lt;/li&gt;
&lt;li&gt;Player 자동 공격으로 전투&lt;/li&gt;
&lt;li&gt;Enemy 처치 &amp;rarr; EXP 드랍 &amp;rarr; Player 수집&lt;/li&gt;
&lt;li&gt;레벨업 발생 &amp;rarr; 게임 일시정지 + 중앙 UI 표시&lt;/li&gt;
&lt;li&gt;업그레이드 선택 &amp;rarr; 스탯 적용 &amp;rarr; 게임 재개&lt;/li&gt;
&lt;li&gt;피격 시 HP 감소 + 무적 시간/피격 이펙트&lt;/li&gt;
&lt;li&gt;HP 0이면 게임오버/메뉴 전환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;기능 간 데이터 흐름(Flow chart):&lt;br /&gt;예시: Input &amp;rarr; Player Move &amp;rarr; Enemy MoveTo(Player) &amp;rarr; Collision(AABB/Quadtree) &amp;rarr; Damage/Drop &amp;rarr; EXP Accumulate &amp;rarr; LevelUp UI &amp;rarr; Stat Apply&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 주요 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 오브젝트 풀링(Object Pooling) 적용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 소개:&lt;br /&gt;Enemy/Gem/Bullet 등 반복 생성되는 객체를 미리 풀링 하여 재사용하도록 구현했습니다.&lt;/li&gt;
&lt;li&gt;작업 과정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;풀링 대상으로 Gem/Enemy/Bullet 선정&lt;/li&gt;
&lt;li&gt;prewarm 방식으로 초기 풀 확보&lt;/li&gt;
&lt;li&gt;활성/비활성(IsActive) 기준으로 재사용 흐름 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정/고도화 사항:&lt;br /&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 충돌 처리 최적화: AABB &amp;rarr; Quadtree&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 소개:&lt;br /&gt;기본 AABB 충돌을 구현한 뒤, 대량 객체 상황에서 후보군을 줄이기 위해 Quadtree 공간 분할을 도입했습니다.&lt;/li&gt;
&lt;li&gt;작업 과정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AABB 충돌 구현(기본 충돌 처리)&lt;/li&gt;
&lt;li&gt;Quadtree 자료구조 구현(Rect 포함)&lt;/li&gt;
&lt;li&gt;AABB/Quadtree 모두 디버그 가능하도록 함수화 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수정/고도화 사항:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;죽은 객체까지 충돌 체크에 포함되던 문제 해결(Actor-&amp;gt;IsActive 필터)&lt;/li&gt;
&lt;li&gt;Query 역할 이해 부족으로 Player-Enemy 충돌 누락 &amp;rarr; 사용 방식 정리 후 수정&lt;/li&gt;
&lt;li&gt;컨테이너 const 문제 해결(const Actor* vs Actor*) - const 객체를 const가 아닌 컨테이너에 넣을 수 없음!&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;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 문제 해결 및 기술적 도전&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 이슈 1: 오브젝트 풀링 이후 프로세스 메모리 무한 증가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 상황:&lt;br /&gt;오브젝트 풀링 적용 이후 프로세스 메모리가 지속적으로 증가(무한 증가)하는 현상이 발생했습니다.&lt;/li&gt;
&lt;li&gt;원인 분석:&lt;br /&gt;beginPlay()가 루프 내부에 있고 hasbeganplay로 거르는 구조였는데, GameLevel 단계의 beginPlay()에 불리는 prewarm은 Actor 산하의 hasbeganplay로 차단되지 않아 tick마다 beginPlay/prewarm이 반복 호출되며 리소스가 계속 누적되는 구조였습니다.&lt;/li&gt;
&lt;li&gt;해결 방법:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택지 1: hasbeganplay 플래그가 prewarm까지 포함하도록 구조 변경&lt;/li&gt;
&lt;li&gt;선택지 2: beginPlay를 루프 외부로 이동하거나, prewarm을 명시적 1회 호출로 분리&lt;br /&gt;최종 해결책: GameLevel의 hasbeganplay를 생성하고 따로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해결 과정:&lt;br /&gt;호출 위치/주기 확인 &amp;rarr; tick에서 반복 실행됨을 확인 &amp;rarr; 초기화 루틴을 1회성으로 분리/보장하도록 수정&lt;/li&gt;
&lt;li&gt;해결 결과:&lt;br /&gt;메모리 무한 증가 현상 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 이슈 2: Quadtree 전환 후 메모리 에러 및 충돌 누락&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도전 과제:&lt;br /&gt;AABB에서 Quadtree로 전환하며 Enemy 사망 시 메모리 에러(delete array, 접근 오류)와 Player-Enemy 충돌 누락이 발생했습니다.&lt;/li&gt;
&lt;li&gt;원인 분석:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;죽은 Enemy까지 충돌 체크/Query에 포함되어 이미 제거/비활성 객체 접근 가능성이 생김&lt;/li&gt;
&lt;li&gt;Query 역할 이해 부족으로 충돌 판정 흐름이 깨짐&lt;/li&gt;
&lt;li&gt;디버그 시각화는 라인이 따라오는데 실제 정보가 첫 화면 기준으로 고정(카메라 좌표계 불일치)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해결 방법:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Actor-&amp;gt;IsActive 필터로 살아있는 객체만 Quadtree 처리&lt;/li&gt;
&lt;li&gt;Query 반환/사용 방식 재정리 후 충돌 흐름 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해결 결과:&lt;br /&gt;메모리 오류 제거 및 Player-Enemy 충돌 정상화&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enemy 추격: A* 시도 후 방향벡터 기반 추격 방식으로 수정&lt;/li&gt;
&lt;li&gt;충돌: AABB &amp;rarr; Quadtree 확장&lt;/li&gt;
&lt;li&gt;대량 객체: Object Pooling&lt;/li&gt;
&lt;li&gt;벡터 메모리 삭제 최적화: swap-pop(O(1) 삭제)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;선택 이유:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A*: 지형/장애물이 없는 상태에서는 효용이 낮고 추격이 어색하게 동작&lt;/li&gt;
&lt;li&gt;방향벡터: 구현 단순 + 안정적 + 정규화로 속도 일관성 확보&lt;/li&gt;
&lt;li&gt;raw pointer: 구현 단순, 메모리 관리 공부(단, 소유권/삭제 책임을 명확히 해야 함)&lt;/li&gt;
&lt;li&gt;swap-pop: 순서 보장 불필요한 컨테이너에서 성능 이점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;개선 아이디어:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;A* 의미 확보를 위한 타일 맵/장애물/절차적 맵 생성 추가&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Enemy끼리 충돌 시 버벅거림 &amp;rarr; 충돌 Layer 생성 후 충돌 검사 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;raw pointer 구간 소유권 규칙 문서화 및 해제 책임 일원화&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 결과 및 회고&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;목표 달성도:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Player/Enemy/Bullet + HP/EXP/레벨업 + Pause UI 통합 완료&lt;/li&gt;
&lt;li&gt;Object Pooling 및 Quadtree 통합 완료&lt;/li&gt;
&lt;li&gt;카메라 스크롤 월드(잔디 타일링 포함) 완료&lt;/li&gt;
&lt;li&gt;디버그 모드(좌표/Quadtree 라인 등) 일부 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;성과 지표:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stress Test에서 FPS 저하 체감 없음&lt;/li&gt;
&lt;li&gt;vector 삭제(erase) O(N) &amp;rarr; swap-pop으로 O(1) 최적화 적용&lt;/li&gt;
&lt;li&gt;메모리 무한 증가 버그 해결&lt;/li&gt;
&lt;li&gt;Quadtree 통합 후 충돌 정상화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;임팩트 액션:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대량 객체 처리 + 충돌 최적화 + UI/pause + 스탯 시스템을 하나의 루프에 통합&lt;/li&gt;
&lt;li&gt;통합 과정에서 발생한 메모리/흐름/좌표계 문제를 분석-수정하며 프로젝트 수행 역량을 확보&lt;/li&gt;
&lt;/ul&gt;
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진 루프 관점에서 문제를 추적(super Tick, return flow 등)&lt;/li&gt;
&lt;li&gt;정규화/형변환/const/링킹 등 기본기에서 터지는 버그를 케이스로 축적&lt;/li&gt;
&lt;li&gt;Quadtree 디버그와 카메라 좌표계까지 맞추며 통합 난이도를 경험&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다시 한다면 어떻게 설계할까?:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FPS/메모리 측정 루틴을 프로젝트 초기에 내장&lt;/li&gt;
&lt;li&gt;Quadtree 소유권/해제 책임을 문서로 먼저 확정&lt;/li&gt;
&lt;li&gt;beginPlay/prewarm 같은 초기화 루틴을 1회 보장하는 구조로 고정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;&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;상단 파란색 바 - 현재 EXP&lt;/li&gt;
&lt;li&gt;상단 빨간색 바 - 현재 HP&lt;/li&gt;
&lt;li&gt;좌측 하단 플레이어 Stat&lt;/li&gt;
&lt;li&gt;카메라 스크롤 월드(잔디 타일 포함)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2206&quot; data-origin-height=&quot;1466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSWfh/dJMcaiPJBZR/rI6liaSksMckkI5TG22BWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSWfh/dJMcaiPJBZR/rI6liaSksMckkI5TG22BWK/img.png&quot; data-alt=&quot;플레잉 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSWfh/dJMcaiPJBZR/rI6liaSksMckkI5TG22BWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSWfh%2FdJMcaiPJBZR%2FrI6liaSksMckkI5TG22BWK%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;596&quot; height=&quot;396&quot; data-origin-width=&quot;2206&quot; data-origin-height=&quot;1466&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;플레잉 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로파일링&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FPS 출력 화면&lt;/li&gt;
&lt;li&gt;Quadtree 적용 전/후 Stress Test 조건 화면&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;오히려 AABB가 더 빠르다(?) - 사실상 텍스트만 출력하는 게임에서는 큰 성능 차이는 없는 듯하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oB9D2/dJMcafrY64Y/2y0KLTjSOdfHZokRlcs3bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oB9D2/dJMcafrY64Y/2y0KLTjSOdfHZokRlcs3bk/img.png&quot; data-origin-width=&quot;2195&quot; data-origin-height=&quot;1324&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.5287%; margin-right: 10px;&quot; data-widthpercent=&quot;53.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oB9D2/dJMcafrY64Y/2y0KLTjSOdfHZokRlcs3bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoB9D2%2FdJMcafrY64Y%2F2y0KLTjSOdfHZokRlcs3bk%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;2195&quot; height=&quot;1324&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL6kNL/dJMcai3gug3/44ePafmC4dn6reZTatdmkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL6kNL/dJMcai3gug3/44ePafmC4dn6reZTatdmkK/img.png&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;1508&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;46.85&quot; style=&quot;width: 46.3085%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL6kNL/dJMcai3gug3/44ePafmC4dn6reZTatdmkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL6kNL%2FdJMcai3gug3%2F44ePafmC4dn6reZTatdmkK%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;2204&quot; height=&quot;1508&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;QuadTree / AABB&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오브젝트 풀링 전/후 프로세스 메모리 변화&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이미지가 거의 같아 보이지만 구현 방법은 다르다.. 그러나 이 또한 큰 성능 차이는 없는 듯 하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w0qeU/dJMcafld2dB/ZpUsJGKfGAwKngSuqFWiL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w0qeU/dJMcafld2dB/ZpUsJGKfGAwKngSuqFWiL0/img.png&quot; data-origin-width=&quot;2189&quot; data-origin-height=&quot;1497&quot; data-is-animation=&quot;false&quot; width=&quot;442&quot; height=&quot;302&quot; style=&quot;width: 48.2713%; margin-right: 10px;&quot; data-widthpercent=&quot;48.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w0qeU/dJMcafld2dB/ZpUsJGKfGAwKngSuqFWiL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw0qeU%2FdJMcafld2dB%2FZpUsJGKfGAwKngSuqFWiL0%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;2189&quot; height=&quot;1497&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkFk9d/dJMcagEsvGY/BP6joakd8ZqA1na4SjyZdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkFk9d/dJMcagEsvGY/BP6joakd8ZqA1na4SjyZdK/img.png&quot; data-origin-width=&quot;2218&quot; data-origin-height=&quot;1448&quot; data-is-animation=&quot;false&quot; width=&quot;499&quot; height=&quot;326&quot; style=&quot;width: 50.5659%;&quot; data-widthpercent=&quot;51.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkFk9d/dJMcagEsvGY/BP6joakd8ZqA1na4SjyZdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkFk9d%2FdJMcagEsvGY%2FBP6joakd8ZqA1na4SjyZdK%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;2218&quot; height=&quot;1448&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Object Pooling 으로 미리 공간할당 / 매 프레임 할당과 삭제 반복&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1차 프로젝트를 되돌아보며&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;어려웠던 Quadtree 시각화를 성공해 낸 점 - 어려운 기술도 계속 부딪혀 나감&lt;/li&gt;
&lt;li&gt;발표 피드백으로 프로파일링이 안 되는 것을 고친 점 - 피드백 수용 능력 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;부족
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임규모에 맞게 공통 스탯을 관리하는 클래스로 인터페이스 클래스를 쓴 점 - 현재 게임 규모에서는 좋았던 선택이었을 수 있으나 게임규모가 커진다는 가정 하에 component 설계를 하였으면 확장성이 좋았을 듯&lt;/li&gt;
&lt;li&gt;시간 부족의 핑계로 게임성을 높이지 않은 점 - 기술 구현을 빠르게 끝내고 게임성 높이는 데에도 시간을 두어야 할 듯&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>개발</category>
      <category>게임</category>
      <category>원티드</category>
      <category>원티드 포텐업 게임개발 4기</category>
      <category>콘솔</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/27</guid>
      <comments>https://learn-forever.tistory.com/27#entry27comment</comments>
      <pubDate>Fri, 13 Feb 2026 19:04:25 +0900</pubDate>
    </item>
    <item>
      <title>2025 NC soft 신입공채 게임 프로그래머 채용 코딩테스트 후기</title>
      <link>https://learn-forever.tistory.com/25</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;20251025&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔씨소프트 코딩테스트가 있었다. 프로그래머스 사이트로 시험이 진행되었고 총 3문제 120분이 주어졌다. 문제의 난이도는 크게 어렵지 않았다. 1,2번은 간단한 구현 문제여서 문제만 잘 읽고 그대로 표현하면 되는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 문제는 3번이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;255&quot; data-start=&quot;183&quot; data-ke-size=&quot;size16&quot;&gt;주어진 벡터의 원소들이 각각 무한히 뻗어가는 배수열을 가질 때,&lt;br /&gt;그 전체 수열에서 &lt;b&gt;k번째로 작은 수&lt;/b&gt;를 구하는 문제와 비슷하였다.&lt;/p&gt;
&lt;p data-end=&quot;476&quot; data-start=&quot;257&quot; data-ke-size=&quot;size16&quot;&gt;처음 문제를 읽었을 때는 &amp;ldquo;이건 간단하겠는데?&amp;rdquo; 싶었다.&lt;br /&gt;배수들을 전부 나열해서 정렬한 다음, k번째 원소를 찾으면 되겠다고 생각했다.&lt;br /&gt;하지만 곧바로 그 방식은 &lt;b&gt;TLE(Time Limit Exceeded)&lt;/b&gt; 이 날 수밖에 없다는 걸 깨달았다.&lt;br /&gt;왜냐하면 각 원소의 배수열이 무한히 이어지기 때문에&lt;br /&gt;모든 수를 직접 생성하고 정렬하는 건 &lt;b&gt;불가능한 접근&lt;/b&gt;이었기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;526&quot; data-start=&quot;478&quot; data-ke-size=&quot;size16&quot;&gt;그때부터 본격적으로 &amp;ldquo;이걸 어떻게 유한하게 줄일 수 있을까?&amp;rdquo;라는 고민이 시작됐다.&lt;/p&gt;
&lt;hr data-end=&quot;531&quot; data-start=&quot;528&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;553&quot; data-start=&quot;533&quot; data-ke-size=&quot;size23&quot;&gt;첫 시도: 이분 탐색&lt;/h3&gt;
&lt;p data-end=&quot;645&quot; data-start=&quot;554&quot; data-ke-size=&quot;size16&quot;&gt;평소처럼 &amp;ldquo;k번째 원소를 찾는 문제&amp;rdquo;를 보고 k의 범위가 아주 큰 값이면 자연스럽게 &lt;b&gt;이분 탐색&lt;/b&gt;이 떠오른다.&lt;br /&gt;범위를 잡고 mid 이하의 원소 개수를 세서 조정하면 되겠지 했다.&lt;/p&gt;
&lt;p data-end=&quot;725&quot; data-start=&quot;647&quot; data-ke-size=&quot;size16&quot;&gt;그런데 금방 벽에 부딪혔다.&lt;br /&gt;배수들이 서로 겹치기 때문에 단순히 &amp;ldquo;&amp;lt;= mid&amp;rdquo;인 값의 개수를 정확히 셀 수 없었다.&lt;br /&gt;예를 들어,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;813&quot; data-start=&quot;726&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;813&quot; data-start=&quot;726&quot;&gt;12는 3&amp;times;4로도, 4&amp;times;3으로도 만들어질 수 있다.&lt;br /&gt;이 중복이 정렬 순서를 교란시켜버리기 때문에,&lt;br /&gt;이분 탐색으로는 정확한 순서를 구할 수 없었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;818&quot; data-start=&quot;815&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;848&quot; data-start=&quot;820&quot; data-ke-size=&quot;size23&quot;&gt;사고 전환: 유한한 부분으로 줄이기&lt;/h3&gt;
&lt;p data-end=&quot;909&quot; data-start=&quot;849&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;무한히 뻗어가니 답이 없다고 생각했지만,&lt;br /&gt;k번째 작은 수는 어차피 &lt;b&gt;유한한 구간 안에&lt;/b&gt; 존재한다.&amp;rsquo;&lt;/p&gt;
&lt;p data-end=&quot;1011&quot; data-start=&quot;911&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 {1,2,3,4}에서 k = 29라면,&lt;br /&gt;벡터 크기 n이 4이므로 k보다 큰 4의 배수 중 가장 작은 값은 32다.&lt;br /&gt;즉, &lt;b&gt;8배수까지만 보면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1031&quot; data-start=&quot;1013&quot; data-ke-size=&quot;size16&quot;&gt;그럼 가능한 값들은 다음과 같다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;1,2,3,4,5,6,7,8, 2,4,6,8,10,12,14,16, 3,6,9,12,15,18,21,24, 4,8,12,16,20,24,28,32 &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1221&quot; data-start=&quot;1124&quot; data-ke-size=&quot;size16&quot;&gt;여기서 정렬하면 29번째 원소는 24가 된다.&lt;br /&gt;즉, 문제는 **무한한 수열이 아니라,&lt;br /&gt;&amp;lsquo;k보다 큰 벡터 크기의 배수까지 제한된 부분 문제&amp;rsquo;**로 축소할 수 있었다.&lt;/p&gt;
&lt;hr data-end=&quot;1226&quot; data-start=&quot;1223&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1264&quot; data-start=&quot;1228&quot; data-ke-size=&quot;size23&quot;&gt;전환점: priority_queue(우선순위 큐)&lt;/h3&gt;
&lt;p data-end=&quot;1341&quot; data-start=&quot;1265&quot; data-ke-size=&quot;size16&quot;&gt;문제를 다시 바라보다가 문득 priority_queue가 떠올랐다.&lt;br /&gt;&amp;lsquo;가장 큰 값들만 관리하면서 하나씩 줄여나가면 되지 않을까?&amp;rsquo;&lt;/p&gt;
&lt;p data-end=&quot;1416&quot; data-start=&quot;1343&quot; data-ke-size=&quot;size16&quot;&gt;그 순간 퍼즐이 맞춰졌다.&lt;br /&gt;각 배수열의 마지막 값(가장 큰 배수)과 어떤 수의 배수인지 알 수 있는 수를 pair 형식으로 힙에 넣고,&lt;br /&gt;가장 큰 수를 하나씩 꺼내며 줄여가면 된다.&lt;/p&gt;
&lt;p data-end=&quot;1425&quot; data-start=&quot;1418&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;pq&lt;/span&gt;&lt;/span&gt;&lt;span&gt; = { {&lt;/span&gt;&lt;span&gt;&lt;span&gt;32&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt;4&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, {&lt;/span&gt;&lt;span&gt;&lt;span&gt;24&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, {&lt;/span&gt;&lt;span&gt;&lt;span&gt;16&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, {&lt;/span&gt;&lt;span&gt;&lt;span&gt;8&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;} } &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1506&quot; data-start=&quot;1473&quot; data-ke-size=&quot;size16&quot;&gt;형태로 넣어두면, top은 항상 가장 큰 수(32)가 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1629&quot; data-start=&quot;1508&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1543&quot; data-start=&quot;1508&quot;&gt;top을 꺼내서 4를 빼고 다시 넣는다 &amp;rarr; {28,4}&lt;/li&gt;
&lt;li data-end=&quot;1575&quot; data-start=&quot;1544&quot;&gt;다시 top을 꺼내서 4를 빼고 &amp;rarr; {24,4}&lt;/li&gt;
&lt;li data-end=&quot;1629&quot; data-start=&quot;1576&quot;&gt;&amp;hellip;이 과정을 k번째까지 반복하면,&lt;br /&gt;마지막으로 꺼낸 값이 곧 k번째로 작은 값이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1642&quot; data-start=&quot;1631&quot; data-ke-size=&quot;size16&quot;&gt;이 방식의 핵심은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1745&quot; data-start=&quot;1643&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1669&quot; data-start=&quot;1643&quot;&gt;&lt;b&gt;배수 중복이 자연스럽게 처리되고&lt;/b&gt;,&lt;/li&gt;
&lt;li data-end=&quot;1745&quot; data-start=&quot;1670&quot;&gt;&lt;b&gt;항상 최대 n개의 값만 관리&lt;/b&gt;하기 때문에&lt;br /&gt;k가 아무리 커도 &lt;b&gt;O(n log n)&lt;/b&gt; 수준의 효율로 해결된다는 점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1750&quot; data-start=&quot;1747&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1769&quot; data-start=&quot;1752&quot; data-ke-size=&quot;size23&quot;&gt;시간복잡도 직관&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1891&quot; data-start=&quot;1770&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1783&quot; data-start=&quot;1770&quot;&gt;n = 벡터 크기&lt;/li&gt;
&lt;li data-end=&quot;1813&quot; data-start=&quot;1784&quot;&gt;k = 매우 큰 수 (최대 10⁸ 이상 가능)&lt;/li&gt;
&lt;li data-end=&quot;1891&quot; data-start=&quot;1814&quot;&gt;하지만 힙에는 n개의 원소만 유지되므로,&lt;br /&gt;각 pop/push가 O(log n), 전체는 O(n log n) 내에 해결 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1897&quot; data-start=&quot;1893&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-end=&quot;1954&quot; data-start=&quot;1898&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1954&quot; data-start=&quot;1900&quot; data-ke-size=&quot;size16&quot;&gt;n이 10,000이고 k가 100,000,000이라도&lt;br /&gt;최대 10,000번만 순회하면 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-end=&quot;1985&quot; data-start=&quot;1961&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;예시: k = 29일 때의 과정&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;초기 힙: {32,4}, {24,3}, {16,2}, {8,1} &lt;/span&gt;&lt;span&gt;&lt;span&gt;32번째: top {32,4} &amp;rarr; {28,4}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;31번째: top {28,4} &amp;rarr; {24,4}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;30번째: top {24,4} &amp;rarr; {20,4}&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;29번째: top {24,3} &amp;rarr; 답 = 24&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2145&quot; data-start=&quot;2135&quot; data-ke-size=&quot;size16&quot;&gt;결과: &lt;b&gt;24&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;2150&quot; data-start=&quot;2147&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2164&quot; data-start=&quot;2152&quot; data-ke-size=&quot;size23&quot;&gt;마무리하며&lt;/h3&gt;
&lt;p data-end=&quot;2225&quot; data-start=&quot;2165&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 단순한 구현 문제가 아니라,&lt;br /&gt;&amp;ldquo;&lt;b&gt;무한을 유한으로 바꾸는 사고의 전환&lt;/b&gt;&amp;rdquo;이 필요한 문제였다.&lt;/p&gt;
&lt;p data-end=&quot;2359&quot; data-start=&quot;2227&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 단순히 모든 배수를 나열하고 정렬하는 단순한 brute-force 접근이 떠올랐고,&lt;br /&gt;그다음엔 이분 탐색으로 풀 수 있지 않을까 시도했지만 실패했다.&lt;br /&gt;결국 핵심은 &lt;b&gt;정렬을 뒤집어 시뮬레이션하는 우선순위 큐의 관점&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;2383&quot; data-start=&quot;2361&quot; data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 배운 점은 명확하다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2451&quot; data-start=&quot;2384&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2451&quot; data-start=&quot;2386&quot; data-ke-size=&quot;size16&quot;&gt;문제를 풀 때는 &lt;b&gt;풀어야 할 데이터의 크기&lt;/b&gt;가 아니라,&lt;br /&gt;&lt;b&gt;&amp;ldquo;진짜 필요한 범위&amp;rdquo;를 먼저 찾아내야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>코딩테스트</category>
      <category>c++</category>
      <category>priority_queue</category>
      <category>문제풀이</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>회고</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/25</guid>
      <comments>https://learn-forever.tistory.com/25#entry25comment</comments>
      <pubDate>Fri, 19 Dec 2025 18:00:13 +0900</pubDate>
    </item>
    <item>
      <title>DrawCall을 줄여야 하는 이유, drawcall,batches,setpass call</title>
      <link>https://learn-forever.tistory.com/26</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;게임개발에서 드로우콜을 줄여야하는 것은 개발자의 의무이다. 그렇다면 드로우콜은 무엇이고 왜 줄여야 하며 줄이는 방법에는 무엇이 있을까? 더하여 드로우콜과 batches 그리고 setpass call은 무엇일까?&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;본론&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;251&quot; data-start=&quot;173&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Draw Call&lt;/b&gt;은 CPU가 GPU에게&lt;br /&gt;&amp;ldquo;이 오브젝트를 이 머티리얼과 셰이더로 렌더링하라&amp;rdquo;&lt;br /&gt;라고 명령하는 호출 단위입니다.&lt;/p&gt;
&lt;p data-end=&quot;465&quot; data-start=&quot;253&quot; data-ke-size=&quot;size16&quot;&gt;GPU는 수천 개의 스레드로 병렬 처리가 가능하지만,CPU는 이러한 Draw Call들을 직렬적으로 하나씩 처리해야 합니다.&lt;br /&gt;따라서 &lt;b&gt;Draw Call의 수가 많아지면 CPU가 병목(Bottleneck)&lt;/b&gt; 이 되고,결국 프레임 처리 속도가 떨어집니다.&lt;br /&gt;즉, &lt;b&gt;Draw Call을 줄인다는 것은 CPU의 명령 전송 부담을 줄여 최적화를 한다는 의미&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-end=&quot;470&quot; data-start=&quot;467&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;551&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;Unity에서는 &lt;b&gt;Batches&lt;/b&gt;라는 개념을 볼 수 있습니다.이는 Draw Call과 유사하지만, &lt;b&gt;약간 더 큰 개념&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;741&quot; data-start=&quot;553&quot; data-ke-size=&quot;size16&quot;&gt;OpenGL의 렌더링 과정을 예로 들면 다음과 같습니다.오브젝트를 그리기 위해서는&lt;/p&gt;
&lt;p data-end=&quot;741&quot; data-start=&quot;553&quot; data-ke-size=&quot;size16&quot;&gt;glBindBuffer(VBO/IBO), glUseProgram(Shader), glBindTexture(Texture)&lt;br /&gt;등의 &lt;b&gt;바인딩(상태 전환)&lt;/b&gt; 이 먼저 이루어지고,그 다음 glDraw__() 호출이 수행됩니다.이때 &lt;b&gt;같은 상태(State)&lt;/b&gt; 를 공유하는 오브젝트들이 많다면 한 번의 바인딩으로 여러 번의 glDraw__() 호출을 실행할 수 있습니다.&lt;br /&gt;이러한 &lt;b&gt;한 세트의 바인딩과 그 안의 여러 Draw Call 묶음이 Batch&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;1002&quot; data-start=&quot;884&quot; data-ke-size=&quot;size16&quot;&gt;즉, Draw Call은 glDraw__() 호출 하나하나를 의미하지만, Batch는 그보다 상위의 개념으로,&lt;br /&gt;&lt;b&gt;같은 상태에서 연속으로 처리되는 Draw Call들의 묶음&lt;/b&gt;이라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1166&quot; data-start=&quot;1004&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로, &lt;b&gt;Draw Call의 절대 수보다 Batch의 수를 줄이는 것이 더 직접적인 최적화 효과&lt;/b&gt;를 줍니다.&lt;br /&gt;왜냐하면, Batch를 줄인다는 것은 &lt;b&gt;상태 전환(binding)&lt;/b&gt; 횟수를 줄인다는 뜻이고, 이 바인딩 과정이 CPU에서 수행되며 비용이 매우 비싸기 때문입니다.&lt;/p&gt;
&lt;hr data-end=&quot;1171&quot; data-start=&quot;1168&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;1331&quot; data-start=&quot;1173&quot; data-ke-size=&quot;size16&quot;&gt;다음으로 &lt;b&gt;SetPass Call&lt;/b&gt;은 GPU의 실제 렌더링 파이프라인 상태(머티리얼, 셰이더, 텍스처 등)가 변경될 때마다 발생합니다.&lt;br /&gt;즉, glUseProgram, glBindTexture, glBlendFunc 등의 상태 전환이 일어날 때마다 증가합니다.&lt;/p&gt;
&lt;p data-end=&quot;1423&quot; data-start=&quot;1333&quot; data-ke-size=&quot;size16&quot;&gt;SetPass Call은 Batch와 거의 1:1로 대응되며, &lt;b&gt;최종적으로는 SetPass Call을 줄이는 것이 진정한 의미의 렌더링 최적화&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-end=&quot;1428&quot; data-start=&quot;1425&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;1497&quot; data-start=&quot;1430&quot; data-ke-size=&quot;size16&quot;&gt;Draw Call을 줄이는 대표적인 방법으로는 &lt;b&gt;Batching&lt;/b&gt;과 &lt;b&gt;GPU Instancing&lt;/b&gt;이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1759&quot; data-start=&quot;1499&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1631&quot; data-start=&quot;1499&quot;&gt;&lt;b&gt;Batching&lt;/b&gt;은 여러 오브젝트의 버텍스 데이터를 묶어서 CPU가 한 번에 전달하도록 하여 Draw Call을 줄이는 방법입니다.&lt;br /&gt;(예: Static/Dynamic Batching, SRP Batcher 등)&lt;/li&gt;
&lt;li data-end=&quot;1759&quot; data-start=&quot;1633&quot;&gt;&lt;b&gt;GPU Instancing&lt;/b&gt;은 동일한 머티리얼을 사용하는 오브젝트를 GPU 내부에서 복제하여 렌더링하는 기술입니다.&lt;br /&gt;즉, CPU가 동일한 명령을 여러 번 보내지 않아도 GPU가 병렬적으로 처리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1764&quot; data-start=&quot;1761&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1776&quot; data-start=&quot;1766&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1885&quot; data-start=&quot;1778&quot; data-ke-size=&quot;size16&quot;&gt;드로우콜을 줄인다는 것은 단순히 호출 수를 줄인다는 의미가 아니라,&lt;br /&gt;&lt;b&gt;CPU에서 비싼 바인딩(State Change)을 최소화하여 GPU의 병렬 성능을 최대한 활용한다는 뜻&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;2014&quot; data-start=&quot;1887&quot; data-ke-size=&quot;size16&quot;&gt;정확히 말하면, &amp;ldquo;Draw Call을 줄인다&amp;rdquo;는 말은 &amp;ldquo;&lt;b&gt;SetPass Call(상태 변경 수)을 줄인다&lt;/b&gt;&amp;rdquo;와 같은 의미이며,&lt;br /&gt;이는 곧 &lt;b&gt;CPU의 부담을 줄이고 GPU가 쉬지 않고 일하게 만드는 최적화의 핵심&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 내용을 바탕으로 개발자 면접 간&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. Drawcall이 무엇인가요?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. Drawcall을 왜 줄여야 하나요?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 경험해본 게임 프로젝트에서 Drawcall을 줄이기 위해 했던 노력이 있나요?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;등에 대한 질문을 생각해 볼 수 있겠다. 스스로에게 질문을 해보고 답해보자.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;헷갈리는 부분은 보완하기! 찡긋 O.&amp;lt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;틀린 부분이나 보완해줬으면 하는 내용이 있으면 댓글로 알려주세요! 추가 설명이 필요하신 부분도 댓글로 알려주세요.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떠한 피드백도 환영입니다. 긴 글 읽어주셔서 감사합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출처 : ChatGPT&lt;/p&gt;</description>
      <category>개발면접준비</category>
      <category>batches</category>
      <category>DrawCall</category>
      <category>SetPass Call</category>
      <category>최적화</category>
      <author>개발지식 블로그</author>
      <guid isPermaLink="true">https://learn-forever.tistory.com/26</guid>
      <comments>https://learn-forever.tistory.com/26#entry26comment</comments>
      <pubDate>Thu, 6 Nov 2025 22:31:25 +0900</pubDate>
    </item>
  </channel>
</rss>