<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sejun-2.log</title>
        <link>https://velog.io/</link>
        <description>초보 개발자</description>
        <lastBuildDate>Thu, 23 Oct 2025 04:07:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sejun-2.log</title>
            <url>https://velog.velcdn.com/images/sejun-2/profile/844e00ec-d1d1-4537-ae0e-ef409c9d8d66/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sejun-2.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sejun-2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[🎉[경일게임IT아카데미] 온라인 부트캠프 게임 개발자 리얼 후기! – 좌충우돌 8개월 생존기🐣]]></title>
            <link>https://velog.io/@sejun-2/%EA%B2%BD%EC%9D%BC%EA%B2%8C%EC%9E%84IT%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%98%A8%EB%9D%BC%EC%9D%B8-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-8%EA%B0%9C%EC%9B%94-%EA%B3%BC%EC%A0%95-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A6%AC%EC%96%BC-%ED%9B%84%EA%B8%B0-%EC%A2%8C%EC%B6%A9%EC%9A%B0%EB%8F%8C-8%EA%B0%9C%EC%9B%94-%EC%83%9D%EC%A1%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sejun-2/%EA%B2%BD%EC%9D%BC%EA%B2%8C%EC%9E%84IT%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%98%A8%EB%9D%BC%EC%9D%B8-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-8%EA%B0%9C%EC%9B%94-%EA%B3%BC%EC%A0%95-%EA%B2%8C%EC%9E%84-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A6%AC%EC%96%BC-%ED%9B%84%EA%B8%B0-%EC%A2%8C%EC%B6%A9%EC%9A%B0%EB%8F%8C-8%EA%B0%9C%EC%9B%94-%EC%83%9D%EC%A1%B4%EA%B8%B0</guid>
            <pubDate>Thu, 23 Oct 2025 04:07:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="span-stylecolororange🎮-시작-계기span"><span style="color:orange">🎮 시작 계기</span></h3>
</blockquote>
<p>코로나 팬데믹으로 인생이 뒤바뀌고, 어머니 간병이라는 뜻밖의 임무를 마친 뒤, 인생 2막을 고민하던 중 우연히 접한 게임 개발! 이때부터 “아, 이거구나!” 하고 반짝이는 눈으로 경일게임IT아카데미 온라인 부트캠프 8개월 과정을 선택했습니다. ‘이게 내 길일지도?’ 하는 설렘과 함께 대장정을 시작했죠.
<img src="https://velog.velcdn.com/images/sejun-2/post/e3a77353-20af-4b00-87a6-2b12eac38933/image.jpg" alt=""></p>
<hr>
<blockquote>
<h3 id="span-stylecolororange💻-온라인-학습의-속살span"><span style="color:orange">💻 온라인 학습의 속살</span></h3>
</blockquote>
<p>처음엔 ‘혼자 온라인이라 괜찮으려나’ 했는데, 의외로 수업 녹화본과 라이브 강의를 무한 반복하며 나만의 페이스로 배웠어요. Notion과 diagrams, Figma 덕분에 팀원들과 실시간으로 교감하고, 과제·프로젝트 진행 리듬도 척척 맞췄죠. 자기주도 학습, 멋지게 해내기 위한 ‘나만의 비법’도 터득! 꾸준한 복습과 토론이 진짜 실력으로 이어졌습니다.
<img src="https://velog.velcdn.com/images/sejun-2/post/d98913fc-2d1c-47eb-a9fc-09aac917baa4/image.png" alt=""></p>
<hr>
<blockquote>
<h3 id="span-stylecolororange📃-찐-프로젝트-경험--wintercupspan"><span style="color:orange">📃 찐 프로젝트 경험 – WinterCup</span></h3>
</blockquote>
<p>&quot;이것이 진짜 게임 개발이다!&quot; 강렬한 첫맛을 느낀 프로젝트였어요. 동계 올림픽 국가대표 팀을 관리하는 모바일 시뮬레이터, Unity와 C#으로 튜토리얼 시스템, 사운드매니저, 선수·코치 관리 UI까지 싹싹 직접 만들며 개발세계에 ‘입덕’했습니다. 팀원과의 소통 덕분에 Git 충돌, 코드 꼬임, 버그 수십 개도 웃으며 넘겼죠. 완성작을 보고 “우와, 내가 만들었어?” 하며 뿌듯함이 끝없이 밀려왔던 순간 기억나요.
<img src="https://velog.velcdn.com/images/sejun-2/post/7bde3a31-2a1b-48be-862c-b74243d70bb9/image.gif" alt=""></p>
<hr>
<blockquote>
<h3 id="span-stylecolororange✈️-수료-소감과-미래를-향해span"><span style="color:orange">✈️ 수료 소감과 미래를 향해</span></h3>
</blockquote>
<p>8개월이란 시간에 뼈를 깎는 노력과 웃음, 갈등, 배움이 공존했지만 전 지금 화면 아래에 ‘입문→초급→중급’ 올라가는 계단을 딛고 서 있습니다. 미래 수강생 분들께는 ‘포기 말고, 소통하고, 팀플을 즐겨라!’ 권하고 싶어요. 경일게임IT아카데미는 단지 학원이나 강의가 아니라 게임 개발자의 꿈을 현실로 만들어주는 여행이니까요!
<img src="https://velog.velcdn.com/images/sejun-2/post/7fb580a4-b229-442f-9c59-9907ec118593/image.jpg" alt=""></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[애니메이션 이벤트, 내적을 활용한 공격, 피격 판정 구현]]></title>
            <link>https://velog.io/@sejun-2/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%82%B4%EC%A0%81%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%B5%EA%B2%A9-%ED%94%BC%EA%B2%A9-%ED%8C%90%EC%A0%95-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@sejun-2/%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%82%B4%EC%A0%81%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%B5%EA%B2%A9-%ED%94%BC%EA%B2%A9-%ED%8C%90%EC%A0%95-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 23 May 2025 15:00:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ol>
<li>개요</li>
</ol>
</blockquote>
<p>애니메이션 이벤트 : 실행되는 애니메이션의 특정 타이밍에 함수를 호출할 수 있는 기능, 프레임 단위로 설정 가능</p>
<p>사용 예시&gt; </p>
<ol>
<li>발이 지면에 닿을 때 먼지 이펙트 및 발걸음 사운드</li>
<li>공격 타이밍에 맞춰 파티클 생성</li>
<li>무기를 휘두르는 타이밍에 맞춰 공격 로직과 이펙트 발생</li>
</ol>
<blockquote>
<p>사용 에셋</p>
</blockquote>
<p><a href="https://www.mixamo.com/#/?page=1&amp;query=slash">https://www.mixamo.com/#/?page=1&amp;query=slash</a> 애니메이션(Great Sword Pack)</p>
<p><a href="https://www.mixamo.com/#/?page=1&amp;query=&amp;type=Character">https://www.mixamo.com/#/?page=1&amp;query=&amp;type=Character</a> 캐릭터(Maia W/Prop J J Ong)</p>
<blockquote>
<ol start="2">
<li>애니메이션 이벤트 사용 방법</li>
</ol>
</blockquote>
<ul>
<li><strong>1.  애니메이션 클립에서 이벤트 추가 (Read-Only 파일 불가능)</strong><ol>
<li>애니메이션 클립 더블 클릭하여 Animation 창 진입</li>
</ol>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sejun-2/post/9b2c4edf-83c0-48a9-a405-9f327c5b52de/image.png" alt=""></p>
<pre><code>2. 프레임 단위로 이동하여 메서드를 호출할 프레임 지정
3. 이벤트 버튼 클릭하여 추가 가능</code></pre><p><img src="https://velog.velcdn.com/images/sejun-2/post/bcddeb6b-4e9c-4c84-a952-0ec8e573af8b/image.png" alt=""></p>
<ul>
<li><strong>2. 애니메이션 모델 파일 (클립X)의 Animation 설정에서 이벤트 추가</strong><ol>
<li>애니메이션 모델 파일 클릭</li>
</ol>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sejun-2/post/336284f9-52fa-4081-a9fa-b229953c39fa/image.png" alt=""></p>
<pre><code>2. Animation 클릭하고 아래에서 Event 토글 열기</code></pre><p><img src="https://velog.velcdn.com/images/sejun-2/post/91fe3e2b-751c-45be-b93b-00d2cda475aa/image.png" alt=""></p>
<pre><code>3. 미리보기 클릭하여 재생하며 메서드 호출할 프레임 지정 (아바타 설정 필요)</code></pre><p><img src="https://velog.velcdn.com/images/sejun-2/post/6c1ab2b6-3ae9-48e6-af36-6f721949a51d/image.png" alt=""></p>
<pre><code>    Other 클릭하여 파일에 맞는 아바타 선택

4. Add Event 버튼을 눌러 지정한 프레임에 이벤트 추가</code></pre><p><img src="https://velog.velcdn.com/images/sejun-2/post/d255075b-9bcd-4e0e-87c1-ee3f3534b113/image.png" alt=""></p>
<pre><code>5. 오브젝트에 부착되어 있는 스크립트에 포함되어 있는 메서드 이름 작성
    1. 메서드에 매개변수가 있다면 추가 가능
    2. Object에 참조 필요 X
6. Apply 눌러 저장</code></pre><blockquote>
<ol start="3">
<li>주의점</li>
</ol>
</blockquote>
<ul>
<li>해당 애니메이션이 100% 재생되기 전에 Transition되면서 다음 애니메이션으로 넘어가면 호출이 되지 않는 문제 발생<ul>
<li>다음 애니메이션으로 Transition되는 부분에서 50% 이상 진행된 이후로는 이벤트 호출 X</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[간단한 인벤토리 만들기]]></title>
            <link>https://velog.io/@sejun-2/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%9D%B8%EB%B2%A4%ED%86%A0%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sejun-2/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%9D%B8%EB%B2%A4%ED%86%A0%EB%A6%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 13 May 2025 16:19:46 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static UnityEditor.Progress;

public class Inventory : MonoBehaviour
{
    // 인벤토리 클래스
    // 인벤토리 클래스는 플레이어의 아이템 목록을 관리합니다.
    // 아이템 추가, 제거, 사용 등의 기능을 제공합니다.

    #region Singleton    // 하나의 인벤토리만 존재하기 위해 싱글톤 사용
    private static Inventory instance;
    public static Inventory Instance
    {
        get
        {
            if (instance == null)
            {
                Inventory prefab = Resources.Load&lt;Inventory&gt;(&quot;Inventory&quot;); // 인벤토리 로드
                Instantiate(prefab); // 인스턴스 생성
            }
            return instance;
        }
    }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this; // 인스턴스 설정
            //DontDestroyOnLoad(gameObject); // 씬 전환 시 파괴되지 않도록 설정
        }
        else
        {
            Destroy(gameObject); // 중복 인스턴스 제거
        }
    }
    #endregion

    #region Item
    public List&lt;Item&gt; items = new List&lt;Item&gt;(); // 아이템 목록

    // 아이템 추가
    public void AddItem(Item item)
    {
        // 아이템 추가 로직
        items.Add(item); // 아이템 추가
        item.gameObject.SetActive(false); // 아이템 비활성화
        item.transform.parent = transform; // 아이템 부모를 인벤토리로 설정
    }

    //public void AddPotion(GameObject potionPrefab)
    //{
    //    GameObject newPotion = Instantiate(potionPrefab);
    //    // 인벤토리 UI에 연결 등 추가 작업
    //}

    // 아이템 사용
    public void UseItem(Item item)
    {
        // 아이템 사용 로직
        item.Use(); // 아이템 사용
        RemoveItem(item); // 아이템 제거
    }
    public void UseItem(int index)
    {
        // 아이템 사용 로직
        Item item = items[index]; // 사용하려는 아이템
        items.RemoveAt(index); // 아이템 목록에서 제거
        item.Use(); // 아이템 사용
        Destroy(item.gameObject); // 아이템 오브젝트 파괴

    }

    // 아이템 드롭 로직
    public void DropItem(Item item)
    {        
        items.Remove(item); // 아이템 목록에서 제거
        item.gameObject.SetActive(true); // 아이템 활성화
        item.transform.parent = null; // 아이템 부모를 null로 설정
    }

    public void DropItem(int index)
    {
        Item item = items[index]; // 드롭할 아이템
        items.RemoveAt(index); // 아이템 목록에서 제거
        item.gameObject.SetActive(true); // 아이템 활성화
        item.transform.parent = null; // 아이템 부모를 null로 설정
    }


    // 아이템 제거
    public void RemoveItem(Item item)
    {
        // 아이템 제거 로직
        items.Remove(item); // 아이템 제거
    }


    // 아이템 목록 반환
    //public List&lt;Item&gt; GetItems()
    //{
    //    return items; // 아이템 목록 반환
    //}
    #endregion


}</code></pre>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Item : MonoBehaviour
{
    public string itemName; // 아이템 이름
    public string itemDescription; // 아이템 설명

    public abstract void Use();

    //private void OnCollisionEnter(Collision collision)    
    //{
    //    if (collision.gameObject.CompareTag(&quot;Player&quot;))
    //    {
    //        Use();
    //        Destroy(gameObject); // 아이템 사용 후 파괴
    //    }
    //}

}
</code></pre>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Potion : Item
{
    Player player; // 플레이어 객체
    private float rotSpeed = 100f;        // 돌아가는 속도

    public Potion()
    {
        itemName = &quot;힐링 포션&quot;; // 아이템 이름 설정ㄴㄴㄴ
        itemDescription = &quot;소량의 체력을 회복하는 아이템&quot;; // 아이템 설명 설정
    }

    public override void Use()
    {
        Debug.Log(&quot;포션 사용 체력 30 회복&quot;); // 포션 사용 로그 출력
        player.Heal(30); // 플레이어 체력 회복
    }

    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.CompareTag(&quot;Player&quot;))
        {
            player = collision.gameObject.GetComponent&lt;Player&gt;(); // 플레이어 컴포넌트 가져오기
            Inventory inventory = collision.gameObject.GetComponentInChildren&lt;Inventory&gt;();
            inventory.AddItem(this); 
            Use(); // 포션 사용
            //Destroy(gameObject); // 포션 오브젝트 파괴
        }
    }

    void Update()
    {
        Rotate(); // 포션 회전
    }

    private void Rotate()
    {
        transform.Rotate(Vector3.up, rotSpeed * Time.deltaTime);    // Vector3.up 을 기준으로 돈다. -&gt; Up 위를 기준!으로 도는 것 
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본지원UI, UI연동]]></title>
            <link>https://velog.io/@sejun-2/%EA%B8%B0%EB%B3%B8%EC%A7%80%EC%9B%90UI-UI%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@sejun-2/%EA%B8%B0%EB%B3%B8%EC%A7%80%EC%9B%90UI-UI%EC%97%B0%EB%8F%99</guid>
            <pubDate>Mon, 12 May 2025 17:38:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/sejun-2/post/bd297049-5677-483a-bc37-43ee06c46cc0/image.png" alt=""></p>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class PlayerPresenter : MonoBehaviour
{
    [Header(&quot;Model&quot;)]
    [SerializeField] PlayerModel model;

    [Header(&quot;View&quot;)]
    [SerializeField] TMP_Text playerHPTextUI;
    [SerializeField] TMP_Text playerMaxTextUI;
    [SerializeField] Slider playerHPSliderUI;
    //[SerializeField] TMP_Text playerSpeedTextUI;
    [SerializeField] TMP_Text playerJumpTextUI;
    private int jump = 0;

    private void OnEnable()
    {
        model.OnHpChanged += SetHP;
        model.OnMaxHPChanged += SetMaxHP;
        SetMaxHP(model.MaxHP);
        SetHP(model.HP);
    }

    private void OnDisable()
    {
        model.OnHpChanged -= SetHP;
        model.OnMaxHPChanged -= SetMaxHP;
    }

    private void Update()
    {
        //SetSpeed(model.Velocity);

        if (Input.GetKeyDown(KeyCode.Space)){
            SetJumpCount();
        }

    }

    public void SetHP(int hp)
    {
        playerHPTextUI.text = $&quot;{hp}&quot;;
        playerHPSliderUI.value = hp;
    }

    public void SetMaxHP(int maxHP)
    {
        playerMaxTextUI.text = $&quot;{maxHP}&quot;;
        playerHPSliderUI.maxValue = maxHP;
    }

    //public void SetSpeed(Vector3 velocity)
    //{
    //    playerSpeedTextUI.text = $&quot;{velocity.magnitude}&quot;;
    //}

    public void SetJumpCount()
    {
        jump += 1;  // 점프시 카운트
        playerJumpTextUI.text = $&quot;jump : {jump}&quot;;   // 현제 카운트 시각화

    }
}</code></pre>
<pre><code class="language-cpp">using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerModel : MonoBehaviour
{
    [SerializeField] int hp;
    public int HP { set { hp = value; OnHpChanged?.Invoke(hp); } get { return hp; } }
    public event Action&lt;int&gt; OnHpChanged;    // 이벤트 

    [SerializeField] int maxHP;
    public int MaxHP { set { maxHP = value; OnMaxHPChanged?.Invoke(maxHP); } get { return maxHP; } }
    public event Action&lt;int&gt; OnMaxHPChanged;

    [SerializeField] Rigidbody rigid;
    public Vector3 Velocity { set { rigid.velocity = value; OnVelocityChanged?.Invoke(rigid.velocity); } get { return rigid.velocity; } }
    public Action&lt;Vector3&gt; OnVelocityChanged;
}</code></pre>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 컨트롤러 : 논리로직(동작)
public class PlayerController : MonoBehaviour
{
    [SerializeField] PlayerModel model;
    [SerializeField] Rigidbody rigid;
    [SerializeField] float jumpPower;

