오늘 배운 내용
-
개인과제 C# Reflection 사용
-
게임 저장 구현
-
팀 프로젝트 진행 - CuteEscaper
-
5주차 숙제 - leetcode 733 flood-fill
C# Reflection
개인과제에서 SceneManager가 List에 Scene을 추가할때 하나씩 객체를 생성해서 List에 Add하게되면
새로운 Scene이 제작될때마다 List에도 다시 Add 해줘야 한다.
하지만 C#의 Reflection 기능을 사용한다면 enum SceneType 타입을 추가하기만 하면
scene의 이름을 Enum.Getname()으로 받아와 Type.GetType()으로 Type 형태로 바꾼뒤
Activator.CreateInstance();으로 해당 Scene을 생성해서 넣어준다.
public SceneManager()
{
for (int i =0; i< (int)SceneType.EndPoint; i++)
{
SceneType selectedScene = (SceneType)i;
string sceneName = Enum.GetName(selectedScene);
Type type = Type.GetType($"TextBased_Dungeon_Game.{sceneName}");
Scene instance = (Scene)Activator.CreateInstance(type);
// Activator로 객체를 생성하려면 default 생성자가 있어야함
_scenes.Add(instance);
// Enum이름으로 Type을 구분해서 List에 추가
// Reflection 사용법 익히기
}
/*_scenes.Add(new StartScene(SceneType.StartScene));
_scenes.Add(new StatusScene(SceneType.StatusScene));
_scenes.Add(new InventoryScene(SceneType.InventoryScene));
_scenes.Add(new ShopScene(SceneType.ShopScene));
_scenes.Add(new DungeonEnterScene(SceneType.DungeonEnterScene));
_scenes.Add(new RestScene(SceneType.RestScene));
_scenes.Add(new EquipmentScene(SceneType.EquipmentScene));
_scenes.Add(new BuyScene(SceneType.BuyScene));
_scenes.Add(new SellScene(SceneType.SellScene));
_scenes.Add(new DungeonClearScene(SceneType.DungeonClearScene));
_scenes.Add(new DungeonFailScene(SceneType.DungeonFailScene));*/
}
게임 저장 구현
게임 저장 구현에 관한 부분은 구글링과 chat GPT를 통해서 검색을 해봤는데
일단 먼저 데이터를 저장하려는 클래스들에 [Serializable] 선언을 한 뒤
데이터를 직렬화하고 역직렬화하기 위한 클래스 BinaryFormatter() 객체를 생성해준다.
그리고 Stream을 FileStream으로 생성해 파일을 읽거나 쓰기 시작한다.
데이터를 저장할때는 FileMode.Create, FileAccess.Write를 매개변수로 주면 이미 파일이 있는 경우에도 덮어쓰기가 가능하고
데이터를 불러올떄는 FileMode.Open, FileAccess.Read를 매개변수로 주면 파일을 읽어올수 있다.
하지만 데이터를 불러올때는 파일이 없으면 stream이 null이 되기 때문에 예외처리를 통해 파일이 없다면 새로 객체를 생성하도록 만들어줬다.
static public void PlayerSave()
{
IFormatter formatter = new BinaryFormatter();
// 파일 저장을 위한 BinaryFormatter객체 생성
using (Stream stream = new FileStream("player_info.bin", FileMode.Create, FileAccess.Write))
{
// 스트림을 생성 , 파일을 생성해서 쓰기모드에 들어간다.(파일을 출력할 스트림을 생성)
formatter.Serialize(stream, player);
// player 객체의 정보를 직렬화하여 stream에 저장한다.
}
}
static public void ShopSave()
{
IFormatter formatter = new BinaryFormatter();
using (Stream stream = new FileStream("shop_info.bin", FileMode.Create, FileAccess.Write))
{
formatter.Serialize(stream, shop);
}
}
public void Read()
{
IFormatter formatter = new BinaryFormatter();
try
{
using (Stream stream = new FileStream("player_info.bin", FileMode.Open, FileAccess.Read))
{
// 스트림을 생성, 파일을 오픈해서 읽기모드에 들어간다. (파일을 입력받는 스트림을 생성)
player = (Warrior)formatter.Deserialize(stream);
// 스트림을 역직렬화해서 player에 정보를 넣어준다.
}
}
catch (SerializationException)
{
player = new Warrior();
}
try
{
using (Stream stream = new FileStream("shop_info.bin", FileMode.Open, FileAccess.Read))
{
// 스트림을 생성, 파일을 오픈해서 읽기모드에 들어간다. (파일을 입력받는 스트림을 생성)
shop = (Shop)formatter.Deserialize(stream);
// 스트림을 역직렬화해서 player에 정보를 넣어준다.
}
}
catch (SerializationException)
{
shop = new Shop();
}
}
팀 프로젝트 진행 - CuteEscaper
주말에 만들어둔 팀프로젝트 기능 코드들을 정리했다.
프리펩으로 대체할 수 있는 부분은 코드를 삭제하고 프리펩으로 대체했고,
GameManager에 모여있던 코드들을 SpringBoard, CharacterList로 기능 별로 분할했다.
그리고 Button 클래스를 새로만들어 모든 Button과 관련된 기능들은 Button클래스를 통해서 호출할 수 있도록 만들었다.
마지막으로 캐릭터가 골인 지점에 들어가면 OnTriggerEnter2D로 숨겨둔 오브젝트를 새로 설치해
더 이상 같은 골인 지점에 들어오지 못하도록 했고
다른 캐릭터가 막힌 골인지점 벽에 부딪히면 다른 곳으로 다시 튕겨져 나가도록 만들어줬다.
그리고 점프나 튕겨져 나가는 값들을 Random.Range()메서드를 통해서 일정한 범위안에서 랜덤으로 생성되도록 만들었다.
그리고 모든 충돌 부분은 캐릭터가 충돌하기 때문에 Character 클래스에서 관리하도록 만들었다.
public class Character : MonoBehaviour
{
public string name;
Rigidbody2D rb2D;
public bool isJumping = false;
private void Awake()
{
rb2D = transform.GetComponent<Rigidbody2D>();
}
void Start()
{
}
// Update is called once per frame
void Update()
{
GetCamera();
}
public void SizeUp()
{
transform.localScale = new Vector3(2.0f, 2.0f, 0);
}
public void DefaultSize()
{
transform.localScale = new Vector3(1.0f, 1.0f, 0);
}
public void GetCamera()
{
if (isJumping)
{
Vector3 targetPos = transform.localPosition;
Camera.main.transform.position = new Vector3(targetPos.x, targetPos.y, -10);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Branch"))
{
// Àå¾Ö¹° Ã浹ó¸®
}
else if (collision.gameObject.CompareTag("Closed"))
{
rb2D.AddForce(new Vector2(Random.Range(-25f, 25f), Random.Range(25f, 50f)));
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("TriggerObject"))
{
rb2D.AddTorque(0.05f);
rb2D.AddForce(new Vector2(Random.Range(-7f, -12f), 10f));
}
else if (collision.gameObject.CompareTag("EndPoint"))
{
transform.parent = collision.transform;
GameManager.Instance.SpringBoard.GetComponent<SpringBoard>().FillSpringBoard();
isJumping = false;
Camera.main.transform.position = new Vector3(0, 0, -10);
collision.transform.GetChild(0).gameObject.SetActive(true);
}
}
}
다음으로 구현해야 되는 기능은 나뭇가지 같은 장애물에 부딪히게 됬을때
튕겨 나가는 기능을 만들어 주어야 된다.
leetcode 733 flood-fill
1번 문제는 이상하게 어려운 문제였는데 2번 문제는 간단한 문제가 나왔다.
완전탐색으로 배열을 순회하면 풀수 있는 문제여서 BFS를 사용해서 풀었다
public class Solution
{
public int[][] FloodFill(int[][] image, int sr, int sc, int color)
{
//2차원 배열이 주어지고
//배열에는 0,1의 값이 들어있다
//배열에서 4방향을 고려해서 시작 부터 이어진 곳 1을 따라간다.
// 이어져있는곳을 전부 순회해야되기 때문에 완전탐색 dfs,bfs를 사용해야한다.
int[] dirX = { 0, 0, -1, 1 };
int[] dirY = { -1, 1, 0, 0 };
int maxX = image.Length;
int maxY = image[0].Length;
Queue<int[]> queue = new Queue<int[]>();
bool[,] visited = new bool[maxX, maxY];
int answer = image[sr][sc];
queue.Enqueue(new int[] { sr, sc });
image[sr][sc] = color;
while(queue.Count > 0)
{
int[] pos = queue.Dequeue();
for(int i =0; i < 4; i++)
{
int nextX = pos[0] + dirX[i];
int nextY = pos[1] + dirY[i];
if(nextY < 0 || nextY >= maxY || nextX<0 || nextX >= maxX)
{
continue;
}
if (image[nextX][nextY] != answer || visited[nextX,nextY])
{
continue;
}
queue.Enqueue(new int[] { nextX, nextY });
image[nextX][nextY] = color;
visited[nextX, nextY] = true;
}
}
return image;
}
}