[개발일지] 18. 레벨에 맞게 장난감 소환 & 데이터 적용

2025. 1. 20. 00:00·유니티 프로젝트/점토게임

0. 들어가기 전에

이번엔 미리 만들어놓았던 장난감들을 점토 집을 업그레이드 할 때 소환되도록 했다. 그리고 데이터를 저장하고 다시 시작했을 때 저장해놓은 데이터를 반영해서 장난감들이 그대로 소환되도록 했다.

 

 


 

 

1. 게임 오브젝트

Plant Panel 게임 오브젝트의 Upgrade Panel 컴포넌트 속 Toys 배열에 미리 만들어놓은 장난감 버튼 게임 오브젝트를 넣어놓았다.

 

점토의 집을 업그레이드 할 때마다 각 단계에 맞는 장난감 게임 오브젝트가 나타나도록 하기 위함이다.

 

 


 

 

2. 스크립트

이번에 새로 만든 스크립트는 따로 없고 기존의 스크립트를 수정했다. 수정한 스크립트는 GameManager, UpgradePanel 이다.

 

2.1  GameManager 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    [Header("Game Data")]
    public float love; // 애정
    public float gold; // 골드
    public bool[] unLockedClays; // 점토들의 해금 여부
    public bool[] catchedClays; // 야생에서 잡아왔는지 확인용
    public int clayHouseLevel = 1; // 점토 아파트 레벨
    public int clayClickLevel = 1; // 점토 클릭 레벨
    public int[] clayHouseLoveList; // 업그레이드 비용 
    public int[] clayClickLoveList; // 업그레이드 비용
    public int curPossibleClayNum = 1; // 최대로 키울 수 있는 점토의 개수(1레벨은 1마리, 2레벨은 2마리...)

    [Header("Game Manager")]
    public static GameManager instance; // 싱글톤 이용하기 위함

    [Header("GameDataUI")]
    public GameDataUIController gameDataUI;
    public delegate void SetInfoPanelHandler(string text); // 델리게이트 선언 
    public event SetInfoPanelHandler OnSetInfoPanel;

    [Header("Pool Manager")]
    public PoolManager poolManager;

    [Header("Coroutine")]
    public Coroutine updateTextUICoroutine;

    [Header("Game Exit")]
    public Button gameExitButton;

    [Header("Effect")]
    // 0: 점토 레벨업, 1: 점토 판매, 2: 점토 해금, 3: 업그레이드
    public ParticleSystem[] effectsPrefabs; // 프리팹 넣어놓기
    public ParticleSystem[] effects; // 관리용 변수
    public string[] effectGameObjectNames;

    [Header("Toy Control")]
    public int curToyIdx = -1; // 현재 선택된 장난감
    public RuntimeAnimatorController[] clayToyAnimators; // 가구랑 상호작용하는 애니메이터
    public string[] toyInfo; // 가구를 클릭하면 안내 판넬에 띄울 내용
    public delegate void SetClayHouseLevel(int houseLevel, int clickLevel);
    public SetClayHouseLevel OnSetClayHouseInfo;


    private void Awake()
    {
        // 싱글톤 패턴
        if (instance != null && instance != this)
        {
            // 이미 존재하면 새로 만든거 없애
            Destroy(gameObject);
            return;
        }

        instance = this;
        DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임

        poolManager = GameObject.Find("PoolManager").GetComponent<PoolManager>(); // 풀매니저 찾아서 할당

        for (int i=0; i < effectsPrefabs.Length; i++)
        {
            // 이펙트 생성해서 넣어놓기
            effects[i] = Instantiate(effectsPrefabs[i], GameObject.Find(effectGameObjectNames[i]).transform);
            effects[i].gameObject.SetActive(false); // 비활성화
        }
    }


    void Start()
    {
        gameExitButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Exit Button").GetComponent<Button>();
        gameExitButton.onClick.AddListener(DataManager.instance.SaveGameData); // 게임 데이터 저장 메서드 연결
        gameExitButton.onClick.AddListener(GameExit); // 게임 종료 메서드 연결

        GetLove(0);
        GetGold(1000);

        // 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
        if (DataManager.instance.data.unlockClays != null)
        {
            // 저장된 데이터 반영해서 가져오기
            for (int i = 0; i < unLockedClays.Length; i++)
            {
                unLockedClays[i] = DataManager.instance.data.unlockClays[i];
                catchedClays[i] = DataManager.instance.data.catchClays[i];
            }
        }

        // 저장된 게임 수치 데이터가 있는 경우 데이터 가져와서 반영
        if (DataManager.instance.data.valueDatas != null)
        {
            // 저장된 데이터 반영해서 가져오기
            gold = DataManager.instance.data.valueDatas.gold;
            love = DataManager.instance.data.valueDatas.love;
            clayHouseLevel = DataManager.instance.data.valueDatas.clayHouseLevel;
            clayClickLevel = DataManager.instance.data.valueDatas.clayClickLevel;
            curPossibleClayNum = DataManager.instance.data.valueDatas.curPossibleClayNum;

            SetUpgradePanel(); // 델리게이트 호출
        }

        // 메서드 연결하기
        DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
        DataManager.instance.OnSave += SetSaveData;
    }

    public void SetUpgradePanel()
    {
        Debug.Log("반영이 되고 있나여..?");
        OnSetClayHouseInfo?.Invoke(clayHouseLevel, clayClickLevel); // UpgradePanel 클래스의 SetUpgardePanel() 메서드 호출
    }


    // 재화 얻는 함수
    public void GetGold(float capacity)
    {
        // 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함. 
        if (updateTextUICoroutine != null)
        {
            StopCoroutine(updateTextUICoroutine);
        }

        updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("gold", gold + capacity, gold));
        gold += capacity;

        //PlayerPrefs.SetFloat("Gold", gold); // 데이터 저장
    }

    public void GetLove(float capacity)
    {
        // 이미 코루틴이 종료되지 않은 중에 동일한게 또 들어오면 겹쳐서 반영이 돼서 이상하게 될 수 있으므로 null 인지 판단해야함. 
        if (updateTextUICoroutine != null)
        {
            StopCoroutine(updateTextUICoroutine);
        }

        updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("love", love + capacity, love));
        love += capacity;

        //PlayerPrefs.SetFloat("Love", love); // 데이터 저장
    }

    public void SetSaveData()
    {
        int size = unLockedClays.Length;

        DataManager.instance.data.unlockClays = new List<bool>();
        DataManager.instance.data.catchClays = new List<bool>();

        for (int i = 0; i < size; i++)
        {
            DataManager.instance.data.unlockClays.Add(unLockedClays[i]); // 해금 여부 저장
            DataManager.instance.data.catchClays.Add(catchedClays[i]); // 포획 여부 저장
        }

        // 수치 데이터 저장
        DataManager.instance.data.valueDatas = new ValueDatas();
        DataManager.instance.data.valueDatas.gold = gold;
        DataManager.instance.data.valueDatas.love = love;
        DataManager.instance.data.valueDatas.clayHouseLevel = clayHouseLevel;
        DataManager.instance.data.valueDatas.clayClickLevel = clayClickLevel;
        DataManager.instance.data.valueDatas.curPossibleClayNum = curPossibleClayNum;
    }


    public void GameExit()
    {
        // 게임 종료
        Application.Quit();
    }

    
    public void StartInfoPanel(string text)
    {
        // 연결된 메서드 실행시키기
        OnSetInfoPanel?.Invoke(text);
    }
}

 

2.2  GameManager 스크립트 변경 사항 설명

더보기

1. 변수

델리게이트를 추가했다. 게임을 다시 시작할 때 로드한 데이터를 반영하는 일에 사용하기 위함이다.

public delegate void SetClayHouseLevel(int houseLevel, int clickLevel);
public SetClayHouseLevel OnSetClayHouseInfo;

 

 

2. Start()

SetUpgradePanel() 메서드를 호출하는 부분이 추가되었다. 로드한 데이터를 업그레이트 판넬에 반영하기 위함이다.

void Start()
{
    gameExitButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Exit Button").GetComponent<Button>();
    gameExitButton.onClick.AddListener(DataManager.instance.SaveGameData); // 게임 데이터 저장 메서드 연결
    gameExitButton.onClick.AddListener(GameExit); // 게임 종료 메서드 연결

    GetLove(0);
    GetGold(1000);

    // 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
    if (DataManager.instance.data.unlockClays != null)
    {
        // 저장된 데이터 반영해서 가져오기
        for (int i = 0; i < unLockedClays.Length; i++)
        {
            unLockedClays[i] = DataManager.instance.data.unlockClays[i];
            catchedClays[i] = DataManager.instance.data.catchClays[i];
        }
    }

    // 저장된 게임 수치 데이터가 있는 경우 데이터 가져와서 반영
    if (DataManager.instance.data.valueDatas != null)
    {
        // 저장된 데이터 반영해서 가져오기
        gold = DataManager.instance.data.valueDatas.gold;
        love = DataManager.instance.data.valueDatas.love;
        clayHouseLevel = DataManager.instance.data.valueDatas.clayHouseLevel;
        clayClickLevel = DataManager.instance.data.valueDatas.clayClickLevel;
        curPossibleClayNum = DataManager.instance.data.valueDatas.curPossibleClayNum;

        SetUpgradePanel(); // 델리게이트 호출
    }

    // 메서드 연결하기
    DataManager.instance.OnSave -= SetSaveData; // 중복 방지하기 위해 먼저 빼줌
    DataManager.instance.OnSave += SetSaveData;
}

 

 

