Merge pull request #12 from Degulleo/DEG-23-세이브-로드-로직-구현

Deg 23 세이브 로드 로직 구현
This commit is contained in:
99jamin56 2025-04-23 11:50:56 +09:00 committed by GitHub
commit 39abb506da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 418 additions and 0 deletions

8
Assets/KJM.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dbfd37452c39a544895c4f842901be82
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/KJM/KJM.unity (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fce4c0b08a429b64695c3c0aed06df0f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/KJM/KJM_Test.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cf313a0298660aa4db16ced680557810
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using System;
using UnityEngine;
// 던전 관련 저장 데이터
[Serializable]
public class DungeonSave
{
// 강화 수치
public int attackLevel;
public int attackSpeedLevel;
public int heartLevel;
public int moveSpeedLevel;
public int evasionTimeLevel;
// 현재 진행 중인 스테이지
public int stageLevel;
}
// 일상(자취방) 관련 저장 데이터
[Serializable]
public class HomeSave
{
// 일상 시간
public float time;
public int day;
// 체력 및 평판 수치
public float health;
public float reputation;
//이벤트
public bool isEvent;
public int mealCount;
public int houseworkCount;
}
// 게임 전체 저장 구조
[Serializable]
public class Save
{
public HomeSave homeSave;
public DungeonSave dungeonSave;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 883ac325bc606014fa78662a7b7ec954
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,188 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using CI.QuickSave;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SaveManager : Singleton<SaveManager>
{
private const string SaveFolder = "QuickSave";
private string MainSaveFilePath => GetSavePath("Save_Main");
private string BackupSaveFilePath => GetSavePath("Save_Backup");
private Save mainSave;
private Save backupSave;
void Start()
{
Load(); //저장된 메인,백업 세이브를 로드
}
public void Save()
{
if(JsonUtility.ToJson(mainSave) == JsonUtility.ToJson(StatManager.instance.ToSaveData())) //같은 상태는 저장되지 않음. 백업 덮어쓰기 방지.
return;
EnsureSaveExists();
backupSave = LoadMain(); //메인 세이브를 백업 세이브에 로드
SaveBackup(); //백업 세이브 저장
UpdateSaveInfo(); //세이브를 현재 정보로 업데이트
SaveMain(); //메인 세이브 저장
Debug.Log("세이브 되었습니다.");
}
public void Load()
{
EnsureSaveExists();
mainSave = LoadMain();
backupSave = LoadBackup();
StatManager.instance.loadSaveData2StataManager(mainSave);
Debug.Log("메인 로드" + mainSave.homeSave.reputation); //임시 코드
Debug.Log("백업 로드" + backupSave.homeSave.reputation); //임시 코드
}
private void UpdateSaveInfo()
{
mainSave = StatManager.instance.ToSaveData(); //스탯을 관리하는 클래스에 선언된 스탯 업데이트 함수를 호출
}
private void SaveMain()
{
QuickSaveWriter.Create("Save_Main")
.Write("Main", mainSave)
.Commit();
}
private void SaveBackup()
{
QuickSaveWriter.Create("Save_Backup")
.Write("Backup", backupSave)
.Commit();
}
private Save LoadMain()
{
try
{
return QuickSaveReader.Create("Save_Main").Read<Save>("Main");
}
catch (System.Exception e)
{
Debug.LogWarning("Main 세이브 로드 실패: " + e.Message);
// 백업 시도
if (QuickSaveRaw.Exists(BackupSaveFilePath))
{
Debug.LogWarning("백업 세이브로 복구 시도");
return LoadBackup();
}
// 백업도 없을 경우 새 세이브 생성
Debug.LogError("세이브 전체 손상 → 새 세이브 생성");
return CreateNewSave();
}
}
private Save LoadBackup()
{
try
{
return QuickSaveReader.Create("Save_Backup").Read<Save>("Backup");
}
catch (System.Exception e)
{
Debug.LogWarning("Backup 세이브 로드 실패: " + e.Message);
// 백업 시도
if (QuickSaveRaw.Exists(MainSaveFilePath))
{
Debug.LogWarning("메인 세이브로 복구 시도");
return LoadMain();
}
// 메인도 없을 경우 새 세이브 생성
Debug.LogError("세이브 전체 손상 → 새 세이브 생성");
return CreateNewSave();
}
}
//더미 세이브 파일 생성
private Save CreateNewSave()
{
var fresh = StatManager.instance.ToSaveData();
SaveMain();
SaveBackup();
return fresh;
}
//세이브 파일의 존재 여부 확인
private void EnsureSaveExists()
{
if (!QuickSaveRaw.Exists(MainSaveFilePath)) // Save_Main 파일이 없을때
{
if (!QuickSaveRaw.Exists(BackupSaveFilePath)) //Save_Backup 파일도 존재하지 않을때
{
UpdateSaveInfo();
SaveMain(); //Save_Main 파일 생성
backupSave = LoadMain();
SaveBackup(); //Save_Backup 파일 생성
Debug.Log("세이브가 존재하지 않아 새로운 세이브를 생성했습니다.");
}
else
{
mainSave = LoadBackup(); //백업을 메인으로 로드.
SaveMain();
Debug.Log("메인을 백업 세이브로 로드했습니다.");
}
}
else
{
if (!QuickSaveRaw.Exists(BackupSaveFilePath)) //Save_Backup 파일이 없을떄
{
backupSave = LoadMain();
SaveBackup();
Debug.Log("백업을 메인 세이브로 로드했습니다.");
}
}
}
//세이브 파일 디렉토리 확인
private string GetSavePath(string fileNameWithoutExt)
{
string directory = Path.Combine(Application.persistentDataPath, SaveFolder);
// 폴더가 없다면 생성
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
return Path.Combine(directory, fileNameWithoutExt + ".json");
}
// 씬이 바뀔 때 마다 자동저장
protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
StartCoroutine(SaveAfterOneFrame());
}
//Start함수 이후에 호출되도록 1프레임 지연
IEnumerator SaveAfterOneFrame()
{
yield return null;
Save();
Debug.Log("자동저장 되었습니다.");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c85bf88c19d95947bc288da9dfd85bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using Random = UnityEngine.Random;
/// <summary>
/// 테스트용 임시 클래스
/// </summary>
public class StatManager : MonoBehaviour
{
public static StatManager instance;
public int attackLevel;
public int attackSpeedLevel;
public int heartLevel;
public int moveSpeedLevel;
public int evasionTimeLevel;
public int stageLevel;
public float time;
public int day;
public float health;
public float reputation;
public bool isEvent; //Todo 이벤트 여부 및 관련 조건들 추가
public int mealCount;
public int houseworkCount;
private void Awake()
{
instance = this;
}
/// <summary>
/// 스탯값 변화
/// </summary>
public void ChangeValue()
{
float floatValue = Random.Range(0f, 2f);
Debug.Log(floatValue);
int intValue = Random.Range(0, 10);
Debug.Log(intValue);
attackLevel = intValue;
attackSpeedLevel = intValue;
heartLevel = intValue;
moveSpeedLevel = intValue;
evasionTimeLevel = intValue;
stageLevel = intValue;
time = floatValue;
day = intValue;
health = floatValue;
reputation = floatValue;
isEvent = false;
mealCount = intValue;
houseworkCount = intValue;
Debug.Log("ChangeValue");
}
/// <summary>
/// 스탯값 반환
/// </summary>
/// <returns></returns>
public Save ToSaveData()
{
return new Save
{
dungeonSave = new DungeonSave
{
attackLevel = this.attackLevel,
attackSpeedLevel = this.attackSpeedLevel,
heartLevel = this.heartLevel,
moveSpeedLevel = this.moveSpeedLevel,
evasionTimeLevel = this.evasionTimeLevel,
stageLevel = this.stageLevel
},
homeSave = new HomeSave
{
time = this.time,
day = this.day,
health = this.health,
reputation = this.reputation,
isEvent = false,
mealCount = this.mealCount,
houseworkCount = this.houseworkCount,
}
};
}
public void loadSaveData2StataManager(Save saveData)
{
attackLevel = saveData.dungeonSave.attackLevel;
attackSpeedLevel = saveData.dungeonSave.attackSpeedLevel;
heartLevel = saveData.dungeonSave.heartLevel;
moveSpeedLevel = saveData.dungeonSave.moveSpeedLevel;
evasionTimeLevel = saveData.dungeonSave.evasionTimeLevel;
stageLevel = saveData.dungeonSave.stageLevel;
time = saveData.homeSave.time;
day = saveData.homeSave.day;
health = saveData.homeSave.health;
reputation = saveData.homeSave.reputation;
isEvent = saveData.homeSave.isEvent;
mealCount = saveData.homeSave.mealCount;
houseworkCount = saveData.homeSave.houseworkCount;
}
public void SceneChange()
{
SceneManager.LoadScene("Main");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afa35303e7c5e6141b05a11a7b5233ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Scripts/Save.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0bc3d5a0f81acf64b850cc1570498ebc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: