<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SMN 블로그</title>
    <link>https://smnnmn.tistory.com/</link>
    <description>개인적인 개발 정리용으로 작성합니다</description>
    <language>ko</language>
    <pubDate>Tue, 2 Jun 2026 21:29:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SMNNMN</managingEditor>
    <item>
      <title>Step of Love - 색 변경</title>
      <link>https://smnnmn.tistory.com/79</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번에 구현할 기능은 색 변경이다.&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwcVmK/dJMcaayKhCM/tg0JEhUW2FUTP41jQlSu5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwcVmK/dJMcaayKhCM/tg0JEhUW2FUTP41jQlSu5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwcVmK/dJMcaayKhCM/tg0JEhUW2FUTP41jQlSu5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwcVmK%2FdJMcaayKhCM%2Ftg0JEhUW2FUTP41jQlSu5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;426&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 상황에서 여러 오브젝트의 기본 색이 변경될수 있게 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 기능을 만들고 나면 나머지 기획했던 기능들을 실제 게임을 구현하면서 차츰 만들어갈 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ColorManager&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색을 관리하는 매니저&lt;/p&gt;
&lt;pre id=&quot;code_1780214762107&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using UnityEngine;

public class ColorManager : Singleton&amp;lt;ColorManager&amp;gt;
{
    public event Action&amp;lt;ColorEvent&amp;gt; C_ChangeEvent;
    public enum ColorEvent
    {
        NOON,
        NIGHT,
    }
    protected override void Awake()
    {
        base.Awake();
    }
    public void ChangeColor(ColorEvent colorEvent)
    {
        C_ChangeEvent?.Invoke(colorEvent);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 상황을 낮과 밤으로 만 만들어 주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 확장하려면 ColorEvent에다가 여러 상황을 넣어서 관리하면 될 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ChangeColorObject&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색이 변경되는 오브젝트 스크립트&lt;/p&gt;
&lt;pre id=&quot;code_1780214827939&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class ChangeColorObject : MonoBehaviour
{
    private SpriteRenderer sprite;
    [SerializeField] Color[] colors;
    private void Awake()
    {
        sprite = GetComponent&amp;lt;SpriteRenderer&amp;gt;();
    }
    private void OnEnable()
    {
        ColorManager.Instance.C_ChangeEvent += ChangeColor;
    }
    private void OnDisable()
    {
        ColorManager.Instance.C_ChangeEvent -= ChangeColor;
    }
    // 색이 변경된다.
    void ChangeColor(ColorManager.ColorEvent colorEvent)
    {
        int index = (int)colorEvent;

        sprite.color = colors[index];
        Debug.Log(&quot;색 변경&quot;);
        Debug.Log(&quot;index : &quot; + index);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색이 변경되는 오브젝트는 이 스크립트를 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 각 상황에 따라서 자신이 지니는 색을 가지고 있고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에 따른 이벤트가 호출되면 해당 색으로 sprite.color를 변경한다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;InputController&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의로 이벤트를 호출하는 코드&lt;/p&gt;
&lt;pre id=&quot;code_1780214929778&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    void Update()
    {
        Mouse_Pos();
        Mouse_L();
        if (Input.GetMouseButtonDown(1))
        {
            CameraManager.Instance.EventCam(Vector2.zero);
            ColorManager.Instance.ChangeColor(ColorManager.ColorEvent.NOON);
        }
        else if(Input.GetMouseButtonDown(2))
        {
            CameraManager.Instance.FollowCam(player);
            ColorManager.Instance.ChangeColor(ColorManager.ColorEvent.NIGHT);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아키텍처&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpw2vO/dJMcaak9SRD/ujVM7g7wDSHoZ1FNRWpKZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpw2vO/dJMcaak9SRD/ujVM7g7wDSHoZ1FNRWpKZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpw2vO/dJMcaak9SRD/ujVM7g7wDSHoZ1FNRWpKZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpw2vO%2FdJMcaak9SRD%2FujVM7g7wDSHoZ1FNRWpKZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1075&quot; height=&quot;671&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완성본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-31-17-08-24.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mPxg9/dJMcacpNeie/abTFFoWLJ9CxS8FujuX521/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mPxg9/dJMcacpNeie/abTFFoWLJ9CxS8FujuX521/img.gif&quot; data-alt=&quot;낮과 밤으로 스프라이트 색이 변경되는 것을 볼수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mPxg9/dJMcacpNeie/abTFFoWLJ9CxS8FujuX521/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/mPxg9/dJMcacpNeie/abTFFoWLJ9CxS8FujuX521/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;992&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-31-17-08-24.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;낮과 밤으로 스프라이트 색이 변경되는 것을 볼수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>GameDev/Step of Love</category>
      <author>SMNNMN</author>
      <guid isPermaLink="true">https://smnnmn.tistory.com/79</guid>
      <comments>https://smnnmn.tistory.com/79#entry79comment</comments>
      <pubDate>Sun, 31 May 2026 17:16:58 +0900</pubDate>
    </item>
    <item>
      <title>Step of Love 미니게임</title>
      <link>https://smnnmn.tistory.com/78</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번엔 구현할 것은 미니게임이다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8fpvn/dJMcadPGpIn/WBRemzjTkM1t7eJiGTKvIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8fpvn/dJMcadPGpIn/WBRemzjTkM1t7eJiGTKvIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8fpvn/dJMcadPGpIn/WBRemzjTkM1t7eJiGTKvIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8fpvn%2FdJMcadPGpIn%2FWBRemzjTkM1t7eJiGTKvIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;397&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트처럼 플레이어가 미니게임 존에 들어가거나 특정 상황이 되었을 때, 일단은 UI로 할수 있는 미니게임이 등장하고, 이 미니게임을 완료하면 다시 이동할 수 있는 상태가 되는 것을 구현해볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MiniGameManager&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니게임을 생성과 삭제등을 관리하는 스크립트&lt;/p&gt;
&lt;pre id=&quot;code_1780209577212&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class MiniGameManager : Singleton&amp;lt;MiniGameManager&amp;gt;
{
    protected override void Awake()
    {
        base.Awake();
    }
    public void StartMiniGame(GameObject miniGame)
    {
        GameManager.Instance.SetEventState(GameManager.EventState.MINIGAME);
        Time.timeScale = 0;
        Instantiate(miniGame);
    }
    public void FinishMiniGame(GameObject miniGame)
    {
        GameManager.Instance.SetEventState(GameManager.EventState.NORMAL);
        Time.timeScale = 1f;
        Destroy(miniGame);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니게임이 시작되었을 때와 끝났을 때를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MiniGameZone&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니게임이 실행되는 트리거 역할&lt;/p&gt;
&lt;pre id=&quot;code_1780209632076&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class MiniGameZone : MonoBehaviour
{
    [SerializeField] GameObject miniGame; // 발생하는 미니게임
    private void OnTriggerEnter2D(Collider2D collision)
    {
        FootPrint foot = collision.GetComponent&amp;lt;FootPrint&amp;gt;();
        if (foot != null)
        {
            Debug.Log(&quot;Start MiniGame&quot;);
            MiniGameManager.Instance.StartMiniGame(miniGame);
            gameObject.SetActive(false);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EventZone과 마찬가지로, 발자국과 닿았을 때, 미니게임을 실행한다. MiniGameManager를 거쳐 실행코드를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;miniGame이라는 자신이 실행할 미니게임 오브젝트를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Minigame&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 미니게임이 가지는 공통 부모 코드&lt;/p&gt;
&lt;pre id=&quot;code_1780209716444&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class MiniGame : MonoBehaviour
{
    [SerializeField] GameObject myMiniGame;
    protected void GameFinish()
    {
        MiniGameManager.Instance.FinishMiniGame(myMiniGame);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 미니게임은 현재기준으로 끝이 존재하기 때문에, 게임이 끝났을 때를 MiniGame 이라는 부모 클래스에 구현해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;A_MiniGameManager&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의로 만든 하나의 미니게임 시스템&lt;/p&gt;
&lt;pre id=&quot;code_1780209837851&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.UI;

public class A_MiniGameManager : MiniGame
{
    [SerializeField] Text myText;
    [SerializeField] int clickNum;
    private void Awake()
    {
        myText.text = $&quot;Click {clickNum}&quot;.ToString();
    }
    public void buttonClick()
    {
        clickNum--;
        if(clickNum &amp;lt;= 0)
        {
            GameFinish();
            return;
        }
        myText.text = $&quot;Click {clickNum}&quot;.ToString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 클릭하는 미니게임을 임의로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아키텍처&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rULjh/dJMcaijdbfh/YZeZYJBiGtCQ59tjQSzvk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rULjh/dJMcaijdbfh/YZeZYJBiGtCQ59tjQSzvk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rULjh/dJMcaijdbfh/YZeZYJBiGtCQ59tjQSzvk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrULjh%2FdJMcaijdbfh%2FYZeZYJBiGtCQ59tjQSzvk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1153&quot; height=&quot;559&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완성본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-28-02-11-25.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ht3Dk/dJMcaaFw5L6/7fPwG5cLzYT2mTgwl7DTYk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ht3Dk/dJMcaaFw5L6/7fPwG5cLzYT2mTgwl7DTYk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ht3Dk/dJMcaaFw5L6/7fPwG5cLzYT2mTgwl7DTYk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Ht3Dk/dJMcaaFw5L6/7fPwG5cLzYT2mTgwl7DTYk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1000&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-28-02-11-25.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>GameDev/Step of Love</category>
      <author>SMNNMN</author>
      <guid isPermaLink="true">https://smnnmn.tistory.com/78</guid>
      <comments>https://smnnmn.tistory.com/78#entry78comment</comments>
      <pubDate>Sun, 31 May 2026 15:53:47 +0900</pubDate>
    </item>
    <item>
      <title>Step of Love 카메라 제어</title>
      <link>https://smnnmn.tistory.com/77</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번엔 카메라를 제어할 것이다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로, 카메라는 특정한 타겟을 따라가거나, 특정 위치에 고정되게끔 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CameraManager&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 카메라를 관리하는 스크립트를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1779901633015&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Unity.Cinemachine;
using UnityEngine;

public class CameraManager : Singleton&amp;lt;CameraManager&amp;gt;
{
    [SerializeField] CinemachineCamera follow_Cam; // 플레이어를 따라다니는 카메라
    [SerializeField] CinemachineCamera event_Cam; // 이벤트 발동시 특정 위치에 존재하는 카메라

    private CinemachineCamera currentCam; // 현재 사용중인 카메라

    private void ChangeCamera(CinemachineCamera cam)
    {
        if(currentCam != null)
        {
            currentCam.Priority = 0;
        }
        cam.Priority = 10;
        currentCam = cam;
    }
    // 타켓을 따라다니는 카메라
    public void FollowCam(GameObject target)
    {
        ChangeCamera(follow_Cam);
        follow_Cam.Follow = target.transform;
        follow_Cam.LookAt = target.transform;
    }

    // 특정 위치를 비추는 카메라
    public void EventCam(Vector2 targetPos)
    {
        ChangeCamera(event_Cam);
        event_Cam.transform.position = new Vector3(targetPos.x, targetPos.y, -10f);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사용하는 카메라 형식은 총 2개로 특정 오브젝트를 따라가는 follow_Cam과 특정 위치를 촬영하는 event_Cam이 존재한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChangeCamera로 전환할 카메라의 Priority값을 높여주어 전환을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FollowCam에서 따라갈 대상을 받아와 카메라가 그 오브젝트를 따라가게끔 하고, EventCam에서는 특정 위치를 받아와 해당 위치를 비추는 기능을 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;unity Editor 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 Cinemachine Brain이 될 mainCamera에 brain을 달아주고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;followCam과 eventCam을 만들어서 Cinemachine을 넣어주고 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lddoP/dJMcacDixpK/9eKdcE0H6u00zAyccmNZG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lddoP/dJMcacDixpK/9eKdcE0H6u00zAyccmNZG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lddoP/dJMcacDixpK/9eKdcE0H6u00zAyccmNZG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlddoP%2FdJMcacDixpK%2F9eKdcE0H6u00zAyccmNZG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;931&quot; height=&quot;620&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블렌딩 방식으로는 지금은 이렇게 사용하지만 원하는 연출이 있을때는 카메라를 전환하기 전에 블렌드 방식을 바꾸는 식으로 사용할 것 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완성본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-28-02-11-25.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q07YH/dJMcadB4thx/cyhzvJ6zrFkWs3h61ZMXBK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q07YH/dJMcadB4thx/cyhzvJ6zrFkWs3h61ZMXBK/img.gif&quot; data-alt=&quot;임의로 카메라 전환 입력기능을 만들었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q07YH/dJMcadB4thx/cyhzvJ6zrFkWs3h61ZMXBK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Q07YH/dJMcadB4thx/cyhzvJ6zrFkWs3h61ZMXBK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1000&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-28-02-11-25.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;임의로 카메라 전환 입력기능을 만들었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>GameDev/Step of Love</category>
      <author>SMNNMN</author>
      <guid isPermaLink="true">https://smnnmn.tistory.com/77</guid>
      <comments>https://smnnmn.tistory.com/77#entry77comment</comments>
      <pubDate>Thu, 28 May 2026 02:15:13 +0900</pubDate>
    </item>
    <item>
      <title>Step of Love 달리기 시스템</title>
      <link>https://smnnmn.tistory.com/76</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번엔 달리기 시스템을 만들 것이다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEivuJ/dJMcaiwHvPj/qKJ41qWM7pgVjDQMrz1RT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEivuJ/dJMcaiwHvPj/qKJ41qWM7pgVjDQMrz1RT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEivuJ/dJMcaiwHvPj/qKJ41qWM7pgVjDQMrz1RT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEivuJ%2FdJMcaiwHvPj%2FqKJ41qWM7pgVjDQMrz1RT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;434&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달리기 시스템은 플레이어가 마우스 좌클릭을 했을 때, 발걸음 속도가 빨라지고, 다시 뗐을때 원래 속도로 돌아오게끔 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;InputController&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 좌클릭을 클릭했을 때와 뗐을 때 입력을 만들어 줘야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779880683747&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using UnityEngine;

public class InputController : Singleton&amp;lt;InputController&amp;gt;
{
    [SerializeField] Vector2 mousePos; // 마우스포인터 위치
    public Vector2 MousePos =&amp;gt; mousePos;

    // 마우스 좌클릭 이벤트
    public event Action MouseLeftDown;
    public event Action MouseLeftUp;

    protected override void Awake()
    {
        base.Awake();
    }
    // Update is called once per frame
    void Update()
    {
        Mouse_Pos();
        Mouse_L();
    }
    // 마우스 좌클릭
    void Mouse_L()
    {
        if (Input.GetMouseButtonDown(0))
        {
            MouseLeftDown?.Invoke();
        }
        else if (Input.GetMouseButtonUp(0))
        {
            MouseLeftUp?.Invoke();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Update에서 if문으로 bool 값을 사용하여 현재 클릭상태인지 아닌지를 판단하는 코드를 사용하려 했지만 Event Driven 아키텍처에 맞게, 눌렀을 때와, 뗏을 때 각각 이벤트를 호출하는 식으로 만들어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 좌클릭을 클릭하면 MouseLeftDown 델리게이트를 호출하고, 좌클릭을 떼면 MouseLeftUp 델리게이트를 호출한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FPMovement&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력에 따라 발걸음을 제어하는 행동을 구현할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1779880452828&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;

public class FPMovement : MonoBehaviour
{
    [SerializeField] InputController input_C; // 입력 컨트롤러
    private NavMeshAgent agent; // 발자국 Nav
    [SerializeField] float maxSpeed; // 최대 속도
    [SerializeField] float walkSpeed; // 걸음 속도

    private void Awake()
    {
        footPrintRb = footPrint.GetComponent&amp;lt;Rigidbody2D&amp;gt;();
        agent = footPrint.GetComponent&amp;lt;NavMeshAgent&amp;gt;();
    }
    void OnEnable()
    {
        InputController.Instance.MouseLeftDown += SpeedIncrease;
        InputController.Instance.MouseLeftUp += SpeedDecrease;

    }
    private void OnDisable()
    {
        InputController.Instance.MouseLeftDown -= SpeedIncrease;
        InputController.Instance.MouseLeftUp -= SpeedDecrease;
    }
    // 발자국 움직임
    void FootPrintMove()
    {
        agent.SetDestination(input_C.MousePos);
    }
    void SpeedIncrease()
    {
        agent.speed = 7;
        walkSpeed = 0.3f;
    }
    void SpeedDecrease()
    {
        agent.speed = 3.5f;
        walkSpeed = 0.5f;
    }
    ~~~~~~~~~~~~~~~~~생략~~~~~~~~~~~~
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 간단하게, 속도가 빨라지는 함수와 느려지는 코드를 만들고 각각의 이벤트에 구독하여 사용해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;agent, 발걸음 속력이 변화하고, 이에 맞게, 발걸음 빠르기가 변화한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Script Execution Order&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위와 같이 사용했을 때, Awake와 OnEnable의 실행순서가 꼬여서 OnEnable에서 InputController를 가져오지 못하는 현상이 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTYvIN/dJMcaiXH3n6/bN4fL9DPfwFQmmDEjI5O81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTYvIN/dJMcaiXH3n6/bN4fL9DPfwFQmmDEjI5O81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTYvIN/dJMcaiXH3n6/bN4fL9DPfwFQmmDEjI5O81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTYvIN%2FdJMcaiXH3n6%2FbN4fL9DPfwFQmmDEjI5O81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;967&quot; height=&quot;494&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 Edit - Project Settings에서 Script Execution Order에 InputController를 추가하고, Default Time보다 더 빠르게 실행되게끔 잡아준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완성본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-20-28-47.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MOgbq/dJMcabj5SVS/W1Yei1telogs1fMCNjeWcK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MOgbq/dJMcabj5SVS/W1Yei1telogs1fMCNjeWcK/img.gif&quot; data-alt=&quot;마우스를 클릭하면 FootPrint의 속력이 빨라져, 발걸음의 속력이 빨라지고 이에 맞춰 발걸음 빠르기가 빨라진다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MOgbq/dJMcabj5SVS/W1Yei1telogs1fMCNjeWcK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/MOgbq/dJMcabj5SVS/W1Yei1telogs1fMCNjeWcK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1000&quot; data-filename=&quot;Step-of-Love-Prototype-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-20-28-47.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;마우스를 클릭하면 FootPrint의 속력이 빨라져, 발걸음의 속력이 빨라지고 이에 맞춰 발걸음 빠르기가 빨라진다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>GameDev/Step of Love</category>
      <author>SMNNMN</author>
      <guid isPermaLink="true">https://smnnmn.tistory.com/76</guid>
      <comments>https://smnnmn.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 27 May 2026 20:33:29 +0900</pubDate>
    </item>
    <item>
      <title>Step of Love 이벤트 상호작용</title>
      <link>https://smnnmn.tistory.com/75</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번엔 이벤트 상호작용을 만들 것이다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bafr7v/dJMcaak6IGX/JaPxhWhaE1g6IyrJbOF0c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bafr7v/dJMcaak6IGX/JaPxhWhaE1g6IyrJbOF0c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bafr7v/dJMcaak6IGX/JaPxhWhaE1g6IyrJbOF0c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbafr7v%2FdJMcaak6IGX%2FJaPxhWhaE1g6IyrJbOF0c0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;502&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발자국이 특정 이벤트 존에 도달하게 되면 이벤트가 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 만들어본 이벤트는 애니메이션 이벤트로 만들어 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들면서 배운 Timeline을 사용하여 만들어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EventZone&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트의 트리거 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779792753358&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

public class EventZone : MonoBehaviour
{
    private PlayableDirector myDirector;

    private void Awake()
    {
        myDirector = GetComponent&amp;lt;PlayableDirector&amp;gt;();
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        FootPrint footPrint = collision.GetComponent&amp;lt;FootPrint&amp;gt;();

        if (footPrint != null)
        {
            Debug.Log(&quot;Foot in Event Area&quot;);
            EventManager.Instance.StartEvent(myDirector);
        }
    }
    // 자신의 이벤트 종료
    public void MyEventFinish()
    {
        EventManager.Instance.FinishEvent();
        Destroy(gameObject);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발자국이라는 컴포넌트를 가진 오브젝트와 부딧혔을 때, 자신의 타임라인 이벤트를 이벤트 매니저에 넘겨준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyEventFinish함수에서 이벤트 매니저에게 이벤트의 종료를 알리고, 삭제한다. (1회성 이벤트)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FootPrint&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌 확인을 위해 넣은 스크립트&lt;/p&gt;
&lt;pre id=&quot;code_1779792919782&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class FootPrint : MonoBehaviour
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽,오른쪽의 발자국에 넣어준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GameManager&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임의 여러 상태를 관리하는 게임매니저를 미리 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1779793013909&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class GameManager : Singleton&amp;lt;GameManager&amp;gt;
{
    // 이벤트 상태
    public enum EventState
    {
        NORMAL,
        EVENT,
    }
    public EventState eventState { get; private set; } = EventState.NORMAL;

    protected override void Awake()
    {
        base.Awake();
    }
    public void SetEventState(EventState state)
    {
        eventState = state;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 이벤트상태인지 아닌지만 확인하기 위해 EventState만 만들어 준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(싱글톤은 나중에 설명)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EventManager&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트를 관리하는 매니저 컴포넌트&lt;/p&gt;
&lt;pre id=&quot;code_1779793106486&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
public class EventManager : Singleton&amp;lt;EventManager&amp;gt;
{
    
    protected override void Awake()
    {
        base.Awake();
        // director = GetComponent&amp;lt;PlayableDirector&amp;gt;();
    }

    // 이벤트 시작
    public void StartEvent(PlayableDirector director)
    {
        GameManager.Instance.SetEventState(GameManager.EventState.EVENT);
        director.Play();
    }

    // 이벤트 종료
    public void FinishEvent()
    {
        GameManager.Instance.SetEventState(GameManager.EventState.NORMAL);
        Debug.Log(&quot;이벤트 끝&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타임라인을 실행하기 위해 자신의 PlayableDirector를 가져와 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StartEvent에서 이벤트를 실행한다. 먼저 게임의 상태를 이벤트중으로 변경해주고, 받아온 타임라인을 실행할 타임라인에 넣어준 이후 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FinishEvent에서 게임의 상태를 다시 기본적인 상태로 바꿔준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타임라인 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 타임라인을 생성하거나, 타임라인을 생성할 오브젝트를 클릭후 Create를 눌러준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/35vbr/dJMcafUirwS/pOXgFW7cOeJo8bljqshhCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/35vbr/dJMcafUirwS/pOXgFW7cOeJo8bljqshhCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/35vbr/dJMcafUirwS/pOXgFW7cOeJo8bljqshhCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F35vbr%2FdJMcafUirwS%2FpOXgFW7cOeJo8bljqshhCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;658&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이후 Event Area 오브젝트를 만들어주고, Playable Director를 넣어준다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Playable에는 생성한 타임라인을 넣어주고, 활성상태 재생은 해제한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwfFel/dJMcacpJFph/haePhMd8KxKLPDTy48TFVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwfFel/dJMcacpJFph/haePhMd8KxKLPDTy48TFVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwfFel/dJMcacpJFph/haePhMd8KxKLPDTy48TFVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwfFel%2FdJMcacpJFph%2FhaePhMd8KxKLPDTy48TFVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;954&quot; height=&quot;651&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 타임라인에서 여러 오브젝트를 변형해가며 애니메니션을 만들어준다. (Animation Track)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때, 이동하는 오브젝트의 경우, Animator 컴포넌트가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 끝이 날때는 Signal Track으로 종료를 알린다.&amp;nbsp; 때문에 Event Area에는 Singal Receiver라는 컴포넌트가 필요한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애니메이션의 특수환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 게임에서 발자국이 끊기면서 애니메이션이 되게끔, 노드사이의 자연스러운 이동을 없앨 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽 탄젠트의 일정을 눌러 부드러운 움직임을 제거한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK1Xqi/dJMcah5EY2U/hzBuBWAoEYUx73EeO9tJ2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK1Xqi/dJMcah5EY2U/hzBuBWAoEYUx73EeO9tJ2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK1Xqi/dJMcah5EY2U/hzBuBWAoEYUx73EeO9tJ2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK1Xqi%2FdJMcah5EY2U%2FhzBuBWAoEYUx73EeO9tJ2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1372&quot; height=&quot;541&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/Packages/com.unity.timeline@1.8/manual/curves-tangents.html&quot;&gt;https://docs.unity3d.com/Packages/com.unity.timeline@1.8/manual/curves-tangents.html&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-SampleScene-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-00-32-53.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhH69Y/dJMcacQLhA6/gEwMYEJuTHWmK40fWgLN11/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhH69Y/dJMcacQLhA6/gEwMYEJuTHWmK40fWgLN11/img.gif&quot; data-alt=&quot;빨간 발자국은 선형, 파란 발자국은 일정으로 애니메이션 무브가 되어있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhH69Y/dJMcacQLhA6/gEwMYEJuTHWmK40fWgLN11/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bhH69Y/dJMcacQLhA6/gEwMYEJuTHWmK40fWgLN11/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1000&quot; data-filename=&quot;Step-of-Love-SampleScene-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-00-32-53.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빨간 발자국은 선형, 파란 발자국은 일정으로 애니메이션 무브가 되어있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;완성본&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Step-of-Love-SampleScene-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-00-41-36.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ5h4F/dJMcabEkLN5/c1L0ufi6TqRQVzynOdxcdk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ5h4F/dJMcabEkLN5/c1L0ufi6TqRQVzynOdxcdk/img.gif&quot; data-alt=&quot;이벤트 실행과 끝 상호작용을 만들었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ5h4F/dJMcabEkLN5/c1L0ufi6TqRQVzynOdxcdk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cJ5h4F/dJMcabEkLN5/c1L0ufi6TqRQVzynOdxcdk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1000&quot; data-filename=&quot;Step-of-Love-SampleScene-Windows_-Mac_-Linux-Unity-6.0-_6000.0.67f1_-_DX11_-2026-05-27-00-41-36.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 실행과 끝 상호작용을 만들었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 아키텍처&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qa7Wk/dJMcabddW9w/rH89KrVrrKZq8IqKgNgBrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qa7Wk/dJMcabddW9w/rH89KrVrrKZq8IqKgNgBrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qa7Wk/dJMcabddW9w/rH89KrVrrKZq8IqKgNgBrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqa7Wk%2FdJMcabddW9w%2FrH89KrVrrKZq8IqKgNgBrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;426&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D7HYJ/dJMcad3aJvF/4KWBb0okgY7P7NXyOqR3k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D7HYJ/dJMcad3aJvF/4KWBb0okgY7P7NXyOqR3k0/img.png&quot; data-alt=&quot;이후 이벤트를 사용할 때, 프리팹에 이벤트 내용을 추가해서 사용하면 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D7HYJ/dJMcad3aJvF/4KWBb0okgY7P7NXyOqR3k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD7HYJ%2FdJMcad3aJvF%2F4KWBb0okgY7P7NXyOqR3k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;356&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이후 이벤트를 사용할 때, 프리팹에 이벤트 내용을 추가해서 사용하면 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제네릭 싱글톤&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 게임에서는 제네릭 싱글톤을 만들것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 싱글톤이란, 싱글톤을 사용하는 컴포넌트의 부모 클래스를 제네릭으로 만들어, 일일이 싱글톤을 만들지 않아도, 상속만 받게되면 싱글톤이 완성되는 패턴이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Singleton&lt;/h4&gt;
&lt;pre id=&quot;code_1779810269897&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class Singleton&amp;lt;T&amp;gt; : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    public static T Instance
    {
        get { return instance; }
    }
    protected virtual void Awake()
    {
        if (instance == null)
        {
            instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Singleton이라는 클래스를 만들고, 제네릭을 사용해준다. &amp;lt;T&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭의 T는 아무 변수가 들어갈수 있지만 제약을 걸어주기위해 where이라는 예약어를 사용해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;where T : MonoBehaviour의 경우, 상속받는 컴포넌트는 유니티 컴포넌트(유니티 기능을 사용하는 컴포넌트)로 제약을 걸어준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 T라는 변수로 instance를 만들어 주고, 싱글톤과 똑같이 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 제네릭 싱글톤을 작성하고 나면 싱글톤을 사용하는 다른 클래스에서 더 수월하게 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본적인 사용방법&lt;/h4&gt;
&lt;pre id=&quot;code_1779810604817&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class GameManager : Singleton&amp;lt;GameManager&amp;gt;
{
    protected override void Awake()
    {
        base.Awake();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Singleton을 상속받고 제네릭에는 자신의 클래스를 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;virtual 함수인 Awake를 재정의를 통해 부모클래스의 함수를 사용한다.&lt;/p&gt;</description>
      <category>GameDev/Step of Love</category>
      <author>SMNNMN</author>
      <guid isPermaLink="true">https://smnnmn.tistory.com/75</guid>
      <comments>https://smnnmn.tistory.com/75#entry75comment</comments>
      <pubDate>Tue, 26 May 2026 23:55:52 +0900</pubDate>
    </item>
  </channel>
</rss>