3. SetUpgradePanel()

OnSetClayHouseInfo 델리게이트에 연결된 메서드를 호출하는 메서드이다. 이 메서드는 보통 상황에서는 안 쓰고 데이터를 로드할 때만 쓸 것이다.

public void SetUpgradePanel()
{
    OnSetClayHouseInfo?.Invoke(clayHouseLevel, clayClickLevel); // UpgradePanel 클래스의 SetUpgardePanel() 메서드 호출
}

 

2.3 UpgradePanel 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using System.Net;

public class UpgradePanel : MonoBehaviour
{
    [Header("Plant Panel UI")]
    public Text houseSubText; // 업그레이드 내용
    public Text houseLoveText; // 필요한 애정 수치 
    public Button houseUpButton; // 업그레이드 버튼

    public Text clickSubText;
    public Text clickLoveText;
    public Button clickUpButton;

    [Header("Effect")]
    public int effectIdx = 3;

    [Header("Clay Toys")]
    public Button[] toys; // 0: 캣타워, 1: 러그, 2: 블라인더, 3: 조명


    private void Awake()
    {
        GameManager.instance.OnSetClayHouseInfo -= SetUpgardePanel; // 메서드 연결
        GameManager.instance.OnSetClayHouseInfo += SetUpgardePanel; // 메서드 연결
    }


    private void Start()
    {
        SetUpgardePanel(); // 판넬 정보 설정

        // 버튼에 메서드 연결
        houseUpButton.onClick.AddListener(UpgradeHouse);
        clickUpButton.onClick.AddListener(UpgradeClick);
    }

    private void OnEnable()
    {
        // 활성화 될 때 호출되는 함수
        SetUpgardePanel(); // ui 세팅
    }

    // 게임매니저의 델리게이트에 연결할거임
    // 얘는 게임이 처음 시작할 때 호출할 것..
    public void SetUpgardePanel(int houseLevel, int clickLevel)
    {
        // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
        if (GameManager.instance.clayHouseLevel == 5)
        {
            houseLoveText.text = "max";
        }
        else
        {
            houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
            houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
        }

        // 집 레벨에 맞게 장난감 활성화
        if (houseLevel >= 2)
        { 
            for (int i=0; i<=houseLevel-2; i++)
            {
                // 장난감 활성화
                toys[i].gameObject.SetActive(true);
            }
        }


        // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
        if (GameManager.instance.clayClickLevel == 5)
        {
            clickLoveText.text = "max";
        }
        else
        {
            clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
            clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
        }
    }

    // 얘는 게임이 진행되는 중에 언제든지 그냥 이용할 수 있음..
    public void SetUpgardePanel()
    {
        // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
        if (GameManager.instance.clayHouseLevel == 5)
        {
            houseLoveText.text = "max";
        }
        else
        {
            houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
            houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
        }

        // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
        if (GameManager.instance.clayClickLevel == 5)
        {
            clickLoveText.text = "max";
        }
        else
        {
            clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
            clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
        }
    }

    public void UpgradeHouse()
    {
        // 이미 레벨이 최고면 걍 빠져나가도록..
        if (GameManager.instance.clayHouseLevel == 5) {
            // 안내창 띄우기
            GameManager.instance.StartInfoPanel("최대 레벨입니다!");
            return;
        }
        else if (GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] > GameManager.instance.love)
        {
            // 안내창 띄우기
            GameManager.instance.StartInfoPanel("애정이 부족해요 ㅠ_ㅠ");
            return;
        }

        GameManager.instance.GetLove(-GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel]); // 애정 차감
        GameManager.instance.clayHouseLevel++;
        GameManager.instance.curPossibleClayNum++; // 키울 수 있는 점토 수 +1 
        toys[GameManager.instance.clayHouseLevel - 2].gameObject.SetActive(true); // 장난감 게임 오브젝트 활성화
        if (GameManager.instance.clayHouseLevel == 5)
        {
            houseLoveText.text = "max";
        }
        PlayEffect(effectIdx); // 이펙트 소환
        SetUpgardePanel();
    }

    public void UpgradeClick()
    {
        // 이미 레벨이 최고면 걍 빠져나가도록..
        if (GameManager.instance.clayClickLevel == 5)
        {
            GameManager.instance.StartInfoPanel("최대 레벨입니다!");
            return;
        }
        else if (GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] > GameManager.instance.love)
        {
            // 안내창 띄우기
            GameManager.instance.StartInfoPanel("애정이 부족해요 ㅠ_ㅠ");
            return;
        }

        GameManager.instance.GetLove(-GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel]); // 애정 차감
        GameManager.instance.clayClickLevel++;
        if (GameManager.instance.clayClickLevel == 5)
        {
            clickLoveText.text = "max";
        }
        PlayEffect(effectIdx); // 이펙트 소환
        SetUpgardePanel();
    }

    public void PlayEffect(int idx)
    {
        if (GameManager.instance.effects[idx].gameObject.activeSelf == false)
            GameManager.instance.effects[idx].gameObject.SetActive(true); // 활성화하기
        GameManager.instance.effects[idx].Play();
    }

    public void ClosePanel()
    {
        gameObject.SetActive(false); // 활성화 끄기..
    }
}

 

2.4 UpgradePanel 스크립트 변경 사항 설명

더보기

1. 변수

버튼 게임 오브젝트(장난감) 을 넣어놓을 변수이다.

[Header("Clay Toys")]
public Button[] toys; // 0: 캣타워, 1: 러그, 2: 블라인더, 3: 조명

 

 

2. Awake()

OnSetClayHouseInfo 델리게이트에 SetUpgradePanel 메서드를 연결하는 로직이 들어있다. 중복 연결을 막기 위해서 메서드를 먼저 뺀 후 다시 더해준다.

 

만약 해당 메서드가 연결되어 있지 않을 때에도 메서드를 빼는게 문제되지 않는다고 한다.

private void Awake()
{
    GameManager.instance.OnSetClayHouseInfo -= SetUpgardePanel; // 메서드 연결
    GameManager.instance.OnSetClayHouseInfo += SetUpgardePanel; // 메서드 연결
}

 

 

3. SetUpgardePanel(int houseLevel, int clickLevel)

