1. 초기 구조의 한계
처음에는 다음과 같이 게임 규칙과 로직이 모두 MainForm 안에 포함되어 있었습니다.
public partial class MainForm : Form
{
int stageIndex = 0;
StageData[] stageData = new StageData[3];
int ballSpeedX = 7;
int ballSpeedY = 7;
int blockRow = 3;
int blockColumn = 5;
int blockWidth = 100;
}
이 구조에서는 다음과 같은 문제가 발생합니다.
- 스테이지 데이터와 게임 로직이 섞여 있어 가독성이 저하됩니다.
- Stage 관련 정보를 수정하려면 반드시 MainForm 내부를 수정해야 합니다.
- 다른 화면(Form)에서 동일 데이터를 공유하려면 또 복사하거나 별도 관리가 필요합니다.
즉, 데이터와 로직이 분리되지 않아 확장성과 유지보수성이 떨어지는 구조가 됩니다.
2. GameData 클래스로 데이터만 분리한 이유
게임의 규칙(난이도, 블록 배치, 공 속도 등)을 담당하는 부분을 따로 분리하면 다음과 같은 장점이 생깁니다.
로직과 데이터의 명확한 분리
GameData에는 게임 규칙만 존재하고, MainForm에는 움직임 및 충돌 처리와 같은 순수 로직만 남게 됩니다. 이를 통해 코드를 읽을 때 목적이 명확하게 구분됩니다.
규칙 변경이 쉬워짐
스테이지 개수, 공 속도, 블록 개수 등은 게임 규칙에 해당하며, 이러한 데이터는 GameData만 수정하면 전체 게임에 반영됩니다. 로직 코드를 건드릴 필요가 없습니다.
여러 화면에서 동일하게 접근 가능
TitleForm, ResultForm, OptionForm 등 앞으로 화면이 늘어날 때 데이터를 공유하기가 훨씬 편해집니다.
3. 왜 GameData를 static 클래스로 만들었는가
데이터 분리뿐 아니라 static 클래스로 만든 이유도 중요합니다.
new GameData() 방식의 문제점
만약 일반 클래스로 GameData를 만든다면, 다음과 같이 사용해야 합니다.
GameData data = new GameData();
MainForm form = new MainForm(data);
이 방식에서는 다음 문제가 생깁니다.
- GameData를 어디에서 new 할지 결정해야 합니다.
- Program.cs?
- MainForm 내부?
- TitleForm에서?
- 여러 Form에서 GameData 인스턴스를 공유하기 어렵습니다.
생성자를 통해 계속 전달하거나 전역 변수처럼 관리해야 합니다.
- 실수로 new를 여러 번 생성할 위험이 있습니다.
- 서로 다른 GameData 인스턴스가 존재하면 데이터 불일치 문제가 발생합니다.
즉, “전역에서 하나만 존재해야 하는 데이터”를 일반 클래스로 만들면 오히려 관리 난이도가 높아집니다.
static 클래스로 만들면 해결되는 점
static class는 다음과 같은 특징을 가집니다.
- 인스턴스 생성이 불가능합니다. (new GameData() 금지)
- 어디서든 바로 접근할 수 있습니다.
- static 생성자는 딱 한 번 자동으로 호출되며, 초기화 시점을 C# 런타임이 보장합니다.
즉, 게임의 규칙처럼 전역에서 하나만 존재해야 하는 데이터는 static이 가장 자연스러운 구조입니다.
internal static class GameData
{
public static StageData[] stageData;
public static int StageCount { get; set; }
public static StageData CurrentStage => stageData[StageCount];
static GameData()
{
StageCount = 0;
stageData = new StageData[3]
{
new StageData() { ballSpeed = 5, blockRow = 3, blockColumn = 5, blockWidth = 100 },
new StageData() { ballSpeed = 7, blockRow = 4, blockColumn = 6, blockWidth = 80 },
new StageData() { ballSpeed = 9, blockRow = 5, blockColumn = 7, blockWidth = 60 }
};
}
}
4. MainForm에서는 이렇게 간단하게 사용 가능
ballSpeedX = GameData.CurrentStage.ballSpeed;
ballSpeedY = GameData.CurrentStage.ballSpeed;
blockRow = GameData.CurrentStage.blockRow;
blockColumn= GameData.CurrentStage.blockColumn;
blockWidth = GameData.CurrentStage.blockWidth;
이제 메인 로직(MainForm)은 규칙을 신경 쓸 필요가 없습니다.
단지 “현재 스테이지의 규칙을 읽어와서 적용한다”는 행위만 수행하면 됩니다.
5. 결론
- 게임 규칙과 로직은 분리하는 것이 유지보수에 가장 유리합니다.
- 전역에서 하나만 존재해야 하는 규칙 데이터는 static 클래스로 관리하는 것이 가장 자연스럽습니다.
- static 생성자는 자동으로 한 번만 호출되기 때문에 초기화 타이밍에 대한 고민이 사라집니다.
- 여러 Form이나 클래스에서 손쉽게 접근할 수 있으며, 데이터 일관성이 보장됩니다.
따라서 GameData를 static 클래스로 구성하는 방식은 WinForms 기반 게임 구조에서 매우 실용적이며, 확장성 있는 설계 방식이라고 할 수 있습니다.
'C#' 카테고리의 다른 글
| C#에서 float 형 random 이 없는 이유와 그래도 필요하다면? (0) | 2025.11.16 |
|---|---|
| C# 에서 static GameData 클래스 설계 예시 (0) | 2025.11.15 |
| Dictionary 에서 구조체를 TValue 로 사용시, 주의할 점 (3) | 2025.07.24 |
| 컬렉션 (Collection) 의 깊이 있는 이해 (feat. IEnumerable, ICollection) (1) | 2025.07.24 |
| Dictionary 정리 : 기초부터 활용까지 (0) | 2025.07.23 |