2022. 1. 11. 17:16ㆍC#/수업 내용
static 이 뭔지 정확히 알아야 이해할 것 같다.
싱글턴 패턴 생성 ----------------------------------------------------------------------------------
public App()
{
DataMgr mgr = DataMgr.GetInstance();
DataMgr mgr2 = DataMgr.GetInstance();
mgr.testnum += 1;
Console.WriteLine(mgr.testnum);
Console.WriteLine(mgr2.testnum);
}
----------------------------------------------------------------------------------
class DataMgr
{
private static DataMgr instance;
public int testnum;
private DataMgr()
{
testnum = 5;
}
public static DataMgr GetInstance()
{
if(DataMgr.instance == null)
{
Console.WriteLine("새 객체를 생성합니다.");
DataMgr.instance = new DataMgr();
}
else
{
Console.WriteLine("기존 객체를 반환합니다.");
}
return DataMgr.instance;
}
}
객체가 이미 생성되었을 땐 기존 객체를 반환한다.
그리고 mgr, mgr2 모두 6을 반환함으로써 같은 객체의 주소를 참조한다는 걸 확인할 수 있다.
싱글턴 패턴을 사용한 실습 --------------------------------------------------------------------------------------------
1) 먼저 게임의 모든 데이터를 총괄하는 매니저 DataMgr의 인스턴스를 생성한다.
2) 그 인스턴스로 접근하여 json파일로부터 아이템 데이터를 가져오는 메서드를 실행한다.
3) 그 인스턴스로 접근하여 GameInfo.json파일이 있는지 유무에 따라 신규 유저인지, 기존 유저인지 판별하는 메서드를 실행한다.
3-1) 신규 유저이면, GameInfo.json 파일을 새로 만들어서 로컬에 저장한다. 단, 새로 시작하는 신규 유저의 지원을 위해 시작 무기 +5강을 지급한다. 무기가 지급된 정보가 담긴 후에 GameInfo 객체를 직렬화하여 json파일로 저장한다.
3-2) 기존 유저이면, 유저의 컴퓨터에 있는 GameInfo.json 파일을 역직렬화하여 GameInfo 객체로 가져온다.
1번부터 3번까지의 과정을 통해 게임이 시작되기까지의 준비과정을 마쳤다.
4번부터는 간단한 게임 세이브 과정을 구현해본다.
4) 대장간에서 무기를 만들고, 만든 무기를 제련한 다음, 장착한다.
5) 이 상태 정보를 GameInfo 객체에 저장한 후, 그 객체를 직렬화하여 유저의 컴퓨터에 json 파일로 저장한다.
6) 저장된 json 파일을 다시 불러와서 정보가 정상적으로 저장되었는지 확인한다.
이하 실습과정-----------------------------------------------------
1) 먼저 게임의 모든 데이터를 총괄하는 매니저 DataMgr의 인스턴스를 생성한다.
public App()
{
DataMgr mgr = DataMgr.GetInstance();
}
class DataMgr
{
private static DataMgr instance;
public static DataMgr GetInstance()
{
if(DataMgr.instance == null)
{
Console.WriteLine("새 객체를 생성합니다.");
DataMgr.instance = new DataMgr();
}
else
{
Console.WriteLine("기존 객체를 반환합니다.");
}
return DataMgr.instance;
}
}
2) 그 인스턴스로 접근하여 json파일로부터 아이템 데이터를 가져오는 메서드를 실행한다.
public App()
{
DataMgr mgr = DataMgr.GetInstance();
mgr.LoadData();
}
class DataMgr
{
private const string WEAPON_DATA_PATH = "./weapon_data.json";
private Dictionary<int, weapon_data> dict = new Dictionary<int, weapon_data>();
public void LoadData()
{
string getWeaponsJson = File.ReadAllText(WEAPON_DATA_PATH);
Console.WriteLine(getWeaponsJson);
weapon_data[] weapons = JsonConvert.DeserializeObject<weapon_data[]>(getWeaponsJson);
foreach(weapon_data weapon in weapons)
{
dict.Add(weapon.id, weapon);
}
}
}
3) 그 인스턴스로 접근하여 GameInfo.json파일이 있는지 유무에 따라 신규 유저인지, 기존 유저인지 판별하는 메서드를 실행한다.
public App()
{
DataMgr mgr = DataMgr.GetInstance();
mgr.LoadData();
mgr.DiscernUserType();
}
class DataMgr
{
private const string GAME_INFO_PATH = "./game_info.json";
private GameInfo gi;
private Dictionary<int, weapon_data> dict = new Dictionary<int, weapon_data>();
public void DiscernUserType()
{
if (File.Exists(GAME_INFO_PATH))
{
Console.WriteLine("기존 유저입니다.");
string getJson = File.ReadAllText(GAME_INFO_PATH);
this.gi = JsonConvert.DeserializeObject<GameInfo>(getJson);
this.gi.CheckWeaponStatus();
} else
{
Console.WriteLine("신규 유저입니다.");
Console.WriteLine("");
Console.WriteLine("신규 유저를 위해 5단계 무기를 지급했습니다.");
this.gi = new GameInfo();
weapon_data data = GetWeaponData(0);
Weapon startWeapon = new Weapon(data.id, data.name, data.damage, data.price, 5);
this.gi.EquipWeapon(startWeapon);
this.gi.CheckWeaponStatus();
string saveJson = JsonConvert.SerializeObject(this.gi);
File.WriteAllText(GAME_INFO_PATH, saveJson);
}
}
public weapon_data GetWeaponData(int id)
{
return dict[id];
}
}
class GameInfo
{
private Weapon startWeapon;
public void CheckWeaponStatus()
{
Console.WriteLine("{0} {3}단계 / 무기 공격력 : {1} / 아이템 가격 : {2}",
startWeapon.name, startWeapon.damage, startWeapon.price, startWeapon.reinforcement);
}
public void EquipWeapon(Weapon weapon)
{
this.startWeapon = weapon;
}
}
3-1) 신규 유저이면, GameInfo.json 파일을 새로 만들어서 로컬에 저장한다. 단, 새로 시작하는 신규 유저의 지원을 위해 시작 무기 +5강을 지급한다. 무기가 지급된 정보가 담긴 후에 GameInfo 객체를 직렬화하여 json파일로 저장한다.
3-2) 기존 유저이면, 유저의 컴퓨터에 있는 GameInfo.json 파일을 역직렬화하여 GameInfo 객체로 가져온다.
그러나 3-1번, 3-2번을 구현하려는데 한 번 막혔다.
5단계 무기를 장착한 상태의 GameInfo 객체를 json파일로 저장해서 다음에 파일을 불러와 역직렬화할 때는 GameInfo 객체의 멤버 startWeapon 변수에 5단계 무기의 객체가 들어있기를 바랐는데, 오류가 났기 때문이다.
몇 번 삽질하다가 직렬화할 클래스의 멤버는 public이어야 정상적으로 변환한다는 것을 알았다.
여기까지 작성한 코드의 실행결과는 아래와 같다. (1번부터 3-2번까지)
4) 대장간에서 무기를 만들고, 만든 무기를 제련한 다음, 장착한다.
class Smithy
{
DataMgr mgr = DataMgr.GetInstance();
public Smithy()
{
Console.WriteLine("쇠를 두들기는 소리의 근원을 따라 마을 한 구석에 도착하자, \n" +
"팔 근육이 터질듯한 갈색의 드워프가 운영하는 대장간이 있었다. \n무엇을 할까?");
while (true)
{
Console.WriteLine("\n0 => 무기 제작\n1 => 무기 강화\n2 => 나간다.\n");
string choice = Console.ReadLine();
if (choice == "0")
{
Console.WriteLine("\n어떤 무기를 제작할까?");
for(int i = 0; i < 4; i++)
{
Console.WriteLine("\n{0} => {1}", i, mgr.GetWeaponData(i).name);
}
string choice2 = Console.ReadLine();
int choice22 = Convert.ToInt32(choice2);
Console.WriteLine("주인장 드워프에게 {0}을 부탁한다고 하자, 그건 자기 전문 분야라며 자신감을 뽐냈다.", mgr.GetWeaponData(choice22).name);
Console.WriteLine("결과를 보려면 아무키나 누르세요.");
this.CreateWeapon(choice22);
Console.ReadLine();
Console.WriteLine("갓 탄생한 {0}에게서 왠지 모를 강인함이 느껴진다...", mgr.gi.startWeapon.name);
}
else if (choice == "1")
{
int beforeNum = mgr.gi.startWeapon.reinforcement;
float beforeDamage = mgr.gi.startWeapon.damage;
Console.WriteLine("착용 중인 {0}를 강화해보자!", mgr.gi.startWeapon.name);
Console.WriteLine("주인장 드워프가 내 무기를 세차게 두들기고 담금질한다.");
Console.WriteLine("결과를 보려면 아무키나 누르세요.");
this.EnhanceWeapon();
Console.ReadLine();
Console.WriteLine("강화 성공!");
Console.WriteLine("{0} {1}단계 -> {2}단계 / 무기 공격력 : {3} -> {4}",
mgr.gi.startWeapon.name, beforeNum, mgr.gi.startWeapon.reinforcement,
beforeDamage, mgr.gi.startWeapon.damage);
}
else if (choice == "2")
{
Console.WriteLine("화로의 뜨거운 열기와 귀를 찌르는 금속음을 뒤로 하고 대장간을 나왔다.");
break;
}
}
}
private void CreateWeapon(int id)
{
weapon_data info = mgr.GetWeaponData(id);
Weapon weapon = new Weapon(info.id, info.name, info.damage, info.price, 1);
mgr.gi.EquipWeapon(weapon);
}
private void EnhanceWeapon()
{
mgr.gi.startWeapon.reinforcement++;
mgr.gi.startWeapon.damage += 1.5f;
}
}
}
선택지 분기점에서 enum을 적용해볼까도 했지만 이미 dict가 있고 선택지도 한정적이라 넣지 않았다.
꿈에서나 그릴 법한 무기 23강을 띄운 후, 현재 착용 중인 무기 정보를 확인하고 프로그램을 끝냈다.
5) 현재의 무기 상태 정보를 GameInfo 객체에 저장한 후, 그 객체를 직렬화하여 유저의 컴퓨터에 json 파일로 저장한다.
public App()
{
DataMgr mgr = DataMgr.GetInstance();
mgr.LoadData();
mgr.DiscernUserType();
new Smithy();
mgr.gi.CheckWeaponStatus();
mgr.SaveData();
}
class DataMgr
{
public GameInfo gi;
public void SaveData()
{
string saveJson = JsonConvert.SerializeObject(this.gi);
File.WriteAllText(GAME_INFO_PATH, saveJson);
}
}
6) 저장된 json 파일을 다시 불러와서 정보가 정상적으로 저장되었는지 확인한다.
싱글턴 패턴을 사용해보니,
어떤 클래스에서든, 주로 사용하는 클래스의 객체를 손쉽게 가져다 쓸 수 있다는 점이 정말 편리해서 좋았다.
언제 한 번 싱글턴 패턴의 단점에 대해서도 알아보면 좋을 듯
'C# > 수업 내용' 카테고리의 다른 글
22.01.17 텍스트기반 게임 만들기 Part.2 (0) | 2022.01.17 |
---|---|
22.01.12 싱글턴 패턴으로 텍스트 기반 게임 만들기 (0) | 2022.01.12 |
22.01.08 Dictionary 키값쌍과 JSON 연동 (0) | 2022.01.08 |
22.01.05 대리자 delegate Part.2 (0) | 2022.01.05 |
22.01.04 대리자 delegate (0) | 2022.01.04 |