로드한 데이터를 반영할 때 호출하는 메서드이다.

 

점토 집 레벨에 맞게 업그레이드 판넬의 정보도 설정해주고, 장난감이 소환되도록 했다.

 

점토 클릭 레벨에 맞게 업그레이드 판넬의 정보도 설정해줬다.

// 게임매니저의 델리게이트에 연결할거임
// 얘는 게임이 처음 시작할 때 호출할 것..
public void SetUpgardePanel(int houseLevel, int clickLevel)
{
    // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
    if (houseLevel == 5)
    {
        houseLoveText.text = "max";
    }
    else
    {
        houseSubText.text = "점토 수용량 " + (houseLevel + 1);
        houseLoveText.text = GameManager.instance.clayHouseLoveList[houseLevel] + "";
    }

    // 집 레벨에 맞게 장난감 활성화
    if (houseLevel >= 2)
    { 
        for (int i=0; i<=houseLevel-2; i++)
        {
            // 장난감 활성화
            toys[i].gameObject.SetActive(true);
        }
    }


    // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
    if (clickLevel == 5)
    {
        clickLoveText.text = "max";
    }
    else
    {
        clickSubText.text = "클릭 생산량 x " + (clickLevel + 1);
        clickLoveText.text = GameManager.instance.clayClickLoveList[clickLevel] + "";
    }
}

 

 

4. SetUpgardePanel()

얘는 업그레이드 할 때마다 호출되는 메서드이다. 위의 SetUpgradePanel 이랑 논리적으로 다른 부분은 딱히 없다.

// 얘는 업그레이드 할 때 호출되는 메서드..
public void SetUpgardePanel()
{
    // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
    if (GameManager.instance.clayHouseLevel == 5)
    {
        houseLoveText.text = "max";
    }
    else
    {
        houseSubText.text = "점토 수용량 " + (GameManager.instance.clayHouseLevel + 1);
        houseLoveText.text = GameManager.instance.clayHouseLoveList[GameManager.instance.clayHouseLevel] + "";
    }

    // 만렙이면 max 로, 아니면 레벨 반영해서 텍스트 설정
    if (GameManager.instance.clayClickLevel == 5)
    {
        clickLoveText.text = "max";
    }
    else
    {
        clickSubText.text = "클릭 생산량 x " + (GameManager.instance.clayClickLevel + 1);
        clickLoveText.text = GameManager.instance.clayClickLoveList[GameManager.instance.clayClickLevel] + "";
    }
}

 

2.5 DataManager 스크립트

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class DataManager : MonoBehaviour
{
    [Header("Data Manager")]
    public static DataManager instance;

    [Header("Save Datas")]
    string GameDataFileName = "GameData.json";
    public Datas data = new Datas(); // 저장용 클래스 변수
    public delegate void SaveDataHandler(); // 데이터 저장 관리
    public event SaveDataHandler OnSave;

    [Header("Save Button")]
    public Button saveButton; // 얘는 게임 시작할 때랑, 다른 씬에서 돌아올 때마다 값 할당해줘야함.


    private void Awake()
    {
        // 싱글톤 이용
        if (instance != null && instance != this)
        {
            // 만약 이미 존재하면 그냥 없애
            Destroy(gameObject);
            return;
        }

        instance = this;
        DontDestroyOnLoad(gameObject); // 얘는 다른 씬으로 전환되어도 안 없앨 거임

        // 일단 맨 처음엔 null 로 설정..
        data.unlockClays = null;
        data.catchClays = null;
        data.valueDatas = null;
        LoadGameData(); // 게임 데이터 가져오기
    }

    private void Start()
    {
        saveButton = GameObject.Find("OptionPanelParent").transform.Find("Option Panel").transform.Find("Image").transform.Find("Save Button").GetComponent<Button>(); // 버튼 찾아서 할당
        saveButton.onClick.AddListener(SaveGameData); // 데이터 저장 메서드 연결
    }

    // 불러오기
    public void LoadGameData()
    {
        string filePath = Application.persistentDataPath + "/" + GameDataFileName;

        // 저장된 게임이 있다면
        if (File.Exists(filePath))
        {
            // 저장된 파일 읽어오고 Json 을 클래스 형식으로 전환해서 할당
            string FromJsonData = File.ReadAllText(filePath);
            print(FromJsonData);

            data = JsonUtility.FromJson<Datas>(FromJsonData);
            print("불러오기 완료");
        }
    }

    // 저장하기
    public void SaveGameData()
    {
        OnSave?.Invoke(); // OnSave 에 연결된 메서드 모두 실행

        // 클래스를 Json 형식으로 변환(true: 가독성 좋게 작성)
        string ToJsonData = JsonUtility.ToJson(data, true);
        Debug.Log(ToJsonData + "저장할건디용");

        string filePath = Application.persistentDataPath + "/" + GameDataFileName;

        // 이미 저장된 파일이 있다면 덮어쓰고, 없다면 새로 만들어서 저장
        File.WriteAllText(filePath, ToJsonData);

        // 올바르게 저장됐는지 확인
        print("저장완료");
    }
}

 

 

 


 

 