    private void Update()
    {
        //Move();  

        if (Input.GetKeyDown(KeyCode.Space))
        {
            rigid.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);  // Impulse = 한번 펑! 힘을 주는 것
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            model.HP += 1;
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            model.HP -= 1;
        }
    }

    private void Move()
    {
        float xInput = Input.GetAxis(&quot;Horizontal&quot;);
        float zInput = Input.GetAxis(&quot;Vertical&quot;);

        Vector3 dir = new Vector3(xInput, 0, zInput);
        if (dir.sqrMagnitude &gt; 1)
        {
            dir = dir.normalized;
        }

        model.Velocity = Vector3.MoveTowards(model.Velocity, dir * 5, 10 * Time.deltaTime);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[레이어 마스크]]></title>
            <link>https://velog.io/@sejun-2/%EB%A0%88%EC%9D%B4%EC%96%B4-%EB%A7%88%EC%8A%A4%ED%81%AC</link>
            <guid>https://velog.io/@sejun-2/%EB%A0%88%EC%9D%B4%EC%96%B4-%EB%A7%88%EC%8A%A4%ED%81%AC</guid>
            <pubDate>Mon, 12 May 2025 17:32:44 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cpp">public class CameraRaycaster : MonoBehaviour
{
    private Camera _cam;

    [Header(&quot;Set Raycast Config&quot;)]
    [SerializeField][Range(0, 100)] private float _rayDistance = 100;
    [SerializeField] private KeyCode _actionKey = KeyCode.Mouse0;
    [SerializeField] private LayerMask _targetLayer = 0;

    private void Awake() =&gt; Init();
    private void Update() =&gt; RayShoot();

    private void Init()
    {
        _cam = Camera.main;

        //LayerUtil.AddLayer(ref _targetLayer, &quot;Player&quot;, &quot;Monster&quot;);
        //LayerUtil.RemoveLayer(ref _targetLayer, &quot;Item&quot;);
    }

    private void RayShoot()
    {
        if(!Input.GetKeyDown(_actionKey)) return;

        Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if(Physics.Raycast(ray, out hit, _rayDistance, _targetLayer))
        {
            Debug.Log($&quot;검출 대상 Layer = {_targetLayer.value}&quot;);
            Debug.Log($&quot;오브젝트 검출 : Layer = {hit.transform.gameObject.layer}&quot;);

            // 0000 0000 _ 0000 0000 _ 0000 0000 _ 0000 0000

            // EX&gt; Monster (9 Layer) 를 클릭해서 &#39;제외&#39;한다고 가정.

            // hitLayerNumber = 9
            int hitLayerNumber = hit.transform.gameObject.layer;    // 마우스로 클릭한 오브젝트 layer 번호, 자릿수

            // ...0000 0010 0000 0000
            int hitLayer = (1 &lt;&lt; hitLayerNumber);   // 1을 왼쪽으로 hitLayerNumber 만큼 미룬다.

            // ...1111 1101 1111 1111
            int notLayer = ~hitLayer;       // ~hitLayer -&gt; not연산자 ~, 클릭한 오브젝트를 안보이게 하기 위해

            // _cam.cullingMask =  ...1111 1111 1111 1111
            // not Layer        =  ...1111 1101 1111 1111
            // And              =&gt; ...1111 1101 1111 1111   =&gt; 둘다 1인것만 남긴다.
            _cam.cullingMask &amp;= notLayer;
        }
    }

        // OR 연산
        // 둘중 하나라도 1이면 1
        // 중첩하고 싶은 레이어를 더할 때 쓸 수 있음.
        //
        //    ...1000 0000
        // OR ...0000 1000
        // ---------------
        // =&gt; ...1000 1000

        // NOT 연산
        // 뒤집어버림
        //
        //        ...0101 0010
        // NOT =&gt; ...1010 1101

        // AND 연산
        // 둘 다 1이어야 1이 나옴.
        // 하나라도 0이면 0이 나온다.
        //
        //     ...1010 0010
        // AND ...0010 0100
        // ---------------
        // =&gt;  ...0010 0000
}</code></pre>
<pre><code class="language-cpp">public static class LayerUtil
{
    public static void AddLayer(ref LayerMask target, params string[] names)
    {
        foreach(string n in names)
        {
            target |= 1 &lt;&lt; LayerMask.NameToLayer(n);
        }
    }

    public static void RemoveLayer(ref LayerMask target, string name)
    {
        target &amp;= ~(1 &lt;&lt; LayerMask.NameToLayer(name));  // name으로 layer의 번호를 반환, 그리고 반전, 엔드 연산
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[씬 Scene]]></title>
            <link>https://velog.io/@sejun-2/%EC%94%AC-Scene</link>
            <guid>https://velog.io/@sejun-2/%EC%94%AC-Scene</guid>
            <pubDate>Tue, 22 Apr 2025 17:27:47 GMT</pubDate>
            <description><![CDATA[<h2 id="씬-scene">씬 (Scene)</h2>
<ul>
<li>유니티에서 게임 월드를 구성하는 단위 </li>
<li>프로젝트에 원하는 수만큼 씨능 포함할 수 있고 씬전환을 통해 다른 게임월드를 불러올 수 있음</li>
<li>다중 씬을 이용하여 여러 씬을 동시에 열어 같은 게임월드에서 사용도 가능함</li>
</ul>
<h3 id="빌드설정">&lt;빌드설정&gt;</h3>
<ul>
<li>유니티에서 여러 씬을 사용하기 위해선 빌드 설정에서 씬을 포함해야 함 </li>
<li>빌드 설정에서 포함한 씬을 게임 빌드 결과물에 포함함</li>
</ul>
<h3 id="씬-전환">&lt; 씬 전환&gt;</h3>
<ul>
<li>프로젝트에 포함된 다른 씬을 로딩하고 기존의 씬의 내용을 삭제함.</li>
</ul>
<pre><code class="language-cpp">  public class SceneChanger : MonoBehaviour
  {
        private void Awake()
        {
            Debug.Log(&quot;씬 체인저 생성!&quot;);
        }

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Alpha0))
            {
                SceneManager.LoadScene(&quot;TitleScene&quot;);  //씬 순서 번호 or 씬 이름 으로 로드 가능
            }
            else if (Input.GetKeyDown(KeyCode.Alpha1))
            {
                SceneManager.LoadScene(&quot;GameScene&quot;);
            }

        }

    /**********************************************************/


    // &lt;씬 전환&gt;
    // 프로젝트에 포함된 다른 씬을 로딩하고 기존의 씬의 내용을 삭제함
    public void ChageScene(string sceneName)
    {
        SceneManager.LoadScene(sceneName);
    }


    // &lt;씬 추가&gt;
    // 프로젝트에 포함된 다른 씬을 로딩하고 기존의 씬의 내용을 유지함
    public void AddScene(string sceneName)
    {
        SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
    }


    // &lt;비동기 씬 로드&gt;
    // 씬 로딩을 백그라운드로 진행하여 게임 중 멈춤이 없도록하는 비동기 방법
    public void ChangeSceneASync()
    {
        AsyncOperation operation = SceneManager.LoadSceneAsync(&quot;StageScene2&quot;, LoadSceneMode.Additive);

        operation.allowSceneActivation = true;      // 씬 로딩 완료시 바로 씬 전환을 진행하는지 여부
        bool isLoaded = operation.isDone;           // 씬 로딩의 완료여부 확인
        float progress = operation.progress;        // 씬 로딩의 진행률 확인
        operation.completed += (oper) =&gt; { };       // 씬 로딩의 완료시 진행할 이벤트 추가
    }


    // &lt;씬 언로드&gt;
    // 로딩되어 있는 씬을 언로드 시키는 방법
    public void UnloadScene()
    {
        Scene scene = SceneManager.GetSceneByName(&quot;StageScene2&quot;);
        if (scene.isLoaded == true)
        {
            SceneManager.UnloadSceneAsync(scene);
        }
    }


    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag != &quot;Player&quot;)
            return;

        SceneManager.LoadSceneAsync(&quot;StageScene2&quot;, LoadSceneMode.Additive);
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject.tag != &quot;Player&quot;)
            return;

        SceneManager.UnloadSceneAsync(&quot;StageScene2&quot;);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Singleton Pattern, Adapter Pattern]]></title>
            <link>https://velog.io/@sejun-2/Singleton-Pattern</link>
            <guid>https://velog.io/@sejun-2/Singleton-Pattern</guid>
            <pubDate>Mon, 21 Apr 2025 15:04:09 GMT</pubDate>
            <description><![CDATA[<h2 id="디자인-패턴-singleton">디자인 패턴 Singleton</h2>
<h3 id="span-stylecolor00ff00싱글톤-패턴span"><span style="color:#00FF00">&lt;싱글톤 패턴&gt;</span></h3>
<ul>
<li>오직 한 개의 클래스 인스턴스만을 갖도록 보장</li>
<li>이에 대한 전역적인 접근점을 제공</li>
</ul>
<h3 id="span-stylecolor00ff00구현span"><span style="color:#00FF00">&lt;구현&gt;</span></h3>
<ol>
<li>단일 인스턴스를 유지하기 위해 생성자의 접근권한을 외부에서 생성할 수 없도록 제한</li>
<li>단일 인스턴스를 보관할 정적 변수를 구성</li>
<li>외부에서 접근이 가능한 GetInstance 함수를 구성</li>
<li>GetInstance 함수에서 단일 인스턴스가 없을 경우 인스턴스를 생성하여 정적 변수에 참조</li>
<li>GetInstance 함수에서 단일 인스턴스가 있을 경우 정적 변수에 참조된 인스턴스를 반환</li>
</ol>
<h3 id="span-stylecolor00ff00장점span"><span style="color:#00FF00">&lt;장점&gt;</span></h3>
<ol>
<li>하나뿐인 존재로 주요 클래스 &amp; 관리자 역할에 적합함</li>
<li>전역적 접근으로 참조에 필요한 작업이 없이 빠른 접근이 가능함</li>
<li>인스턴스들이 싱글톤을 통하여 데이터를 공유하기 쉬워짐</li>
</ol>
<h3 id="span-stylecolor00ff00주의점span"><span style="color:#00FF00">&lt;주의점&gt;</span></h3>
<ol>
<li>싱글톤은 너무 많은 책임을 짊어지는 경우가 많으며 단일책임원칙을 위반</li>
<li>싱글톤은 전역접근으로 코드의 결합도를 높이므로 남발하지 않아야함</li>
<li>싱글톤은 단위 테스트를 하기 어렵게 함</li>
</ol>
<pre><code class="language-cpp">public class Singleton : MonoBehaviour
{
    private static Singleton instance; 

    public static Singleton GetInstance()   // 반드시 하나만 있음.
    {
        if (instance == null)
        {
            instance = new Singleton(); // 처음만 만들어 주고 
        }
        return instance;    // 다음부턴 만들어 진 것을 가져다 씀.
    }

    private Singleton() { }
}</code></pre>
<h2 id="디자인-패턴-adapter-pattern">디자인 패턴 Adapter Pattern</h2>
<pre><code class="language-cpp">public class DamageAdapter : MonoBehaviour, IDamagable
{
    public UnityEvent&lt;GameObject, int&gt; OnDamaged;

    // Switch 가 직접 데미지를 받지 않고 현제 어뎁터를 통해서 Switch를 이벤트를 통해 SwitchAction 을 연결시켜서 스위치 기능 활성화
    public void TakeDamage(GameObject dealer, int damage)
    {
        OnDamaged?.Invoke(dealer, damage);
    }
}</code></pre>
<pre><code class="language-cpp"> public interface IDamagable
{
    public GameObject gameObject {  get; }
    public void TakeDamage(GameObject dealer, int damage);
} </code></pre>
<pre><code class="language-cpp">public class Monster : MonoBehaviour, IDamagable
{
    [Header(&quot;Component&quot;)]
    [SerializeField] Transform eyeTransform;

    [Header(&quot;Property&quot;)]
    [SerializeField] float sightRange;
    [SerializeField] float moveSpeed;
    [SerializeField] int hp;
    public int HP {  get { return hp; } private set { hp = value; } }

    [SerializeField] GameObject target;

    private void Update()
    {
        DetectTarget(); // Physics로 타겟확인

        if (target != null) // target 이 있으면
        {
            Trace();    // 쫒아가라
        }
    }

    private void DetectTarget() // Player를 찾아라
    {
        if (Physics.Raycast(eyeTransform.position, eyeTransform.forward, out RaycastHit hitInfo, sightRange))
        {
            Debug.DrawLine(eyeTransform.position, hitInfo.point, Color.green);
            if (hitInfo.collider.gameObject.tag == &quot;Player&quot;)    //DrawLine으로 player접촉시 타겟 확인
            {
                target = hitInfo.collider.gameObject;
            }
            else
            {
                target = null;
            }
            //TankController tank = hitInfo.collider.gameObject.GetComponent&lt;TankController&gt;();
            //if (tank != null)
            //{
            //    // 부딪쳤을때 탱크야? 그럼 데미지줘.
            //}
        }
        else
        {
            Debug.DrawLine(eyeTransform.position, eyeTransform.position + eyeTransform.forward * sightRange, Color.red);
            target = null;
        }

    }

    private void Trace()    // 쫒아가는 함수
    {
        transform.position = Vector3.MoveTowards(transform.position, target.transform.position, moveSpeed * Time.deltaTime);
        transform.LookAt(target.transform.position);
    }

    public void TakeDamage(GameObject dealer, int damage)
    {
        // TODO : 
        Debug.Log($&quot;{gameObject.name} 이/가 {dealer.name}에게 피해를 {damage} 받았습니다.&quot;);
        HP -= damage;
        if (HP &lt;= 0)    // 몬스터 hp가 0이 되면
        {
            Die();  // 몬스터 오브젝트 삭제
        }
    }

    private void Die()
    {
        GameManager.Instance.score += 10;   // 몬스터 제거시 10점
        Destroy(gameObject);    // 몬스터 제거
    }


}  </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[코루틴 (Coroutine), 유니티이벤트]]></title>
            <link>https://velog.io/@sejun-2/%EC%BD%94%EB%A3%A8%ED%8B%B4-Coroutine-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@sejun-2/%EC%BD%94%EB%A3%A8%ED%8B%B4-Coroutine-%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Fri, 18 Apr 2025 17:37:23 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolorgreen코루틴-coroutinespan"><span style="color:green">코루틴 (Coroutine)</span></h3>
<pre><code>* 코루틴 (Coroutine)   -&gt; 시간 절약 X! , 작업을 분산

* 작업을 다수의 프레임에 분산하여 처리하는 비동기식 작업
* 실행을 일시정지하고 중단한 부분부터 재개하여 처리하는 것으로 작업을 분산처리함
* 코루틴은 스레드가 아니며 코루틴의 작업은 메인 스레드에서 실행</code></pre><hr>
<pre><code class="language-cpp">IEnumerator Routine()   // 코루틴은 IEnumerator 을 사용
{
    yield return null;                      // Update 끝날 때
    yield return new WaitForSeconds(1f);    // n초간 기다리고, Update 끝날 때
    yield return new WaitForSecondsRealtime(1f);    // 현실시간 n 초간 기다리고, Update 끝날 떄
    yield return new WaitForFixedUpdate();          // FixedUpdate 끝날 때
    yield return new WaitForEndOfFrame();           // 프레임이 끝날 때 (LateUpdate 다음)

    yield return new WaitUntil(() =&gt; Input.GetKeyDown(KeyCode.Space));  // 조건이 맞을때까지 대기
}</code></pre>
<hr>
<pre><code class="language-cpp">[SerializeField] Rigidbody rigid;
[SerializeField] float junpPower;

private Coroutine countDownCoroutine;

private void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        if (countDownCoroutine == null) // 코루틴이 없을때 1번사용 - 중복해서 여러번 작업하는것을 방지
        {
            countDownCoroutine = StartCoroutine(Routine());
        }
    }
    if (Input.GetKeyDown(KeyCode.Escape))
    {
        //StopCoroutine(Routine());   // 루틴을 멈추는 것이 아닌 코루틴 담당자를 멈춰야 함

        if (countDownCoroutine != null) // 코루틴이 있어야 멈출 수 있다.
        {
            StopCoroutine(countDownCoroutine);
            countDownCoroutine = null;   // 다시 초기화 해줘야 재사용 가능.
        }
    }

}


 &lt;코루틴 반복 작업&gt;
