<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>머리없는개발자</title>
    <link>https://nybot-house.tistory.com/</link>
    <description>C#, 유니티 게임 개발자</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 21:49:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HeadlessCreator</managingEditor>
    <image>
      <title>머리없는개발자</title>
      <url>https://tistory1.daumcdn.net/tistory/3149735/attach/5be99c65c3b241918d9e04c233f82a29</url>
      <link>https://nybot-house.tistory.com</link>
    </image>
    <item>
      <title>[Unity] Photon Fusion 2 멀티플레이 개발, 핵심 요약 8가지</title>
      <link>https://nybot-house.tistory.com/129</link>
      <description>&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;싱글플레이 게임을 만들다가 멀티플레이(Photon Fusion 2)로 넘어오면 가장 헷갈리는 것이 '누가 계산하고,&lt;/span&gt;&lt;span&gt; 어떻게 공유하는가?'&lt;/span&gt;&lt;span&gt;&amp;nbsp;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일주일간 삽질하며 깨달은 &lt;/span&gt;&lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;5&quot;&gt;Fusion의 필수 개념 5가지&lt;/b&gt;&lt;span&gt;와,&lt;/span&gt;&lt;span&gt; &amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;5&quot;&gt;디테일 3가지&lt;/b&gt;&lt;span&gt;를 정리해 본다.&lt;/span&gt;&lt;span&gt; 이 8가지만 알면 포톤 퓨전을 활용하 웬만한 멀티플레이 로직은 다 짤 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;Part 1. 기본 뼈대 (필수 5가지)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 기초가 되는 5가지 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;1. NetworkBehaviour (모든 것의 시작)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;개념&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 유니티의 &lt;/span&gt;MonoBehaviour&lt;span&gt; 대신 상속받는 클래스.&lt;/span&gt;&lt;span&gt; 네트워크 기능을 쓰려면 무조건 이걸 상속받아야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;주요 메서드&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spawned()&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;br /&gt;&lt;/span&gt;Start()&lt;span&gt;와 같다.&lt;/span&gt;&lt;span&gt; 네트워크 객체가 생성되고 &lt;/span&gt;&lt;b data-index-in-node=&quot;40&quot; data-path-to-node=&quot;10,1,1,0,0&quot;&gt;동기화가 준비 완료된 시점&lt;/b&gt;&lt;span&gt;에 실행된다.&lt;/span&gt;&lt;span&gt; (초기화는 여기서!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;FixedUpdateNetwork()&lt;span&gt; (FUN):&lt;/span&gt;&lt;span&gt; &lt;br /&gt;&lt;/span&gt;FixedUpdate()&lt;span&gt;와 같다.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;b data-index-in-node=&quot;49&quot; data-path-to-node=&quot;10,1,1,1,0&quot;&gt;서버와 동기화된 물리 연산/로직&lt;/b&gt;&lt;span&gt;은 반드시 여기서 돌려야 한다.&lt;/span&gt;&lt;span&gt; (틱 단위 실행)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Render()&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;br /&gt;&lt;/span&gt;Update()&lt;span&gt;와 같다.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;10,1,1,2,0&quot;&gt;부드러운 화면 이동, 애니메이션, UI 갱신&lt;/b&gt;&lt;span&gt; 등은 여기서 처리한다.&lt;/span&gt;&lt;span&gt; (프레임 단위 실행)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;2. NetworkObject &amp;amp; NetworkTransform &lt;/b&gt;컴포넌트&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;NetworkObject&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 이 게임오브젝트가 네트워크상에서 **고유한 ID를 가진 존재**임을 증명하는 주민등록증.&lt;/span&gt;&lt;span&gt; 프리팹 최상단에 위치해야만 한다!&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;NetworkTransform&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 위치(Position)와 회전(Rotation)을 자동으로 동기화해 주는 컴포넌트.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,0,0&quot;&gt;팁: 크기(Scale) 동기화는 끄고, 필요하다면 별도 스크립트로 제어하는 게 대역폭 절약에 좋다.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;3. NetworkRunner (게임의 신)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;개념&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 방을 만들고(Host),&lt;/span&gt;&lt;span&gt; 접속하고(Client),&lt;/span&gt;&lt;span&gt; 네트워크 전반을 관리하는 &lt;/span&gt;&lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;14,0,0&quot;&gt;매니저&lt;/b&gt;&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;사용&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;Runner.Spawn(...)&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;Runner.Despawn(...)&lt;span&gt; 처럼 객체를 생성하고 파괴할 때 항상 이 친구를 통해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15&quot;&gt;4. [Networked] 변수 (상태 동기화)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;개념&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;내 컴퓨터에 있는 변수 값을 다른 사람 컴퓨터에도 똑같이 보여줘!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;사용&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; HP,&lt;/span&gt;&lt;span&gt; 공격력,&lt;/span&gt;&lt;span&gt; 공의 크기(Radius) 등 &lt;/span&gt;&lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;16,1,0&quot;&gt;게임의 중요한 상태값&lt;/b&gt;&lt;span&gt;은 무조건 이걸 써야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770642317827&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Networked] public float HP { get; set; } // 자동으로 동기화됨&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17&quot;&gt;5. Object.HasStateAuthority (권한 체크)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;18&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,0,0&quot;&gt;개념&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;이 물건의 주인이 나인가?&lt;/span&gt;&lt;span&gt;&quot;를 묻는 것.&lt;/span&gt;&lt;span&gt; (Shared 모드 기준:&lt;/span&gt;&lt;span&gt; 객체를 생성한 사람)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0&quot;&gt;왜 중요한가?&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 멀티플레이에서는 코드 한 줄이 10명의 컴퓨터에서 동시에 실행되기 때문에 안 막아주면 한 번 실행이 아니라 10번 실행되는 참사가 발생하는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,2,0&quot;&gt;필수 패턴&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770642365747&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 내가 주인이 아니면 실행하지 마! (중복 실행 방지)
if (!Object.HasStateAuthority) return;

// 주인인 나만 이 아래 로직(점수 계산, 파괴 등)을 수행한다.
CalculateScore();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20&quot;&gt;Part 2. 디테일&lt;/b&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본 뼈대 위에 살을 붙이는 고급 기술.&lt;/span&gt;&lt;span&gt; 이걸 알아야 &quot;렉 없는 게임&quot;을 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;22&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22&quot;&gt;6. 입력 시스템 (Prediction &amp;amp; Input)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0,0&quot;&gt;문제&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 키보드를 누르자마자 서버에 갔다 오면 반응이 느려서 게임이 답답해집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,1,0&quot;&gt;해결 (Client-Side Prediction)&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;23,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GetInput()&lt;span&gt;으로 입력을 받는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;클라이언트는 서버 허락 없이 &lt;/span&gt;&lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;23,1,1,1,0&quot;&gt;일단 먼저 움직인다(예측).&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;나중에 서버가 &quot;너 위치 거기 아니야&quot;라고 하면 Fusion이 알아서 부드럽게 보정해 준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,2,0&quot;&gt;결론&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 이동 로직은 &lt;/span&gt;FixedUpdateNetwork&lt;span&gt; 안에서 &lt;/span&gt;GetInput&lt;span&gt;을 사용해 처리하도록!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24&quot;&gt;7. OnChanged (변경 감지 &amp;amp; 최적화)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;25&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,0,0&quot;&gt;문제&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;Render()&lt;span&gt;에서 매번 &lt;/span&gt;if (HP &amp;lt; 0)&lt;span&gt; 체크하는 건 낭비.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,1,0&quot;&gt;해결&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 값이 &lt;/span&gt;&lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;25,1,0&quot;&gt;바뀌었을 때만&lt;/b&gt;&lt;span&gt; 특정 함수를 실행하게 만든다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1770642426663&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Networked(OnChanged = nameof(OnHpChanged))]
public float HP { get; set; }