3. 결과물

 

점토 집을 업그레이드 하면 장난감이 생겨나는 걸 볼 수 있다. 데이터를 저장하고 다시 플레이 버튼을 누르면 저장된 데이터를 반영하여 장난감이 그대로 소환된 모습을 볼 수 있다.

 

 

'유니티 프로젝트/점토게임' 카테고리의 다른 글
  • [개발일지] 20. 2D Light 적용
  • [개발일지] 19. 블라인더 장난감 소환시 배경 변경
  • [개발일지] 17. 가구 추가 & 애니메이션 적용
  • [개발일지] 16. ClayMove 스크립트 수정
dubu0721
dubu0721
dubu0721 님의 블로그 입니다.
  • dubu0721
    dubu0721 님의 블로그
    dubu0721
  • 전체
    오늘
    어제
    • 분류 전체보기 (335)
      • 프로그래밍언어론 정리 (0)
      • 컴퓨터네트워크 정리 (5)
      • 알고리즘&자료구조 공부 (64)
        • it 취업을 위한 알고리즘 문제풀이 입문 강의 (60)
        • 학교 알고리즘 수업 (3)
        • 실전프로젝트I (0)
      • 백준 문제 (193)
        • 이분탐색 (7)
        • 투포인트 (10)
        • 그래프 (7)
        • 그리디 (24)
        • DP (25)
        • BFS (15)
        • MST (7)
        • KMP (4)
        • Dijkstra (3)
        • Disjoints Set (4)
        • Bellman-Ford (2)
        • 시뮬레이션 (3)
        • 백트래킹 (15)
        • 위상정렬 (5)
        • 자료구조 (25)
        • 기하학 (1)
        • 정렬 (11)
        • 구현 (8)
        • 재귀 (8)
        • 수학 (8)
      • 유니티 공부 (11)
        • 레트로의 유니티 게임 프로그래밍 에센스 (11)
        • 유니티 스터디 자료 (0)
        • C# 공부 (0)
      • 유니티 프로젝트 (48)
        • 케이크게임 (13)
        • 점토게임 (35)
      • 언리얼 공부 (10)
        • 이득우의 언리얼 프로그래밍 (10)
      • 진로 (1)
      • 논문 읽기 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    큐
    투포인터
    유니티 공부 정리
    해시
    백트래킹
    C#
    시뮬레이션
    바킹독
    언리얼
    유니티 프로젝트
    골드메탈
    정렬
    이벤트 트리거
    그래프
    이분탐색
    우선순위큐
    BFS
    백준
    재귀
    그리디
    유니티
    dp
    수학
    스택
    이득우
    오블완
    맵
    티스토리챌린지
    레트로의 유니티 프로그래밍
    자료구조
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
dubu0721
[개발일지] 18. 레벨에 맞게 장난감 소환 & 데이터 적용
상단으로

티스토리툴바