37.바이너리파일 세이브 구현
using System.Collections.Generic;
using UnityEngine;
using System.IO; //세이브파일 입출력
using System.Runtime.Serialization.Formatters.Binary; //시리얼라이즈 직렬화
using UnityEngine.SceneManagement; //scene사용
public class SaveNLoad : MonoBehaviour
{
[System.Serializable]
public class Data
{
public float PlayerX;
public float PlayerY;
public float PlayerZ;//Vector3는 직렬화가 안됨
public int playerLv;
public int playerHP;
public int playerMP;
public int playerCurrentHP;
public int playerCurrentMP;
public int playerCurrentEXP;
public int playerHPR;
public int playerMPR;
public int playerATK;
public int playerDEF;
public int added_atk;
public int added_def;
public int added_hpr;
public int added_mpr;
public List<int> playerItemInventory;
public List<int> playerItemInventoryCount;
public List<int> playerEquipItem;
public string mapName;
public string sceneName;
public List<bool> swList;
public List<string> swNameList;
public List<string> varNameList;
public List<float> varNumberList;
}
private PlayerManager thePlayer;
private PlayerStat thePlayerStat;
private DatabaseManager theDatabase;
private Inventory theInven;
private Equipment theEquip;
public Data data;
private Vector3 vector;
public void CallSave()
{
theDatabase = FindAnyObjectByType<DatabaseManager>();
thePlayer = FindAnyObjectByType<PlayerManager>();
thePlayerStat = FindAnyObjectByType<PlayerStat>();
theEquip = FindAnyObjectByType<Equipment>();
theInven = FindAnyObjectByType<Inventory>();
data.PlayerX = thePlayer.transform.position.x;
data.PlayerX = thePlayer.transform.position.y;
data.PlayerX = thePlayer.transform.position.z;
data.playerLv = thePlayerStat.character_Lv;
data.playerHP = thePlayerStat.hp;
data.playerMP = thePlayerStat.mp;
data.playerCurrentHP = thePlayerStat.currentHP;
data.playerCurrentMP = thePlayerStat.currentMP;
data.playerCurrentEXP = thePlayerStat.currentEXP;
data.playerATK = thePlayerStat.atk;
data.playerDEF = thePlayerStat.def;
data.playerHPR = thePlayerStat.recover_hp;
data.playerMPR = thePlayerStat.recover_mp;
data.added_atk = theEquip.added_atk;
data.added_def = theEquip.added_def;
data.added_hpr = theEquip.added_hpr;
data.added_mpr = theEquip.added_mpr;
data.sceneName = thePlayer.currentSceneName;
Debug.Log("기초 데이터 성공");
data.playerItemInventory.Clear();
data.playerItemInventoryCount.Clear();
data.playerEquipItem.Clear();
for(int i = 0; i < theDatabase.var_name.Length; i++)
{
data.varNameList.Add(theDatabase.var_name[i]);
data.varNumberList.Add(theDatabase.var[i]);
}
for (int i = 0; i < theDatabase.switch_name.Length; i++)
{
data.swNameList.Add(theDatabase.switch_name[i]);
data.swList.Add(theDatabase.switches[i]);
}
List<Item> itemList = theInven.SaveItem();
for(int i =0; i < itemList.Count; i++)
{
Debug.Log("인벤토리 아이템 저장완료" + itemList[i].itemID);
data.playerItemInventory.Add(itemList[i].itemID);
data.playerItemInventoryCount.Add(itemList[i].itemCount);
}
for(int i =0; i < theEquip.equipItemList.Length; i++)
{
data.playerEquipItem.Add(theEquip.equipItemList[i].itemID);
Debug.Log("장착 아이템 저장 완료 : " + theEquip.equipItemList[i].itemID);
}
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.dataPath + "/SaveFile.dat");
bf.Serialize(file, data);
file.Close();
Debug.Log(Application.dataPath + "의 위치에 저장했습니다");
}
public void CallLoad()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.dataPath + "/SaveFile.dat", FileMode.Open);
if(file != null && file.Length > 0)
{
data = (Data)bf.Deserialize(file);
theDatabase = FindAnyObjectByType<DatabaseManager>();
thePlayer = FindAnyObjectByType<PlayerManager>();
thePlayerStat = FindAnyObjectByType<PlayerStat>();
theEquip = FindAnyObjectByType<Equipment>();
theInven = FindAnyObjectByType<Inventory>();
thePlayer.currentSceneName = data.sceneName;
vector.Set(data.PlayerX, data.PlayerY, data.PlayerZ);
thePlayer.transform.position = vector;
thePlayerStat.character_Lv = data.playerLv;
thePlayerStat.hp = data.playerHP;
thePlayerStat.mp = data.playerMP;
thePlayerStat.currentHP = data.playerCurrentHP;
thePlayerStat.currentMP = data.playerCurrentMP;
thePlayerStat.currentEXP = data.playerCurrentEXP;
thePlayerStat.atk = data.playerATK;
thePlayerStat.def = data.playerDEF;
thePlayerStat.recover_hp = data.playerHPR;
thePlayerStat.recover_mp = data.playerHPR;
theEquip.added_atk = data.added_atk;
theEquip.added_def = data.added_def;
theEquip.added_hpr = data.added_hpr;
theEquip.added_mpr = data.added_mpr;
theDatabase.var = data.varNumberList.ToArray();
theDatabase.var_name = data.varNameList.ToArray();
theDatabase.switches = data.swList.ToArray();
theDatabase.switch_name = data.swNameList.ToArray();
for(int i = 0; i < theEquip.equipItemList.Length; i++)
{
for(int x =0; x < theDatabase.itemList.Count; x++)
{
if (data.playerEquipItem[i] == theDatabase.itemList[x].itemID)
{
theEquip.equipItemList[i] = theDatabase.itemList[x];
Debug.Log("장착된 아이템을 로드했습니다 : " + theEquip.equipItemList[i].itemID);
break;
}
}
}
List<Item> itemlist = new List<Item>();
for (int i = 0; i < data.playerItemInventory.Count; i++)
{
for (int x = 0; x < theDatabase.itemList.Count; x++)
{
if (data.playerItemInventory[i] == theDatabase.itemList[x].itemID)
{
itemlist.Add(theDatabase.itemList[x]);
Debug.Log("인벤토리 아이템을 로드했습니다 : " + theDatabase.itemList[x].itemID);
break;
}
}
}
for(int i =0; i<data.playerItemInventoryCount.Count; i++)
{
itemlist[i].itemCount = data.playerItemInventoryCount[i];
}
theInven.LoadItem(itemlist);
theEquip.ShowText();
SceneManager.LoadScene(data.sceneName);
}
else
{
Debug.Log("저장된 세이브파일이 없습니다");
}
file.Close();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Inventory : MonoBehaviour
{
public static Inventory Instance;//인벤토리 인스턴스
private DatabaseManager theDatabase;//데이터 베이스 접근
private OrderManager theOrder; //오더매니져
private AudioManager theAudio; //오디오매니져
private OkOrCancel theOOC; //아이템 선택 받아옴
private Equipment theEquip;//<추가
public string key_sound;
public string enter_sound;
public string cancel_sound;
public string open_sound;
public string beep_sound; //비프음을 포함한 각종 효과음
private InventorySlot[] slots; //인벤토리 슬롯들
private List<Item> inventoryItemList; //플레이어가 소지한 아이템 리스트
private List<Item> inventoryTabList; //선택한 탭에 따라 다르게 보여질 아이템 리스트
public Text Description_Text; //아이템 부연설명
public string[] tabDescription; //탭 부연설명
public Transform tf; //slot(슬롯)의 부모객체
public GameObject go; //인벤토리 활성화 비활성화
public GameObject[] selectedTabImages; //4개의 탭 패널
public GameObject go_OOC; //선택지 활성화 비활성화
public GameObject prefab_Floating_text; //플로팅 텍스트 아이템 획득시 이름 알림
private int selectedItem; //선택된 아이템
private int selectedTab; //선택 된 텝
private bool activated; //인벤토리 활성화시 true;
private bool tabActivated; //탭 활성화시 true;
private bool itemActivated; //아이템 활성화시 true;
private bool stopkeyInput; //키 입력제한(소비 할때 질의가 나올텐데, 그때 입력방지)
private bool preventExec; //중복실행 제한
private WaitForSeconds waitTime = new WaitForSeconds(0.01f);
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
Instance = this;//인벤토리 인스턴스
theAudio = FindAnyObjectByType<AudioManager>(); //오디오매니져
theOrder = FindAnyObjectByType<OrderManager>(); //오더매니져
theDatabase = FindAnyObjectByType<DatabaseManager>();
theOOC = FindAnyObjectByType<OkOrCancel>(); //아이템 선택 매니져
theEquip = FindAnyObjectByType<Equipment>();//<추가
inventoryItemList = new List<Item>(); //아이템 리스트화
inventoryTabList = new List<Item>(); //탭 리스트화
slots = tf.GetComponentsInChildren<InventorySlot>();//겟 컴포넌츠 인 칠드런으로 인벤토리 슬롯들을 불러옴
}
public List<Item> SaveItem()//Save에서 private로 선언된 아이템 받아오기
{
return inventoryItemList;
}
public void LoadItem(List<Item> _itemList)//Load에서 로드하면 아이템을 덮어 씌워서 생기게 함
{
inventoryItemList = _itemList;
}
public void EquipToInventory(Item _item)
{
inventoryItemList.Add(_item);
}
public void GetAnItem(int _itemID, int _count = 1)
{
for (int i = 0; i < theDatabase.itemList.Count; i++)//데이터베이스 아이템 검색
{
if (_itemID == theDatabase.itemList[i].itemID)//데이터 베이스에 아이템 발견
{
var clone = Instantiate(prefab_Floating_text, PlayerManager.instance.transform.position, Quaternion.Euler(Vector3.zero));
//var형식은 변수의 타입이 뭔지 모를때 사용한다
//Instantiate는 복제값을 생성해주는 함수이다
//prefab_Floating_text라는 프리팹의 복제를 생성해주고 생성위치는 플레이어매니저의 위치이다
//Quaternion.Euler(Vector3.zero)으로 상하좌우 위치를 설정해 벡터를 0으로 만들어준다
clone.GetComponent<FloatingText>().text.text = theDatabase.itemList[i].itemName + " " + _count + "개 획득 +";
//clone를 겟컴포넌트로 불러와서 아이템을 습득할때 데이터베이스에서 반응하는 넘버를 불러와서 이름과 갯수를 띠워준다
clone.transform.SetParent(this.transform);
//SetParent를 부모로 지정하여 생성해준다
for (int j = 0; j < inventoryItemList.Count; j++)//소지품에 같은 아이템이 있는지 확인
{
if (inventoryItemList[j].itemID == _itemID)//소지품에 같은 아이템이 있다 -> 개수만 증감시켜줌
{
if (inventoryItemList[j].itemType == Item.ItemType.Use)
{
inventoryItemList[j].itemCount += _count;
}else
{
inventoryItemList.Add(theDatabase.itemList[i]);
}
return;
}
}
inventoryItemList.Add(theDatabase.itemList[i]);//없을시 해당 아이템 추가
inventoryItemList[inventoryItemList.Count - 1].itemCount = _count;
return;
}
}
Debug.LogError("데이터 베이스에 해당 ID값을 가진 아이템이 존재하지 않습니다");//데이터베이스에 ItemID 없음
}
public void RemoveSlot()//모든 슬롯을 비활성화
{
for (int i = 0; i < slots.Length; i++) //i = 0이고 i는 slots들보다 적고 i++로 증감
{
slots[i].RemoveItem(); //InventorySlot스크립트에서 아이템이 사라지는 로직 slots[i]로 여러개를 선택
slots[i].gameObject.SetActive(false); //로직 slots[i]로 여러개를 선택 후 오브젝트를 비활성화
}
}//인벤토리 슬롯 초기화
public void ShowTab()//탭 활성화
{
RemoveSlot();
SelectedTab();
}
public void SelectedTab()//선택된 탭을 제외하고 다른 모든 탭의 컬러 알파값 0으로 조정
{
StopAllCoroutines();//모든 코루틴 정지
Color color = selectedTabImages[selectedTab].GetComponent<Image>().color;//탭의 반짝임을 위해 배열을 받아옴
color.a = 0f; //위에 선택된 배열의 컬러 알파값이 0
for(int i =0; i < selectedTabImages.Length; i++) //i=0과 같고 i는 선택된 탭보다 작다, i++로 증감
{
selectedTabImages[i].GetComponent<Image>().color = color; //위의 i번째에 선택된 오브젝트의 컬러값을 가져옴
}
Description_Text.text = tabDescription[selectedTab]; //각 탭에 위치한 설명을 출력함
StartCoroutine(SelectedTabEffectCoroutine()); //스타트 코루틴, 탭효과를 출력!
}
IEnumerator SelectedTabEffectCoroutine() //위의 탭 효과를 출력
{
while (tabActivated) //탭이 true면 반복됨
{
Color color = selectedTabImages[selectedTab].GetComponent<Image>().color; //선택한 탭의 color, Image를 받아옴
while (color.a < 0.5f) //반복문 알파값이 반절보다 작을때
{
color.a += 0.03f;
selectedTabImages[selectedTab].GetComponent<Image>().color = color; //컬러 알파값에 계속 0.03f정도 더해줌
yield return waitTime; //0.01f 정도 대기
}
while (color.a > 0f) //알파값이 0보다 클 경우 반복
{
color.a -= 0.03f; //알파값에 0.03f정도 계속 빼서 내려줌
selectedTabImages[selectedTab].GetComponent<Image>().color = color; //컬러값에 적용
yield return waitTime; //0.01f 대기
}
yield return new WaitForSeconds(0.3f); //0.3초 대기
}
}//선택된 탭 반짝임 효과
public void ShowItem()//아이템 활성화(inventoryTabList에 조건에 맞는 아이템들만 넣어주고, 인벤토리 슬롯에 출력)
{
inventoryTabList.Clear(); //인벤토리 탭 리스트 초기화
RemoveSlot(); //표시되는 슬롯 초기화
selectedItem = 0; //선택된 아이템 배열의 0번째 선택
switch (selectedTab)//탭에 따른 아이템 분류
{
case 0:
for(int i =0; i < inventoryItemList.Count; i++)
{
if(Item.ItemType.Use == inventoryItemList[i].itemType)//소모품
inventoryTabList.Add(inventoryItemList[i]);//Add로 쫘악 펼쳐줌
}
break;
case 1:
for (int i = 0; i < inventoryItemList.Count; i++)
{
if (Item.ItemType.Equip == inventoryItemList[i].itemType)//장비품
inventoryTabList.Add(inventoryItemList[i]);//Add로 쫘악 펼쳐줌
}
break;
case 2:
for (int i = 0; i < inventoryItemList.Count; i++)
{
if (Item.ItemType.Quest == inventoryItemList[i].itemType)//퀘스트 아이템
inventoryTabList.Add(inventoryItemList[i]);//Add로 쫘악 펼쳐줌
}
break;
case 3:
for (int i = 0; i < inventoryItemList.Count; i++)
{
if (Item.ItemType.ETC == inventoryItemList[i].itemType)//기타
inventoryTabList.Add(inventoryItemList[i]);//Add로 쫘악 펼쳐줌
}
break;
}//탭에 따른 아이템 분류, 그것을 인벤토리 탭 리스트에 추가
for(int i = 0;i < inventoryTabList.Count; i++)//인벤토리 탭 리스트의 내용을 인벤토리 슬롯에 추가
{
slots[i].gameObject.SetActive(true);//인벤토리 아이템 리스트를 활성화
slots[i].Additem(inventoryTabList[i]);//Additem으로 쫘악 배열을 펼쳐줌
}
SelectedItem();
}
public void SelectedItem()//선택된 아이템을 제외하고, 다른 모든 탭의 컬러 알파값을 0으로 조정
{
StopAllCoroutines();//모든 코루틴 종료
if (inventoryTabList.Count > 0)//조건문 인벤토리 탭 리스트가 0보다 크다면
{
Color color = slots[0].selected_Item.GetComponent<Image>().color;//슬롯 0번재의 Image와 Color을 받아옴
color.a = 0f; //초기값은 0f
for (int i = 0; i < inventoryTabList.Count; i++) //반복문 i는 0이고 i는 인벤토리 탭 카운트리스트보다 작고 i++증감
slots[i].selected_Item.GetComponent<Image>().color = color; //반복문의 Image와 Color을 받아옴
Description_Text.text = inventoryTabList[selectedItem].itemDescription;//설명문에 인벤토리 탭 리스트의 배열의 셀렉티드 아이템의 아이템 설명과 같다
StartCoroutine(SelectedItemEffectCoroutine()); //선택된 아이템 이팩트 실행
}
else
Description_Text.text = "해당 타입의 아이템을 소유하고 있지 않습니다";//만약 해당탭에 해당하는 아이템이 없을 경우 메시지 출력
}
IEnumerator SelectedItemEffectCoroutine()//선택된 아이템 반짝임 효과
{
while (itemActivated) //아이템 선택이 true가 될 경우
{
Color color = slots[0].GetComponent<Image>().color; //슬롯의 배열에 해당하는 슬롯의 Image와 color을 받아와서
while (color.a < 0.5f) //반복문 알파값이 0.5f보다 클 경우
{
color.a += 0.03f; //0.03f만큼 계속 더해짐
slots[selectedItem].selected_Item.GetComponent<Image>().color = color;//위의 컬러 적용
yield return waitTime; //0.01초 대기
}
while (color.a > 0f) //알파값이 0보다 클 경우
{
color.a -= 0.03f; //0.03f만큼 계속 빼줌
slots[selectedItem].selected_Item.GetComponent<Image>().color = color;//컬러에 적용
yield return waitTime; //0.01초 대기
}
yield return new WaitForSeconds(0.3f);
}
}
// Update is called once per frame
void Update()
{
if (!stopkeyInput)//키 입력제한이 false일때
{
if (Input.GetKeyDown(KeyCode.I))//I키로 실행
{
activated = !activated;//true/false on/off
if (activated)//인벤토리 활성화를 True
{
theAudio.Play(open_sound); //여는 소리 재생
theOrder.NotMove(); //오더매니져의 움직임 정지
go.SetActive(true); //오브젝트 활성화
selectedTab = 0; //탭창에서 0번째 탭을 선택
tabActivated = true; //탭부터 보게 탭부분을 true화
itemActivated = false; //아이템 창은 false화
ShowTab();
}
else
{
theAudio.Play(cancel_sound); //캔슬소리 재생
StopAllCoroutines(); //모든 코루틴 종료
go.SetActive(false); //오브젝트 false로 비활성화
tabActivated = false; //탭 비활성화
itemActivated = false; //아이템 부분 비활성화
theOrder.Move(); //오더매니져 이동 활성화
}
}
if (activated) //인벤토리 활성화시
{
if (tabActivated) //탭 활성화시 키 입력 처리
{
if (Input.GetKeyDown(KeyCode.RightArrow)) //오른쪽 버튼을 누름
{
if (selectedTab < selectedTabImages.Length - 1) //선택 된 탭이 선택된 탭 이미지에서 -1한 값보다 작을 경우
selectedTab++; //선택된 탭 값을 ++로
else //아니라면
selectedTab = 0; //탭 선택을 0으로 함
theAudio.Play(key_sound); //사운드 재생
SelectedTab(); //탭선택 실행
}
if (Input.GetKeyDown(KeyCode.LeftArrow)) //왼쪽 버튼 누름
{
if (selectedTab > 0) //선택된 탭 값이 0보다 크다면
selectedTab--; //-- 감소 선택
else //아니라면 선택된 탭과 선택된 탭 이미지에서 -1한 값과 같다
selectedTab = selectedTabImages.Length - 1;
theAudio.Play(key_sound); //소리재생
SelectedTab(); //탭선택 실행
}
else if (Input.GetKeyDown(KeyCode.Z)) //Z키 입력으로 탭 선택시
{
theAudio.Play(enter_sound); //소리 재생
Color color = selectedTabImages[selectedTab].GetComponent<Image>().color;//선택된 배열 탭의 배열에 선택된 탭 넘버를 넣어준 후 해당 Image와 color을 받아와서
color.a = 0.25f; //선택된 컬러의 알파값을 0.25f로 바꿔 줌
selectedTabImages[selectedTab].GetComponent<Image>().color = color; //위의 바꾼 컬러를 적용해 줌
itemActivated = true; //아이템 선택 활성화
tabActivated = false; //탭선택 비활성화
preventExec = true; //키 중복입력 방지
ShowItem(); //Item선택 활성화
}
}
else if (itemActivated) //아이템 창 활성화시
{
if(inventoryTabList.Count > 0) //아이템 탭 리스트 카운트가 0보다 클 경우
{
if (Input.GetKeyDown(KeyCode.DownArrow)) //키보드 아래쪽 화살표 누를시
{
if (selectedItem < inventoryTabList.Count - 2) //조건문 선택된 아이템이 인벤토리탭리스트 카운트에서 2뺀 값보다 작을 경우
selectedItem += 2; //2를 계속 더 해줌
else //조건이 아니라면
selectedItem %= 2; //2를 나누고 남은 값을 대입해준다
theAudio.Play(key_sound);
SelectedItem(); //선택된 아이템의 알파값을 제외하고 다른 슬롯의 알파값을 초기화
}
else if (Input.GetKeyDown(KeyCode.UpArrow)) //위쪽 버튼을 누를 경우
{
if (selectedItem > 1) //조건문 1보다 클 경우
selectedItem -= 2; //2를 빼준다
else //조건이 아니라면
selectedItem = inventoryTabList.Count - 1 - selectedItem; //선택 된 아이템은 선택된 아이템 값 카운트에서 1을 빼주고 선택된 아이템을 빼준 값과 같다
theAudio.Play(key_sound);
SelectedItem(); //선택된 아이템의 알파값을 제외하고 다른 슬롯의 알파값을 초기화
}
else if (Input.GetKeyDown(KeyCode.RightArrow)) //오른쪽을 버튼을 누른 경우
{
if (selectedItem < inventoryTabList.Count - 1) //선택된 아이템이 인벤토리 배열에서 카운트된 숫자 -1한 값보다 적을 경우
selectedItem++; //선택된 아이템 번호를 증감하여 오른쪽으로 이동
else
selectedItem = 0; //아니라면 0과 같다
theAudio.Play(key_sound);
SelectedItem(); //선택된 아이템의 알파값을 제외하고 다른 슬롯의 알파값을 초기화
}
else if (Input.GetKeyDown(KeyCode.LeftArrow)) //왼쪽 버튼을 누름
{
if (selectedItem > 0) //0보다 클 경우
selectedItem--; //숫자를 감소하여 왼쪽으로 이동
else
selectedItem = inventoryTabList.Count - 1; //그게 아니라면 선택된 아이탬 순서는 카운트된 인벤토리 탭에서 -1 값과 같다
theAudio.Play(key_sound);
SelectedItem(); //선택된 아이템의 알파값을 제외하고 다른 슬롯의 알파값을 초기화
}
else if (Input.GetKeyDown(KeyCode.Z) && !preventExec) //z를 클릭하면서 중복이 방지된 경우
{
if (selectedTab == 0)//소모품
{
StartCoroutine(OOCCoroutine("사용", "취소"));
}
else if (selectedTab == 1)
{
StartCoroutine(OOCCoroutine("장착", "취소"));
}
else
{
theAudio.Play(beep_sound);
}
}
}
if (Input.GetKeyDown(KeyCode.X))//x버튼을 눌른 경우
{
theAudio.Play(cancel_sound);//캔슬 사운드 재생
StopAllCoroutines(); //모든 코루틴 종료
itemActivated = false; //아이템 선택 비활성화
tabActivated = true; //탭선택 활성화
ShowTab();
Debug.Log("X키가 눌렸습니다");
}
}
if (Input.GetKey(KeyCode.Z))//중복 실행 방지
preventExec = false;
}
}
}
IEnumerator OOCCoroutine(string _up, string _down)//OK,Cancel
{
theAudio.Play(enter_sound); //사운드 재생
stopkeyInput = true; //키 입력 활성화
go_OOC.SetActive(true);//OkOrC panel 오브젝트를 활성화
theOOC.ShowTwoChoice(_up, _down);//사용 취소 텍스트를 아이템 종류에 맞게 텍스트 변경
yield return new WaitUntil(() => !theOOC.activated);//theOOC가 false가 될때 멈춤
if (theOOC.GetResult())//레절트 값을 받아옴
{
for (int i = 0; i < inventoryItemList.Count; i++)//인벤토리에서 아이템 리스트를 검색
{
if (inventoryItemList[i].itemID == inventoryTabList[selectedItem].itemID)//검색한 아이템이 맞는지 확인
{
if(selectedTab == 0)//소모품 아이템이라면
{
theDatabase.UseItem(inventoryItemList[i].itemID);//데이터베이스에서 Use아이템일 경우 효과를 검색해서 사용
if (inventoryItemList[i].itemCount > 1)//1보다 클 경우
inventoryItemList[i].itemCount--;//하나 감소
else
inventoryItemList.RemoveAt(i);//만약 하나일 경우 없셈
ShowItem();
break;
}
else if(selectedTab == 1)//장비품 아이템이라면
{
theEquip.EquipItem(inventoryItemList[i]);//EquipItem배열에 inventoryItemList[i]를 넣어주고
inventoryItemList.RemoveAt(i); //장비된 아이템을 지워주고
ShowItem(); //ShowItem함수를 실행
break; //break해줌
}
}
}
}
stopkeyInput = false;
go_OOC.SetActive(false);
}
}
using UnityEngine;
//바운드의 경우 필자가 자동으로 인식되게 했음 save
public class NowBound : MonoBehaviour
{
private PlayerManager thePlayer;
private CameraManager theCamera;
private BoxCollider2D theBoxCollider2D;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
theCamera = FindAnyObjectByType<CameraManager>();
thePlayer = FindAnyObjectByType<PlayerManager>();
theBoxCollider2D = this.GetComponent<BoxCollider2D>();
}
void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.name == "Player")
{
theCamera.SetBound(theBoxCollider2D);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerManager : MovingObjcet
{
static public PlayerManager instance;
public string currentSceneName;//플레이어가 어느씬에 있는지 save
//public string currentMapName;
public string walkSound_1;
public string walkSound_2;
public string walkSound_3;
public string walkSound_4;
private AudioManager theAudio;
private SaveNLoad theSaveNLode;
//public AudioClip walkSound_1;//사운드 파일
//public AudioClip walkSound_2;
private AudioSource audioSource;//사운드 플레이어
//뛰기,평범하게 걷기
public float runSpeed;
private float applyRunSpeed;
private bool applyRunFlag = false;
public bool notMove = false;//이벤트 실행시 움직임 정지
private bool attacking = false; //Attack
public float attackDelay; //Attack
private float currentAttackDelay; //Attack
//한칸한칸 이동할때 플레그 형식으로 구현
private bool canMove = true;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
if (instance == null)
{
queue = new Queue<string>();
DontDestroyOnLoad(this.gameObject);
boxCollider = GetComponent<BoxCollider2D>();
//audioSource = GetComponent<AudioSource>();
animator = GetComponent<Animator>();
theAudio = FindAnyObjectByType<AudioManager>();
instance = this;
theSaveNLode = FindAnyObjectByType<SaveNLoad>();
}
else
{
Destroy(this.gameObject);
}
}
//코루틴으로 이동제어 한칸 한칸 이동하게
IEnumerator MoveCoroutine()
{
while (Input.GetAxisRaw("Vertical") != 0 || Input.GetAxisRaw("Horizontal") != 0 && !notMove && !attacking)
{
//시프트 키 입력 여부에 따라 뛰기 걷기 구분
if (Input.GetKey(KeyCode.LeftShift))
{
applyRunSpeed = runSpeed;
applyRunFlag = true;
}
else
{
applyRunSpeed = 0;
applyRunFlag = false;
}
//이동방향 벡터 움직임 구현
vector.Set(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"), transform.position.z);
//좌상,좌하 등 대각선 방향이동 장금
if (vector.x != 0)
vector.y = 0;
//에니메이터 불러오기
animator.SetFloat("DirX", vector.x);
animator.SetFloat("DirY", vector.y);
bool checkCollsionFlag = base.CheckCollsion();
if (checkCollsionFlag)
break;
animator.SetBool("Walking", true);
int temp = Random.Range(1, 4);
switch (temp)
{
case 1:
theAudio.Play(walkSound_1);
break;
case 2:
theAudio.Play(walkSound_2);
break;
case 3:
theAudio.Play(walkSound_3);
break;
case 4:
theAudio.Play(walkSound_4);
break;
}
theAudio.SetVolumn(walkSound_2, 0.5f);
boxCollider.offset = new Vector2(vector.x * 0.02f * speed * walkCount, vector.y * 0.02f * speed * walkCount);//박스콜라이더를 미리 옮겨서 충돌방지
//캐릭터 이동구현 코드
while (currentWalkCount < walkCount)
{
if (vector.x != 0)
{
//Translate는 현제 값에서 +를 해주는 코드
transform.Translate(vector.x * (speed + applyRunSpeed), 0, 0);
}
else if (vector.y != 0)
{
transform.Translate(0, vector.y * (speed + applyRunSpeed), 0);
}
if (applyRunFlag)
currentWalkCount++;//위의 applyRunFlas가 되면 추가되는 카운트 만약 아니라면 더해지지 않음
currentWalkCount++;//위의 카운트와는 별개의 카운트로 구분 됨
if (currentWalkCount == 12)
boxCollider.offset = Vector2.zero;//박스콜라이더를 원위치하여 충돌방지
yield return new WaitForSeconds(0.01f);//0.01초마다 대기
/*if(currentWalkCount % 9 == 2)
{
int temp = Random.Range(1, 2);
switch (temp)
{
case 1:
//audioSource.clip = walkSound_1;
//audioSource.Play();
break;
case 2:
//audioSource.clip = walkSound_2;
//audioSource.Play();
break;
}
}*/
}
currentWalkCount = 0;//0을 넣어서 반복문 초기화
}
animator.SetBool("Walking", false);
//한칸한칸 이동할때 bool값
canMove = true;
}
//매 프레임마다 이동명령을 받음
void Update()
{
if (Input.GetKeyDown(KeyCode.F5))
{
theSaveNLode.CallSave();
}
if (Input.GetKeyDown(KeyCode.F9))
{
theSaveNLode.CallLoad();
}
if (canMove && !notMove && !attacking)
{
//이동 방향키 입력을 할때 쯔구르 처럼 상하좌우 구분하게 하는 코드
if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0)
{
//한칸 한칸 이동할때 bool값으로 canMove를 이용함
canMove = false;
StartCoroutine(MoveCoroutine());
}
}
if(!notMove && !attacking)
{
if (Input.GetKeyDown(KeyCode.Space))
{
currentAttackDelay = attackDelay;
attacking = true;
animator.SetBool("Attacking", true);
}
}
if(attacking)
{
currentAttackDelay -= Time.deltaTime;
if(currentAttackDelay <= 0)
{
animator.SetBool("Attacking", false);
attacking = false;
}
}
}
}
이번 강좌에서는 바이너리 파일 방식으로 유니티엔진 세이브와 로드를 구현할 것이다
케이디님이 올리신 영상을 보면 시간이 길어서 부담이 느껴질 수 있는데 차근차근 따라가면 필자처럼 한번에 성공할 수 있다
일단 PlayerManager 스크립트를 열어보자
체크한 변수를 작성해주자
이 변수는 플레이어가 어느씬에 있는지 string타입으로 저장하는 변수이다
위 스샷에 빨간색으로 체크한 부분은 우리가 작성한 세이브 엔 로드 기능을 불러올 변수입니다
그리고 void Update 함수에
빨간줄로 그어진 스크립트를 작성해줍니다
우리가 만들을 SaveNLode 스크립트에서 세이브, 로드를 구현한 함수를 로드해주는 부분입니다
그리고 여기서 중요한게 케이디님의 영상에서는 두번째로 수정할 곳에서 Bound 스크립트를 켜서 수정했는데
필자는 자동인식으로 바운드를 구현해서 쓸 필요가 없었습니다
그래서 일단 필자가 만들은 바운드 스크립트를 올립니다
using UnityEngine;
//바운드의 경우 필자가 자동으로 인식되게 했음 save
public class NowBound : MonoBehaviour
{
private PlayerManager thePlayer;
private CameraManager theCamera;
private BoxCollider2D theBoxCollider2D;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
theCamera = FindAnyObjectByType<CameraManager>();
thePlayer = FindAnyObjectByType<PlayerManager>();
theBoxCollider2D = this.GetComponent<BoxCollider2D>();
}
void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.name == "Player")
{
theCamera.SetBound(theBoxCollider2D);
}
}
}
다음으로 Inventory 스크립트를 수정해줍시다
위 두 함수를 작성해줍니다
작성해주는 이유는 List<Item>이 private로 되어있기 때문에 외부로 보내주기 위해 62~65번의 함수를 작성해주는 겁니다
반면 67번에서 70번 줄의 함수는 세이브파일을 로드하면 기존의 아이템 리스트를 덮어씌우기 위해서 작성해주는 함수입니다
다음은 Equipment 스크립트를 열어줍시다
빨간색으로 체크된 부분은 원래는 private로 되어 있었으나 public로 수정해줘야 합니다
이제 스크립트를 새로 만들어 줍니다 이름은 SaveNLode 입니다
일단 using부터 선언해주겠습니다
System.IO는 파일 입출력하는 것이고
Systehttp://m.Runtime.Serialization.Formatters.Binary는 포멧을 직렬화하고 이를 복원해줍니다
UnityEngine.SceneManagement 는 유니티에서 씬을 관리해줍니다
그뒤 아래에
[System.Serializable] 작성하여 직렬화해 줍니다
빨간 줄로 체크한 부분은 플레이어의 x,y,z를 각각 따로 저장해준 것 입니다
psoition이며 PC는 이 3값을 한꺼번에 기억하지 못 합니다
따라서 따로 지정해 줍니다
다음 보라색으로 체크 부분은 플레이어의 능력치를 저장할 변수입니다 우리가 그동안 만들어준 수치입니다
하늘색으로 체크된 부분은 인벤토리 List를 받오는 것 입니다
순서대로 인벤토리, 인벤토리 아이템 숫자, 장비인벤토리 입니다
녹색으로 체크한 부분은 씬로드 관련으로 작성한 것 입니다
mapName는 어느맵에 있는지 그리고 sceneName는 어느씬에 있는지 입니다
위 함수들은 우리가 전혀 만지지 않았는데
DatabaseManager 에서 찾을 수 있습니다
스위치 이름과 각종 트리거 들 입니다
이걸로 적 보스를 쓰러뜨린 맵이 있으면 스위치를 조작해서 보스를 두번 잡게하지 않는 등 스토리진행 분기도 만들 수 있습니다
이제 그 아래에 작성될 것은 우리가 받아올 능력치 관련 스크립트를 선언해주는 겁니디ㅏ
54번은 우리가 만들은 커스텀 Data입니다
56번은 position 백터3고요
그리고 CallSave() 함수를 작성해줍시다
전부 우리가 위에 선언해준 기초데이터들 입니다
89번부터 91번에는 인벤토리에 Clear()을 선언해주었습니다
이유는 아이템이 중복으로 들어가는 것을 방지하기 위함입니다
93번줄은 반복문으로 우리가 작성해준 이름을 받아오는 곳이고
98번줄은 반복문으로 우리가 작성해준 스위치를 받아오는 곳 입니다
104번줄은 Inventory에서 작성한
이 부분을 호출하여 아이템을 받오는 것 입니다
105번째 줄은 반복문으로 i는 아이템 리스트 카운트보다 적으로 i를 반복합니다
107번은 불러온 아이템을 저장했다고 알려주는 디버그이고
108번은 그렇게 아이템ID을 반복하고
109번은 반복한 아이템의 카운트를 채워줍니다
다음 112번은
반복문으로 i는 장비한 아이템 보다 적으며 i를 반복합니다
114번은 그렇게 장착한 아이템을 추가하는 반복문입니다
115번 디버그는 그렇게 장착된 아이템을 텍스트로 알려줍니다
여기는 우리가 세이브파일을 저장하는 곳 입니다
118번은 바이너리 포멧 변환기입니다 새로운 바이너리 포멧을 선언해줍니다
119번은 어플리케이션 폴더 내부에 새로운 SaveFile.dat를 만들어준다는 뜻 입니다
121번은 우리가 선언한 bf를 직렬화해서 데이터화하는 것 입니다
122번은 중복이 안되게 지워줍니다
124번은 그렇게 어느 위치에 세이브 파일을 저장했는지 나타냅니다
이제 로드하는 함수를 만들어 줍니다
의외로 간단하게 로드해주는 함수는 세이브의 반대로 로드해서 적용하면 됩니다
129번은 위의 직렬화를 다시 해주는 것이고
130번은 파일을 오픈해주는 것 입니다
132번은 file가 널이 아니라면 파일을 반복해준다는 뜻 입니다
134번 줄은 파일을 반대로 직렬화해서 열어준다는 뜻 입니다
144번 줄은 우리가 position을 따로 X,Y,Z로 기록했는데 이걸 하나로 합쳐준다는 뜻 입니다
168번은 장착한 아이템을 로드합니다
170번에서 데이터베이스 아이템을 로드해서 카운트해주고
172에 세이브파일에서 추출한 아이템과 데이터베이스 아이템을 같게해줍니다
그리고 174번줄에 전부 장비해줍니다
181번은 아이템 리스트에 새 아이템 리스트를 덮어 씌워주는 것이고
183번은 세이브 파일의 플레이어 아이템인벤토리를 반복해주는 것이고
185번은 데이터베이스의 아이템 인벤토리를 반복해주는 것 입니다
187은 비교해서 id가 같은 아이템을 인벤토리에 넣어주고
189는 아이템리스트를 채워줍니다
190번에 아이템을 로드했다고 알려줍니다
그리고 아래에 196번의 반복은 로드된 아이템의 개수를 채워넣어줍니다
그리고 201번은 인벤토리를 로드한 것을 넣어줍니다
우리가 이렇게 Inventory 스크립트에 구현해 놨었죠 ㅎㅎ
그리고 202번 줄은 ShowText 함수를 사용하여 장비창 수치를 조절해줍니다
204번은 명명된 씬으로 우리를 보내줍니다
206번은 세이브가 없을 경우 디버그만 작동되게 해 줍니다
211은 파일을 닫아줍니다
이제 유니티엔진으로 들어와서
Player 오브젝트에 우리가 만들은 Save N Load 스크립트를 넣어줍니다
그리고 Player 객체에 SceneNa...에 우리가 스타트할 씬의 이름을 넣읍시다
이렇게 해야 씬으로 찾아갑니다
이제 우리가 F5를 누르면 세이브가 되고 F9를 누르면 로드가 되게 다 만들어진 것 입니다
gif 올려야하는데 세이브로드는 어떻게 찍어야 할지 모를거 같아서 이번에는 gif 안올리겠습니다