// HP가 변할 때만 호출됨 (UI 갱신, 피격 애니메이션 등에 최고)
static void OnHpChanged(Changed&amp;lt;NWCharacter&amp;gt; changed) {
    changed.Behaviour.UpdateHPBar();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-path-to-node=&quot;26&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;26&quot;&gt;8. RPC (Remote Procedure Call)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;27&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,0,0&quot;&gt;개념&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;상태&quot;가 아니라 **&quot;사건(Event)&quot;**을 알릴 때 쓴다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,1,0&quot;&gt;특징&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;[Networked]&lt;span&gt; 변수와 달리,&lt;/span&gt;&lt;span&gt; 중간에 데이터가 유실되거나 늦게 도착할 수도 있다.&lt;/span&gt;&lt;span&gt; (Fire and Forget)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,2,0&quot;&gt;용도&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 총알 발사 소리,&lt;/span&gt;&lt;span&gt; 폭발 이펙트,&lt;/span&gt;&lt;span&gt; 감정 표현 등 &lt;/span&gt;&lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;27,2,0&quot;&gt;순간적이고 일회성인 연출&lt;/b&gt;&lt;span&gt;에 사용한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;27,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,2,1,0,0&quot;&gt;주의: HP나 점수 같은 중요한 데이터는 RPC로 보내면 안 돼! (싱크 깨짐의 주범)&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;29&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29&quot;&gt;[실전 적용] 공 먹기 게임의 로직 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;내가 만들었던 멀티 플레이 게임 &quot;공 먹기(EatBall)&quot; 게임에서 이 개념들이 어떻게 연결되는지 정리해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;31&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,0,0&quot;&gt;입력&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 플레이어가 키보드를 누르면 &lt;/span&gt;NetworkRunner&lt;span&gt;가 입력을 수집해서 서버로 보냄 (&lt;/span&gt;&lt;b data-index-in-node=&quot;51&quot; data-path-to-node=&quot;31,0,0&quot;&gt;개념 6&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,1,0&quot;&gt;이동&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;FixedUpdateNetwork&lt;span&gt;에서 &lt;/span&gt;GetInput&lt;span&gt;을 받아 캐릭터를 이동시킴 (&lt;/span&gt;&lt;b data-index-in-node=&quot;49&quot; data-path-to-node=&quot;31,1,0&quot;&gt;개념 1&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,2,0&quot;&gt;충돌&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 파편과 캐릭터가 충돌(&lt;/span&gt;OnTriggerEnter&lt;span&gt;).&lt;/span&gt;&lt;span&gt; 이때 &lt;/span&gt;if (!HasStateAuthority) return;&lt;span&gt;으로 마스터만 계산하게 함 (&lt;/span&gt;&lt;b data-index-in-node=&quot;82&quot; data-path-to-node=&quot;31,2,0&quot;&gt;개념 5&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,3,0&quot;&gt;판정&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 파편을 먹었으니 반지름(&lt;/span&gt;Radius&lt;span&gt;)을 증가시킴.&lt;/span&gt;&lt;span&gt; 이 변수는 &lt;/span&gt;[Networked]&lt;span&gt;로 선언되어 있음 (&lt;/span&gt;&lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;31,3,0&quot;&gt;개념 4&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,4,0&quot;&gt;동기화&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [Networked] &lt;/span&gt;Radius&lt;span&gt; 값이 변하자마자 모든 클라이언트에게 전파됨.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,5,0&quot;&gt;화면 갱신&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;Render()&lt;span&gt; 혹은 &lt;/span&gt;OnChanged&lt;span&gt;에서 변화된 &lt;/span&gt;Radius&lt;span&gt;를 감지하고,&lt;/span&gt;&lt;span&gt; 캐릭터의 크기(Scale)를 키움 (&lt;/span&gt;&lt;b data-index-in-node=&quot;69&quot; data-path-to-node=&quot;31,5,0&quot;&gt;개념 7&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;31,6,0&quot;&gt;연출&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 먹는 순간 &quot;냠!&lt;/span&gt;&lt;span&gt;&quot; 소리와 파티클 효과를 &lt;/span&gt;RPC&lt;span&gt;로 전송해 모든 사람에게 보여줌 (&lt;/span&gt;&lt;b data-index-in-node=&quot;49&quot; data-path-to-node=&quot;31,6,0&quot;&gt;개념 8&lt;/b&gt;&lt;span&gt;).&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>유니티 C#/Photon Fusion 2</category>
      <category>fusion</category>
      <category>photon</category>
      <category>Photon Fusion 2</category>
      <category>unity</category>
      <category>네트워크</category>
      <category>멀티게임</category>
      <category>유니티</category>
      <category>포톤</category>
      <category>포톤 퓨전 2</category>
      <category>퓨전 2</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/129</guid>
      <comments>https://nybot-house.tistory.com/129#entry129comment</comments>
      <pubDate>Mon, 9 Feb 2026 22:10:28 +0900</pubDate>
    </item>
    <item>
      <title>슈퍼 먹방 드래곤 Mega Chomp Dragon 이 출시되었습니다</title>
      <link>https://nybot-house.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;24년 7월 초부터 기획을 시작했던 게임이 25년 2월에 되어서야 마무리 되었습니다.&lt;br /&gt;여러가지 시행착오와, 여러번의 기획 수정을 거쳐 마침내 구글 플레이 스토어 및 IOS에 퍼블리싱 완료!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UlEifRJ3XX4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게임 트레일러 영상&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;구글 플레이 스토어 링크&quot; href=&quot;https://play.google.com/store/apps/details?id=com.gamonsterz.megachompdragon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구글 플레이 스토어 링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/%EC%8A%88%ED%8D%BC-%EB%A8%B9%EB%B0%A9-%EB%93%9C%EB%9E%98%EA%B3%A4/id6743288753&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;애플 앱스토어 링크&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1740360710511&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;슈퍼 먹방 드래곤 - Google Play 앱&quot; data-og-description=&quot;ENDLESS SURVIVAL OFFLINE 3D MOBILE GAME&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.gamonsterz.megachompdragon&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.gamonsterz.megachompdragon&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bIv18N/hyYjxdu5uL/1zKn2ey3zJurQiwe9yohK1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/IY0iM/hyYjs4vFe6/miXGVHqn2o7WLKKv45CxY0/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/FqHRT/hyYjrq0eMH/46Fal4FlE3csFvrsL99LHk/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/%EC%8A%88%ED%8D%BC-%EB%A8%B9%EB%B0%A9-%EB%93%9C%EB%9E%98%EA%B3%A4/id6743288753&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.gamonsterz.megachompdragon&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bIv18N/hyYjxdu5uL/1zKn2ey3zJurQiwe9yohK1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/IY0iM/hyYjs4vFe6/miXGVHqn2o7WLKKv45CxY0/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/FqHRT/hyYjrq0eMH/46Fal4FlE3csFvrsL99LHk/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;슈퍼 먹방 드래곤 - Google Play 앱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ENDLESS SURVIVAL OFFLINE 3D MOBILE GAME&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게임 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;귀여운 드래곤이 무방비 상태의 도시 한복판에 떨어진다면? 아마 먹방을 찍을 수 있지 않을까요?&lt;br /&gt;작은 강아지부터, 사람(시민), 자동차, 집, 빌딩까지, 드래곤이 점점 커지며 더 큰 것들을 먹어치우는 쾌감이 폭발합니다.&lt;br /&gt;&amp;nbsp;플레이어는 드래곤을 조종해 생존 시간 안에 최대한 많은 것을 먹으며 성장해야 합니다. 하지만 조심하세요! 더 많은 것을 먹어치울 수록 위험도가 올라가고, 그로 인해 경찰, 군대까지 출동하여 당신을 가로막으려고 할 것입니다. 다양한 드래곤 스킨과 능력 업그레이드를 통해 최강의 먹방 드래곤으로 진화하세요!&lt;br /&gt;&amp;nbsp;간편한 조작, 직관적인 게임 플레이, 그리고 중독성 있는 성장 시스템! 과연 당신의 드래곤은 어디까지 먹어치울 수 있을까요?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게임 진행 및 기본 조작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.드래곤이 도시 한복판에 떨어지며 게임이 시작됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq2Wy9/btsMvaHBk37/00FYk3KZWbCLyAZ89g3MA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq2Wy9/btsMvaHBk37/00FYk3KZWbCLyAZ89g3MA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq2Wy9/btsMvaHBk37/00FYk3KZWbCLyAZ89g3MA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq2Wy9%2FbtsMvaHBk37%2F00FYk3KZWbCLyAZ89g3MA1%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;300&quot; height=&quot;643&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 터치 앤 드래그로 드래곤을 움직이고, 다른 손으로 화면을 또 터치하면 부스트!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOx93f/btsMtercc9l/DuX8AJfjcTE2VQGS2gSXyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOx93f/btsMtercc9l/DuX8AJfjcTE2VQGS2gSXyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOx93f/btsMtercc9l/DuX8AJfjcTE2VQGS2gSXyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOx93f%2FbtsMtercc9l%2FDuX8AJfjcTE2VQGS2gSXyk%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;300&quot; height=&quot;643&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 레벨1의 자잘한 물체들, 시민들을 먹어치워 보세요! 드래곤이 경험치를 얻고 체력을 회복합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcbbSv/btsMs8EqARw/HFxMe3bB69gVdxCYqTohNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcbbSv/btsMs8EqARw/HFxMe3bB69gVdxCYqTohNk/img.png&quot; data-alt=&quot;체력은 계속 떨어지니 0 이하로 떨어지지 않도록 주의해 주세요!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcbbSv/btsMs8EqARw/HFxMe3bB69gVdxCYqTohNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcbbSv%2FbtsMs8EqARw%2FHFxMe3bB69gVdxCYqTohNk%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;300&quot; height=&quot;643&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;체력은 계속 떨어지니 0 이하로 떨어지지 않도록 주의해 주세요!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 시민과 같은 생명체를 먹으면 많은 경험치를 얻지만, 위험도 레벨이 상승해요!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djWs9c/btsMuhm7tD2/7ScqR789PAnC5R5NeLYR30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djWs9c/btsMuhm7tD2/7ScqR789PAnC5R5NeLYR30/img.png&quot; data-alt=&quot;우상단 별 주의!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djWs9c/btsMuhm7tD2/7ScqR789PAnC5R5NeLYR30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjWs9c%2FbtsMuhm7tD2%2F7ScqR789PAnC5R5NeLYR30%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;300&quot; height=&quot;39&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;39&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;6. 무생물체를 먹으면 위험도 레벨은 상승하지 않지만, 얻는 경험치와 체력 회복이 적어요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC776U/btsMuxQRc05/oJXUcuoWh9jf3A67mFE6C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC776U/btsMuxQRc05/oJXUcuoWh9jf3A67mFE6C0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC776U/btsMuxQRc05/oJXUcuoWh9jf3A67mFE6C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC776U%2FbtsMuxQRc05%2FoJXUcuoWh9jf3A67mFE6C0%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;300&quot; height=&quot;179&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 드래곤이 레벨 업 했어요! 드래곤의 덩치가 커지며 조금 더 높은 레벨의 물체들을 잡아먹을 수 있어요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJDYa/btsMtuAvmP6/8tWKjtHHKtKonCmDRNN5uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJDYa/btsMtuAvmP6/8tWKjtHHKtKonCmDRNN5uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJDYa/btsMtuAvmP6/8tWKjtHHKtKonCmDRNN5uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJDYa%2FbtsMtuAvmP6%2F8tWKjtHHKtKonCmDRNN5uK%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;300&quot; height=&quot;385&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 위험도 레벨이 충분히 오르면 경찰이 출동합니다. 위험도 레벨이 상승할수록 더 높은 등급의 경찰, 군대까지 출동하니 주의해 주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wzp4b/btsMuxDiDcg/KuOd9pdDx2HfBDLkfU0ZJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wzp4b/btsMuxDiDcg/KuOd9pdDx2HfBDLkfU0ZJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wzp4b/btsMuxDiDcg/KuOd9pdDx2HfBDLkfU0ZJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwzp4b%2FbtsMuxDiDcg%2FKuOd9pdDx2HfBDLkfU0ZJ0%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;300&quot; height=&quot;643&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 계속 레벨 업 하여 도시의 모든 것을 먹어치우세요!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5i0sn/btsMtb2lQ8s/4b2ozkNuE0Rk7Wds7rOH71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5i0sn/btsMtb2lQ8s/4b2ozkNuE0Rk7Wds7rOH71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5i0sn/btsMtb2lQ8s/4b2ozkNuE0Rk7Wds7rOH71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5i0sn%2FbtsMtb2lQ8s%2F4b2ozkNuE0Rk7Wds7rOH71%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;300&quot; height=&quot;650&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;1. 밸런스 문제 픽스.&lt;br /&gt;&amp;nbsp;2. 난이도 다양화 - 고난이도 모드 추가&lt;br /&gt;&amp;nbsp;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;&amp;nbsp;슈퍼 먹방 드래곤 게임은 학창시절 즐겁게 플레이 했던 게임인 '스포어'에서 영감을 얻고, 실제 게임 진행 방식은 모바일 게임 '헝그리 샤크'에서 영향을 받았습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%8A%A4%ED%8F%AC%EC%96%B4(%EA%B2%8C%EC%9E%84)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스포어&lt;/a&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1736911974835&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스포어(게임)&quot; data-og-description=&quot;공식 트레일러 Maxis 에서 제작하고 EA 에서 유통한 시뮬레이션 게임 . 시스템 요구 사항 구분 최소 사양&quot; data-og-host=&quot;namu.wiki&quot; data-og-source-url=&quot;https://namu.wiki/w/%EC%8A%A4%ED%8F%AC%EC%96%B4(%EA%B2%8C%EC%9E%84)&quot; data-og-url=&quot;https://namu.wiki/w/%EC%8A%A4%ED%8F%AC%EC%96%B4(%EA%B2%8C%EC%9E%84)&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zAmnx/hyX4zu8uWf/NB2gyPVp32gvn2TBu0HuTK/img.jpg?width=320&amp;amp;height=449&amp;amp;face=0_0_320_449&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%8A%A4%ED%8F%AC%EC%96%B4(%EA%B2%8C%EC%9E%84)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://namu.wiki/w/%EC%8A%A4%ED%8F%AC%EC%96%B4(%EA%B2%8C%EC%9E%84)&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zAmnx/hyX4zu8uWf/NB2gyPVp32gvn2TBu0HuTK/img.jpg?width=320&amp;amp;height=449&amp;amp;face=0_0_320_449');&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;스포어(게임)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;공식 트레일러 Maxis 에서 제작하고 EA 에서 유통한 시뮬레이션 게임 . 시스템 요구 사항 구분 최소 사양&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;namu.wiki&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스포어 게임은 세포 단계에서부터 시작하여, 세포가 조그마한 세포들을 먹어 치우며 점차 성장해 나가고, 진화하여 뭍으로 나가 부족 사회를 이루고, 이후 계속해서 진화해 나가며 도시를 이루고 우주까지 정복해 나가는 종족을 만들어 나가는 게임입니다.&lt;br /&gt;처음에는 이것을 똑같이 따라하고 싶어 벌레, 풀 부터 먹어 나가며 나중에 사람들을 먹어 치우는 게임을 만들려고 했죠.&lt;br /&gt;하지만 이런 느린 전개 방식이 빠르게 즐거움을 주어야 하는 모바일 게임 환경과는 맞지 않는다고 판단하여 사람부터 먹는 방식으로 기획을 변경했습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://namu.wiki/w/%ED%97%9D%EA%B7%B8%EB%A6%AC%20%EC%83%A4%ED%81%AC%20%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;헝그리 샤크&lt;/a&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1736912254814&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;헝그리 샤크 에볼루션&quot; data-og-description=&quot;헝그리 샤크 시리즈 의 5번째 작품. 이 작품부터 헝그리 샤크 시리즈가 본격적으로 알려지기 시작하였다. 게임 플레이&quot; data-og-host=&quot;namu.wiki&quot; data-og-source-url=&quot;https://namu.wiki/w/%ED%97%9D%EA%B7%B8%EB%A6%AC%20%EC%83%A4%ED%81%AC%20%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98&quot; data-og-url=&quot;https://namu.wiki/w/%ED%97%9D%EA%B7%B8%EB%A6%AC%20%EC%83%A4%ED%81%AC%20%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdwz3z/hyX0xyYKLM/HOi8eMySdNykblUoxZOANK/img.jpg?width=230&amp;amp;height=229&amp;amp;face=0_0_230_229&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/%ED%97%9D%EA%B7%B8%EB%A6%AC%20%EC%83%A4%ED%81%AC%20%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://namu.wiki/w/%ED%97%9D%EA%B7%B8%EB%A6%AC%20%EC%83%A4%ED%81%AC%20%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdwz3z/hyX0xyYKLM/HOi8eMySdNykblUoxZOANK/img.jpg?width=230&amp;amp;height=229&amp;amp;face=0_0_230_229');&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;헝그리 샤크 에볼루션&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;헝그리 샤크 시리즈 의 5번째 작품. 이 작품부터 헝그리 샤크 시리즈가 본격적으로 알려지기 시작하였다. 게임 플레이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;namu.wiki&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;게임 진행 방식에 대해서도 여러 번의 수정이 있었는데, 스포어 방식으로 성장, 진화시켜 끝내는 도시를 먹어치우는 게임을 어떻게 구성해야 빠르고 즐겁게 게임을 즐길 수 있을지에 집중했습니다. 결국 모바일 게임이자 캐주얼 아케이드 게임의 대명사인 헝그리 샤크 게임의 진행 방식을 참고하여 슈퍼먹방드래곤 게임도 월드 맵 스테이지를 구성하여&amp;nbsp; 전부를 먹어 치우는 방식으로 구성하자고 기획의 방향을 수정하게 되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3D 게임의 최적화 이슈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임을 개발하면서 그 무엇보다도 최적화가 정말 힘들었습니다.&lt;br /&gt;게임을 제작하며 왜 대다수의 사람들이 모바일 게임을 2D로 제작하는지에 대해 완전히 이해하게 되었달까요?&amp;nbsp;&lt;br /&gt;3D 게임의 모바일 최적화의 난이도는 매우 귀찮은 작업이고 난이도가 높은 편이라는 것을 제대로 느낄 수 있었던 시간이었습니다. &lt;br /&gt;모바일 환경의 성능은 제한되어 있는데, 고사양의 3D모델들을 쑤셔 넣다 보니 다양한 최적화 기법들이 필요했죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대표적으로 슈퍼 먹방 드래곤의 맵 내에서 많은 차량들이 도로 위를 움직이고 있는데, 이를 처음에는 별 생각 없이 A* 알고리즘을 통해 구현하였습니다. 하지만 만들어 놓고 보니 A* 알고리즘을 런타임에 계산해 가면서 사용한다는 것은 모바일 기기의 성능으로는 불가능했던 거죠. 그렇다고 만들어 놓은 A* 알고리즘을 포기할 수가 없어 미련하게도 사전에 경로를 계산한 다음 미리 딕셔너리에 데이터를 저장하고, 이 저장해 놓은 데이터를 로딩하여 게임에서 사용하는 방식으로 바꾸었는데, 여기에 든 시간이 아까워서 아직도 자다가도 벌떡 일어나곤 합니다. 차라리 스플라인을 사용하여 차량의 이동 경로를 일일히 직접 수동으로 그려주어 설정했더라면 훨씬 더 시간을 절약했을 텐데... OMG.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;또한 대표적인 최적화 기법인 컬링을 위해 카메라가 비추는 범위를 제한했습니다. &lt;/span&gt;프러스텀 컬링과 오클루전 컬링 기법을 사용하여 카메라 바깥의 모델들을 렌더링 하지 않도록 하였으며, 그럼에도 불구하고 첫 기획대로 비스듬한 각도로 플레이 할 경우 성능이 나오지 않아 수직에 가까운 각도에서 플레이 하도록 카메라 각도를 수정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 해도 모바일 환경에서 매끄럽게 플레이 되지 않았기에, 게임 전체를 잘게잘게 영역별로 쪼개서 영역 외부의 작동은 중지되도록 구성하였습니다. 즉 드래곤의 활동 범위 근처에서만 게임이 작동되도록 게임 전체의 로직을 수정할 수 밖에 없었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;동적으로 로드되던 모든 리소스들을 게임 시작되기 전에 미리 로드해 놓는 방식으로 변경하였습니다. 이 부분을 바꾸자 성능 측면에서 많은 개선이 있었죠. 물론 게임 내에서 중복으로 등장하는 모든 리소스들은 오브젝트 풀링 기법으로 재사용 하는 방식으로 메모리를 절약하려 노력했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;텍스처는 허용 가능한 선에서 최대한 압축하였으며, UI는 스프라이트 아틀라스를 사용하여 조금이라도 메모리를 절약하려고 노력하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그럼에도 불구하고 이 게임은 수많은 3D 오브젝트를 사용하는 월드 맵 방식이고, 처음 설계한 맵 자체가 쓸데없이 큰 편이기에(기획 자체가 모바일 게임에는 적합한 방식이 아니었던 거죠) 아직도 게임 구동에 많은 메모리를 차지하고 있어 여전히 최적화에 대한 고민은 이어지고 있는 상황입니다. 저사양의 폰에서는 아직도 조금씩의 렉이 느껴지니까요. 추후 다른 맵들을 설계할 때에는 최대한 덜어내어 필요한 규모의 맵 만큼만 구현할 생각입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;*현재 슈퍼먹방 드래곤 게임은 갤럭시 A15의 환경에서 잘 구동되는 것을 확인하였으나, 그보다 성능이 떨어지는 모바일 기기에서 렉 없이 구동될지는 미지수인 상황입니다.&lt;/p&gt;</description>
      <category>게임 개발 일지</category>
      <category>gamonsterz</category>
      <category>megachompdragon</category>
      <category>unity</category>
      <category>개발</category>
      <category>게임</category>
      <category>게임 개발</category>
      <category>게임몬스터즈</category>
      <category>슈퍼먹방드래곤</category>
      <category>유니티</category>
      <category>일지</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/128</guid>
      <comments>https://nybot-house.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 24 Feb 2025 11:43:49 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조C#] 그래프 탐색 알고리즘 - BFS 너비 우선 탐색</title>
      <link>https://nybot-house.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 그래프 탐색 이론 중 하나인 DFS (깊이 우선 탐색) 에 대해 배우고, 실제 구현하는 코드를 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nybot-house.tistory.com/115&quot;&gt;&amp;nbsp;그래프 탐색 알고리즘 - DFS (깊이 우선 탐색) 코드 구현&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718160998014&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조C#]  그래프 탐색 알고리즘 - DFS (깊이 우선 탐색) 코드 구현&quot; data-og-description=&quot;위 포스팅에서 우리는 그래프 예시를 통해 DFS가 무엇인지에 대해 이해했다.이제 실제로 이 인물 관계도 그래프와 DFS를 C# 코드로 구현해보자.지난 포스팅에서도 잠깐 살펴 보았지만, 우선 이 인&quot; data-og-host=&quot;nybot-house.tistory.com&quot; data-og-source-url=&quot;https://nybot-house.tistory.com/115&quot; data-og-url=&quot;https://nybot-house.tistory.com/115&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dnJoAL/hyWlkNSI45/qesP9dX5xdkgMxopuucc41/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/zgVm9/hyWg1vEr1t/h79Hy1WUtd6gZm14oacxLk/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/bi4Eq8/hyWlleV7gU/dExxAemt2FkmKwkX0tSu9K/img.png?width=750&amp;amp;height=446&amp;amp;face=44_44_654_345&quot;&gt;&lt;a href=&quot;https://nybot-house.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nybot-house.tistory.com/115&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dnJoAL/hyWlkNSI45/qesP9dX5xdkgMxopuucc41/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/zgVm9/hyWg1vEr1t/h79Hy1WUtd6gZm14oacxLk/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/bi4Eq8/hyWlleV7gU/dExxAemt2FkmKwkX0tSu9K/img.png?width=750&amp;amp;height=446&amp;amp;face=44_44_654_345');&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;[자료구조C#] 그래프 탐색 알고리즘 - DFS (깊이 우선 탐색) 코드 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;위 포스팅에서 우리는 그래프 예시를 통해 DFS가 무엇인지에 대해 이해했다.이제 실제로 이 인물 관계도 그래프와 DFS를 C# 코드로 구현해보자.지난 포스팅에서도 잠깐 살펴 보았지만, 우선 이 인&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nybot-house.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 그래프 탐색 이론 중 하나인 BFS (Breadth-First Search, 너비 우선 탐색)에 대해 알아보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BFS 너비 우선 탐색이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS는 그래프 탐색 알고리즘 중 하나로, 시작 정점에서 출발하여 가까운 정점들부터 탐색하는 방법이다.&amp;nbsp;&lt;br /&gt;이전과 같은 가상의 인물 관계도를 다시 보며 생각해 보자.&lt;br /&gt;필자는 관계도의 모든 인물들과 데이트를 하고 싶다. BFS 탐색 방법을 사용한다면 순서가 어떻게 될까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAk46W/btsHUCDImWm/kdbMLrFBAtRggo13vitQMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAk46W/btsHUCDImWm/kdbMLrFBAtRggo13vitQMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAk46W/btsHUCDImWm/kdbMLrFBAtRggo13vitQMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAk46W%2FbtsHUCDImWm%2FkdbMLrFBAtRggo13vitQMK%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;600&quot; height=&quot;357&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 먼저 0번 카리나부터 방문을 시작해 보자. 그리고 오늘은 다시는 없을 기회, 신중하게 접근하려고 한다. 따라서 BFS를 사용해 순회해볼 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BFS 기본 개념&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;큐(Queue): BFS는 주로 큐를 사용하여 구현된다. 큐는 선입선출(FIFO, First In First Out)&amp;nbsp; 방식으로 동작한다.&lt;br /&gt;&lt;a href=&quot;https://nybot-house.tistory.com/112&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(*큐의 특징을 이해해야 BFS를 이해할 수 있다.)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;방문(visited): 각 정점이 방문되었는지를 기록한다.&lt;/li&gt;
&lt;li&gt;탐색 순서: 시작 정점에서 인접한 정점들을 모두 큐에 넣고, 큐에서 하나씩 꺼내면서 다시 그 정점과 인접한 정점들을 큐에 넣는 방식으로 진행된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS의 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레벨 단위 탐색: BFS는 &lt;u&gt;가장 가까운 정점부터 차례대로 탐색&lt;/u&gt;하므로, 탐색 과정에서 정점은 탐색 깊이에 따라 레벨이 나뉜다.&lt;/li&gt;
&lt;li&gt;최단 경로: 가중치가 없는 그래프에서 BFS는 시작 정점에서 다른 모든 정점으로 가는 최단 경로를 찾는데 유용하다.&lt;/li&gt;
&lt;li&gt;시간 복잡도: O(V+E) - V는 정점의 수, E는 간선의 수.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위의 소셜 네트워킹 관계도를, 이전과 같이 그래프로 구현해 보고, BFS 코드를 구현해 보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1718169267586&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection.Metadata;

namespace Exercise
{
    //스택: LIFO(후입선출 Last In First Out)
    //큐: FIFO(선입선출 First In First Out)

    class Graph
    {
        //모든 정보를 들고 있는 방식, 배열 사용
        int[,] adj = new int[6, 6]
        {
            {0, 1, 0, 1, 0, 0}, //0번 카리나 정점 연결 정보
            {1, 0, 1, 1, 0, 0}, //1번 제니 정점 연결 정보
            {0, 1, 0, 0, 0, 0}, //2번 지수 정점 연결 정보
            {1, 1, 0, 0, 1, 0}, //3번 수지 정점 연결 정보
            {0, 0, 0, 1, 0, 1}, //4번 아이린 정점 연결 정보
            {0, 0, 0, 0, 1, 0}, //5번 원영 정점 연결 정보
            //팁 - 배열을 유심히 보면 대각선 방향으로 대칭됨 (양방향으로 연결되어 있는 그래프이기 때문)
        };        
        
        public void BFS(int start)
        {
            bool[] found = new bool[6];

            Queue&amp;lt;int&amp;gt; que = new Queue&amp;lt;int&amp;gt;();
            que.Enqueue(start);
            found[start] = true;

            while (que.Count &amp;gt; 0)
            {
                int now = que.Dequeue();
                Console.WriteLine(now);

                for (int next = 0; next &amp;lt; 6; next++)
                {
                    if (adj[now, next] == 0)
                        continue;
                    if (found[next]) 
                        continue;
                    que.Enqueue(next);
                    found[next] = true;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //BFS (Breadth First Search 너비 우선 탐색)
            Graph graph = new Graph();
            graph.BFS(0);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GbLsc/btsHXmeCBOH/u8kcYknljD1CKI9ebSuhdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GbLsc/btsHXmeCBOH/u8kcYknljD1CKI9ebSuhdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GbLsc/btsHXmeCBOH/u8kcYknljD1CKI9ebSuhdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGbLsc%2FbtsHXmeCBOH%2Fu8kcYknljD1CKI9ebSuhdK%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;600&quot; height=&quot;83&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카리나(0)&amp;nbsp; -&amp;gt; 제니(1) -&amp;gt; 수지(3)&amp;nbsp; -&amp;gt; 지수(2)&amp;nbsp; -&amp;gt; 아이린(4)&amp;nbsp; -&amp;gt; 원영(5) 순서로 방문하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS는 다양한 용도로 사용되는 반면, BFS는 일반적으로 길 찾기 용도로만 사용된다.&lt;br /&gt;또한 BFS에는 그 부모 정점은 누구인지, 정점 간의 거리는 얼마인지 등의 다양한 정보도 삽입하여 추출할 수 있다.&lt;br /&gt;이번에는 소셜 네트워크 관계도에서 각자 가장 친한 사람이 있다고 가정해 보고, 또한 각자의 거주지의 거리 정보가 저장되어 있다고 가정해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1718169740375&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection.Metadata;

namespace Exercise
{
    //스택: LIFO(후입선출 Last In First Out)
    //큐: FIFO(선입선출 First In First Out)

    class Graph
    {
        //모든 정보를 들고 있는 방식, 배열 사용
        int[,] adj = new int[6, 6]
        {
            {0, 1, 0, 1, 0, 0}, //0번 카리나 정점 연결 정보
            {1, 0, 1, 1, 0, 0}, //1번 제니 정점 연결 정보
            {0, 1, 0, 0, 0, 0}, //2번 지수 정점 연결 정보
            {1, 1, 0, 0, 1, 0}, //3번 수지 정점 연결 정보
            {0, 0, 0, 1, 0, 1}, //4번 아이린 정점 연결 정보
            {0, 0, 0, 0, 1, 0}, //5번 원영 정점 연결 정보
            //팁 - 배열을 유심히 보면 대각선 방향으로 대칭됨 (양방향으로 연결되어 있는 그래프이기 때문)
        };
        
        public void BFS(int start)
        {
            bool[] found = new bool[6];
            int[] bestie = new int[6];
            int[] distance = new int[6];

            Queue&amp;lt;int&amp;gt; que = new Queue&amp;lt;int&amp;gt;();
            que.Enqueue(start);
            found[start] = true;
            bestie[start] = start;
            distance[start] = 0;


            while (que.Count &amp;gt; 0)
            {
                int now = que.Dequeue();
                Console.WriteLine(now);

                for (int next = 0; next &amp;lt; 6; next++)
                {
                    if (adj[now, next] == 0) //인접하지 않았다면 (경로가 없다면) 스킵
                        continue;
                    if (found[next]) //이미 방문한 사람이라면 스킵
                        continue;
                    que.Enqueue(next);
                    found[next] = true;
                    bestie[next] = now;
                    distance[next] = distance[now] + 1;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //BFS (Breadth First Search 너비 우선 탐색)
            Graph graph = new Graph();
            graph.BFS(0);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드처럼 bestie 와 distance 라는 정보를 BFS() 메서드 내에 관련 로직을 추가하여 저장하고 추출해 낼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C# 자료구조, 알고리즘, 길찾기/그래프</category>
      <category>BFS</category>
      <category>c#</category>
      <category>그래프</category>
      <category>그래프탐색</category>
      <category>너비우선탐색</category>
      <category>알고리즘</category>
      <category>자료구조</category>
      <category>코딩</category>
      <category>프로그래밍</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/117</guid>
      <comments>https://nybot-house.tistory.com/117#entry117comment</comments>
      <pubDate>Wed, 12 Jun 2024 14:26:04 +0900</pubDate>
    </item>
    <item>
      <title>[C# Basics] 재귀 함수</title>
      <link>https://nybot-house.tistory.com/116</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;재귀 함수란?&lt;/b&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;b&gt;재귀함수 기본 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀함수는 두 가지 주요 부분으로 구성된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;기저 조건(Base Case)&lt;/b&gt;: 함수가 더 이상 자기 자신을 호출하지 않고 종료되는 조건.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재귀 단계(Recursive Step)&lt;/b&gt;: 함수가 자기 자신을 호출하는 단계.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;피보나치 수열 재귀 함수 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;피보나치 수열이란?&quot; href=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EC%88%98%ED%95%99%EC%97%90%EC%84%9C%20%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98(%EC%98%81%EC%96%B4,0%EC%9C%BC%EB%A1%9C%20%EB%91%90%EA%B8%B0%EB%8F%84%20%ED%95%9C%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;*피보나치 수열이란?&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718086537881&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;피보나치 수 - 위키백과, 우리 모두의 백과사전&quot; data-og-description=&quot;위키백과, 우리 모두의 백과사전. 피보나치 수를 이용한 사각형 채우기 수학에서 피보나치 수(영어: Fibonacci numbers)는 첫째 및 둘째 항이 1이며 그 뒤의 모든 항은 바로 앞 두 항의 합인 수열이다. &quot; data-og-host=&quot;ko.wikipedia.org&quot; data-og-source-url=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EC%88%98%ED%95%99%EC%97%90%EC%84%9C%20%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98(%EC%98%81%EC%96%B4,0%EC%9C%BC%EB%A1%9C%20%EB%91%90%EA%B8%B0%EB%8F%84%20%ED%95%9C%EB%8B%A4.&quot; data-og-url=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EC%88%98%ED%95%99%EC%97%90%EC%84%9C%20%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98(%EC%98%81%EC%96%B4,0%EC%9C%BC%EB%A1%9C%20%EB%91%90%EA%B8%B0%EB%8F%84%20%ED%95%9C%EB%8B%A4.&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tE8Pb/hyWg1Cjs2C/mwkAJdUDlhpNk81fxyH0zK/img.png?width=223&amp;amp;height=138&amp;amp;face=0_0_223_138,https://scrap.kakaocdn.net/dn/befsSI/hyWlfyTpku/CQbDh3nLGtsIJiqNIWIKfk/img.png?width=223&amp;amp;height=138&amp;amp;face=0_0_223_138&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EC%88%98%ED%95%99%EC%97%90%EC%84%9C%20%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98(%EC%98%81%EC%96%B4,0%EC%9C%BC%EB%A1%9C%20%EB%91%90%EA%B8%B0%EB%8F%84%20%ED%95%9C%EB%8B%A4.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.wikipedia.org/wiki/%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98_%EC%88%98#:~:text=%EC%88%98%ED%95%99%EC%97%90%EC%84%9C%20%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98%20%EC%88%98(%EC%98%81%EC%96%B4,0%EC%9C%BC%EB%A1%9C%20%EB%91%90%EA%B8%B0%EB%8F%84%20%ED%95%9C%EB%8B%A4.&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tE8Pb/hyWg1Cjs2C/mwkAJdUDlhpNk81fxyH0zK/img.png?width=223&amp;amp;height=138&amp;amp;face=0_0_223_138,https://scrap.kakaocdn.net/dn/befsSI/hyWlfyTpku/CQbDh3nLGtsIJiqNIWIKfk/img.png?width=223&amp;amp;height=138&amp;amp;face=0_0_223_138');&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;피보나치 수 - 위키백과, 우리 모두의 백과사전&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;위키백과, 우리 모두의 백과사전. 피보나치 수를 이용한 사각형 채우기 수학에서 피보나치 수(영어: Fibonacci numbers)는 첫째 및 둘째 항이 1이며 그 뒤의 모든 항은 바로 앞 두 항의 합인 수열이다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기초적인 재귀함수의 예로, 피보나치 수열을 구하는 함수를 살펴보자.&lt;br /&gt;피보나치 수열은 다음과 같이 정의된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 수는 0이다.&lt;/li&gt;
&lt;li&gt;두 번째 수는 1이다.&lt;/li&gt;
&lt;li&gt;그 이후의 수는 바로 앞 두 수를 더한 값이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 수열은 0,1,1,2,3,5,8,13,... 이렇게 진행된다. &lt;br /&gt;이 피보나치 수열의 n번째 수를 구하는 재귀함수를 C# 코드로 작성하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1718086607103&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public int Fibonacci(int n)
{
    if (n &amp;lt;= 1) // 기저 조건: n이 0 또는 1인 경우
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2); // 재귀 단계
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;기저 조건(Base Case): 'n'이 0또는 1인 경우, 그대로 'n'을 반환한다.&lt;br /&gt;'n == 0' 이면 '0'을 반환&lt;br /&gt;'n == 1' 이면 '1'을 반환&lt;/li&gt;
&lt;li&gt;재귀 단계(Recursive Step): 'n'이 1보다 큰 경우, 피보나치 수열의 정의에 따라 'n-1'번째와 'n-2'번째의 수를 재귀적으로 구하여 더한 값을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 위 함수로 피보나치 수열의 5번째 수를 구해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fibonacci(5)는 Fibonacci(4) + Fibonacci(3)을 호출.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fibonacci(4)는 Fibonacci(3) + Fibonacci(2)를 호출.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fibonacci(3)는 Fibonacci(2) + Fibonacci(1)를 호출.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fibonacci(2)는 Fibonacci(1) + Fibonacci(0)을 호출.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fibonacci(1)는 1을 반환.&lt;/li&gt;
&lt;li&gt;Fibonacci(0)은 0을 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, Fibonacci(2)는 1 + 0 = 1.&lt;/li&gt;
&lt;li&gt;Fibonacci(1)는 1을 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, Fibonacci(3)는 1 + 1 = 2.&lt;/li&gt;
&lt;li&gt;Fibonacci(2)는 1을 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, Fibonacci(4)는 2 + 1 = 3.&lt;/li&gt;
&lt;li&gt;Fibonacci(3)는 2를 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, Fibonacci(5)는 3 + 2 = 5이다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;호출 과정 요약&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1718086850126&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Fibonacci(5)
= Fibonacci(4) + Fibonacci(3)
= (Fibonacci(3) + Fibonacci(2)) + (Fibonacci(2) + Fibonacci(1))
= ((Fibonacci(2) + Fibonacci(1)) + (Fibonacci(1) + Fibonacci(0))) + ((Fibonacci(1) + Fibonacci(0)) + Fibonacci(1))
= (((Fibonacci(1) + Fibonacci(0)) + Fibonacci(1)) + ((Fibonacci(1) + Fibonacci(0)) + Fibonacci(1)))
= (((1 + 0) + 1) + ((1 + 0) + 1))
= (1 + 1) + (1 + 1)
= 2 + 2
= 4&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전체 출력 코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718086885287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Fibonacci(5)); // 5
        Console.WriteLine(Fibonacci(7)); // 13
        Console.WriteLine(Fibonacci(10)); // 55
    }

    public static int Fibonacci(int n)
    {
        if (n &amp;lt;= 1)
            return n;
        else
            return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 코드를 타이핑 해 보며 한줄 한줄 이해해 보시길 바란다.&lt;br /&gt;이번에는 우리가 이전 포스팅에서 구현해 보았던 DFS 재귀 함수의 로직을 이해해 보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS 재귀함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;DFS 깊이 우선 탐색&quot; href=&quot;https://nybot-house.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nybot-house.tistory.com/115&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718087071951&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조C#] 그래프 - DFS (깊이 우선 탐색) 코드 구현&quot; data-og-description=&quot;위 포스팅에서 우리는 그래프 예시를 통해 DFS가 무엇인지에 대해 이해했다.이제 실제로 이 인물 관계도 그래프와 DFS를 C# 코드로 구현해보자.지난 포스팅에서도 잠깐 살펴 보았지만, 우선 이 인&quot; data-og-host=&quot;nybot-house.tistory.com&quot; data-og-source-url=&quot;https://nybot-house.tistory.com/115&quot; data-og-url=&quot;https://nybot-house.tistory.com/115&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bUfi0q/hyWlbwvJga/xZgEKOiYeN3Zb00x9UNaKK/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/bxU6UH/hyWgVa1nJr/cfpSAkX7DdqkUwwuMVaQi0/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/nLU1z/hyWlh4xT8S/wwC1Ww8tY2OaTrKAK1Czm0/img.png?width=750&amp;amp;height=446&amp;amp;face=44_44_654_345&quot;&gt;&lt;a href=&quot;https://nybot-house.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nybot-house.tistory.com/115&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bUfi0q/hyWlbwvJga/xZgEKOiYeN3Zb00x9UNaKK/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/bxU6UH/hyWgVa1nJr/cfpSAkX7DdqkUwwuMVaQi0/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/nLU1z/hyWlh4xT8S/wwC1Ww8tY2OaTrKAK1Czm0/img.png?width=750&amp;amp;height=446&amp;amp;face=44_44_654_345');&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;[자료구조C#] 그래프 - DFS (깊이 우선 탐색) 코드 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;위 포스팅에서 우리는 그래프 예시를 통해 DFS가 무엇인지에 대해 이해했다.이제 실제로 이 인물 관계도 그래프와 DFS를 C# 코드로 구현해보자.지난 포스팅에서도 잠깐 살펴 보았지만, 우선 이 인&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nybot-house.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1718087082597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void DFS(int here)
{
    Console.WriteLine(here); // 현재 정점 출력
    visited[here] = true; // 현재 정점을 방문했음을 표시

    for(int next = 0; next &amp;lt; adj.GetLength(0); next++) // 모든 정점을 순회
    {
        if (adj[here, next] == 0) // 연결되어 있지 않으면 스킵
            continue;
        if (visited[next]) // 이미 방문한 정점이면 스킵
            continue;
        DFS(next); // 연결된 정점을 재귀적으로 방문
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS 재귀함수 동작 과정&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 탐색을 제니(1번)에서 시작한다고 가정.&lt;/p&gt;
&lt;pre id=&quot;code_1718087134626&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;graph.DFS(1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 제니(1) 방문:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;here = 1&lt;/li&gt;
&lt;li&gt;Console.WriteLine(1) 출력: 1&lt;/li&gt;
&lt;li&gt;visited[1] = true&lt;/li&gt;
&lt;li&gt;제니(1)와 연결된 정점을 순회: 카리나(0), 지수(2), 수지(3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 카리나(0) 방문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;here = 0&lt;/li&gt;
&lt;li&gt;Console.WriteLine(0) 출력: 0&lt;/li&gt;
&lt;li&gt;visited[0] = true&lt;/li&gt;
&lt;li&gt;카리나(0)와 연결된 정점을 순회: 제니(1), 수지(3)&lt;/li&gt;
&lt;li&gt;제니(1)는 이미 방문했으므로 스킵&lt;/li&gt;
&lt;li&gt;수지(3)를 재귀적으로 방문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 수지(3) 방문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;here = 3&lt;/li&gt;
&lt;li&gt;Console.WriteLine(3) 출력: 3&lt;/li&gt;
&lt;li&gt;visited[3] = true&lt;/li&gt;
&lt;li&gt;수지(3)와 연결된 정점을 순회: 카리나(0), 제니(1), 아이린(4)&lt;/li&gt;
&lt;li&gt;카리나(0)와 제니(1)는 이미 방문했으므로 스킵&lt;/li&gt;
&lt;li&gt;아이린(4)를 재귀적으로 방문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 아이린(4) 방문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;here = 4&lt;/li&gt;
&lt;li&gt;Console.WriteLine(4) 출력: 4&lt;/li&gt;
&lt;li&gt;visited[4] = true&lt;/li&gt;
&lt;li&gt;아이린(4)와 연결된 정점을 순회: 수지(3), 원영(5)&lt;/li&gt;
&lt;li&gt;수지(3)는 이미 방문했으므로 스킵&lt;/li&gt;
&lt;li&gt;원영(5)를 재귀적으로 방문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 원영(5) 방문&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;here = 5&lt;/li&gt;
&lt;li&gt;Console.WriteLine(5) 출력: 5&lt;/li&gt;
&lt;li&gt;visited[5] = true&lt;/li&gt;
&lt;li&gt;원영(5)와 연결된 정점: 아이린(4)&lt;/li&gt;
&lt;li&gt;아이린(4)는 이미 방문했으므로 스킵&lt;/li&gt;
&lt;li&gt;더 이상 방문할 정점이 없으므로 종료하고 이전 호출로 돌아감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 탐색 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 다시 처음 DFS(1) 로직으로 돌아가서 함수 실행. -&amp;gt;&amp;nbsp; DFS(2) 실행, 종료.&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;b&gt;재귀함수의 장점&lt;/b&gt;&lt;/h3&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;b&gt;문제 분할&lt;/b&gt;: 복잡한 문제를 더 작은 문제로 나누어 해결 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h3&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;b&gt;성능&lt;/b&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;</description>
      <category>C# 기초 프로그래밍/C# Basic</category>
      <category>c#</category>
      <category>재귀</category>
      <category>재귀함수</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/116</guid>
      <comments>https://nybot-house.tistory.com/116#entry116comment</comments>
      <pubDate>Tue, 11 Jun 2024 15:29:55 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조C#]  그래프 탐색 알고리즘 - DFS (깊이 우선 탐색) 코드 구현</title>
      <link>https://nybot-house.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;위 포스팅에서 우리는 그래프 예시를 통해 DFS가 무엇인지에 대해 이해했다.&lt;br /&gt;이제 실제로 이 인물 관계도 그래프와 DFS를 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;C# 코드로&lt;span&gt; &lt;/span&gt;&lt;/span&gt;구현해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHBNlG/btsHVqoeoaW/kc82vx6DgjkIpXzebHS6JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHBNlG/btsHVqoeoaW/kc82vx6DgjkIpXzebHS6JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHBNlG/btsHVqoeoaW/kc82vx6DgjkIpXzebHS6JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHBNlG%2FbtsHVqoeoaW%2Fkc82vx6DgjkIpXzebHS6JK%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;600&quot; height=&quot;357&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서도 잠깐 살펴 보았지만, 우선 이 인물 관계도를 그래프로 구현하는 방법에는 크게 2가지가 있다. 배열을 사용해 인접 행렬을 만드는 방법과, 리스트 배열을 사용해서 인접 리스트를 만드는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718082407230&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Graph
{
    //모든 정보를 들고 있는 방식, 배열 사용
    int[,] adj = new int[6, 6]
    {
        {0, 1, 0, 1, 0, 0}, //0번 카리나 정점 연결 정보
        {1, 0, 1, 1, 0, 0}, //1번 제니 정점 연결 정보
        {0, 1, 0, 0, 0, 0}, //2번 지수 정점 연결 정보
        {1, 1, 0, 0, 1, 0}, //3번 수지 정점 연결 정보
        {0, 0, 0, 1, 0, 1}, //4번 아이린 정점 연결 정보
        {0, 0, 0, 0, 1, 0}, //5번 원영 정점 연결 정보
        //팁 - 배열을 유심히 보면 대각선 방향으로 대칭됨 (양방향으로 연결되어 있는 그래프이기 때문)
    };

    //필요한 인덱스 정보만 들고 있는 방식, 리스트 배열 사용
    List&amp;lt;int&amp;gt;[] adj2 = new List&amp;lt;int&amp;gt;[]
    {
        new List&amp;lt;int&amp;gt;() { 1, 3 },    //0번 카리나 정점 연결 정보
        new List&amp;lt;int&amp;gt;() { 0, 2, 3},  //1번 제니 정점 연결 정보
        new List&amp;lt;int&amp;gt;() { 1 },       //2번 지수 정점 연결 정보
        new List&amp;lt;int&amp;gt;() { 0, 1, 4 }, //3번 수지 정점 연결 정보
        new List&amp;lt;int&amp;gt;() { 3, 5 },    //4번 아이린 정점 연결 정보
        new List&amp;lt;int&amp;gt;() { 4 },       //5번 원영 정점 연결 정보
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adj 배열에서는 각각의 정점 연결 정보를 0이면 연결되어 있지 않음, 1이면 연결되어 있음으로 표시했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adj2 리스트 배열에서는 각각의 연결 정보를 숫자로 표시해 주었다. 배열의 각 인덱스는 특정 정점(인물)을 나타내고, 그 인덱스에 해당하는 리스트는 해당 정점(인물)과 연결된 정점들(인물들)을 나타낸다. 예를 들어 adj2[0]은 정점 0(카리나)와 연결된 정점들을 나타내며, 1(제니)와 3(수지)와 연결되어 있다.&lt;br /&gt;우선 adj를 순회할 DFS 메서드를 만들어 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1718082781732&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool[] visited = new bool[6];
// 1) 우선 here부터 방문
// 2) here와 연결된 정점들을 하나씩 확인해서, [아직 미방문 상태라면] 방문.
public void DFS(int here)
{
    Console.WriteLine(here);        // 현재 정점 출력
    visited[here] = true;           // 현재 정점을 방문했음을 표시
        
    for (int next = 0; next &amp;lt; adj.GetLength(0); next++)
    {
        if (adj[here, next] == 0)   // 연결되어 있지 않으면 스킵
            continue;
        if (visited[next])          // 이미 방문한 정점이면 스킵
            continue;
        DFS(next);                  // 연결된 정점을 재귀적으로 방문
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS 함수는 현재 정점(here)을 방문하고, 연결된 정점을 재귀적으로 방문한다. 로직은 다음과 같다.&lt;br /&gt;&lt;a title=&quot;재귀함수&quot; href=&quot;https://nybot-house.tistory.com/116&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(*재귀 함수: 자기 자신을 호출하는 프로그래밍 기법)&lt;/a&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작 정점을 방문하고 출력한다. 'Console.WriteLine(here)'&lt;/li&gt;
&lt;li&gt;방문한 정점을 기록한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;'&lt;/span&gt;visited[here] = true'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;현재 정점에 연결된 모든 정점을 확인한다. 'for()...'&lt;/li&gt;
&lt;li&gt;방문하지 않은 정점이 있으면 해당 정점으로 이동하여 DFS를 재귀적으로 수행한다. 'DFS(next)'&lt;br /&gt;(이미 방문한 정점은 스킵)&lt;/li&gt;
&lt;li&gt;더 이상 방문할 정점이 없으면 재귀를 종료하고 이전 정점으로 돌아간다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 살펴보았듯이 제니(1)부터 방문했을 경우 DFS 로직에 따르면 방문 순서가&amp;nbsp;&amp;nbsp;&lt;br /&gt;제니(1) -&amp;gt; 카리나(0) -&amp;gt; 수지(3) -&amp;gt; 아이린(4) -&amp;gt; 원영(5) -&amp;gt; 지수(2)&lt;br /&gt;이렇게 되었었는데, 실제 프로그램 실행 결과도 동일한지 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1718084933929&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection.Metadata;

namespace Exercise
{
    class Graph
    {
        //모든 정보를 들고 있는 방식, 배열 사용
        int[,] adj = new int[6, 6]
        {
            {0, 1, 0, 1, 0, 0}, //0번 카리나 정점 연결 정보
            {1, 0, 1, 1, 0, 0}, //1번 제니 정점 연결 정보
            {0, 1, 0, 0, 0, 0}, //2번 지수 정점 연결 정보
            {1, 1, 0, 0, 1, 0}, //3번 수지 정점 연결 정보
            {0, 0, 0, 1, 0, 1}, //4번 아이린 정점 연결 정보
            {0, 0, 0, 0, 1, 0}, //5번 원영 정점 연결 정보
            //팁 - 배열을 유심히 보면 대각선 방향으로 대칭됨 (양방향으로 연결되어 있는 그래프이기 때문)
        };        

        bool[] visited = new bool[6];
        // 1) 우선 here부터 방문
        // 2) here와 연결된 정점들을 하나씩 확인해서, [아직 미방문 상태라면] 방문.
        public void DFS(int here)
        {
            Console.WriteLine(here);        // 현재 정점 출력
            visited[here] = true;           // 현재 정점을 방문했음을 표시
                
            for (int next = 0; next &amp;lt; adj.GetLength(0); next++)
            {
                if (adj[here, next] == 0)   // 연결되어 있지 않으면 스킵
                    continue;
                if (visited[next])          // 이미 방문한 정점이면 스킵
                    continue;
                DFS(next);                  // 연결된 정점을 재귀적으로 방문
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //DFS (Depth First Search 깊이 우선 탐색)
            //BFS (Breadth First Search 너비 우선 탐색)
            Graph graph = new Graph();
            graph.DFS(1);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WQdWJ/btsHVGEvqVD/ptNBh8JJjq1BD19qWjHGnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WQdWJ/btsHVGEvqVD/ptNBh8JJjq1BD19qWjHGnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WQdWJ/btsHVGEvqVD/ptNBh8JJjq1BD19qWjHGnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWQdWJ%2FbtsHVGEvqVD%2FptNBh8JJjq1BD19qWjHGnK%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;600&quot; height=&quot;85&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제니(1) -&amp;gt; 카리나(0) -&amp;gt; 수지(3) -&amp;gt; 아이린(4) -&amp;gt; 원영(5) -&amp;gt; 지수(2) 로 결과는 같은 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 adj2 를 사용해서 adj와 똑같은 결과가 나오는지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1718090918966&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection.Metadata;

namespace Exercise
{
    //스택: LIFO(후입선출 Last In First Out)
    //큐: FIFO(선입선출 First In First Out)

    class Graph
    {
        //모든 정보를 들고 있는 방식, 배열 사용
        int[,] adj = new int[6, 6]
        {
            {0, 1, 0, 1, 0, 0}, //0번 카리나 정점 연결 정보
            {1, 0, 1, 1, 0, 0}, //1번 제니 정점 연결 정보
            {0, 1, 0, 0, 0, 0}, //2번 지수 정점 연결 정보
            {1, 1, 0, 0, 1, 0}, //3번 수지 정점 연결 정보
            {0, 0, 0, 1, 0, 1}, //4번 아이린 정점 연결 정보
            {0, 0, 0, 0, 1, 0}, //5번 원영 정점 연결 정보
            //팁 - 배열을 유심히 보면 대각선 방향으로 대칭됨 (양방향으로 연결되어 있는 그래프이기 때문)
        };

        //필요한 인덱스 정보만 들고 있는 방식, 리스트 배열 사용
        List&amp;lt;int&amp;gt;[] adj2 = new List&amp;lt;int&amp;gt;[]
        {
            new List&amp;lt;int&amp;gt;() { 1, 3 },    //0번 카리나 정점 연결 정보
            new List&amp;lt;int&amp;gt;() { 0, 2, 3},  //1번 제니 정점 연결 정보
            new List&amp;lt;int&amp;gt;() { 1 },       //2번 지수 정점 연결 정보
            new List&amp;lt;int&amp;gt;() { 0, 1, 4 }, //3번 수지 정점 연결 정보
            new List&amp;lt;int&amp;gt;() { 3, 5 },    //4번 아이린 정점 연결 정보
            new List&amp;lt;int&amp;gt;() { 4 },       //5번 원영 정점 연결 정보
        };

        bool[] visited = new bool[6];
        // 1) 우선 here부터 방문
        // 2) here와 연결된 정점들을 하나씩 확인해서, [아직 미방문 상태라면] 방문.
        public void DFS(int here)
        {
            Console.WriteLine(here);        // 현재 정점 출력
            visited[here] = true;           // 현재 정점을 방문했음을 표시
                
            for (int next = 0; next &amp;lt; adj.GetLength(0); next++)
            {
                if (adj[here, next] == 0)   // 연결되어 있지 않으면 스킵
                    continue;
                if (visited[next])          // 이미 방문한 정점이면 스킵
                    continue;
                DFS(next);                  // 연결된 정점을 재귀적으로 방문
            }
        }

        public void DFS2(int here)
        {
            Console.WriteLine(here);
            visited[here] = true;

            foreach(int next in adj2[here])
            {
                if (visited[next])
                    continue;
                DFS2(next);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //DFS (Depth First Search 깊이 우선 탐색)
            //BFS (Breadth First Search 너비 우선 탐색)
            Graph graph = new Graph();
            graph.DFS2(1);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS2() 는 인접 리스트를 사용하여 깊이 우선 탐색을 구현해 본 메서드이다. 이 방식은 현재 정점과 직접 연결된 정점만 순회하는 방식이다. (DFS는 모든 정점을 순회하면서 연결 여부를 확인해 보았었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 실행해 보면 DFS()와 DFS2()는 같은 탐색 순서 결과가 나오는 것을 확인할 수 있다.&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;제니(1) -&amp;gt; 카리나(0) -&amp;gt; 수지(3) -&amp;gt; 아이린(4) -&amp;gt; 원영(5) -&amp;gt; 지수(2) (이미 방문한 정점은 스킵)&lt;/li&gt;
&lt;/ul&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;blob&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLQAj/btsHVqvi435/MNXMXGyViKddwZJag3sz00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLQAj/btsHVqvi435/MNXMXGyViKddwZJag3sz00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLQAj/btsHVqvi435/MNXMXGyViKddwZJag3sz00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLQAj%2FbtsHVqvi435%2FMNXMXGyViKddwZJag3sz00%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;1434&quot; height=&quot;199&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;/figure&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;b&gt;인접 행렬 VS 인접 리스트&lt;/b&gt;&lt;/h3&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;&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;b&gt;구현 방식&lt;/b&gt;: 2차원 배열을 사용하여 그래프를 표현합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선의 존재 여부를 O(1) 시간에 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;그래프의 정점 수가 적고 간선 수가 많은 경우(즉, 밀집 그래프(Dense Graph))에 효율적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 사용량이 많습니다. &lt;span&gt;&lt;span&gt;V&amp;times;VV \times V&lt;/span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;times;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;V&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 크기의 배열이 필요하므로, 간선의 수가 적은 경우(즉, 희소 그래프(Sparse Graph)) 비효율적입니다.&lt;/li&gt;
&lt;li&gt;모든 정점을 순회해야 하므로 시간 복잡도가 O(V^2)입니다.&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;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;b&gt;구현 방식&lt;/b&gt;: 각 정점에 연결된 정점들을 리스트로 표현합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 사용량이 적습니다. 정점의 수가 많고 간선의 수가 적은 경우(희소 그래프)에 효율적입니다.&lt;/li&gt;
&lt;li&gt;정점과 연결된 정점만 순회하므로 시간 복잡도가 O(V + E)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선의 존재 여부를 확인하는 데 O(V) 시간이 걸릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 인접 리스트를 사용하는 이유는 메모리 효율성과 탐색 효율성 때문이다. 인접 리스트는 정점과 연결된 정점들만 저장하므로, 정점의 수가 많고 간선의 수가 적은 경우 메모리를 효율적으로 사용할 수 있다. 또한 연결된 정점만 순회하므로 탐색 속도가 빨라진다. 특히 DFS와 같은 알고리즘에서는 현재 정점과 연결된 정점만 탐색하면 되므로 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C# 자료구조, 알고리즘, 길찾기/그래프</category>
      <category>c#</category>
      <category>DFS</category>
      <category>그래프</category>
      <category>깊이 우선 탐색</category>
      <category>알고리즘</category>
      <category>자료구조</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/115</guid>
      <comments>https://nybot-house.tistory.com/115#entry115comment</comments>
      <pubDate>Tue, 11 Jun 2024 14:54:53 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조C#] 그래프 탐색 알고리즘- DFS (깊이 우선 탐색) 개론</title>
      <link>https://nybot-house.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nybot-house.tistory.com/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nybot-house.tistory.com/113&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718075147152&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조] 그래프 개요&quot; data-og-description=&quot;자료 구조 중 그래프에 대해 알아보자.(기본 교과 과정에서 배웠던 수학의 그래프와는 전혀 관계 없음을 밝힌다)그래프란?: 현실 세계의 사물이나 추상적인 개념 간의 연결 관계를 표현하는데 &quot; data-og-host=&quot;nybot-house.tistory.com&quot; data-og-source-url=&quot;https://nybot-house.tistory.com/113&quot; data-og-url=&quot;https://nybot-house.tistory.com/113&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6enZo/hyWlktoLvZ/6YtoQlhJ2ApvlukInKxfAk/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/t3xCc/hyWg64E2Eg/MZXiCfzOQhsXfA8C9X5crK/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/wknss/hyWljnKqIx/8y9Ow44mKG9oMDkGMglYkk/img.png?width=3358&amp;amp;height=2000&amp;amp;face=199_194_2932_1547&quot;&gt;&lt;a href=&quot;https://nybot-house.tistory.com/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nybot-house.tistory.com/113&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6enZo/hyWlktoLvZ/6YtoQlhJ2ApvlukInKxfAk/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/t3xCc/hyWg64E2Eg/MZXiCfzOQhsXfA8C9X5crK/img.png?width=800&amp;amp;height=476&amp;amp;face=47_47_696_369,https://scrap.kakaocdn.net/dn/wknss/hyWljnKqIx/8y9Ow44mKG9oMDkGMglYkk/img.png?width=3358&amp;amp;height=2000&amp;amp;face=199_194_2932_1547');&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;[자료구조] 그래프 개요&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자료 구조 중 그래프에 대해 알아보자.(기본 교과 과정에서 배웠던 수학의 그래프와는 전혀 관계 없음을 밝힌다)그래프란?: 현실 세계의 사물이나 추상적인 개념 간의 연결 관계를 표현하는데&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nybot-house.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스트에서 그래프의 개요에 대해 알아 보았다. 이제 실제 그래프를 구현해보고, 이 그래프를 탐색하는 방법 중 하나인 DFS(깊이 우선 탐색) 방법에 대해 알아보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 구현해보기에 앞서서, 앞서 만들어 봤던 가상의 소셜 네트워크 관계도를 생각해보자.&lt;br /&gt;이제 우리는 그녀들과 데이트를 하려고 한다. 다만 서로 관계가 있는, 즉 연결되어 있는 인물들만 만날 수 있다. &lt;br /&gt;우리는 그녀들을 모두 만나기 위해 그녀들을 순회할 알고리즘을 만들어 볼 건데, 그럼 필자가 제일 좋아하는 1번 제니부터 만난다고&amp;nbsp; 하면 대체 어떤 기준으로 방향을 선택해서 데이트 순회를 해야 하는 것일까?&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOiVs6/btsHU9T9hiM/3OLFIfwN95dvuv2528xLJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOiVs6/btsHU9T9hiM/3OLFIfwN95dvuv2528xLJK/img.png&quot; data-alt=&quot;대체 어떤 기준으로 방향을 선택해서 각 인물 정점들을 순회해야 할까?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOiVs6/btsHU9T9hiM/3OLFIfwN95dvuv2528xLJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOiVs6%2FbtsHU9T9hiM%2F3OLFIfwN95dvuv2528xLJK%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;600&quot; height=&quot;357&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&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;우선, 순회하기 전 주어진 관계도를 그래프로 표현해보면 다음과 같게 될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1718081230297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0: [1, 3]
1: [0, 2, 3]
2: [1]
3: [0, 1, 4]
4: [3, 5]
5: [4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 인물에 0번부터 5번까지 번호를 붙였고, 연결된 관계를 정리해서 그래프로 표현해 봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래프의 순회 기준은?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DFS (Depth First Search) 깊이 우선 탐색&lt;/li&gt;
&lt;li&gt;BFS (Breadth First Search) 너비 우선 탐색&lt;br /&gt;(*BFS는 추후 다른 포스트에서 다룰 예정)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS (Depth First Search) 깊이 우선 탐색&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;깊이 우선 탐색은 그래프 탐색 알고리즘 중 하나로, 시작 정점에서 출발하여 &lt;u&gt;한 정점의 분기를 최대한 깊이 탐색&lt;/u&gt;한 후, 더 이상 깊이 갈 수 없으면 다시 되돌아와 다른 분기를 탐색하는 방법이다. 즉, DFS는 인접한 정점 중 가장 먼저 나오는 정점으로 이동하고 그 정점의 분기를 최대한 깊이 탐색한 후에 더 이상 탐색할 곳이 없으면 다시 되돌아와 다른 분기로 탐색하는 방식이다.&amp;nbsp;&lt;br /&gt;위 예시를 통해 자세히 살펴보자. 우리가 첫 번째 데이트를 시작할 인물을 1번 제니라고 한다면, 그 다음으로 제니와 연결되어 있는 0번 카리나 또는 3번 수지를 선택하여 방문 해야 하는데, DFS는인접한 정점 중 가장 먼저 나오는 정점으로 이동한다. 우리는 각 정점의 번호(임의로 붙인 번호인데, 데이터에는 당연히 순서가 있으므로 그것을 나타낸다) 를 통해&amp;nbsp; 순서를 알 수 있다. 즉, 제니(1)에서 연결된 첫 번째 정점인 카리나(0)으로 이동한다. 순서를 정리해보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제니(1)에서 시작.&lt;/li&gt;
&lt;li&gt;제니(1)에서 연결된 첫 번째 정점인 카리나(0)로 이동한다.&lt;/li&gt;
&lt;li&gt;카리나(0)에서 연결된 첫 번째 정점인 제니는 이미 방문했으므로, 두 번째 정점인 수지(3)로 이동한다.&lt;/li&gt;
&lt;li&gt;수지(3)에서 연결된 첫 번째 정점인 카리나(0)와 두 번째 정점 제니(1)는 이미 방문했으므로, 아이린(4)으로 이동.&lt;/li&gt;
&lt;li&gt;아이린(4)에서 연결된 첫 번째 정점 수지(3)는 이미 방문했으므로 두 번째 정점인 원영(5)으로 이동.&lt;/li&gt;
&lt;li&gt;원영(5)에서 연결된 유일한 정점인 아이린(4)은 이미 방문했다. 여기서 더 이상 갈 수 없으므로 이전 정점 아이린(4)으로 되돌아간다!&lt;/li&gt;
&lt;li&gt;아이린(4)으로 되돌아와서 다른 미탐색 경로를 찾으려 하지만 모든 인접 정점이 이미 방문되었다. 다시 수지(3)로 되돌아간다.&lt;/li&gt;
&lt;li&gt;수지(3)로 되돌아와서 다른 미탐색 경로를 찾으려 하지만 모든 인접 정점이 이미 방문되었으니 카리나(0)으로 되돌아간다.&lt;/li&gt;
&lt;li&gt;카리나(0)로 되돌아와서 다른 미탐색 경로 탐색 시도, 모든 인접 정점 이미 방문.제니(1)로 되돌아간다.&lt;/li&gt;
&lt;li&gt;제니(1)에서 다시 탐색을 이어가는데 연결된 두 번째 정점인 지수(2)를 방문하지 않았다! 지수(2)로 이동한다.&lt;/li&gt;
&lt;li&gt;지수(2)에서 연결된 유일한 정점인 제니(1)는 이미 방문했다. 여기서 더 이상 갈 수 없으므로 이전 정점으로 리턴.&lt;/li&gt;
&lt;li&gt;제니(1)에서 연결된 세 번째 정점인 수지(3)로 이동하는데, 수지(3)도 이미 방문했다. 더 이상 탐색할 경로가 없으므로 탐색을 종료한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 제니(1번)부터 시작하여 DFS에 따라 탐색하는 순서는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제니(1) -&amp;gt; 카리나(0) -&amp;gt; 수지(3) -&amp;gt; 아이린(4) -&amp;gt; 원영(5) -&amp;gt; 지수(2)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프가 끊겨있을 경우&lt;/b&gt;&lt;/h3&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;그래프_끊긴인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QG6Tc/btsHVr8t3yK/FOZhI1fHr3FSm9CKxjiEs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QG6Tc/btsHVr8t3yK/FOZhI1fHr3FSm9CKxjiEs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QG6Tc/btsHVr8t3yK/FOZhI1fHr3FSm9CKxjiEs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQG6Tc%2FbtsHVr8t3yK%2FFOZhI1fHr3FSm9CKxjiEs0%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;600&quot; height=&quot;357&quot; data-filename=&quot;그래프_끊긴인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제니부터 방문한 우리는 카리나, 수지, 지수와 만날 수 있지만 아이린 에게는 영영 도달할 수가 없게 된다.&amp;nbsp;&lt;br /&gt;물론 시작할 때 아이린부터 방문하게 된다면 아이린과 원영을 만날 수 있겠지만 역시 카리나, 수지, 제니, 지수와는 영영 만나지 못한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS의 특징&lt;/b&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;b&gt;시간 복잡도&lt;/b&gt;: O(V+E), 여기서 V는 정점의 수, E는 간선의 수이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간 복잡도&lt;/b&gt;: 재귀를 사용하면 재귀 호출로 인한 스택 공간을 사용하며, 스택을 사용하면 명시적인 스택이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원전 탐색&lt;/b&gt;: 모든 경로를 탐색하여 목적지에 도달할 수 있는지 여부를 결정할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경로 발견&lt;/b&gt;: 특점 정점에서 다른 정점으로의 경로를 찾는 데 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS의 응용&lt;/b&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;b&gt;미로 찾기&lt;/b&gt;: 미로에서 출구를 찾는 문제&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사이클 검출&lt;/b&gt;: 그래프에서 사이클이 존재하는지 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;강한 연결 요소(SCC)&lt;/b&gt;: 방향 그래프에서 강한 연결 요소를 찾는 알고리즘의 기초&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위상 정렬&lt;/b&gt;: 방향 그래프에서의 위상 정렬&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>C# 자료구조, 알고리즘, 길찾기/그래프</category>
      <category>c#</category>
      <category>DFS</category>
      <category>그래프</category>
      <category>길찾기</category>
      <category>깊이우선탐색</category>
      <category>미로찾기</category>
      <category>알고리즘</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/114</guid>
      <comments>https://nybot-house.tistory.com/114#entry114comment</comments>
      <pubDate>Tue, 11 Jun 2024 13:59:33 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 그래프 개요</title>
      <link>https://nybot-house.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자료 구조 중 그래프에 대해 알아보자.&lt;br /&gt;(기본 교과 과정에서 배웠던 수학의 그래프와는 전혀 관계 없음을 밝힌다)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 현실 세계의 사물이나 추상적인 개념 간의 연결 관계를 표현하는데 사용되는, 정점과 간선으로 구성된 데이터 구조이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정점(Vertex)&lt;/b&gt;: 데이터를 표현 (사물, 개념 등). &lt;br /&gt;그래프의 기본 단위로, 각 정점은 고유한 값을 가질 수 있다. 정점은 일반적으로 원이나 점으로 표시된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간선(Edge)&lt;/b&gt;: 정점간의 연결을 나타낸다. 간선은 방향이 있을 수도 있고, 없을 수도 있다. (일방향일 수도, 양방향일 수도 있다는 것).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 소셜 네트워크 관계도, 지도와 길찾기, 웹 페이지 링크&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NaPBo/btsHUQ1Dpf6/80WTP5Vu5hwCvKcaY3ObjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NaPBo/btsHUQ1Dpf6/80WTP5Vu5hwCvKcaY3ObjK/img.png&quot; data-alt=&quot;아이돌 사진으로 보는 가상의 소셜 네트워크 관계도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NaPBo/btsHUQ1Dpf6/80WTP5Vu5hwCvKcaY3ObjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNaPBo%2FbtsHUQ1Dpf6%2F80WTP5Vu5hwCvKcaY3ObjK%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;3358&quot; height=&quot;2000&quot; data-filename=&quot;그래프_인물관계도.png&quot; data-origin-width=&quot;3358&quot; data-origin-height=&quot;2000&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;위 예시에서 각 인물들이 정점(Vertex)가 될 것이고, 서로 친구 사이임을 표시하는 선이 간선이 될 것이다. &lt;br /&gt;이 간선에 화살표를 주어 방향성을 표시할 수도 있을 것이고, 서로의 호감도 등 여러 정보를 담을 수도 있을 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프의 표현 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인접 행렬&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정점 간의 간선을 2차원 배열로 나타낸다.&lt;/li&gt;
&lt;li&gt;인접 행렬의 각 요소 [i][j]는 정점 i에서 정점 j로의 간선이 있는지를 나타낸다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 정점에 연결된 다른 정점들을 리스트로 표현한다.&lt;/li&gt;
&lt;li&gt;메모리를 효율적으로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 직접 구현해보는 포스트를 업로드 할 예정이니 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 그래프가 중요한 이유는, 복잡한 관계를 시각화하고 분석하는 데 유용한 도구이기 때문이다. 무엇보다도 우리가 개발자로서 필수적으로 알아야 하는 최단 경로 알고리즘(ex: 다익스트라 알고리즘, A* 알고리즘), 네트워크 분석(소셜 네트워크 분석, 네트워크 플로우 문제 해결), 최적화 문제(최대 유량 문제, 최소 비용 문제 등) 등 다양한 분야에서 그래프 개념이 중요하게 활용되기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C# 자료구조, 알고리즘, 길찾기/그래프</category>
      <category>c#</category>
      <category>간선</category>
      <category>그래프</category>
      <category>알고리즘</category>
      <category>자료구조</category>
      <category>정점</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/113</guid>
      <comments>https://nybot-house.tistory.com/113#entry113comment</comments>
      <pubDate>Tue, 11 Jun 2024 12:04:14 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 스택(Stack)과 큐(Queue) 비교</title>
      <link>https://nybot-house.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;선형 자료 구조인 스택과 큐는 데이터 구조에서 중요한 역할을 하는 두 가지 개념이다. &lt;br /&gt;면접에서 자주 물어보는 내용이니 잘 숙지해 둘 필요가 있다. &lt;br /&gt;(*선형 자료 구조: 데이터가 순차적으로 배열되어, 각 요소가 정확히 하나의 앞 요소와 하나의 뒤 요소를 가지는 구조이다. 데이터의 순서가 중요하며, 요소 간의 관계가 일대일로 연결되어 있다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스택(Stack)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 선형 자료 구조의 한 종류로, &lt;b&gt;후입선출 (LIFO, Last In First Out)&lt;/b&gt; 원칙에 따라 작동하는 데이터 구조이다. 즉, 마지막에 삽입된 요소가 가장 먼저 제거된다. 스택의 각 요소는 다음과 같은 방식으로 정렬된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PtVmv/btsHTCh24P8/Cwk0ldSbJASoGTkoLngxek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PtVmv/btsHTCh24P8/Cwk0ldSbJASoGTkoLngxek/img.png&quot; data-alt=&quot;A,B,C의 순서로 push 했다면 pop 했을 때 C부터 나오게 된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PtVmv/btsHTCh24P8/Cwk0ldSbJASoGTkoLngxek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPtVmv%2FbtsHTCh24P8%2FCwk0ldSbJASoGTkoLngxek%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;511&quot; height=&quot;358&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A,B,C의 순서로 push 했다면 pop 했을 때 C부터 나오게 된다&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;Push: 스택의 맨 위에 요소를 추가한다.&lt;/li&gt;
&lt;li&gt;Pop: 스택의 맨 위에 있는 요소를 제거하고 반환한다.&lt;/li&gt;
&lt;li&gt;Peek: 스택의 맨 위에 있는 요소를 제거하지 않고 반환한다.&lt;/li&gt;
&lt;li&gt;IsEmpty: 스택이 비어 있는지 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스택을 사용하는 경우&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀적인 알고리즘에서 호출 스택을 관리할 때&lt;/li&gt;
&lt;li&gt;웹 브라우저의 뒤로가기 기능&lt;/li&gt;
&lt;li&gt;수식 계산기에서 괄호의 짝을 맞출 때&lt;/li&gt;
&lt;li&gt;되돌리기(Undo) 기능 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스택을 배열로 구현할 때의 장단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점: 구현이 간단하고, 인덱스를 통해 빠른 접근이 가능하다.&lt;br /&gt;단점: 크기가 고정되어 있어 메모리 낭비가 발생할 수 있으며, 크기를 초과할 경우에 동적으로 확장이 불가능하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스택 오버플로우와 언더플로우란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 오버플로(Stack Overflow): 스택이 가득 찬 상태에서 Push 연산을 시도할 때 발생한다.&lt;br /&gt;스택 언더플로(Stack Underflow): 스택이 비어있는 상태에서 Pop 연산을 시도할 때 발생한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;간단한&lt;span&gt; &lt;/span&gt;&lt;/span&gt;유니티 스택 활용 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;유니티에서 스택은 주로 게임 오브젝트의 상태를 관리하거나, 되돌리기 기능(Undo)을 구현할 때 유용하다. 예를 들어, 플레이어의 이동 기록을 스택에 저장해두고, 플레이어가 되돌리기 버튼을 눌렀을 때 이전 위치로 돌아가도록 하는 기능을 구현할 때 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1717991319930&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Stack&amp;lt;Vector3&amp;gt; movementHistory = new Stack&amp;lt;Vector3&amp;gt;();

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            movementHistory.Push(transform.position);
            transform.Translate(Vector3.forward);
        }
        if (Input.GetKeyDown(KeyCode.U) &amp;amp;&amp;amp; movementHistory.Count &amp;gt; 0)
        {
            transform.position = movementHistory.Pop();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시 코드에서 플레이어가 'W'키를 누르면 현재 위치를 스택에 저장하고 앞으로 이동한다. 'U'키를 누르면 마지막 위치로 돌아간다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;큐(Queue)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;큐는 선형 자료 구조의 또 다른 종류로, &lt;b&gt;선입선출(FIFO, First In First Out)&lt;/b&gt;의 원칙을 따른다. 즉, 가장 먼저 삽입된 요소가 가장 먼저 제거된다. 큐의 각 요소는 다음과 같은 방식으로 정렬된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dT0sa9/btsHSB5f5Ou/Wve8eZnBxxa7CIkzLkRiq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dT0sa9/btsHSB5f5Ou/Wve8eZnBxxa7CIkzLkRiq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dT0sa9/btsHSB5f5Ou/Wve8eZnBxxa7CIkzLkRiq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdT0sa9%2FbtsHSB5f5Ou%2FWve8eZnBxxa7CIkzLkRiq0%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;500&quot; height=&quot;231&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&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;Enqueue: 큐의 끝에 요소를 추가한다.&lt;/li&gt;
&lt;li&gt;Dequeue: 큐의 앞에서 요소를 제거하고 반환한다.&lt;/li&gt;
&lt;li&gt;Peek: 큐의 앞에 있는 요소를 제거하지 않고 반환한다.&lt;/li&gt;
&lt;li&gt;IsEmpty: 큐가 비어 있는지 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐를 사용해야 하는 상황&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프린터의 인쇄 작업 대기열 관리&lt;/li&gt;
&lt;li&gt;너비 우선 탐색(BFS) 알고리즘&lt;/li&gt;
&lt;li&gt;네트워크 패킷 관리&lt;/li&gt;
&lt;li&gt;작업 스캐줄링&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐를 연결 리스트로 구현할 때의 장단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점: 동적으로 크기를 조절할 수 있어 메모리 효율이 좋다.&lt;br /&gt;단점: 각 요소에 대한 추가 메모리(포인터)가 필요하며, 구현이 배열보다 복잡할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;순환 큐(Circular Queue)란 무엇인가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순환 큐는 고정된 크기의 배열을 사용하여 큐를 구현할 것으로, 마지막 위치가 첫 번째 위치와 연결되어 순환 구조를 갖는다. 이 방식은 메모리 낭비를 줄이고 효율적인 큐 관리를 가능하게 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;간단한 유니티 큐 활용 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티에서 큐는 주로 작업의 순서를 관리하거나 NPC의 행동 대기열을 관리할 때 유리하다.&lt;br /&gt;예를 들어, NPC가 여러 작업을 차례로 수행해야 하는 경우에 큐를 사용하여 작업을 관리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1717991491875&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using UnityEngine;

public class NPCBehavior : MonoBehaviour
{
    private Queue&amp;lt;Vector3&amp;gt; taskQueue = new Queue&amp;lt;Vector3&amp;gt;();

    void Start()
    {
        taskQueue.Enqueue(new Vector3(1, 0, 0));
        taskQueue.Enqueue(new Vector3(2, 0, 0));
        taskQueue.Enqueue(new Vector3(3, 0, 0));
    }

    void Update()
    {
        if (taskQueue.Count &amp;gt; 0 &amp;amp;&amp;amp; !IsMoving())
        {
            Vector3 nextTask = taskQueue.Dequeue();
            StartCoroutine(MoveToPosition(nextTask));
        }
    }

    bool IsMoving()
    {
        // NPC가 현재 이동 중인지 여부를 반환하는 코드
        return false;
    }

    System.Collections.IEnumerator MoveToPosition(Vector3 position)
    {
        while ((transform.position - position).magnitude &amp;gt; 0.1f)
        {
            transform.position = Vector3.MoveTowards(transform.position, position, Time.deltaTime);
            yield return null;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시 코드에서 NPC는 세 가지 위치로 차례로 이동해야 한다. 각 위치는 큐에 저장되고, NPC는 큐에서 위치를 하나씩 꺼내서 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C# 자료구조, 알고리즘, 길찾기/선형자료</category>
      <category>c#</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/112</guid>
      <comments>https://nybot-house.tistory.com/112#entry112comment</comments>
      <pubDate>Mon, 10 Jun 2024 13:00:15 +0900</pubDate>
    </item>
    <item>
      <title>[유니티 C#] Update, LateUpdate, FixedUpdate</title>
      <link>https://nybot-house.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 유니티에서 게임 오브젝트의 동작을 정의할 때, Update, LateUpdate, FixedUpdate 메서드를 자주 사용하게 된다. 이들 메서드는 각각 다른 용도로 사용되는데, 어떤 차이점이 있는지 알아보자.&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론부터&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Update, LateUpdate, FixedUpdate 메서드는 각각 특정 상황에 맞는 용도로 사용된다. 게임 로직과 사용자 입력 처리는 Update에서, 다른 오브젝트 상태에 종속적인 작업은 LateUdpate에서, 물리 연산은 FixedUpdate에서 처리하는 것이 일반적이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;Update 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Update 메서드는 매 프레임마다 한 번 호출된다. 보통 사용자 입력 처리, 애니메이션 업데이트, 게임 로직 등을 처리할 때 사용한다. 즉 지속적으로 상태를 업데이트해야 하는 모든 게임 로직을 처리할 때, 키보드나 마우스의 입력을 매 프레임마다 확인하고 반응해야 할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시&lt;/p&gt;
&lt;pre id=&quot;code_1717900966960&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void Update()
{
    // 사용자 입력을 받아 오브젝트 이동
    float move = Input.GetAxis(&quot;Horizontal&quot;);
    transform.Translate(Vector3.right * move * Time.deltaTime);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LateUpdate 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;LateUpdate 메서드는 모든 Update 메서드가 호출된 후 매 프레임마다 한 번 호출된다. 주로 다른 오브젝트의 Update 로직이 완료된 후 처리해야 하는 작업에 사용된다. 일반적으로 플레이어 캐릭터의 움직임이 완료된 후 카메라를 위치시키는 카메라 추적의 경우와, 다른 오브젝트의 상태에 따라 움직여야 하는 종속적인 움직임을 구현해야 할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시&lt;/p&gt;
&lt;pre id=&quot;code_1717901071540&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Transform player;
private Vector3 offset;

void Start()
{
    // 카메라와 플레이어 간 초기 거리 설정
    offset = transform.position - player.position;
}

void LateUpdate()
{
    // 플레이어의 움직임이 완료된 후 카메라 위치 조정
    transform.position = player.position + offset;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FixedUpdate 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;FixedUpdate 메서드는 고정된 시간 간격마다 호출된다. 이는 물리 엔진 업데이트와 동기화되어 있으며, 물리 계산을 처리하는 데 사용된다. 즉 Rigidbody를 사용한 물리 기반 움직임이나 충돌을 처리할 때 사용하고, 시간 간격이 일정해야 하는 정확한 시간 간격이 필요한 작업들에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예시&lt;/p&gt;
&lt;pre id=&quot;code_1717901144587&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Rigidbody rb;
public float speed;

void FixedUpdate()
{
    // 물리 기반 움직임 처리
    float move = Input.GetAxis(&quot;Horizontal&quot;);
    rb.MovePosition(rb.position + Vector3.right * move * speed * Time.fixedDeltaTime);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;차이점 정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;호출 빈도 및 시점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Update: 매 프레임 호출.&lt;/li&gt;
&lt;li&gt;LateUpdate: 매 프레임 호출되지만 Update 후에 호출.&lt;/li&gt;
&lt;li&gt;FixedUpdate: 고정된 시간 간격으로 호출.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Update: 일반적인 게임 로직 및 사용자 입력 처리.&lt;/li&gt;
&lt;li&gt;LateUpdate: 다른 오브젝트의 업데이트가 완료된 후 처리.&lt;/li&gt;
&lt;li&gt;FixedUpdate: 물리 연산 및 일정한 시간 간격이 필요한 작업.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Update &amp;amp; LateUpdate: 프레임 속도에 따라 변동 (deltaTime을 잘 활용해야 함).&lt;/li&gt;
&lt;li&gt;FixedUpdate: 고정된 시간 간격. (유니티 설정에서 시간 텀 변경 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>유니티 C#/유니티 프로그래밍</category>
      <category>c#</category>
      <category>fixedupdate</category>
      <category>LateUpdate</category>
      <category>Update</category>
      <category>생명주기메서드</category>
      <category>유니티</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/111</guid>
      <comments>https://nybot-house.tistory.com/111#entry111comment</comments>
      <pubDate>Sun, 9 Jun 2024 11:50:23 +0900</pubDate>
    </item>
    <item>
      <title>[유니티] Monobehaviour, 그리고 유니티의 기본 원리</title>
      <link>https://nybot-house.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;유니티를 시작하기에 앞서 가장 먼저 공부해야 하는 것은 유니티의 동작 원리와 &lt;b&gt;Monobehaviour&lt;/b&gt; 이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;유니티 프로그램의 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;유니티는 컴포넌트 기반 아키텍처를 사용한다. 게임 오브젝트는 여러 컴포넌트로 구성되어 있으며, 각 컴포넌트는 특정 기능을 담당한다. MonoBehaviour를 상속받은 스크립트는 게임 오브젝트의 동작을 정의하는 주요 컴포넌트이다.&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;b&gt;게임 오브젝트(GameObject): 씬 내의 모든 개체를 의미한다. 각 게임 오브젝트는 여러 컴포넌트를 가질 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트(Component): 게임 오브젝트에 붙여서 특정 기능을 수행하는 모듈.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;씬(Scene): 게임의 각 단위를 구성하는 환경. 게임 오브젝트와 컴포넌트들이 배치되어 있다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉 유니티는 레고를 조립하듯, 컴포넌트와 게임 오브젝트들을 이리저리 조립하여 게임을 만드는 엔진이다. 유니티는 OOP의 원리를 정확히 따르고 있는 게임 엔진이다. 가끔씩 C++ 나 자체 엔진을 사용하던 회사에서 일하던 분들이 유니티로 코딩할 때 유니티의 기능을 사용하지 않고 직접 클래스를 구현한다거나 상속관계로 복잡하게 얽혀 있는 스크립팅을 하는 경우를 보았는데, 유니티에 적합한 프로그래밍이 아니라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유니티의 MonoBehaviour 란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;MonoBehaviour는 Unity 기본 클래스 중 하나로 모든 스크립트가 상속해야 하는 클래스이다. 이 Monobehaviour를 통해 Unity 엔진의 다양한 기능을 사용할 수 있다.&lt;br /&gt;(물론 상속하지 않고 자체 클래스를 만들어서 상속받아 프로그래밍 하는 경우가 있으나 유니티 게임 개발에서, 특히 주니어 레벨이라면 절대로 추천하는 방법이 아니다. 현업에서 요구하는 역량도 아니라고 생각한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생명 주기 메서드: Monobehaviour는 여러 생명 주기 메서드를 제공하여 게임 오브젝트의 다양한 상태에서 호출된다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유니티 MonoBehaviour 주요 메서드 개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 스크립트를 만들면 자동으로 MonoBehaviour 를 상속받은 상태이고 start와 update 함수가 보일 것이다. 이는 유니티의 생명 주기 메서드인데, 다음과 같은 종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Awake&lt;/b&gt;: 모든 스크립트와 객체가 초기화된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OnEnable&lt;/b&gt;: 객체가 활성화될 때 호출된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Start&lt;/b&gt;: 초기화 작업을 수행하며, 게임 시작 시 한 번 호출된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Update&lt;/b&gt;: 매 프레임마다 호출되어 게임 로직을 업데이트한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LateUpdate&lt;/b&gt;: 모든 Update 호출이 완료된 후에 호출된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FixedUpdate&lt;/b&gt;: 고정된 간격으로 호출되어 물리 연산을 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OnDisable&lt;/b&gt;: 객체가 비활성화될 때 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OnDestroy&lt;/b&gt;: 객체가 파괴될 때 호출한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717899046464&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ExampleScript : MonoBehaviour
{
void Awake()
{
    // 스크립트가 인스턴스화될 때 실행되는 초기화 코드
    Debug.Log(&quot;Awake 호출됨&quot;);
}

void Start()
{
    // 초기화 코드
    Debug.Log(&quot;게임이 시작되었습니다!&quot;);
}

void Update()
{
    // 매 프레임마다 실행되는 코드
    transform.Translate(Vector3.forward * Time.deltaTime);
}

void LateUpdate()
{
    // 매 프레임마다 실행되지만 Update 후에 실행되는 코드
    camera.transform.position = player.transform.position + offset;
}

void FixedUpdate()
{
    // 물리 업데이트
    rb.MovePosition(rb.position + Vector3.forward * Time.fixedDeltaTime);
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Start, Update, LateUpdate, FixedUpdate 등의 메서드를 통해 게임 오브젝트의 동작을 제어하고, Awake와 OnEnable등 메서드를 통해 초기화 작업을 처리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;또한 MonoBehaviour는 게임 오브젝트에 컴포넌트로 추가되어 게임 오브젝트의 동작을 제어하고 확장할 수 있다. 코루틴을 사용하여 비동기 작업을 쉽게 구현할 수 있다는 것도 장점이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유니티 이벤트 실행 순서 개요 (실행 시퀸스)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Unity 스크립트를 실행하면 사전에 지정한 순서대로 여러 개의 이벤트 함수가 실행된다. 이 순서에 익숙해 져야 전체적인 프로그래밍 로직을 짤 수 있다.&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-origin-width=&quot;1151&quot; data-origin-height=&quot;2397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z81O3/btsHSrVO25T/6FBVAaSJx5aabKIqgvtl51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z81O3/btsHSrVO25T/6FBVAaSJx5aabKIqgvtl51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z81O3/btsHSrVO25T/6FBVAaSJx5aabKIqgvtl51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ81O3%2FbtsHSrVO25T%2F6FBVAaSJx5aabKIqgvtl51%2Fimg.jpg&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;1151&quot; height=&quot;2397&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;2397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1717898676282&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;이벤트 함수의 실행 순서 - Unity 매뉴얼&quot; data-og-description=&quot;Unity 스크립트를 실행하면 사전에 지정한 순서대로 여러 개의 이벤트 함수가 실행됩니다. 이 페이지에서는 이러한 이벤트 함수를 소개하고 실행 시퀀스에 어떻게 포함되는지 설명합니다.&quot; data-og-host=&quot;docs.unity3d.com&quot; data-og-source-url=&quot;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&quot; data-og-url=&quot;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html&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;이벤트 함수의 실행 순서 - Unity 매뉴얼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unity 스크립트를 실행하면 사전에 지정한 순서대로 여러 개의 이벤트 함수가 실행됩니다. 이 페이지에서는 이러한 이벤트 함수를 소개하고 실행 시퀀스에 어떻게 포함되는지 설명합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.unity3d.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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유니티는 싱글 쓰레드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;유니티는 기본적으로 싱글 쓰레드 환경에서 동작한다. 이는 게임의 주요 루프(프레임 업데이트)가 단일 메인 쓰레드에서 실행된다는 것을 의미한다. 하지만 유니티는 특정 작업에 대해 멀티 쓰레딩을 지원하며, 개발자는 필요한 경우 멀티 쓰레딩을 직접 구현할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;싱글 쓰레드 환경의 특징&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;메인 스레드&lt;/b&gt;: 유니티의 메인 루프(업데이트 루프)는 메인 스레드에서 실행된다. 이 루프는 프레임마다 모든 게임 오브젝트의 Update, FixedUpdate, LateUpdate 메서드를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전성&lt;/b&gt;: 싱글 쓰레드 환경은 멀티 쓰레드 환경에서 발생할 수 있는 동기화 문제나 데이터 경쟁 상태를 방지하여 코드 작성이 비교적 간단하고 안전하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일관성&lt;/b&gt;: 모든 게임 로직이 동일한 스레드에서 실행되므로 예측 가능하고 일관된 동작을 보장한다.&lt;/li&gt;
&lt;/ol&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;</description>
      <category>유니티 C#/유니티 프로그래밍</category>
      <category>Monobehaviour</category>
      <category>생명주기</category>
      <category>생명주기 메서드</category>
      <category>싱글쓰레드</category>
      <category>유니티</category>
      <author>HeadlessCreator</author>
      <guid isPermaLink="true">https://nybot-house.tistory.com/110</guid>
      <comments>https://nybot-house.tistory.com/110#entry110comment</comments>
      <pubDate>Sun, 9 Jun 2024 11:27:31 +0900</pubDate>
    </item>
  </channel>
</rss>