말감로그

[Unity] Valley - 인벤토리 저장/로드 (새로하기 &이어하기) 본문

TIL

[Unity] Valley - 인벤토리 저장/로드 (새로하기 &이어하기)

habbn 2024. 11. 13. 02:56
728x90

타이틀 씬에서 새로하기, 이어하기 버튼을 클릭 했을 때 각 저장된 인벤토리 데이터에 맞게 로드하고 인 게임씬으로 전환되도록 할 것이다.

 

 

우선 새로하기 버튼은 말 그대로 저장된 인벤토리 데이터가 없거나, 있는데 새로하려고 할 때 인벤토리 데이터를 지우고, 인벤토리를 초기화한다.

이어하기 버튼은 저장된 데이터가 있을 때, 그 데이터를 불러와 게임을 이어서 플레이할수 있도록 한다.

 

 

HasSavedInventory() 함수를 통해 해당 경로에 각 인벤토리 텍스트 파일이 존재하는지 확인해서 저장된 인벤토리 여부를 리턴한다.

//InventorySave.cs
public bool HasSavedInventory()
{
    string backpackPath = Application.persistentDataPath + "/Backpack.txt";
    string toolbarPath = Application.persistentDataPath + "/Toolbar.txt";

    return File.Exists(backpackPath) || File.Exists(toolbarPath);
}

 

그리고 저장된 인벤토리 파일을 지우고 인벤토리 슬롯을 초기화하는 함수도 생성했다.

//InventorySave.cs
public void DeleteSavedFiles()
{
    string[] filePaths = Directory.GetFiles(Application.persistentDataPath, "*.txt");
    foreach (string filePath in filePaths)
    {
        File.Delete(filePath);
    }
    Debug.Log("All inventory files deleted at game start.");
}

 

InventoryManager.cs 에 inventoryByName 딕셔너리에 있는 인벤토리들을 찾아 해당 슬롯을 Clear하게 했다.

//InventoryManger.cs
public void ClearInventory()
{
    foreach (var inventory in inventoryByName.Values)
    {
        inventory.Clear();
    }
}

 


이제 타이틀 씬에서 각 버튼을 클릭했을 때 저장된 인벤토리 파일의 여부에 따라  isNewGame의 값을 변경해주고 

인게임 씬으로 전환되도록 했다.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class TitleUIManager : MonoBehaviour
{
          : 
          :

    void Start()
    {
        GameObject inventoryObj = new GameObject("InventorySave");
        InventorySave inventorySave = inventoryObj.AddComponent<InventorySave>();

        if (!inventorySave.HasSavedInventory())
        {
            isNewGame = true;
            continueGameBtn.gameObject.SetActive(false);
            newGameBtn.onClick.AddListener(() =>
            {
                SceneManager.LoadScene("InGameScene");
            });
        }
        else
        {
            isNewGame = false;
            continueGameBtn.gameObject.SetActive(true);
            continueGameBtn.onClick.AddListener(() =>
            {
                SceneManager.LoadScene("InGameScene");
            });
        }

        exitGameBtn.onClick.AddListener(()=>
        {
            Application.Quit();
        });
    }
}

 

 

그리고 InGameScene으로 전환될 때 GameManager.cs에서 isNewGame의 값에 따라서 저장된 데이터를 로드할 지, 아니면 새로 초기화를 할 지를 정해주었다.

 

SceneManager.sceneLoad +=  OnSceneLoaded;

OnSceneLoaded 메서드를 씬이 로드될 때 호출되는 이벤트에 등록하여, 씬이 로드될 때마다 자동으로 메서드가 실행되게 한다.

public class GameManager : MonoBehaviour
{ 
		 :
         
   void Awake()
    {
    	 :
         
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        if (scene.name == "InGameScene")
        {
            if (TitleUIManager.instance.isNewGame)
            {
                player.inventoryManager.ClearInventory();
                inventorySave.DeleteSavedFiles();
            }
            else
            {
                player.inventoryManager.LoadInventory();
            }
        }
    }

    void OnDestroy()
    {
        SceneManager.sceneLoaded -= OnSceneLoaded;
    }
}

 

 

이미 저장된 데이터가 있는데 새로하기 버튼을 클릭 했을 때의 팝업을 생성하고, 아니요를 누르면 팝업이 꺼지고, 네를 누르면 저장된 데이터를 지워버리는 기능을 추가했다.

 

그러나 팝업이 떴을 때 아니요 버튼을 클릭하고 네 버튼을 클릭하면 MissingReferenceException 에러가 엄청 뜨면서 참조된 객체가 삭제되는 현상이 발생했다.

 

확인해보니 버튼 리스너 증복 등록 때문이라고 한다. (gpt가)

원인 분석

  1. 버튼 리스너 중복 등록:
    • "네" 버튼을 클릭했을 때, 이미 리스너가 등록된 버튼에 대해 다시 리스너를 추가하려고 하면, 이전 리스너들이 계속 남아 있을 수 있습니다. 이 경우에도 MissingReferenceException이 발생할 수 있습니다.

해결 방법

1. 버튼에 리스너 중복 등록 방지

yesBtn과 noBtn에 클릭 리스너를 여러 번 추가하는 문제가 있을 수 있습니다. 예를 들어, "네" 버튼을 클릭할 때마다 리스너가 계속 추가되면, 버튼을 다시 클릭할 때마다 여러 번 실행되거나, 잘못된 객체에 접근하게 될 수 있습니다.

이를 방지하기 위해, 버튼에 리스너를 추가하기 전에 기존의 리스너를 제거하는 것이 필요합니다.

 

    newGameBtn.onClick.AddListener(() =>
    {
        // 기존 리스너 제거 (중복 등록 방지)
        yesBtn.onClick.RemoveAllListeners();
        noBtn.onClick.RemoveAllListeners();

        panel.SetActive(true);

        yesBtn.onClick.AddListener(() => { isNewGame = true; SceneManager.LoadScene("InGameScene"); });
        noBtn.onClick.AddListener(() => { panel.SetActive(false); });
    });

 

그래서 중복 등록 방지를 위해 RemoveAllListeners() 함수를 통해 기존 리스너를 제거해주니 에러가 발생하지 않았다.

 

 

 

저장된 인벤토리 데이터 파일이 있기 때문에 이어하기 버튼이 활성화되었고, 이어하기 버튼을 누르면 각 데이터를 토대로 로드되는 걸 볼 수 있다.

 

 

데이터가 있는 상태에서 새로하기 버튼을 누를 때

 

네 버튼을 누르면 파일도 사라지고, 인벤토리도 초기화 된 것을 볼 수 있다. 

728x90