[개발일지] 22. 데이터 로드 & 반영 로직 수정

2025. 1. 21. 22:02·유니티 프로젝트/점토게임
목차
  1. 0. 들어가기 전에
  2. 1. 스크립트
  3. 1.1 DataManager 스크립트
  4. 1.2 DataManager 스크립트 변경 사항 설명
  5. 1.3 GameManager 스크립트
  6. 1.4 GameManager 스크립트 변경 사항 설명
  7. 1.5 PoolManager 스크립트
  8. 1.6 PoolManager 스크립트 변경 사항 설명
  9. 2. 결과물

0. 들어가기 전에

이번에는 데이터를 로그하고 반영하는 로직을 수정했다. DataManager 가 준비될 때까지 기다렸다가 후에 가져온 데이터를 반영하도록 하기 위함이다.

 

 


 

 

1. 스크립트

이번에 새로 만든 스크립트는 따로 없고 DataManager, GameManager, PoolManager 를 수정했다.

 

1.1 DataManager 스크립트

using System;
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;
    public bool isInitialized = false; // 데이터 초기화 완료 여부


    [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;

        // 데이터를 비동기로 불러오기 시작
        StartCoroutine(LoadDataAsync());

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

    private IEnumerator LoadDataAsync()
    {
        // 이때 데이터 로딩 화면 띄우면 될 것 같은디..
        Debug.Log("데이터 로드 시작...");
        LoadGameData(); // 게임 데이터 가져와용
        yield return new WaitForSeconds(1f);
        isInitialized = true; // 초기화 완료 표시
    }

    // 불러오기
    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("저장완료");
    }
}

 

1.2 DataManager 스크립트 변경 사항 설명

더보기

1. 변수

다음과 같은 변수가 추가되었다. DataManager 에서 데이터 초기화가 완료 되면 true 로 값이 바뀐다.

public bool isInitialized = false; // 데이터 초기화 완료 여부

 

 

2. Awake()

기존에 데이터를 불러오는 메서드를 호출하는 부분이 StartCoroutine(LoadDataAsync()); 로 대체되었다.

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;

    // 데이터를 비동기로 불러오기 시작
    StartCoroutine(LoadDataAsync());

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

 

 

3. LoadDataAsync()

코루틴을 새로 만들었다. 데이터를 가져오는 시간동안 기다리기 위해서 이용한다.

 

LoadGameData() 메서드를 호출한 후 1초 동안 기다리도록 했는데 지금은 임의로 설정한 값이라 다음에 변경할 가능성이 크다.

 

1초가 지나면 isInitialized 값을 true 로 바꿔서 데이터를 성공적으로 로드했음을 알리도록 했다.

private IEnumerator LoadDataAsync()
{
    // 이때 데이터 로딩 화면 띄우면 될 것 같은디..
    Debug.Log("데이터 로드 시작...");
    LoadGameData(); // 게임 데이터 가져와용
    yield return new WaitForSeconds(1f);
    isInitialized = true; // 초기화 완료 표시
}

 

1.3 GameManager 스크립트

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.UI;
using static UnityEditor.Experimental.GraphView.Port;

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;

    // Light & UI Control
    public delegate void SetLightHandler(bool flag);
    public event SetLightHandler OnSetLightHandler; // 여기에 빛 관리하는 메서드 연결해놓을 것(점토의 드래그 시작되면 이 델리게이트에 연결된 메서드를 호출하도록..)


    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); // 비활성화
        }    
    }

    private IEnumerator Start()
    {
        // DataManager 초기화 완료 기다리기
        while (!DataManager.instance.isInitialized)
        {
            yield return null;
        }

        Debug.Log("음 이제 DataManager 이용할 수 있어용~");
        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); // 게임 종료 메서드 연결
        

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

        LoadDataSet(); // 데이터 반영
        //GetLove(0);
        //GetGold(1000);
    }


    private void LoadDataSet()
    {
        // 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
        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;

            SetGoldLove(); // 로드한 데이터 반영해서 데이터 UI 업데이트..
            SetUpgradePanel(); // 델리게이트 호출
        }
    }

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

    public void SetGoldLove()
    {
        // 로드한 데이터에 맞게 데이터 UI 변경할 수 있도록..

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

        updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("gold", gold, gold));
        updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("love", love, love));
    }

    // 재화 얻는 함수
    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);
    }

    public void SetLightAndUI(bool flag)
    {
        OnSetLightHandler?.Invoke(flag); // flag 값을 전달해서 델리게이트에 연결된 메서드 호출 
    }
}

 

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

더보기

1. Start()

기존 Start 메서드를 없애고 새로 만들었다. 코루틴으로 Start 를 만들었다.

 

일단 Start 에 진입하면 DataManager 의 isInitialized 값이 true 가 될 때까지 기다리도록 했다.

 