//IEnumerator를 반환형으로 함수를 구성

IEnumerator Routine()   // 코루틴은 IEnumerator 을 사용
{
    WaitForSeconds delay = new WaitForSeconds(1f);  // 아래 방법보다 이방법을 권장. 계속 new 하면 메모리 누수
    Debug.Log(5);
    yield return delay;     // yield 일서정지, 양보
    Debug.Log(4);
    yield return delay;
    Debug.Log(3);
    yield return delay;
    Debug.Log(2);
    yield return delay;
    Debug.Log(1);
    yield return delay;


    Debug.Log(5);
    yield return new WaitForSeconds(1f);    // 아래 방법
    Debug.Log(4);
    yield return new WaitForSeconds(1f);
    Debug.Log(3);
    yield return new WaitForSeconds(1f);
    Debug.Log(2);
    yield return new WaitForSeconds(1f);
    Debug.Log(1);
    yield return new WaitForSeconds(1f);

    Debug.Log(&quot;점프 대기 시작&quot;);
    yield return new WaitForSeconds(5f);    // yield는 뒤에 조건만큼 기다리다가 -&gt; 5초 뒤에
    Debug.Log(&quot;점프!!&quot;);
    rigid.AddForce(Vector3.up * junpPower, ForceMode.Impulse);  // 점프 시작

    countDownCoroutine = null;  // 다시 초기화 해줘야 재사용 가능.

    //yield return WaitUntil -&gt; 사용으로 중간에 멈췄다 다시 사용 가능
}</code></pre>
<hr>
<h3 id="span-stylecolorgreen유니티이벤트span"><span style="color:green">유니티이벤트</span></h3>
<pre><code class="language-cpp">public class UnityEventTester : MonoBehaviour
{
    public UnityEvent myEvent;  // 유니티 이벤트 사용 -&gt; 클래스
    //public UnityEvent&lt;int&gt; myEvent;   //매개변수 있는 이벤트

    //public event Action&lt;int&gt; OnHpChanged;

    public event UnityAction OnDied;    // UnityAction -&gt; 델리게이트
    public event Action myDelegate;     // UnityAction 과 같은 거임. 차이 없음.

    private void Awake()
    {
        //myEvent.AddListener(Test); // AddListener 로 함수를 추가 해서 사용 가능
        //myEvent.RemoveListener(Test);

        //OnHpChanged += Test;
        //OnHpChanged += Func;
    }

    public void Test(){}

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            myEvent.Invoke(); // Invoke 로 발생시키다.
            //myEvent.Invoke(10); // 매개변수 있는 경우
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Raycast, Rigidbody, 프리팹 Prefab]]></title>
            <link>https://velog.io/@sejun-2/Raycast-Rigidbody-%ED%94%84%EB%A6%AC%ED%8C%B9-Prefab</link>
            <guid>https://velog.io/@sejun-2/Raycast-Rigidbody-%ED%94%84%EB%A6%AC%ED%8C%B9-Prefab</guid>
            <pubDate>Thu, 17 Apr 2025 16:16:17 GMT</pubDate>
            <description><![CDATA[<h2 id="raycast">Raycast</h2>
<pre><code class="language-cpp">public class RaycastTester : MonoBehaviour
{
    private void Update()
    {
        // 레이캐스트 : 시작 위치에서 방향으로 레이저를 발사하여 부딪히는 충돌체를 감지
        if(Physics.Raycast(transform.position, transform.forward, out RaycastHit hitInfo, 10f ))
        {
            // 레이저에 닿은 충돌체가 있다.
            Debug.Log(hitInfo.collider.gameObject.name);
            Debug.DrawLine(transform.position, hitInfo.point, Color.green);
        }
        else
        {
            // 레이저에 닿은 출돌체가 없다.
            Debug.Log(&quot;None&quot;);
            Debug.DrawLine(transform.position, transform.position + transform.forward * 10f, Color.red);
        }


    }

    private void OnDrawGizmos()   // 그냥 계속 충돌체를 그려줌
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, 3f);    // 감지범위?
    }

    private void OnDrawGizmosSelected()   // 오브젝트를 선택하면 충돌체를 그려줌
    {
        Gizmos.color = Color.green;
        Gizmos.DrawLine(transform.position, transform.forward * 10);
    }
}</code></pre>
<hr>
<h2 id="rigidbody">Rigidbody</h2>
<pre><code class="language-cpp">public class RigidbodyTest : MonoBehaviour
{
    [SerializeField] Rigidbody rigid; // new를 붙이거나 rigidbody가 아닌 다른 이름을 쓰자.
    [SerializeField] float power; // 힘의 세기

    private float xInput;

    private void Update()
    {
        xInput = Input.GetAxis(&quot;Horizontal&quot;); // A, D키 입력 - 좌 우

        if (Input.GetButtonDown(&quot;Jump&quot;))
        {
            rigid.AddForce(Vector3.up * power, ForceMode.Impulse);  // Impulse 점프는 예외로 Update() 에 사용
        }

    }

    private void FixedUpdate()  // addForce 는 FixedUpdate()에서 써야함. 컴퓨터 성능에 따라 달라질 수 있기 때문.
    {
        // 1. 힘 가해주기 : 가속시키기
        rigid.AddForce(Vector3.right * xInput * power, ForceMode.Force); // Force는 지속적으로 힘을 가하는 메서드. 무게에 따라 힘이 달라짐
        // Acceleration 무게에 상관없이 속도가 빨라짐
        // Impulse 는 한방에 힘을 펑! 하고 줌

        // 2. 속도를 설정하기 : 속도를 원하는 대로 설정
        rigid.velocity = Vector3.right * power * xInput;

        // 3. 회전력 가해주기
        rigid.AddTorque(Vector3.up * xInput * power);

        // 4. 회전속도 설정하기
        rigid.angularVelocity = Vector3.up * power * xInput;
    }
}</code></pre>
<hr>
<h2 id="prefab">Prefab</h2>
<pre><code class="language-cpp">public class Monster : MonoBehaviour
{
     * 프리팹 (Prefab)

     * 미리 만들어 놓은 게임오브젝트, 템플릿, 설계도
     * 프리팹 시스템을 이용하여 게임오브젝트를 생성, 설정 및 저장할 수 있음
     * 자주 사용하는 게임오브젝트 구성을 복사하여 런타임 시점에 생성하려는 경우 사용


     &lt;프리팹 생성&gt;
     유니티 하이어라키 창의 게임오브젝트를 프로젝트 창을 드래그 &amp; 드롭하여 프리팹에셋 생성


     &lt;프리팹 특징&gt;
    *게임오브젝트를 프리팹화 시킨 프리팹에셋은 게임오브젝트의 모든 컴포넌트, 속성값, 자식 게임오브젝트를 가지고 있음
    * 프리팹에셋을 게임씬에 추가할 경우 프리팹인스턴스로 생성
    * 프리팹에셋의 변경사항은 프리팹인스턴스에게 자동으로 반영
    * 프리팹인스턴스에 변경사항이 있을 경우 해당 변경사항은 프리팹인스턴스에 영향을 받지 않음
    * 프리팹에셋을 클래스, 프리팹인스턴스를 클래스의 인스턴스로 볼 수 있음


    [SerializeField] Transform eyeTransform;
    [SerializeField] float sightRange;
    [SerializeField] float moveSpeed;

    [SerializeField] GameObject target;

    private void Update()
    {
        DetectTarget();    // Physics로 타겟확인

        if (target != null)    // target 이 있으면
        {
            Trace();    // 쫒아가라
        }
    }

    private void DetectTarget()
    {
        if (Physics.Raycast(eyeTransform.position, eyeTransform.forward, out RaycastHit hitInfo, sightRange))
        {
            Debug.DrawLine(eyeTransform.position, hitInfo.point, Color.green);
            if (hitInfo.collider.gameObject.tag == &quot;Player&quot;)    //DrawLine으로 player접촉시 타겟 확인
            {
                target = hitInfo.collider.gameObject;
            }
            else
            {
                target = null;
            }
            //TankController tank = hitInfo.collider.gameObject.GetComponent&lt;TankController&gt;();
            //if (tank != null)
            //{
            //    // 부딪쳤을때 탱크야? 그럼 데미지줘.
            //}
        }
        else
        {
            Debug.DrawLine(eyeTransform.position, eyeTransform.position + eyeTransform.forward * sightRange, Color.red);
            target = null;
        }

    }

    private void Trace()    // 쫒아가는 함수
    {
        transform.position = Vector3.MoveTowards(transform.position, target.transform.position, moveSpeed * Time.deltaTime);
        transform.LookAt(target.transform.position);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[충돌 Collision]]></title>
            <link>https://velog.io/@sejun-2/CollisionTester</link>
            <guid>https://velog.io/@sejun-2/CollisionTester</guid>
            <pubDate>Thu, 17 Apr 2025 16:04:01 GMT</pubDate>
            <description><![CDATA[<p>충돌 (Collision)</p>
<ul>
<li>충돌체 : 게임오브젝트의 물리적 충돌을 목적으로 모양을 정의</li>
<li>게임오브젝트 간의 충돌체로 부딪힘과 반발력을 처리</li>
<li>충돌체는 충돌상황에 있을 경우 유니티 충동 메시지를 받아 상황을 확인</li>
</ul>
<pre><code class="language-cpp"> &lt; 유니티 충돌 메시지&gt;
private void OnCollisionEnter(Collision collision)  // 3D 물리 충돌
{
    // 충돌 진입시 호출
    Debug.Log($&quot;{collision.gameObject.name} 랑 충돌을 시작함&quot;);       // 총을 맞았을때 대미지 입을때
}

private void OnCollisionStay(Collision collision)
{
    // 충돌중 호출
    Debug.Log($&quot;{collision.gameObject.name} 랑 충돌중&quot;);        // 범위공격 지속 대미지
}

private void OnCollisionExit(Collision collision)
{
    // 충돌 해제시 호출
    Debug.Log($&quot;{collision.gameObject.name} 랑 충돌 끝남&quot;);
}</code></pre>
<p>트리거 ( Trigger ) </p>
<ul>
<li>하나의 충돌체가 충돌을 일으키지 않고 다른 충돌체의 공간에 들어가는 것을 감지</li>
<li>트리거는 겹침상황에 있을 꼉우 유니티 트리거 메시지를 받아 상황을 확인</li>
</ul>
<pre><code class="language-cpp">// Box Collider 안에 Is Trigger 박스를 체크시 가능 -&gt; 트리거로 사용하겠다

private void OnTriggerEnter(Collider other)
{
    Debug.Log(&quot;트리거 Enter&quot;);
}

private void OnTriggerStay(Collider other)
{
    Debug.Log(&quot;트리거 stay&quot;);
}

private void OnTriggerExit(Collider other)
{
    Debug.Log(&quot;트리거 Exit&quot;);
}</code></pre>
<p> 레이어기반 충돌 &amp; 트리거 감지</p>
<ul>
<li>게임오브젝트의 레이어를 활용하여 충돌체간의 충돌가능 여부를 설정 가능</li>
<li>충돌 여부를 제외한 레이어간의 충돌은 무시됨</li>
<li>edit -&gt; ProjectSettings -&gt; Physics -&gt; Layer Collision Matrix</li>
</ul>
<hr>
<p>  충돌체 &amp; 트리거 종류</p>
<ul>
<li>두 게임오브젝트가 충돌하면 리지드바디의 환경설정에 따라 다른 반응을 할 수 있음</li>
<li>일부 조합의 경우 두 게임오브젝트 중에서 하나만 충돌의 영향을 받음</li>
<li>일반적으로 Rigidbody 컴포넌트가 있는 게임오브젝트가 물리가 적용되는 것이 원칙</li>
</ul>
<hr>
<p> &lt;충돌체 종류&gt;
 (1) 정적 충돌체 (Static Collider)
 Rigidbody가 없는 충돌체, 외부에 힘에 움직이지 않음
 절대로 움직이지 않는 지형, 구성요소에 주로 사용</p>
<p> (2) 리지드바디 충돌체 (Rigidbody Collider)
 Rigidbody가 있는 충돌체, 외부에 힘을 받아 움직임
 충돌하여 힘을 받아 움직이는 물체에 사용</p>
<p> (3) 키네마틱 충돌체 (Kinematic Collider)
 Kinematic Rigidbody가 있는 충돌체, 외부의 힘에 반응하지 않음
 움직이지만 외부 충격에는 밀리지 않는 물체에 사용
 Kinematic 상태를 활성화/비활성화 하여 움직임 여부를 설정하는 경우에도 사용</p>
<p> &lt;충돌체 &amp; 트리거 상호작용 매트릭스&gt;</p>
<ul>
<li>충돌체와 트리거의 종류에 따라 메시지가 전송되는 경우가 다름</li>
<li>편의상 정적충돌체(SC), 리지드바디충돌체(RC), 키네마틱충돌체(KC)로 표현</li>
<li>편의상 정적트리거(ST), 리지드바디트리거(RT), 키네마틱트리거(KT)로 표현</li>
</ul>
<pre><code class="language-cpp"> 충돌시 충돌 메시지가 전송
      SC  RC  KC  ST  RT  KT
 SC        O
 RC    O   O   O
 KC        O
 ST
 RT
 KT

 트리거시 트리거 메시지가 전송
      SC  RC  KC  ST  RT  KT
 SC                    O   O
 RC                O   O   O
 KC                O   O   O
 ST        O   O       O   O
 RT    O   O   O   O   O   O
 KT    O   O   O   O   O   O</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#유니티 #TransFrom #위치 이동 구현]]></title>
            <link>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-TransFrom-%EC%9C%84%EC%B9%98-%EC%9D%B4%EB%8F%99-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-TransFrom-%EC%9C%84%EC%B9%98-%EC%9D%B4%EB%8F%99-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 16 Apr 2025 10:55:33 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="span-stylecolor00ff00transformspan"><span style="color:#00FF00">Transform</span></h2>
<hr>
<blockquote>
<ul>
<li>Transform 컴포넌트는 오브젝트의 위치, 회전각, 크기(배율)에 대한 데이터를 가지고 있습니다. </li>
</ul>
</blockquote>
<ul>
<li>유니티에서는 더 빠른 연산을 위해 오브젝트의 위치, 회전, 크기에 대한 연산을 4x4 행렬로 구현합니다. </li>
<li>추가로 자신이 가진 데이터를 쉽게 컨트롤 할 수 있는 지원 함수들이 내장되어 있습니다.</li>
</ul>
<pre><code class="language-cpp">Position
유니티에서 오브젝트의 위치 정보는 x, y, z 3축으로 구성됩니다. 이는 유니티에서 Vector3 클래스를 통해 간편하게 다룰 수 있으며, 이를 사용해 게임 오브젝트의 위치 이동을 구현할 수 있습니다.
// Position을 직접적으로 변경하는 방법
Transform.position = new Vector3(x, y, z);

// Translate 함수를 이용한 축(x, y, z) 기준의 위치이동 방법
Transform.Translate(Direction * Speed);

// 선형 보간을 이용해 거리에 따라 빠르게 이동하며 천천히 감속하는 방법
Transform.position = Vector3.Lerp(StartPosition, EndPosition, Interpolation)

// 목표지점을 향해 일정한 속도로 이동하는 방법
Transform.position = Vector3.MoveTowards(transform.position, Target.Position, Speed);

Rotation
인스펙터의 Transform 컴포넌트에서는 회전값이 포지션과 동일하게 x, y, z축으로 보여지지만 내부 동작은 4원소를 사용하는 쿼터니언을 통해 이뤄집니다. 이는 연산속도가 빠르고 3축을 사용했을 때의 짐벌락 현상을 방지하는 데 효과적입니다.
쿼터니언은 연산적으로 효과적이지만, 유니티 입문 시점에서는 이해하기 복잡한 개념입니다. 이 때문에 보다 직관적인 3축으로 컨트롤 할 수 있도록 Quaternion클래스의 Euler를 사용합니다.
// rotation에 값을 직접 지정해 회전하는 방법
Transform.rotation = Quaternion.Euler(x, y, z);

// Rotate 함수를 이용한 회전
Transform.Rotate(Vector3.up, Speed);

// RotateAround를 이용한 중심축 기준으로 회전
Transform.RotateAround(Target.position, Vector3.up, Speed);

// LookAt을 이용한 대상을 향해 회전시키기
Transform.LookAt(Target.position);

Tip : Quaternion (펼치기/접기)

Scale
직관적인 이름 그대로 게임 오브젝트의 크기를 비율로 지정할 수 있는 요소입니다
// Scale 조절
Transform.localScale = new Vector3(x, y, z);

// lossyScale은 부모 오브젝트의 스케일에 상관 없이 자신 고유의 Scale 수치를 반환
Transform.lossyScale</code></pre>
<hr>
<h2 id="span-stylecolor00ff00world-space-vs-local-spacespan"><span style="color:#00FF00">World Space VS Local Space</span></h2>
<blockquote>
<ul>
<li>게임 내에서 모든 오브젝트는 Transform 컴포넌트를 가집니다. </li>
</ul>
</blockquote>
<ul>
<li>형체가 없는 오브젝트라 할 지라도, 위치를 지니고 있다는 뜻이죠 이때, 게임 환경 내의 절대 좌표와 상속 관계에서의 상대 좌표를 사용할 수 있습니다. </li>
<li>우리는 이를 월드 스페이스(World Space)와 로컬 스페이스(Local Space)라고 부릅니다.</li>
</ul>
<blockquote>
<p><span style="color:orange">월드 스페이스 (World Space)</span></p>
</blockquote>
<ul>
<li>월드 스페이스는 게임 환경 내의 고정된 글로벌 좌표계를 의미하며, 절대 좌표라고도 표현할 수 있습니다.</li>
<li>이 좌표계는 게임의 시작부터 끝까지 변하지 않으며, 모든 오브젝트들이 이 월드 스페이스 내에서 위치를 가지게 됩니다.</li>
<li>월드 스페이스에서의 오브젝트의 위치, 회전, 크기는 월드 포지션(World Position), 월드 회전(World Rotation), 월드 스케일(World Scale)로 표현됩니다.</li>
</ul>
<blockquote>
<p><span style="color:orange">로컬 스페이스 (Local Space)</span></p>
</blockquote>
<ul>
<li>로컬 스페이스는 오브젝트나 컴포넌트의 개별적인 좌표계를 의미하며, 상대 좌표라고도 표현할 수 있습니다.</li>
<li>특히 부모-자식 관계에서 중요하며, 자식 오브젝트의 로컬 스페이스는 부모 오브젝트에 대해 상대적입니다.</li>
</ul>
<ol>
<li>Ex&gt; 자식 오브젝트의 로컬 포지션이 (0, 0, 0)이라면, 이는 부모 오브젝트의 위치와 동일하다는 것을 의미합니다.</li>
<li>로컬 스페이스에서의 오브젝트의 위치, 회전, 크기는 로컬 포지션(Local Position), 로컬 회전(Local Rotation), 로컬 스케일(Local Scale)로 표현됩니다.</li>
</ol>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransformTester : MonoBehaviour
{

        트랜스폼 (Transform)
        게임  오브젝트의 위치, 회전, 크기를 정의하는 컴포넌트
        게임오브젝트의 부모-자식 상태를 자장하는 컴포넌트
        게임오브젝트는 반드시 하나의 트랜스폼 컨포넌트를 가지고 있으며, 추가 &amp; 삭제할 수 없다.


    public Vector3 targetPos;
    [SerializeField] float moveSpeed; // 이동 속도
    [SerializeField] float rotateSpeed; // 회전 속도
    [SerializeField] Transform turret;


    [SerializeField] Transform source;
    [SerializeField] Transform target;
    [Range(0f, 1f)]
    [SerializeField] float rate; // 보간 비율



    private void Update()
    {
        #region Position
        // 1. 벡터를 이용한 위치 설정
         transform.position = new Vector3(1, 2, 3); // 트랜스폼의 위치를 (1, 2, 3)으로 설정

        if (Input.GetKeyDown(KeyCode.Space))
        {
            transform.position = targetPos; // 트랜스폼의 위치를 targetPos로 설정
        }

         2. 트랜스레이트를 이용한 위치 설정 -&gt; 원하는 방향으로 이동시키기
        if (Input.GetKeyDown(KeyCode.Space))
        {
            transform.Translate(Vector3.forward); // 트랜스폼을 forward-&gt; 앞으로 이동
        }

        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
        // 1프레임당 걸린시간 -&gt; 단위시간 : 델타타임
        // 1프레임당 걸린시간 = Time.deltaTime

        // 3. 목적지로 이동시키기
        transform.position =  Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
        //Towards : 방향을 설정하는 메서드 -&gt; ~을 향해서

        // 4. 보간해서 이동시키기
        transform.position = Vector3.Lerp(transform.position, target.position, rate);
        // linearly interpolate 선형보간
        #endregion


        #region Rotation
        // 3축을 사용했을 때, 짐벌락 현상이 발생할 수 있다. Quaternion을 사용하자
        // 1. 회전 직접 지정 : Euler를 이용하여 Quaternion으로 변환하여 사용 권장
        transform.rotation = Quaternion.Euler(0, 60, 0);

        // 1-1 오일러를 쿼터니언으로 변환 -&gt; Quaternion.Euler
        Quaternion a = Quaternion.Euler(0, 60, 0);
        // 1-2. 쿼터니언을 오일러로 변환   -&gt; .eulerAngles
        Vector3 b = transform.rotation.eulerAngles;
        // 1-3. 방향벡터를 쿼터니언으로 변환
        Quaternion c = Quaternion.LookRotation(Vector3.right);
        // 1-4. 현재 회전을 방향백터로 변환
        Vector3 d = transform.right; // 오른쪽 방향벡터

        // 2. 축을 기준으로 회전
        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime); // y축을 기준으로 회전
        transform.Rotate(Vector3.forward, rotateSpeed * Time.deltaTime); // z축을 기준으로 회전

        // 3. 지점을 기준으로 회전
        transform.RotateAround(target.position, Vector3.up, rotateSpeed * Time.deltaTime); // y축을 기준으로 회전

        // 4. 지점을 바라보도록 회전
        transform.LookAt(target.position); // target을 바라보도록 회전

        // &lt;target 방향만 바라 봄. 위아래는 무시함&gt;
        Vector3 t = target.position;
        t.y = 0f;
        transform.LookAt(t); 

        //&lt;target 방향을 바라보는 속도 조절&gt;
        Vector3 targetDir = (target.position - transform.position).normalized; // target 방향벡터
        Quaternion targetRot = Quaternion.LookRotation(targetDir); // target 방향벡터를 쿼터니언으로 변환
        transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, rotateSpeed * Time.deltaTime); // 현재 회전과 목표 회전을 보간하여 회전
        #endregion


        // 월드기준
        Vector3 worldPostion = transform.position; // 월드좌표계에서의 위치
        Quaternion worldRotation = transform.rotation; // 월드좌표계에서의 회전

        // 로컬기준
        Vector3 localPosition = transform.localPosition; // 로컬좌표계에서의 위치
        Quaternion localRotation = transform.localRotation; // 로컬좌표계에서의 회전

        turret.rotation = Quaternion.identity;  

        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime, Space.World); // Space.World -&gt; 월드좌표계에서 앞으로 이동

        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime, Space.World); // Space.World -&gt; 월드좌표계에서 y축을 기준으로 회전

    }
}</code></pre>
<hr>
<h2 id="span-stylecolor00ff00위치-이동-구현span"><span style="color:#00FF00">&lt;위치 이동 구현&gt;</span></h2>
<blockquote>
<ul>
<li>유니티는 게임엔진이라는 타이틀에 걸맞게 여러 기본 지원기능을 내장하고 있습니다.</li>
</ul>
</blockquote>
<ul>
<li>Input Class를 통해 여러 키 여부를 알수 있고, 이를 통해 오브젝트의 포지션을 변경해 주는 것이 우리의 목표입니다. </li>
</ul>
<blockquote>
<p><span style="color:orange">GetKey</span></p>
</blockquote>
<ul>
<li>단일 키 입력에 대한 여부를 반환받기 위한 함수입니다. </li>
<li>키가 눌리는 판정에 따라 세 가지 bool 타입 반환 함수를 적절하게 사용합니다.<pre><code class="language-cpp">Input.GetKey()      // 키 입력이 진행되는 동안 true를 반환합니다. 
Input.GetKeyDown()  // 키 입력이 시작되는 순간 1회만 true를 반환합니다.
Input.GetKeyUp()    // 키 입력이 종료되는 순간 1회만 true를 반환합니다.</code></pre>
</li>
</ul>
<blockquote>
<p><span style="color:orange">GetAxis</span></p>
</blockquote>
<ul>
<li>유니티에서 제공하는 Input Manager에 대응하는 코드입니다.</li>
<li>유니티 좌표상의 X(Horizontal), Z(Vertical)축에 해당하는 입력 데이터이며, 미 입력시 0, 방향과 입력에 따라 -1.0 ~ 1.0 사이의 float 타입을 반환합니다. </li>
<li>이 함수는 <span style="color:orange">게임패드(조이스틱)</span>와도 연동되는 코드이며, 숙련된다면 유용하게 사용할 수 있습니다.</li>
</ul>
<blockquote>
<p><span style="color:orange">GetAxisRaw</span></p>
</blockquote>
<ul>
<li>GetAxis와 마찬가지로 Input Manager에 대응하고 유니티 좌표상의 X, Z축에 해당하는 입력 데이터를 -1, 0, 1로 반환합니다.</li>
<li>이는 키보드 입력에 대한 이동 구현에 적합한 함수입니다.</li>
</ul>
<hr>
<pre><code class="language-cpp">public class InputTester : MonoBehaviour
{
    public float result; // 결과값을 저장할 변수

    private void Update()
    {
        #region Device
        // &lt;Device&gt;
         특정한 장치를 기준으로 입력 감지.
         특정한 장치의  입력을 감지하기 위해서는 Input.GetKey()를 사용한다.

         1. 키보드 입력 &amp;&amp; 게임패드 입력
        if (Input.GetKey(KeyCode.Space))     // 키 입력이 진행되는 동안 true
        {
            Debug.Log(&quot;Get Key!&quot;);
        }
        if (Input.GetKeyDown(KeyCode.Space))    // 눌렀을 때 한번만 true
        {
            Debug.Log(&quot;Get Key Down!&quot;);
        }
        if (Input.GetKeyUp(KeyCode.Space))      // 눌렀다 떼었을 때 한번만 true
        {
            Debug.Log(&quot;Get Key Up!&quot;);
        }

        // 2. 마우스 입력
        if (Input.GetMouseButton(0)) // 0 왼쪽 버튼 클릭
        {
            Debug.Log(&quot;Left Mouse Button Clicked&quot;);
        }
        if (Input.GetMouseButtonDown(1)) // 1 오른쪽 버튼 클릭
        {
            Debug.Log(&quot;Right Mouse Button Clicked&quot;);
        }
        if (Input.GetMouseButtonUp(2)) // 2 가운데 버튼 클릭
        {
            Debug.Log(&quot;Middle Mouse Button Released&quot;);
        }
        #endregion       


         &lt;InputManager&gt; -&gt; Edit &gt; Project Settings &gt; Input Manager
         여러 장치의 입력을 입력매니저에 이름과 입력을 정의
         입력매니저의 이름으로 정의한 입력의 변경사항을 확인
         유니티 에디터의 Edit &gt; Project Settings &gt; Input Manager에서 관리
         단, 유니티 초창기의 방식이기 때문에 키도드, 마우스, 조이스틱에 대한 장치만을 고려함

        &lt;InputManager&gt;방식으로 입력을 감지
        if (Input.GetButton(&quot;Fire1&quot;)) // 입력매니저에 정의된 이름으로 입력을 감지
        {
            Debug.Log(&quot;Fire1 버튼 눌림&quot;);
        }

        result = Input.GetAxis(&quot;Horizontal&quot;); 
        result = Input.GetAxisRaw(&quot;Horizontal&quot;);

        float axis = 0;
        if(Input.GetKey(KeyCode.LeftArrow))
        {
            axis = -1;
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            axis = +1;
        }
    }
}</code></pre>
<hr>
<pre><code class="language-cpp">public class TankMover : MonoBehaviour
{
    [SerializeField] float moveSpeed;// 이동 속도
    [SerializeField] float rotateSpeed; // 회전 속도

    private void Update()
    {
        Move(); // 이동
        Rotate(); // 회전
    }

    private void Move()
    {
        float input = Input.GetAxis(&quot;Vertical&quot;); // W, S키 입력
        transform.Translate(Vector3.forward * moveSpeed * input * Time.deltaTime); // 전진 후진
        //transform.Translate(Vector3.forward * moveSpeed * input * Time.deltaTime, Space.World); // Space.World -&gt; 월드를 기준으로 전진 후진
    }

    private void Rotate()
    {
        float input = Input.GetAxis(&quot;Horizontal&quot;); // A, D키 입력
        transform.Rotate(Vector3.up * rotateSpeed * input * Time.deltaTime); // 좌우 회전
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#유니티 #오브젝트생성 #디자인패턴 ObjectPool]]></title>
            <link>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8%EC%83%9D%EC%84%B1-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-ObjectPool</link>
            <guid>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8%EC%83%9D%EC%84%B1-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-ObjectPool</guid>
            <pubDate>Tue, 15 Apr 2025 16:08:19 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolor00ff00오브젝트-생성--프리팹-스폰span"><span style="color:#00FF00">&lt;오브젝트 생성 -프리팹 스폰&gt;</span></h3>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    public GameObject prefab;
    public GameObject target;
    //public GameObject makeOne;


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            Spawn();
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            DeSpawn();
        }
    }

    public void Spawn()
    {
        //makeOne = new GameObject(&quot;Spawned GameObject&quot;); // 새로운 게임 오브젝트 생성
        Instantiate(prefab, new Vector3(1, 1, 1), Quaternion.identity);    // Quaternion -&gt; 회전값 / identity 회전없이
        // Instantiate 로 프리팹을 생성
    }

    public void DeSpawn()
    {
        Destroy(target, 3); // 3초 뒤에 target을 파괴
    }
}</code></pre>
<h3 id="span-stylecolor00ff00디자인-패턴-objectpoolspan"><span style="color:#00FF00">&lt;디자인 패턴 ObjectPool&gt;</span></h3>
<blockquote>
<p>포탄을 미리 일정 수 풀에 생성해서 보관 및 사용</p>
</blockquote>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*      디자인 패턴 ObjectPool       */

/*
&lt;오브젝트 풀 패턴&gt;
프로그램 내에서 빈번하게 재활용하는 인스턴스들을 풀에 보관한 뒤
인스턴스의 생성&amp;삭제 대신 대여&amp;반납을 사용하는 기법

&lt;구현&gt;
1. 인스턴스들을 보관할 풀을 생성
2. 프로그램의 시작시 풀에 인스턴스들을 생성하여 보관
3. 인스턴스 생성이 필요할 때 풀에서 대여하여 사용
4. 인스턴스 삭제가 필요할 때 풀에 반납하여 보관

&lt;장점&gt;
1. 빈번하게 사용하는 인스턴스 생성에 소요되는 오버헤드를 줄임
2. 빈번하게 사용하는 인스턴스 삭제에 부담되는 가비지 콜렉터의 동작을 줄임

&lt;주의점&gt;
1. 미리 생성해놓은 인스턴스들이 사용하지 않는 경우에도 메모리를 차지하고 있음
2. 오브젝트 풀을 힙영역의 여유공간이 줄어들어 오히려 프로그램에 부담이 되는 경우가 있음.
 */


public class ObjectPool : MonoBehaviour
{
    [SerializeField] List&lt;PooledObject&gt; pool = new List&lt;PooledObject&gt;();    // 미리 보관할 풀을 리스트로 생성
    [SerializeField] PooledObject prefab;
    [SerializeField] int size;  // 처음에 춘비해놓을 인스턴스 개수
    [SerializeField] int capacity; // 풀의 최대 개수

    private void Awake()
    {
        for (int i = 0; i &lt; size; i++)
        {
            PooledObject instance = Instantiate(prefab);    // prefab을 복제하여 인스턴스 생성
            instance.gameObject.SetActive(false);   // 비활성화 해서 
            pool.Add(instance); // 인스턴스를 풀에 보관
        }
    }