데이터를 성공적으로 가져왔다면 그제서야 DataManager 를 사용하도록 했다. DataManager 를 사용하는 로직은 기존의 로직과 달라진 부분이 없다.

private IEnumerator Start()
{
    // DataManager 초기화 완료 기다리기
    while (!DataManager.instance.isInitialized)
    {
        yield return null;
    }

    Debug.Log("음 이제 DataManager 이용할 수 있어용~");
    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); // 게임 종료 메서드 연결
    

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

    LoadDataSet(); // 데이터 반영
    //GetLove(0);
    //GetGold(1000);
}

 

 

2. LoadDataSet()

기존 로직을 메서드로 빼서 만들었다.

private void LoadDataSet()
{
    // 저장된 게임 데이터가 있는 경우 데이터 가져와서 반영
    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;

        SetGoldLove(); // 로드한 데이터 반영해서 데이터 UI 업데이트..
        SetUpgradePanel(); // 델리게이트 호출
    }
}

 

 

3. SetGoldLove()

새로 만든 메서드이다. 로드한 데이터에 맞게 데이터 UI 를 변경할 수 있도록 하기 위함이다.

public void SetGoldLove()
{
    // 로드한 데이터에 맞게 데이터 UI 변경할 수 있도록..

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

    updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("gold", gold, gold));
    updateTextUICoroutine = StartCoroutine(gameDataUI.UpdateTextUI("love", love, love));
}

 

1.5 PoolManager 스크립트

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


public class PoolManager : MonoBehaviour
{
    [Header("Clay Info")]
    public GameObject[] clayPrefabs;

    [Header("Clay Pool")]
    public List<GameObject>[] pools;
    public int curClayNum; // 현재 점토 개수

    [Header("Clay Spawn")]
    public GameObject spawnPoint; // 중앙에서 스폰될 것..

    [Header("Clay Sell")]
    public Clay curClay; // 현재 클릭된 대상 클레이
    public bool isSellZone; // 판매 버톤에 들어가있는지 확인용
    public bool isPossibleSell; // 이 값이 true 면 팔아!
    public delegate void SellClayHandler();
    public event SellClayHandler OnSellClay;

    private void Awake()
    {
        pools = new List<GameObject>[clayPrefabs.Length];

        for (int index = 0; index < pools.Length; index++)
            pools[index] = new List<GameObject>();
    }

    private IEnumerator Start()
    {
        // DataManager 초기화 완료 기다리기
        while (!DataManager.instance.isInitialized)
        {
            yield return null;
        }

        // 중복 연결 막기 위해서 미리 뺐다가 다시 더하기
        // - 는 연결되어 있지 않아도 에러 발생시키지 않음.
        DataManager.instance.OnSave -= SetSaveClayData;
        DataManager.instance.OnSave += SetSaveClayData;

        LoadDataSet(); // 불러온 데이터 반영하기
    }

    private void LoadDataSet()
    {
        for (int index = 0; index < DataManager.instance.data.clayInfos.Count; index++)
        {
            ClayDatas tmpData = DataManager.instance.data.clayInfos[index];
            int idx = tmpData.clayIdx;
            int level = tmpData.clayLevel;
            int cnt = tmpData.curTouchCnt;

            GetGameObject(index).GetComponent<Clay>().ResetInfo(level, cnt); // 점토 정보 설정
        }
    }

    public void TrySellClay()
    {
        if (isPossibleSell)
        {
            isPossibleSell = false; // 상태 초기화
            OnSellClay?.Invoke(); // 이벤트 호출
        }
    }

    public GameObject GetGameObject(int index)
    {
        GameObject select = null;

        foreach (GameObject gameObj in pools[index])
        {
            // 풀을 돌면서 놀고 있는 게임 오브젝트 찾기
            if (gameObj.activeSelf == false)
            {
                // 찾으면 반환
                select = gameObj;
                ActivateClay(select);
                break;
            }
        }
        // 놀고 있는 게임 오브젝트가 없다면..
        if (!select)
        {
            // 새로 생성해서 반환..
            // 새로 생성한 게임 오브젝트는 풀 매니저 하위에 놓을 것.. 그래서 부모에 transform 넣어줌..
            select = Instantiate(clayPrefabs[index], transform);
            pools[index].Add(select);
            ActivateClay(select);
        }

        return select;
    }

    public void ActivateClay(GameObject clay)
    {
        clay.SetActive(true);
        clay.transform.position = spawnPoint.transform.position; // 점토의 위치를 중앙으로..
        clay.GetComponent<Clay>().ResetInfo(); // 점토 상태 초기화시키기..
    }

    public void ReturnToPool(GameObject clay)
    {
        clay.SetActive(false);
    }