    public PooledObject GetPool(Vector3 position, Quaternion rotation)  // 대여
    {                           //      어느위치에서 어느각도로 생성할건지
        if (pool.Count == 0) // 풀에 인스턴스가 없으면
        {
            return Instantiate(prefab, position, rotation); // 새로 생성
        }

        PooledObject instance = pool[pool.Count - 1];   // 풀에서 인스턴스 맨 마지막거 가져오기
        pool.RemoveAt(pool.Count - 1); // 풀에서 인스턴스 제거

        instance.returnPool = this; // 인스턴스의 returnPool 변수에 현재 풀을 설정
        instance.transform.position = position; // 위치 설정
        instance.transform.rotation = rotation; // 회전 설정
        instance.gameObject.SetActive(true); // 활성화

        return instance; // 인스턴스 반환
    }

    public void ReturnPool(PooledObject instance)   // 반납
    {
        if (pool.Count &gt;= capacity)
        {
            Destroy(instance.gameObject); // 풀의 개수가 최대치면 인스턴스 삭제
        }
        instance.gameObject.SetActive(false); // 인스턴스 비활성화
        pool.Add(instance); // 풀에 추가
    }

}</code></pre>
<blockquote>
<p>사용한 오브젝트 풀에 반납</p>
</blockquote>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PooledObject : MonoBehaviour
{
    public ObjectPool returnPool; // 오브젝트 풀을 참조하기 위한 변수
    [SerializeField] float returnTime; // 오브젝트 풀에 반납될 시간
    private float timer; // 현재 시각

    private void OnEnable()
    {
        timer = returnTime; // 오브젝트가 활성화될 때 타이머 초기화
    }
    private void Update()
    {
        timer -= Time.deltaTime; // 타이머 감소 - 프레임 마다 진행된 시간
        if (timer &lt;= 0) // 타이머가 0 이하가 되면
        {
            RetuenPool(); // 오브젝트 풀에 반납
        }
    }

    public void RetuenPool()
    {
        if (returnPool == null)
        {
            Destroy(gameObject); // returnPool이 null이면 오브젝트 풀에 반납하지 않고 삭제
        }
        else
        {
            returnPool.ReturnPool(this); // 오브젝트 풀에 반납
        }

    }
}</code></pre>
<blockquote>
<p>포탄 발사 코드 - 발사 후 3초뒤 삭제                    </p>
</blockquote>
<pre><code class="language-cpp">using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shooter : MonoBehaviour
{
    [SerializeField] GameObject bulletPrefab; // 총알 프리팹
    [SerializeField] Transform muzzlePoint; // 총알 발사 위치
    [SerializeField] ObjectPool bulletPool; // 총알 오브젝트 풀

    [Range(10, 30)]
    [SerializeField] float bulletSpeed; // 발사 속도


    public void Fire()
    {
        // 총알을 발사하는 메서드
        GameObject instance = Instantiate(bulletPrefab, muzzlePoint.position, muzzlePoint.rotation); // 총알 인스턴스 생성
                                                                                                     // 머즐 포인트 위치와 회전으로 총알 생성
        //PooledObject instance = bulletPool.GetPool(muzzlePoint.position, muzzlePoint.rotation);   
        // 총알 풀에서 총알 인스턴스 가져오기
        Rigidbody bulletRigidbody = instance.GetComponent&lt;Rigidbody&gt;(); // 총알의 리지드바디 컴포넌트 가져오기
        bulletRigidbody.velocity = muzzlePoint.forward * bulletSpeed; // 총알의 속도 설정

        Destroy(instance, 3); // 3초 후 총알 삭제

    }


}                     </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#유니티 엔진 #이벤트함수 #라이프사이클]]></title>
            <link>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%97%94%EC%A7%84-%EC%9D%B4%EB%B2%A4%ED%8A%B8%ED%95%A8%EC%88%98-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</link>
            <guid>https://velog.io/@sejun-2/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%97%94%EC%A7%84-%EC%9D%B4%EB%B2%A4%ED%8A%B8%ED%95%A8%EC%88%98-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</guid>
            <pubDate>Mon, 14 Apr 2025 14:21:05 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cpp">using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{

        직렬화 (Serialization)

        데이터 구조 또는 게임오브젝트 상태를 보관하고 관리하는 기법
        인스펙터 창에서 오브젝트의 직렬화된 멤버변수 값을 보여줌
        이를 이용하여 소스코드의 수정 없이 유니티 에디터에서 값을 변경 가능

     &lt;데이터 직렬화&gt;
     오브젝트의 멤버변수 값을 확인하거나 변경
     오브젝트의 멤버변수 참조를 드래그 앤 드랍 방식으로 연결

    [Header(&quot;Value&quot;)]
     C# Type
    public bool boolValue;
    public int intValue;
    public float floatValue;
    public string stringValue;

     Unity Type
    public Vector3 vector3;
    public Vector3Int vector3Int;
    public Vector2 vector2;
    public Vector2Int vector2Int;
    public Color color;
    public Rect rect;
    public LayerMask layerMask;
    public AnimationCurve curve;
    public Gradient gradient;

     열거형
    public ForceMode mode;
    public enum Tyep { None, Special, Master }
    public Tyep tyep;

     직렬화 가능한 필드의 배열 및 List&lt;T&gt;
    public int[] array;
    public List&lt;int&gt; intList;

    [Header(&quot;Reference&quot;)]
     Object Type
    public new GameObject gameObject;
    public new Transform transform;
    public new Rigidbody rigidbody;
    public new Collider collider;


     &lt;어트리뷰트&gt;
     클래스, 속성 또는 함수 위에 명시하여 특별한 동작을 나타낼 수 있는 마커
    [Header(&quot;Unity Attribute&quot;)]

    [SerializeField]
    private int privateValue;   // SerializeField -&gt; private 이지만 에디터 상에선 수정 가능!!

    [HideInInspector]
    public int publicValue;

    [Range(0, 100)]
    public float rate;  // 0~100 조절 바 -&gt; 소리 음량?

    [TextArea(3, 5)]
    public string textField;    // npc 대화 창?? 3~5줄

    [Serializable] // 구조체에 직렬화 가능 속성을 포함
    public struct StructType
    {
        public int value1;
        public string value2;
    }
    public StructType structField;

    [Serializable] // 클래스에 직렬화 가능 속성을 포함
    public class ClassType
    {
        public int value1;
        public string value2;
    }
    public ClassType classField;


    /**********************************************************************************************************/


     변수 : 정보
    public Rigidbody rigid;
    public float junpPower;
    public float movePower;


         유니티 메시지 함수

         유니티가 보내는 메시지에 반응하는 함수
         MonoBehaviour 클래스에 메시지와 같은 이름의 함수가 반응
         스크립트는 유니티 엔진이 보내는 메시지를 받아 사건 타이밍을 확인
         메시지 함수에서 자신의 행동을 정의하여 기능을 구성



     함수 : 기능
    private void Awake()
    {
        // 스크립트가 씬에 포함되었을 때 1회 호출
        // 스크립트가 비활성화 되어 있는 경우에도 호출됨

        // 역할 : 스크립트가 필요로 하는 초기화 작업 진행
        //        (외부 게임상황과 무관한 초기화 작업)

        Debug.Log(&quot;Awake&quot;);
    }

    private void Start()
    {
        // 스크립트가 씬에 처음으로 Update하기 직전에 1회 호출 (시작할 때 1번)
        // 스크립트가 비활성화 되어 있는 경우 호출되지 않음

        // 역할 : 스크립트가 필요로 하는 초기화 작업 진행
        //        (외부 게임상황이 필요한 초기화 작업)

        Debug.Log(&quot;Start&quot;);
    }

    private void OnEnable()
    {
        // 스크립트가 활성화될 때마다 호출

        // 역할 : 스크립트가 활성화 되었을 때 작업 진행

        Debug.Log(&quot;OnEnable&quot;);
    }

    private void OnDisable()
    {
        // 스크립트가 비활성화될 때마다 호출

        // 역할 : 스크립트가 비활성화 되었을 때 작업 진행

        Debug.Log(&quot;OnDisable&quot;);
    }

    private void Update()
    {
        // 게임의 프레임 처음마다 호출

        // 역할 : 핵심 게임 로직 구현
        Debug.Log(&quot;Udpate&quot;);

        // 동작중에 계속 호출 됨
        if (Input.GetKey(KeyCode.LeftArrow))    // GetKey -&gt; 누르고 있는동안 입력
        {    // 왼쪽으로 움직이도록 힘을 가함
            rigid.AddForce(Vector3.left * movePower, ForceMode.Force);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {    // 오른쪽으로 움직이도록 힘을 가함
            rigid.AddForce(Vector3.right * movePower, ForceMode.Force);
        }
        if (Input.GetKey(KeyCode.UpArrow))
        {    // 앞으로 움직이도록 힘을 가함
            rigid.AddForce(Vector3.forward * movePower, ForceMode.Force);
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {    // 뒤쪽으로 움직이도록 힘을 가함
            rigid.AddForce(Vector3.back * movePower, ForceMode.Force);  // Force -&gt; 쭈욱 힘을 주는것
        }

        if (Input.GetKeyDown(KeyCode.Space))    //GetKeyDown -&gt; 눌렀을때 입력 , GetKeyup -&gt; 눌렀다 땠을때 입력
        {    // 위쪽으로 움직이도록 힘을 가함
            rigid.AddForce(Vector3.up * junpPower, ForceMode.Impulse);  // Impulse -&gt; 한번 펑! 힘을 주는 것
        }
    }

    private void LateUpdate()
    {
        // 게임의 프레임 마지막마다 호출
        // 씬의 모든 게임오브젝트의 Update가 진행된 후 호출

        // 역할 : 게임프레임의 진행 결과가 필요한 동작이 있는 기능 구현

        Debug.Log(&quot;LateUpdate&quot;);
    }

    private void FixedUpdate()
    {
        // 일정한 시간(기본 1초에 50번) 마다 호출
        // Update와 다르게 프레임당 연산과 단위시간이 일정
        // 물리엔진과 같이 게임의 프레임과 무관하게 일정해야 하는 작업에 사용
        // 게임로직 등 연산이 많은 작업을 FixedUpdate에 구현하지 않아야 함

        // 역할 : 물리엔진 작업 진행

        Debug.Log(&quot;FixedUdpate&quot;);
    }

    private void OnDestroy()
    {
        // 스크립트가 씬에서 삭제되었을 때 1회 호출

        // 역할 : 스크립트가 필요로 하는 마무리 작업 진행

        Debug.Log(&quot;OnDestroy&quot;);
    }


}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#빌더패턴(Builder)]]></title>
            <link>https://velog.io/@sejun-2/%EB%B9%8C%EB%8D%94%ED%8C%A8%ED%84%B4Builder</link>
            <guid>https://velog.io/@sejun-2/%EB%B9%8C%EB%8D%94%ED%8C%A8%ED%84%B4Builder</guid>
            <pubDate>Tue, 08 Apr 2025 14:37:21 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolor00ff00체인-메소드span"><span style="color:#00FF00">&lt;체인 메소드&gt;</span></h3>
<hr>
<pre><code class="language-cpp">StringBuilder -&gt; 매모리 효과적, 스트링을 만드는 과정을 가지고 있다가 마지막에 만듬

static void Main(string[] args)
{
    StringBuilder stringBuilder = new StringBuilder();  // 스트링 빌더를 사용하여 문자열을 조합함
    stringBuilder.AppendLine(&quot;안녕하세요&quot;)   // AppendLine() 한줄 붙이기
        .AppendLine(&quot;반갑습니다.&quot;)   // AppendLine() 메소드를 사용하여 줄바꿈을 추가함
        .Append(&quot;저는 &quot;)
        .Append(&quot;Name&quot;)             // Append() 메소드를 사용하여 줄바꿈 없이 붙이기
        .AppendLine(&quot;라고 합니다.&quot;)
        .Replace(&quot;Name&quot;, &quot;김전사&quot;);   // Replace() 메소드를 사용하여 문자열을 바꿈

    Console.WriteLine(stringBuilder.ToString());   // ToString() 메소드를 사용하여 최종 문자열을 출력함
}</code></pre>
<blockquote>
<p>안녕하세요
  반갑습니다.
  저는 김전사라고 합니다.</p>
</blockquote>
<hr>
<pre><code class="language-cpp">internal class Program
{
    static void Main(string[] args)
    {
        GameObject gameObject = new GameObject();

        gameObject.SetX(10).SetY(5).SetZ(3);    // 아래의 return this; 를 사용하여 체인 메소드를 구현함
    }

}


public class GameObject
{
    public int x;
    public int y;
    public int z;

    public GameObject SetX(int x)
    {
        this.x = x;
        return this;    // this 는 gameObject.SexX(10) 의 gameObject 를 의미함
    }

    public GameObject SetY(int y)
    {
        this.y = y;
        return this;    // this 는 gameObject.SetX(10).SetY(5) 의 gameObject 를 의미함
    }

    public GameObject SetZ(int z)
    {
        this.z = z;
        return this;    // this 는 gameObject.SetX(10).SetY(5).SetZ(3) 의 gameObject 를 의미함
    }
}</code></pre>
<hr>
<pre><code class="language-cpp">internal class Program
{
    static void Main(string[] args)
    {
        MonsterBuilder orcArcherBuilder = new MonsterBuilder();
        orcArcherBuilder.SetName(&quot;오크 궁수&quot;)   // 체인 메소드를 사용하여 MonsterBuilder 를 설정함
            .SetWeapon(&quot;나무 활&quot;)
            .SetArmor(&quot;가죽갑옷&quot;);

        MonsterBuilder orcWarriorBuilder = new MonsterBuilder();
        orcWarriorBuilder.SetName(&quot;오크 전사&quot;)   // 체인 메소드를 사용하여 MonsterBuilder 를 설정함
            .SetWeapon(&quot;철 검&quot;)
            .SetArmor(&quot;철 갑옷&quot;);

        Monster orcArcher = orcArcherBuilder.Build();
        Console.WriteLine(&quot;이름 : {0}, 무기 : {1}, 갑옷 : {2}&quot;, orcArcher.name, orcArcher.weapon, orcArcher.armor);

        Monster orcWarrior = orcWarriorBuilder.Build();
        Console.WriteLine(&quot;이름 : {0}, 무기 : {1}, 갑옷 : {2}&quot;, orcWarrior.name, orcWarrior.weapon, orcWarrior.armor);

    }

}


public class MonsterBuilder
{
    public string name;
    public string weapon;
    public string armor;

    public MonsterBuilder()
    {
        name = &quot;몬스터&quot;;
        weapon = &quot;기본무기&quot;;
        armor = &quot;기본갑옷&quot;;
    }
    public Monster Build()
    {
        Monster monster = new Monster();
        monster.name = this.name;
        monster.weapon = this.weapon;
        monster.armor = this.armor;
        return monster;
    }
    public MonsterBuilder SetName(string name)
    {
        this.name = name;
        return this;    
    }
    public MonsterBuilder SetWeapon(string weapon)
    {
        this.weapon = weapon;
        return this;    
    }
    public MonsterBuilder SetArmor(string armor)
    {
        this.armor = armor;
        return this;    
    }
}
public class Monster
{
    public string name;
    public string weapon;
    public string armor;
}</code></pre>
<hr>
<pre><code class="language-cpp">static void Main(string[] args)
{
    HambergerBuilder chickbergerBuilder = new HambergerBuilder();
    chickbergerBuilder           // 체인 메소드를 사용하여 HambergerBuilder 를 설정함
        .SetSource(&quot;매운양념&quot;)
        .SetPatty(&quot;치킨 패티&quot;);

    HambergerBuilder bigmacBuilder = new HambergerBuilder();
    bigmacBuilder.SetBread(&quot;참깨빵&quot;)   // 체인 메소드를 사용하여 HambergerBuilder 를 설정함
        .SetPatty(&quot;순쇠고기패티두장&quot;)
        .SetSource(&quot;특별한 소스&quot;)
        .SetVegetable(&quot;양상추&quot;);

    HambergerBuilder shirimpbergerBuilder = new HambergerBuilder();
    shirimpbergerBuilder.SetBread(&quot;호밀빵&quot;)   // 체인 메소드를 사용하여 HambergerBuilder 를 설정함
        .SetPatty(&quot;새우 패티&quot;)
        .SetSource(&quot;마요네즈&quot;);

    Hamberger chickberger = chickbergerBuilder.Build();
    Console.WriteLine(&quot;빵 : {0}, 소스 : {1}, 패티 : {2}, 야채 : {3}&quot;, chickberger.bread, chickberger.source, chickberger.patty, chickberger.vegetable);
}


public class HambergerBuilder
{
    public string bread;
    public string source;
    public string patty;
    public string vegetable;
    public HambergerBuilder()
    {
        bread = &quot;기본빵&quot;;
        source = &quot;캐찹&quot;;
        patty = &quot;기본패티&quot;;
        vegetable = &quot;양상추&quot;;
    }
    public Hamberger Build()
    {
        Hamberger hamberger = new Hamberger();
        hamberger.bread = this.bread;
        hamberger.source = this.source;
        hamberger.patty = this.patty;
        hamberger.vegetable = this.vegetable;
        return hamberger;
    }
    public HambergerBuilder SetBread(string bread)
    {
        this.bread = bread;
        return this;    
    }
    public HambergerBuilder SetSource(string source)
    {
        this.source = source;
        return this;
    }
    public HambergerBuilder SetPatty(string patty)
    {
        this.patty = patty;
        return this;
    }
    public HambergerBuilder SetVegetable(string vegetable)
    {
        this.vegetable = vegetable;
        return this;
    }
}
public class Hamberger
{
    public string bread;
    public string source;
    public string patty;
    public string vegetable;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#팩토리패턴(FactoryPattern)]]></title>
            <link>https://velog.io/@sejun-2/%ED%8C%A9%ED%86%A0%EB%A6%AC%ED%8C%A8%ED%84%B4FactoryPattern</link>
            <guid>https://velog.io/@sejun-2/%ED%8C%A9%ED%86%A0%EB%A6%AC%ED%8C%A8%ED%84%B4FactoryPattern</guid>
            <pubDate>Mon, 07 Apr 2025 16:13:12 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-cpp">    static void Main(string[] args)
    {
        // 스테이지 1-1
        MonsterFactory level1Factory = new MonsterFactory();
        level1Factory.upLevel = 0; // 몬스터 레벨 증가량 초기화
        level1Factory.rate = 1; // 몬스터 hp 100%

        // 1. 처음 맵 만들어 졌을 떄 -&gt; 몬스터 3마리
        for (int i = 0; i &lt; 3; i++)
        {
            Monster monster = level1Factory.Create(&quot;슬라임&quot;);
            Console.WriteLine($&quot;몬스터 생성 : {monster.name} , 레벨 : {monster.level}&quot;);
        }

        // 2. 다음 장소로 이동했을 때 -&gt; 몬스터 5마리
        Monster monster1 = level1Factory.Create(&quot;슬라임&quot;);
        Monster monster2 = level1Factory.Create(&quot;슬라임&quot;);
        Monster monster3 = level1Factory.Create(&quot;슬라임&quot;);
        Monster monster4 = level1Factory.Create(&quot;고블린&quot;, 5); // 이 고블린만 레벨 5
        Monster monster5 = level1Factory.Create(&quot;고블린&quot;);

        // 3. 보스룸 입장하면 -&gt; 몬스터 2마리
        Monster bossMonster = level1Factory.Create(&quot;오크족장&quot;);
        Monster subMonster1 = level1Factory.Create(&quot;고블린&quot;);
        Monster subMonster2 = level1Factory.Create(&quot;고블린&quot;);


        // 스테이지 2-1
        MonsterFactory level2Factory = new MonsterFactory();    // level2 factory 생성
        level2Factory.upLevel = 5; // 몬스터 레벨 증가량
        level2Factory.rate = 1.1f; // 몬스터 hp 증가량 110%
        Monster level2monster1 = level2Factory.Create(&quot;슬라임&quot;);
        Monster level2monster2 = level2Factory.Create(&quot;슬라임&quot;);


        // 스테이지 3-1
        MonsterFactory level3Factory = new MonsterFactory();    // level3 factory 생성
        level3Factory.upLevel = 10; // 몬스터 레벨 증가량
        level3Factory.rate = 1.2f; // 몬스터 hp 증가량 120%
    }
}


public class MonsterFactory
{
    public int upLevel; // 몬스터 레벨 증가량
    public float rate; // 몬스터 hp 증가량
    public Monster Create(string name)
    {
        Monster monster; // 몬스터 객체를 초기화
        switch (name)
        {
            case &quot;슬라임&quot;:
                return new Monster(&quot;슬라임&quot;, 1 + upLevel, 100);
            case &quot;고블린&quot;:
                return new Monster(&quot;고블린&quot;, 3 + upLevel, 200);
            case &quot;오크&quot;:
                return new Monster(&quot;오크&quot;, 5 + upLevel, 500);    // 나중에 쉽게 추가도 가능
            case &quot;오크족장&quot;:
                return new Monster(&quot;오크족장&quot;, 10 + upLevel, 2000);
            default:
                return null; // 몬스터가 없을 경우 null 리턴
        }

        monster.hp = (int)(monster.hp * rate); // 몬스터 hp 업스케일링
        return monster; // 몬스터 객체 리턴
    }


    public Monster Create(string name, int level)   // 오버로딩
    {
        Monster monster = Create(name);
        monster.level = level;
        return monster;
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#디자인패턴 개념]]></title>
            <link>https://velog.io/@sejun-2/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@sejun-2/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 07 Apr 2025 14:32:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><span style="color:orange">프로그래밍 디자인 패턴은</span> 소프트웨어 개발에서 자주 발생하는 문제를 해결하기 위한 재사용 가능한 설계 솔루션입니다. 
쉽게 말해, 세상 수많은 프로그래머 선배분들이 각자의 프로그래밍을 해오다가 <span style="color:orange">공통되게 나타난 비슷한 설계 구조들이</span> 있는데, 그 중 이런 설계 구조에 이름을 붙여서 설계 원칙으로 만든 것 입니다. </p>
</blockquote>
<hr>
<h3 id="span-stylecolor00ff00디자인-패턴-개요span"><span style="color:#00FF00">&lt;디자인 패턴 개요&gt;</span></h3>
<blockquote>
<ul>
<li>디자인 패턴은 게임뿐만이 아니라, 다양한 모든 프로그래밍 분야에서 활용 가능한 가이드라인입니다. </li>
</ul>
</blockquote>
<ul>
<li>디자인 패턴은 구체적인 코드를 제공하지 않으며, 일반적인 구조와 상호작용을 설명하는 템플릿으로 이해할 수 있습니다.</li>
<li>예를 들어, 세상 다양한 프로그래머들이 제작하던 코드 중, <span style="color:orange">&quot;수많은 객체를 동시에 인스턴스화 시켜 한 곳에 담아두고, 사용이 끝난 후 메모리가 해제&quot;</span> 방식이 프로그래밍 언어나 구현 방식만 차이가 있을 뿐 마치 공통된 무늬와 같이 여러 코드에서 발견되었다고 가정하겠습니다. </li>
<li>영향력이나 이름이 있는 프로그래머가, 혹은 상용엔진을 만드는 회사가 이러한 방식을 보고 <span style="color:orange">A패턴</span>이라고 이름을 붙여서 널리 알려지게 되면, 추후 다른 개발자들이 비슷한 내용을 구현해아 할 상황에 편하게 A패턴이란 이름으로 정보를 구하고 적용할 수 있게 되는것 입니다.</li>
</ul>
<blockquote>
<ul>
<li>하나의 예시를 들자면, 본인이 만들고자 하는 음악플레이어라는 기능이 프로그램 상에 하나만 존재하여야 하고 어느곳에서도 음악을 재생할 수 있게끔, 전역적으로 접근 가능할 수 있게 구현해야 하는 상황이라고 가정하겠습니다. </li>
</ul>
</blockquote>
<ul>
<li>잘 알려진 디자인 패턴 중, &#39;프로그램에서 유일한 인스턴스를 보장하고 전역적인 접근을 제공&#39; 하는 개념의 <span style="color:orange">싱글톤 패턴</span>이라는 것이 있는데, 이러한 디자인 패턴 개념을 활용하여 본인이 원하는 코드를 해당 개념에 맞추어 작성하면 손쉽게 작성할 수 있는것 입니다.</li>
</ul>
<hr>
<h3 id="span-stylecolor00ff00게임에-자주-쓰이는-패턴span"><span style="color:#00FF00">&lt;게임에 자주 쓰이는 패턴&gt;</span></h3>
<blockquote>
<ul>
<li>수많은 디자인 패턴들이 존재하는데 일부는 확장성에 초점을 맞추기도, 일부는 최적화 및 재활용성에 초점을 맞추기도 합니다. </li>
</ul>
</blockquote>
<ul>
<li>게임의 경우, 퍼포먼스가 중요하기에 최적화와 직결되는 디자인 패턴들의 선호도가 높습니다. </li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00싱글톤-패턴span"><span style="color:#00FF00">싱글톤 패턴</span></h4>
<blockquote>
<ul>
<li>개요에서 설명이 나왔던 싱글톤 패턴은 게임에서 유일한 인스턴스를 보장하고, 전역적인 접근이 필요한 경우에 사용되는 패턴입니다. </li>
</ul>
</blockquote>
<ul>
<li>싱글톤 패턴은 <span style="color:orange">클래스의 인스턴스가 하나만 생성되도록 보장하고, 어디서든 동일한 인스턴스에 접근할 수 있게</span> 합니다. </li>
<li>예를 들어, 게임 매니저, 자원 관리자 등에서 활용됩니다.</li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00상태-패턴span"><span style="color:#00FF00">상태 패턴</span></h4>
<blockquote>
<ul>
<li>게임에서 오브젝트의 동작이 상태에 따라 달라지는 경우 사용되는 패턴입니다. </li>
</ul>
</blockquote>
<ul>
<li>상태 패턴은 <span style="color:orange">각 상태를 클래스로 구현하고, 상태에 따라 다른 동작을 수행</span>합니다. </li>
<li>예를 들어, 캐릭터의 이동 상태, 공격 상태, 대기 상태 등을 다루는 데 유용합니다.</li>
<li>캐릭터의 상태를 객체로 구현하게 되면, 추후 새로운 상태가 추가 되어도 이 상태를 사용하는 캐릭터 클래스의 코드를 대폭 수정할 필요 없이 유연하게 대처가 가능합니다.</li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00오브젝트-풀-패턴span"><span style="color:#00FF00">오브젝트 풀 패턴</span></h4>
<blockquote>
<ul>
<li>게임에서 <span style="color:orange">반복적으로 생성되고 소멸되는</span> 객체들을 효율적으로 관리하기 위한 패턴입니다. </li>
</ul>
</blockquote>
<ul>
<li>객체 풀 패턴은 <span style="color:orange">미리 생성된 객체들을 풀에 저장해두고, 필요할 때마다 객체를 재사용</span>하여 생성 및 소멸의 오버헤드를 줄입니다.</li>
<li>게임에서 객체는 정말 많은데, 이 객체들을 필요한 상황마다 생성하여 사용하거나, 사용이 종료되는 순간순간 없애는 작업이 반복된다면 꽤 큰 부하가 걸릴것입니다. </li>
<li>미리 큰 메모리 공간을 확보해 둔 후에, 객체의 사용이 끝나도 객체를 지우는 것이 아닌, 잠시 비활성화 해 두었다가 재활용하는 방법으로 사용하면 성능상 큰 이득을 볼 수 있습니다.</li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00컴포넌트-패턴span"><span style="color:#00FF00">컴포넌트 패턴</span></h4>
<blockquote>
<ul>
<li>게임 오브젝트의 동작과 기능을 구현하는 데 사용되는 패턴입니다. </li>
</ul>
</blockquote>
<ul>
<li>각각의 게임 오브젝트는 여러 개의 컴포넌트로 구성되며, 컴포넌트는 독립적으로 동작하고 상호작용합니다. </li>
<li>다시 말해, 자동차라는 객체가 있을 경우, 이 객체를 만드는 방법으로 엔진객체, 휠 객체, 미션 객체, 브레이크 객체 등 세분화 된 객체들을 조립하여 하나의 객체를 구성해 나가는 패턴입니다.</li>
<li>Unity 엔진에서 만들어내는 객체들이 컴포넌트 패턴의 예시라고 볼 수 있습니다. </li>
<li>캐릭터를 하나 만들고, 거기에 필요에 따라 물리가 필요하면 물리 객체, 네트워킹이 필요하면 네트워크관련 객체등을 붙여나가며 원하는 객체를 만들어 냅니다.</li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00옵져버-패턴span"><span style="color:#00FF00">옵져버 패턴</span></h4>
<blockquote>
<ul>
<li>이 패턴은 객체 간의 일대다 관계를 구성하여, 한 객체의 상태 변화에 따라 다른 객체들이 자동으로 업데이트되도록 하는 것을 목적으로 합니다. </li>
</ul>
</blockquote>
<ul>
<li>옵저버 패턴은 주제(Subject)와 옵저버(Observer)라는 두 가지 주요 요소로 구성됩니다. </li>
<li>주제는 상태를 가지며, 상태가 변경될 때 옵저버에게 알립니다. </li>
<li>옵저버는 주제를 구독하고, 주제의 상태가 변경되면 업데이트를 수신하여 필요한 동작을 수행합니다.</li>
<li>플레이어의 정보를 표시하는 UI가 있다고 가정 시, 이 UI가 플레이어의 정보를 항시 받아 출력하는 형태는 비효율적일것입니다. </li>
<li>변동사항이 있을때만, 변동사항이 있다는 것을 관련된 객체들에게 전달시키는 방식을 채용하게 되면 객체간의 결합도를 낮추고, 유연성 및 확장성을 제공할 수 있습니다. 이럴때 옵저버 패턴을 고려할 수 있습니다.</li>
</ul>
<hr>
<h4 id="span-stylecolor00ff00기타-디자인-패턴들-및-유의사항span"><span style="color:#00FF00">기타 디자인 패턴들 및 유의사항</span></h4>
<blockquote>
<p>이외에도 컬렉션 객체를 유용하게 관리하게 도와주는 이터레이터 패턴, 많은 수의 유사한 객체를 관리하기 위한 플라이웨이트 패턴, 플레이어 입력 자체를 캡슐화 하여 상호작용 및 제어 흐름에 도움을 주는 커맨드 패턴, 게임 내에서 객체를 새로 만들어 내는 과정을 단일 지점에 집중시켜 코드 단순화를 이루어낼 수 있는 팩토리 패턴 등 유용한 패턴들이 상당히 많습니다.</p>
</blockquote>
<blockquote>
<p>게임의 종류가 다양하듯, 적용시킬 수 있는 디자인 패턴도 상황에 따라 다릅니다. 만들고자 하는 게임에 따라 어울리지 않는 디자인 패턴을 무리하여 적용시키거나, 무리하게 많은 디자인패턴을 적용시켜버리면 구현 시간보다 설계시간이 반대로 더욱 길어져서 자칫 옳지 못한 결과로 이어질 수도 있습니다. 다양한 디자인 패턴 중, 충분히 본인 프로젝트에 어울리는지 판단한 후에 적용시킬지 여부를 결정하는 것이 좋습니다.</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[#탐색(Searching)]]></title>
            <link>https://velog.io/@sejun-2/%ED%83%90%EC%83%89Searching</link>
            <guid>https://velog.io/@sejun-2/%ED%83%90%EC%83%89Searching</guid>
            <pubDate>Thu, 03 Apr 2025 15:12:55 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="span-stylecolor00ff00순차-탐색--span"><span style="color:#00FF00">&lt;순차 탐색&gt;  </span></h3>
<blockquote>
<ul>
<li>자료구조에서 순차적으로 찾고자 하는 데이터를 탐색</li>
</ul>
</blockquote>
<ul>
<li>반드시 찾을 수 있지만, 시간 효율이 높은 탐색은 아니다.</li>
<li>데이터가 1억개 있으면 1억개를 찾아야 할 수 있음</li>
<li>시간복잡도 0(n)</li>
</ul>
<pre><code class="language-cpp">public static int LinearSearch(int[] array, int target)
{
    for (int i=0; i &lt; array.Length; i++)
    {
        if (array[i] == target)
        {
            return i;
        }
    }
    return -1;
}


static void Main(string[] args)
{
    int[] array = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 };
    int fixdIndex = LinearSearch(array, 9);
    Console.WriteLine(&quot;탐색 결과 : {0}&quot;, fixdIndex);
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00이진-탐색span----꼭-정렬-되어-있어야-함"><span style="color:#00FF00">&lt;이진 탐색&gt;</span>  -&gt; 꼭 정렬 되어 있어야 함!!</h3>
<blockquote>
<ul>
<li>정렬이 되어있는 자료구조에서 2분할을 통해 데이터를 탐색</li>
</ul>
</blockquote>
<ul>
<li>단, 이진 탐색은 정렬이 되어 있는 자료에만 적용 가능</li>
<li>시간복잡도 - O(logn)</li>
</ul>
<pre><code class="language-cpp">public static int BinarySearch(int[] array, int target)
{
    int low = 0;    // 처음
    int high = array.Length - 1;    // 끝
    while (true) // 찾을때까지 || 없을때까지
    {
        int mid = (low + high) / 2;
        // 중간 위치를 mid로 잡는다;
        if (array[mid] &gt; target)   //중간값이 찾고자 하는 값보다 더 큰경우
        {
            // 오른쪽 더 큰 값들은 제외
            high = mid - 1;
        }
        else if (array[mid] &lt; target)   //중간값이 찾고자 하는 값보다 더 작은경우
        {
            // 왼쪽 작은 값들은 제외
            high = mid + 1;
        }
        else
        {
            return mid; // 중간 위치가 찾는 값이다
        }
        return -1;
    }
}

static void Main(string[] args)
{
    int[] ints = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int binIndex = BinarySearch(ints, 5);
    Console.WriteLine(&quot;탐색 결과 : {0}&quot;, binIndex); }                                    </code></pre>
<hr>
<h3 id="span-stylecolor00ff00너비-우선-탐색-breadth-first-searchspan"><span style="color:#00FF00">&lt;너비 우선 탐색 (Breadth-First Search)&gt;</span></h3>
<blockquote>
<ul>
<li>그래프의 분기를 만났을 때 모든 분기들을 탐색한 뒤,</li>
</ul>
</blockquote>
<ul>
<li>다음 깊이의 분기들을 탐색</li>
<li>큐를 통해 탐색</li>
<li>장점 : 최단 경로를 보장!!</li>
<li>단점 : 지금 탐색상황에서 필요하지 않은 정점데이터도 큐에 보관할 필요가 있다.</li>
<li>일반적으로 그래프에 사용을 선호함</li>
</ul>
<pre><code class="language-cpp">public static void BFS(bool[,] graph, int start, out bool[] visited, out int[] parents)
{
    int size = graph.GetLength(0);
    //인접행렬 그래프의 특징상 n개 정점의 그래프는[n, m] 으로 만드니
    //GetLength(0) 을 구하면 n을 가질수 있고 이는 곧 정점의 갯수를 의미함
    visited = new bool[size];
    parents = new int[size];

    for (int i = 0; i &lt; size; i++)
    {
        visited[i] = false;
        parents[i] = -1;
    }

    Queue&lt;int&gt; queue = new Queue&lt;int&gt;();   
    queue.Enqueue(start);
    visited[start] = true;

    while (queue.Count &gt; 0) 
    {
        // 큐에서 다음으로 탐색할 정점을 확인한다
        int next = queue.Dequeue();

        //다음으로 탐색할 정점을 기준으로 탐색할 수 있는 정점들을 큐에 담는다
        for (int vertex = 0; vertex &lt;size; vertex++)  // 정점들을 반복하면서
        {    // 연결이 되어 있는 정점이면서 &amp;&amp; 방문한 적 없는 정점
            if (graph[next,vertex] ==true &amp;&amp; visited[vertex]==false)   
            {
                //큐에 탐색할 수 있는 정점을 추가한다.
                queue.Enqueue(vertex);
                //탐색할 수 있는 정점을 방문 표시
                visited[vertex] = true;
                //탐색할 수 있는 정점의 이전 정점을 표시한다
                parents[vertex] = next;
            }
        }
    }

}


static void Main(string[] args)
{
    bool[,] graph = new bool[8, 8];
    graph[0, 1] = true;
    graph[1, 0] = true;
    graph[0, 2] = true;
    graph[2, 0] = true;
    graph[0, 4] = true;
    graph[4, 0] = true;
    graph[1, 3] = true;
    graph[3, 1] = true;
    graph[1, 5] = true;
    graph[5, 1] = true;
    graph[2, 6] = true;
    graph[6, 2] = true;
    graph[4, 7] = true;
    graph[7, 4] = true;
    graph[5, 7] = true;
    graph[7, 5] = true;
    graph[6, 7] = true;
    graph[7, 6] = true;

    // BFS 탐색
    Console.WriteLine(&quot;&lt;BFS&gt;&quot;);
    BFS(graph, 0, out bool[] visited, out int[] parents);
    Console.WriteLine($&quot;{&quot;Vertex&quot;,8}{&quot;Visited&quot;,8}{&quot;Parents&quot;,8}&quot;);
    for (int i = 0; i &lt; visited.Length; i++)
    {
        Console.WriteLine($&quot;{i,8}{visited[i],8}{parents[i],8}&quot;);
    }                                           
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00깊이-우선-탐색-depth-first-searchspan"><span style="color:#00FF00">&lt;깊이 우선 탐색 (Depth-First Search)&gt;</span></h3>
<blockquote>
<ul>
<li>그래프의 분기를 만났을 때 최대한 깊이 내려간 뒤,</li>
</ul>
</blockquote>
<ul>
<li>분기의 탐색을 마쳤을 때 다음 분기를 탐색</li>
<li>스택을 통해 구현</li>
<li>장점 : 지금 탐색상황에서 필요한 정점데이터만 보관 가능하고, 탐색이 끝나면 버려도 무관하다.</li>
<li>단점 : 최단 경로를 보장하지 않음!!</li>
<li>일반적으로 트리에 사용을 선호함 -&gt; 최단 경로가 하나밖에 없음</li>
</ul>
<pre><code class="language-cpp">public static void DFS(bool[,] graph, int start, out bool[] visited, out int[] parents)
{
    int size = graph.GetLength(0);  // 정점의 갯수
    visited = new bool[size];
    parents = new int[size];

    for (int i = 0; i &lt; size; i++)
    {
        visited[i] = false; // 시작전 방문한 적 없다
        parents[i] = -1;    // 나를 찾을 필요는 없다
    }
    // 함수 호출 스텍 쓰는 방법
    SearchNode(graph, start, visited, parents);
}

private static void SearchNode(bool[,] graph, int vertex, bool[] visited, int[] parents)
{
    int size = graph.GetLength(0);  // 정점의 갯수
    visited[vertex] = true;

    for (int i = 0; i &lt; size; i++)  // 모든 정점을 확인
    {
        if (graph[vertex, i]==true &amp;&amp;     // 연결되어 있는 정점이며,
            visited[i]==false)            // 방문한적 없는 정점
        {
            parents[i] = vertex;
            SearchNode(graph, i, visited, parents); // 재귀함수
        }
    }
}


static void Main(string[] args)
{
    bool[,] graph = new bool[8, 8];
    graph[0, 1] = true;
    graph[1, 0] = true;
    graph[0, 2] = true;
    graph[2, 0] = true;
    graph[0, 4] = true;
    graph[4, 0] = true;
    graph[1, 3] = true;
    graph[3, 1] = true;
    graph[1, 5] = true;
    graph[5, 1] = true;
    graph[2, 6] = true;
    graph[6, 2] = true;
    graph[4, 7] = true;
    graph[7, 4] = true;
    graph[5, 7] = true;
    graph[7, 5] = true;
    graph[6, 7] = true;
    graph[7, 6] = true;

    // DFS 탐색
    Console.WriteLine(&quot;&lt;DFS&gt;&quot;);
    DFS(graph, 0, out bool[] visited, out int[] parents);
    Console.WriteLine($&quot;{&quot;Vertex&quot;,8}{&quot;Visited&quot;,8}{&quot;Parents&quot;,8}&quot;);
    for (int i = 0; i &lt; visited.Length; i++)
    {
        Console.WriteLine($&quot;{i,8}{visited[i],8}{parents[i],8}&quot;);
    }</code></pre>
<hr>
<h3 id="span-stylecolor00ff00다익스트라-알고리즘span"><span style="color:#00FF00">&lt;다익스트라 알고리즘&gt;</span></h3>
<blockquote>
<ul>
<li>특정한 노드에서 출발하여 다른 노드까지 가는 각각의 최단 경로를구하는 알고리즘</li>
</ul>
</blockquote>
<ol>
<li>방문하지 않은 노드 중에서 가장 가까운 노드를 선택한 후,</li>
<li>선택한 노드를 거쳐서 더 짧아지는 경로가 있는 경우 대체</li>
</ol>
<pre><code class="language-cpp">const int INF = 99999;
public static void Dijkstra(int[,] graph, int start, out bool[] visited, out int[] parents, out int[] cost)
{
    int size = graph.GetLength(0);    // 정점의 갯수
    visited = new bool[size];
    parents = new int[size];
    cost = new int[size];

    for (int i = 0; i &lt; size; i++)
    {
        visited[i] = false; // 탐색한적 없음. 초기값
        parents[i] = -1;
        cost[i] = INF;
    }
    cost[start] = 0;    // 시작지점 0 부터

    for (int i = 0; i &lt; size; i++)  // 전부 다 보기 위해
    {
        // 1.방문하지 않은 정점 중 가장 가까운 정점 선택
        int minIndex = -1;    //최소값을 확인하기 위해
        int minCost = INF;
        for (int j = 0; j &lt; size; j++)
        {
            if (visited[j]==false &amp;&amp;   // 방문한 적 없으며
                cost[j] &lt; minCost)      // 가장 가까운 정점
            {
                minIndex = j;
                minCost = cost[j];      // 가장 가까운 정점 찾으면 교체
            }
        }
        if (minIndex &lt; 0)
        {
            break;  // 하나도 없는 경우 (-1)
        }
        visited[minIndex] = true;

        // 2. 직접 연결된 거리보다 거쳐서 더 짧아지면 대체
        for (int j = 0; j &lt; size; j++)
        {
            // cost[j] : 목적지까지 직접 연결된 거리    (AB)
            // cost[minIndex] : 중간점까지의 거리 - 탐색중인 정점까지 거리    (AC)
            // graph[minIndex, j] : 중간점 부터 목적지까지 거리 - 탐색중인 정점부터 목적지의 거리    (CB)
            if (cost[j] &gt; cost[minIndex] + graph[minIndex, j])
            {
                cost[j] = cost[minIndex] + graph[minIndex, j];
                parents[j] = minIndex;
            }
        }
    }
}


static void Main(string[] args)
{
    // 다익스트라 최단 경로 알고리즘
    int[,] dijkstra = new int[8, 8]
    {
        {   0,  6,  7,  2,  INF, INF, INF,  INF },
        {   6,  0,  8, INF, INF, INF,   2,  INF },
        {   7,  8,  0, INF, INF,   2,   7,  INF },
        {   2,INF,INF,   0, INF,   2, INF,  INF },
        { INF,INF,INF, INF,   0, INF,   8,  INF },
        { INF,INF,  2,   2, INF,   0,   1,    1 },
        { INF,  2,  7, INF,   8,   1,   0,    4 },
        { INF,INF,INF, INF, INF,   1,   4,    0 },
    };

    Console.WriteLine(&quot;다익스트라&quot;);
    Dijkstra(dijkstra, 0, out bool[] visited, out int[] parent, out int[] cost);

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#정렬(Sorting)]]></title>
            <link>https://velog.io/@sejun-2/%EC%A0%95%EB%A0%ACSorting</link>
            <guid>https://velog.io/@sejun-2/%EC%A0%95%EB%A0%ACSorting</guid>
            <pubDate>Wed, 02 Apr 2025 16:38:30 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="span-stylecolor00ff00선택정렬span------느릴수-있으나-16개-이하에서는-오히려-더-빠름"><span style="color:#00FF00">&lt;선택정렬&gt;</span>    -&gt; 느릴수 있으나 16개 이하에서는 오히려 더 빠름</h3>
<blockquote>
<ul>
<li>데이터 중 가장 작은 값부터 하나씩 선택하여 정렬
시간복잡도 -  O(n²)
공간복잡도 -  O(1)
불안정정렬</li>
</ul>
</blockquote>
<pre><code class="language-cpp">public static void SelectionSort(int[] array)
{
    for (int i=0; i&lt;array.Length; i++)// 배열의 첫 번째 요소부터 마지막까지 반복
    {
        int minIndex = i; // 현재 위치를 최소값의 인덱스로 설정
        for (int j=0; j&lt;array.Length; j++)
        {
            if (array[j] &lt; array[minIndex])// 더 작은 값이 발견되면 최소값 인덱스를 갱신
            {
                minIndex = j;
            }
        }
        Swep(array, i, minIndex);// 현재 위치와 최소값 위치를 교환
    }
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00삽입정렬span"><span style="color:#00FF00">&lt;삽입정렬&gt;</span></h3>
<blockquote>
<ul>
<li>데이터를 하나씩 꺼내어 정렬된 자료 중 적합한 위치에 삽입하여 정렬
시간복잡도 -  O(n²)
공간복잡도 -  O(1)
안정정렬   -  O</li>
</ul>
</blockquote>
<pre><code class="language-cpp">public static void InsertionSort(int[] array)
{
    for (int i = 1; i &lt; array.Length; i++)   // 두 번째 요소부터 시작 -&gt; 맨 처음칸은 정렬될 장소이기 때문 필요없음
    {
        for (int j = i; j &gt; 0; j--)    // 현재 요소를 정렬된 부분과 비교하며 적합한 위치로 이동
        {
            if (array[j - 1] &gt; array[j])  // 다음숫자를 하나씩 꺼내서 앞의 요소가 더 큰지 비교
            {
                Swep(array, j - 1, j);   // 앞 숫자와 비교해서 더 작으면 한칸 뒤로 밀고
            }
            else
            {
                break;                  // 적합한 위치를 찾으면 종료
            }
        }
    }
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00버블정렬span-----인덱스-사용이-불가능-할때-사용가능"><span style="color:#00FF00">&lt;버블정렬&gt;</span>   -&gt; 인덱스 사용이 불가능 할때 사용가능</h3>
<blockquote>
<ul>
<li>서로 인접한 데이터를 비교하여 정렬
시간복잡도 -  O(n²)
공간복잡도 -  O(1)
안정정렬   -  O</li>
</ul>
</blockquote>
<pre><code class="language-cpp">public static void BubbleSort(int[] array)
{
    for (int i = 1; i &lt; array.Length; i++)  // 처음부터 끝까지 여러 번 스캔
    {
        for (int j = 0; j &lt; array.Length - i; j++)  // 인접한 두 요소를 비교
        {
            if (array[j] &gt; array[j + 1])    // 서로 인접한 두 데이터의 앞의 요소가 더 크면
            {
                Swep(array, j, j + 1);    // 교환
            }
        }
    }
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00병합정렬span"><span style="color:#00FF00">&lt;병합정렬&gt;</span></h3>
<blockquote>
<ul>
<li>데이터를 2분할하여 정렬 후 합병</li>
</ul>
</blockquote>
<ul>
<li>데이터 갯수만큼의 추가적인 메모리가 필요
시간복잡도 -  O(nlogn)
공간복잡도 -  O(n)
안정정렬   -  O</li>
</ul>
<pre><code class="language-cpp">public static void MergeSort(int[] array) =&gt; MergeSort(array, 0, array.Length - 1);
public static void MergeSort(int[] array, int start, int end)
{
    if (start == end)    // 배열이 하나의 요소만 남으면 반환
        return;

    int mid = (start + end) / 2;
    MergeSort(array, start, mid);  // 왼쪽 부분 정렬
    MergeSort(array, mid + 1, end);  // 오른쪽 부분 정렬
    Merge(array, start, mid, end);  // 두 부분 병합
        }
}

public static void Merge(int[] array, int start, int mid, int end)
{
    List&lt;int&gt; sortedList = new List&lt;int&gt;();
    int leftIndex = start;
    int rightIndex = mid + 1;

    while (leftIndex &lt;= mid &amp;&amp; rightIndex &lt;= end)
    {
        if (array[leftIndex] &lt; array[rightIndex])
        {
            sortedList.Add(array[leftIndex]);
            leftIndex++;
        }
        else
        {
            sortedList.Add(array[rightIndex]);
            rightIndex++;
        }
    }

    if (leftIndex &lt;= mid)
    {
        while (leftIndex &lt;= mid)
        {
            sortedList.Add(array[leftIndex]);
            leftIndex++;
        }
    }
    else // if (rightIndex &lt;= end)
    {
        while (rightIndex &lt;= end)
        {
            sortedList.Add(array[rightIndex]);
            rightIndex++;
        }
    }

    for (int i = 0; i &lt; sortedList.Count; i++)
    {
        array[start + i] = sortedList[i];
    }
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00퀵정렬span"><span style="color:#00FF00">&lt;퀵정렬&gt;</span></h3>
<blockquote>
<ul>
<li>하나의 기준(피벗)을 기준으로 작은값과 큰값을 2분할하여 정렬</li>
</ul>
</blockquote>
<ul>
<li>최악의 경우(피벗이 최소값 또는 최대값)인 경우 시간복잡도가 O(n²)
시간복잡도 -  평균 : O(nlogn)   최악 : O(n²)
공간복잡도 -  O(1)
불안정정렬</li>
</ul>
<pre><code class="language-cpp">public static void QuickSort(int[] array) =&gt; QuickSort(array, 0, array.Length - 1);

public static void QuickSort(int[] array, int start, int end)
{
    if (start &gt;= end)
    {
        return;
    }

    int pivot = start;
    int left = pivot + 1;
    int right = end;

    while (left &lt;= right)   //left와 right가 교차할때까지 반복
    {
        while (array[pivot] &gt;= array[left] &amp;&amp; left &lt; right)   // left는 pivot 보다 더 큰값을 볼 떄까지 오른쪽으로 이동;
        {
            left++;
        }
        while (array[pivot] &lt; array[right] &amp;&amp; left &lt;= right)  // right는 pivot보다 더 작은 값을 볼때까지 왼쪽으로 이동;
        {
            right--;
        }

        if (left &lt; right)   //left와 right가 교차 안했다면
        {
            Swep(array, left, right);    //left와 right 값을 교체
        }
        else
        {
            Swep(array, pivot, right);   // 아니라면 pivot과 right를 교체
            break;
        }
    }

    QuickSort(array, start, right - 1);
    QuickSort(array, right + 1, end);
}</code></pre>
<hr>
<h3 id="span-stylecolor00ff00힙정렬span"><span style="color:#00FF00">&lt;힙정렬&gt;</span></h3>
<blockquote>
<ul>
<li>힙을 이용하여 우선순위가 가장 높은 요소가 가장 마지막 요소와 교체된 후 제거되는 방법을 이용</li>
</ul>
</blockquote>
<ul>
<li>배열에서 연속적인 데이터를 사용하지 않기 때문에 캐시 메모리를 효율적으로 사용할 수 없어 상대적으로 느림
시간복잡도 -  O(nlogn)
공간복잡도 -  O(1)
불안정정렬</li>
</ul>
<pre><code class="language-cpp">public static void HeapSort(int[] array)
{
    MakeHeap(array);

    for (int i = array.Length - 1; i &gt; 0; i--)
    {
        Swep(array, 0, i);
        Heapify(array, 0, i);
    }
}

private static void MakeHeap(int[] array)
{
    for (int i = array.Length / 2 - 1; i &gt;= 0; i--)
    {
        Heapify(array, i, array.Length);
    }
}

private static void Heapify(int[] array, int index, int size)
{
    int left = index * 2 + 1;
    int right = index * 2 + 2;
    int max = index;
    if (left &lt; size &amp;&amp; array[left] &gt; array[max])
    {
        max = left;
    }
    if (right &lt; size &amp;&amp; array[right] &gt; array[max])
    {
        max = right;
    }

    if (max != index)
    {
        Swep(array, index, max);
        Heapify(array, max, size);
    }
}</code></pre>
<hr>
<pre><code class="language-cpp">static void Main(string[] args)
{
    int num = 200;
    Console.WriteLine($&quot;{num, 5}&quot;);  // 문자 간격 조결. 5 -&gt; 5칸을 가지겠다. 200 이3칸 띄어쓰기2칸 = 5
    Console.WriteLine(num);

    Random random = new Random();
    int count = 100;

    int[] selectArray = new int[count];
    int[] insertArray = new int[count];
    int[] bubbleArray = new int[count];
    int[] mergeArray = new int[count];
    int[] quickArray = new int[count];


    Array.Sort(selectArray);    // c#은 인트로 Sort(정렬)로 되어있다. 하이브리드 정렬. (퀵, 힙, 삽입 정렬로 이루어짐)


    Console.WriteLine(&quot;랜덤 데이터: &quot;);
    for (int i = 0; i &lt; count; i++)
    {
        int rand = random.Next(0, 100);     // 0부터 100미만 랜덤한 숫자 생성
        Console.Write($&quot;{rand, 3}&quot;);

        selectArray[i] = rand;
        insertArray[i] = rand;
        bubbleArray[i] = rand;
        mergeArray[i] = rand;
        quickArray[i] = rand;
    }
    Console.WriteLine();
    Console.WriteLine();

    SelectionSort(selectArray);
    Console.WriteLine(&quot;선택 정렬 결과 : &quot;);
    foreach (int value in selectArray)
    {
        Console.Write($&quot;{value,3}&quot;);
    }
    Console.WriteLine();

    InsertionSort(insertArray);
    Console.WriteLine(&quot;삽입 정렬 결과 : &quot;);
    foreach (int value in insertArray)
    {
        Console.Write($&quot;{value,3}&quot;);
    }
    Console.WriteLine();

    Stopwatch sw1 = new Stopwatch();
    sw1.Start();
    BubbleSort(bubbleArray);
    sw1.Stop();

    Console.WriteLine(&quot;버블 정렬 결과: { 0}&quot;, sw1.ElapsedTicks);
    foreach (int value in bubbleArray)
    {
        Console.Write($&quot;{value,3}&quot;);
    }
    Console.WriteLine();

    Stopwatch sw2 = new Stopwatch();
    sw2.Start();
    MergeSort(mergeArray);
    sw2.Stop();

    Console.WriteLine(&quot;병합 정렬 결과: { 0}&quot;, sw2.ElapsedTicks);
    foreach (int value in mergeArray)
    {
        Console.Write($&quot;{value,3}&quot;);
    }
    Console.WriteLine();


    QuickSort(quickArray);
    Console.WriteLine(&quot;퀵 정렬 결과: &quot;);
    foreach (int value in quickArray)
    {
        Console.Write($&quot;{ value,3}&quot;);
    }
    Console.WriteLine();
}


public static void Swep(int[] array, int left, int right) 
{ 
    int temp = array[left];
    array[left] = array[right];
    array[right] = temp;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#알고리즘(Algorithm) #재귀(Recursion)]]></title>
            <link>https://velog.io/@sejun-2/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98Algorithm-%EC%9E%AC%EA%B7%80Recursion</link>
            <guid>https://velog.io/@sejun-2/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98Algorithm-%EC%9E%AC%EA%B7%80Recursion</guid>
            <pubDate>Wed, 02 Apr 2025 14:46:16 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="span-stylecolor00ff00알고리즘-algorithmspan"><span style="color:#00FF00">알고리즘 (Algorithm)</span></h2>
<hr>
<blockquote>
<ul>
<li>문제를 해결하기 위해 정해진 진행절차나 방법</li>
</ul>
</blockquote>
<ul>
<li>컴퓨터에서 알고리즘은 어떠한 행동을 하기 위해서 만들어진 프로그램 명령어의 집합</li>
</ul>
<hr>
<h3 id="span-stylecolor00ff00알고리즘-조건span"><span style="color:#00FF00">&lt;알고리즘 조건&gt;</span></h3>
<blockquote>
<ol>
<li>입력 : 알고리즘은 0개 이상의 입력을 가져야 함</li>
<li>출력 : 알고리즘은 최소 1개 이상의 결과를 가져야 함</li>
<li>명확성 : 수행 과정은 모호하지 않고 정확한 수단을 제공해야 함</li>
<li>유한성 : 수행 과정은 무한하지 않고 유한한 작업 이후에 정지해야 함</li>
<li>효과성 : 모든 과정은 명백하게 실행 가능해야 함</li>
</ol>
</blockquote>
<hr>
<pre><code class="language-cpp">static void Main(string[] args)
{   
    &lt;의사코드&gt; -&gt; 인간의 언어로 흐름도를 먼저 그려보는것
    // 가장 큰 수를 찾기
    int[] array = { 1, 2, 3, 4, 5 };
    // 1. 가장 큰 수를 저장할 변수 만들기
    int max;
    // 2. 맨 처음부터 시작
    int index = 0;
    // 3. 맨 처음부터 시작한 수가 다음 수보다 작으면 큰 수를 다음수로 변경
    // 4. 다음 수를 또 다시 반복
}
</code></pre>
<hr>
<hr>
<h2 id="span-stylecolor00ff00재귀-recursionspan"><span style="color:#00FF00">재귀 (Recursion)</span></h2>
<hr>
<blockquote>
<ul>
<li>어떠한 것을 정의할 때 자기 자신을 참조 하는것</li>
</ul>
</blockquote>
<ul>
<li>함수를 정의할 때 자기자신을 이용하여 표현하는 방법</li>
</ul>
<hr>
<h3 id="span-stylecolor00ff00재귀함수-조건span"><span style="color:#00FF00">&lt;재귀함수 조건&gt;</span></h3>
<blockquote>
<ol>
<li>함수내용 중 자기자신함수를 다시 호출해야함</li>
<li>종료조건이 있어야 함</li>
</ol>
</blockquote>
<hr>
<p>###<span style="color:#00FF00">&lt;재귀함수 장점&gt;</span></p>
<blockquote>
<ol>
<li>코드로 표현하기 어려운 경우도 직관적이고, 처리가 가능</li>
<li>분할정복을 통한 반절 계산이 가능해서 효율이 높아지게 구현이 가능</li>
</ol>
</blockquote>
<hr>
<h3 id="span-stylecolor00ff00재귀함수-사용span"><span style="color:#00FF00">&lt;재귀함수 사용&gt;</span></h3>
<pre><code class="language-cpp"> Factorial : 정수를 1이 될 때까지 차감하며 곱한 값
 x! = x * (x-1)!;
 1! = 1;
 ex) 5! = 5 * 4!
        = 5 * 4 * 3!
        = 5 * 4 * 3 * 2!
        = 5 * 4 * 3 * 2 * 1!
        = 5 * 4 * 3 * 2 * 1

 재귀함수는 예외 처리가 안되면(종료 조건) -&gt; 오버플로우에 빠짐 (터짐)


int Factorial(int x)
{
    if (x == 1)
        return 1;
    else
        return x * Factorial(x - 1);
}



public class Folder
{
    //public List&lt;string&gt; files;
    public List&lt;Folder&gt; children;
}

public static void RemoveFolder(Folder folder)
{
    // 파일들 삭제하고

    foreach (var child in folder.children)
    {
        RemoveFolder(child);
    }
}

int Fibonaachi(int n)       //  재귀를 써도 최악의 알고리즘. -&gt; 소모되는 시간 2배
{
    if(n==1) return 1;
    else if(n==2) return 1;
    return Fibonaachi(n - 1) + Fibonaachi(n - 2);
}

static void Main(string[] args)
{
    Console.WriteLine(&quot;Hello, World!&quot;);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[#Graph #Tree]]></title>
            <link>https://velog.io/@sejun-2/Graph-Tree</link>
            <guid>https://velog.io/@sejun-2/Graph-Tree</guid>
            <pubDate>Mon, 31 Mar 2025 15:32:11 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="span-stylecolor00ff00그래프-graphspan"><span style="color:#00FF00">그래프 (Graph)</span></h2>
<hr>
<blockquote>
<ul>
<li>정점의 모음과 이 정점을 잇는 간선의 모음의 결합</li>
</ul>
</blockquote>
<ul>
<li>한 노드에서 출발하여 다시 자기 자신의 노드로 돌아오는 순환구조를 가짐</li>
<li>간선의 방향성에 따라 단방향 그래프, 양방향 그래프가 있음</li>
<li>간선의 가중치에 따라   연결 그래프, 가중치 그래프가 있음</li>
</ul>
<hr>
<p><span style="color:#00FF00">&lt;인접행렬 그래프&gt;</span></p>
<blockquote>
<ul>
<li>그래프 내의 각 정점의 인접 관계를 나타내는 행렬</li>
</ul>
</blockquote>
<ul>
<li>2차원 배열을 [출발정점, 도착정점] 으로 표현</li>
<li>장점 : 인접여부 접근이 빠름</li>
<li>단점 : 메모리 사용량이 많음</li>
</ul>
<pre><code class="language-cpp">int[,] graph0 = new int[8,8];

graph0[0, 2] = 122;
graph0[2, 0] = 13;
graph0[0, 4] = 22;
graph0[4, 0] = 14;
graph0[1, 2] = 66;
graph0[2, 1] = 155;
graph0[1, 3] = 55;
graph0[3, 1] = 32;

graph0[0, 1] = 5;  //가중치
graph0[1, 3] = 6;

/********************************************************/
bool[,] graph0 = new bool[8,8];

graph0[0, 2] = true;
graph0[2, 0] = true;
graph0[0, 4] = true;
graph0[4, 0] = true;
graph0[1, 2] = true;
graph0[2, 1] = true;
graph0[1, 3] = true;
graph0[3, 1] = true;

Console.WriteLine(&quot;0에서 3으로 연결되어 있는가? {0}&quot;, graph0[0,3]);    //false


// 예시 - 양방향 연결 그래프
bool[,] matrixGraph1 = new bool[5, 5]
{
{ false, false, false, false,  true },
{ false, false,  true, false, false },
{ false,  true, false,  true, false },
{ false, false,  true, false,  true },
{  true, false, false,  true, false },
};

// [0,2] =&gt; 출발정점(0) -&gt; 도착정점(2)
bool connect = matrixGraph1[0, 2];   // 연결 여부 확인
matrixGraph1[0, 2] = true;           // 연결추가
matrixGraph1[0, 2] = false;          // 연결헤제



const int INF = int.MaxValue;
// 예시 - 단방향 가중치 그래프 (단절은 최대값으로 표현) INF
int[,] matrixGraph2 = new int[5, 5]
{
{   0, 132, INF, INF,  16 },
{  12,   0, INF, INF, INF },
{ INF,  38,   0, INF, INF },
{ INF,  12, INF,   0,  54 },
{ INF, INF, INF, INF,   0 },
};

// [0,2] =&gt; 출발정점(0) -&gt; 도착정점(2)
int distance = matrixGraph2[0, 2];   // 0에서 2까지 가중치
matrixGraph2[0, 2] = 10;           // 가중치 변경. </code></pre>
<hr>
<p><span style="color:#00FF00">&lt;인접리스트 그래프&gt;</span></p>
<blockquote>
<ul>
<li>그래프 내의 각 정점의 인접 관계를 표현하는 리스트</li>
</ul>
</blockquote>
<ul>
<li>인접한 간선만을 리스트에 추가하여 관리</li>
<li>장점 : 메모리 사용량이 적음!!</li>
<li>단점 : 인접여부를 확인하기 위해 리스트 탐색이 필요 (빠르지 않음)</li>
</ul>
<pre><code class="language-cpp">List&lt;int&gt;[] listGraph = new List&lt;int&gt;[5];

listGraph[0] = new List&lt;int&gt;() { 2, 4 };
listGraph[1] = new List&lt;int&gt;() { 2, 3 };
listGraph[2] = new List&lt;int&gt;() { 0, 1 , 4 };
listGraph[3] = new List&lt;int&gt;() { 1, 7 };
listGraph[4] = new List&lt;int&gt;() { 0, 2 };
listGraph[5] = new List&lt;int&gt;() { 6, 7 };
listGraph[6] = new List&lt;int&gt;() { 5, 7 };
listGraph[7] = new List&lt;int&gt;() { 3, 5, 6 };

listGraph[0].Add(5);        // 연결 추가
listGraph[5].Add(0);

listGraph[1].Remove(2);     // 연결 끊기
listGraph[2].Remove(1);

bool connect0 = listGraph[0].Contains(1);   // 연결 여부 확인. -&gt; 0에 1이 연결되어있으면 true</code></pre>
<hr>
<hr>
<pre><code class="language-cpp">public class GraphNode&lt;T&gt;       // 채개적으로 관리 가능. -&gt; 코드가 많이 늘어남.
{
    private T vertax;

    public T Vertax { get { return vertax; } set { vertax = value; } }

    private List&lt;GraphNode&lt;T&gt;&gt; edges = new List&lt;GraphNode&lt;T&gt;&gt;();

    public GraphNode(T vertex)
    {
        this.vertax = vertex;
    }

    public void AddEdge(GraphNode&lt;T&gt; node) 
    {   
        // 연결 하기
        edges.Add(node);
    }

    public void RemoveEdge(GraphNode&lt;T&gt; node)
    {
        // 연결 끊기
        edges.Remove(node);
    }

    public bool IsConnect(GraphNode&lt;T&gt; node)
    {
        return edges.Contains(node); 
    }
}</code></pre>
<pre><code class="language-cpp">static void Main(string[] args)
{
    GraphNode&lt;int&gt; node0 = new GraphNode&lt;int&gt;(0);
    GraphNode&lt;int&gt; node1 = new GraphNode&lt;int&gt;(1);
    GraphNode&lt;int&gt; node2 = new GraphNode&lt;int&gt;(2);
    GraphNode&lt;int&gt; node3 = new GraphNode&lt;int&gt;(3);
    GraphNode&lt;int&gt; node4 = new GraphNode&lt;int&gt;(4);
    GraphNode&lt;int&gt; node5 = new GraphNode&lt;int&gt;(5);
    GraphNode&lt;int&gt; node6 = new GraphNode&lt;int&gt;(6);
    GraphNode&lt;int&gt; node7 = new GraphNode&lt;int&gt;(7);

    node0.AddEdge(node3);
    node3.AddEdge(node0);
    node0.AddEdge(node4);
    node4.AddEdge(node0);
}</code></pre>
<hr>
]]></description>
        </item>
    </channel>
</rss>