    // 점토 데이터를 저장할 것(활성돠 된 점토만..)
    public void SetSaveClayData()
    {
        // 프리팹 사이즈만큼 점토 정보 저장할 리스트 만들기
        DataManager.instance.data.clayInfos = new List<ClayDatas>();

        for (int index = 0; index < pools.Length; index++)
        {
            foreach (GameObject gameObj in pools[index])
            {
                // 풀을 돌면서 활성화 되어 있는 애들만 저장
                if (gameObject.activeSelf == true)
                {
                    // 저장할 내용 만들기
                    ClayDatas clayData = new ClayDatas();
                    Clay tmpClay = gameObj.GetComponent<Clay>();
                    clayData.clayIdx = tmpClay.clayIdx;
                    clayData.clayLevel = tmpClay.clayLevel;
                    clayData.curTouchCnt = tmpClay.curTouchCnt;

                    DataManager.instance.data.clayInfos.Add(clayData); // 리스트에 추가하기
                }
            }
        }
    }
}

 

1.6 PoolManager 스크립트 변경 사항 설명

더보기

1. Start()

기존 Start 메서드를 없애고 코루틴으로 새로 만들었다.

 

DataManager 가 초기화 완료할 때까지 기다린 후 DataManager 를 이용하도록 했다. 

private IEnumerator Start()
{
    // DataManager 초기화 완료 기다리기
    while (!DataManager.instance.isInitialized)
    {
        yield return null;
    }

    // 중복 연결 막기 위해서 미리 뺐다가 다시 더하기
    // - 는 연결되어 있지 않아도 에러 발생시키지 않음.
    DataManager.instance.OnSave -= SetSaveClayData;
    DataManager.instance.OnSave += SetSaveClayData;

    LoadDataSet(); // 불러온 데이터 반영하기
}

 

 

2. LoadDataSet()

기존 로직을 메서드로 빼서 만들었다. 가져온 점토 데이터를 반영해서 게임 월드에 점토를 만들도록 하는 메서드이다.

private void LoadDataSet()
{
    for (int index = 0; index < DataManager.instance.data.clayInfos.Count; index++)
    {
        ClayDatas tmpData = DataManager.instance.data.clayInfos[index];
        int idx = tmpData.clayIdx;
        int level = tmpData.clayLevel;
        int cnt = tmpData.curTouchCnt;

        GetGameObject(index).GetComponent<Clay>().ResetInfo(level, cnt); // 점토 정보 설정
    }
}

 

 


 

 

2. 결과물

 

 

 

 

 

 

  1. 0. 들어가기 전에
  2. 1. 스크립트
  3. 1.1 DataManager 스크립트
  4. 1.2 DataManager 스크립트 변경 사항 설명
  5. 1.3 GameManager 스크립트
  6. 1.4 GameManager 스크립트 변경 사항 설명
  7. 1.5 PoolManager 스크립트
  8. 1.6 PoolManager 스크립트 변경 사항 설명
  9. 2. 결과물
'유니티 프로젝트/점토게임' 카테고리의 다른 글
  • [개발일지] 24. 월드 판넬 제작
  • [개발일지] 23. 도감 애니메이션 적용
  • [개발일지] 21. 점토 도감
  • [개발일지] 20. 2D Light 적용
dubu0721
dubu0721
dubu0721 님의 블로그 입니다.
dubu0721 님의 블로그dubu0721 님의 블로그 입니다.
  • dubu0721
    dubu0721 님의 블로그
    dubu0721
  • 전체
    오늘
    어제
    • 분류 전체보기 (350) N
      • 프로그래밍언어론 정리 (5)
      • 컴퓨터네트워크 정리 (5)
      • 알고리즘&자료구조 공부 (64)
        • it 취업을 위한 알고리즘 문제풀이 입문 강의 (60)
        • 학교 알고리즘 수업 (3)
        • 실전프로젝트I (0)
      • 백준 문제 (203) N
        • 이분탐색 (7)
        • 투포인트 (10)
        • 그래프 (11)
        • 그리디 (24)
        • DP (25)
        • BFS (20) N
        • MST (7)
        • KMP (4)
        • Dijkstra (3)
        • Disjoints Set (4)
        • Bellman-Ford (2)
        • 시뮬레이션 (3)
        • 백트래킹 (15)
        • 위상정렬 (5)
        • 자료구조 (25)
        • 기하학 (1)
        • 정렬 (11)
        • 구현 (8)
        • 재귀 (8)
        • 수학 (8)
        • 트리 (1)
      • 유니티 공부 (11)
        • 레트로의 유니티 게임 프로그래밍 에센스 (11)
        • 유니티 스터디 자료 (0)
        • C# 공부 (0)
      • 유니티 프로젝트 (48)
        • 케이크게임 (13)
        • 점토게임 (35)
      • 언리얼 공부 (10)
        • 이득우의 언리얼 프로그래밍 (10)
      • 진로 (1)
      • 논문 읽기 (1)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
dubu0721
[개발일지] 22. 데이터 로드 & 반영 로직 수정

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.