| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- git
- Online Judge
- Network Programming
- BOJ
- Data Structure
- 독서
- Unity
- System Programming
- PS
- c#
- Toy Project
- multi-thread
- C++
- Today
- Total
I'm FanJae.
[My Turn Based Console RPG] Day 1. 기본 구상 및 메인 화면 구현 본문
[My Turn Based Console RPG] Day 1. 기본 구상 및 메인 화면 구현
FanJae 2026. 5. 14. 08:551. 시작에 앞서
- 부트캠프 과제 중 하나였지만, 퀄리티를 높일 수 있는 부분은 최대한 높여서 Toy Project로서 진행해 보면 의미가 있을 것 같았다.
2. 기본 명세 구현 및 정리
1) 턴제 전투 구현
- 과제의 기본 전제가 턴제 RPG의 구현이었다.
- 따라서, 턴제 시스템에 맞게 배틀 시스템을 처리하였고, 우선 다음과 같은 형태로 구상하였다.
namespace MyConsoleMapleRPG.Systems
{
internal class BattleSystem // 기본 배틀 시스템
{
private Player player;
private Monster monster;
public BattleSystem(Player player, Monster monster) // 기본적으로 배틀은 1vs1. 추후 늘어날수도?
{
this.player = player;
this.monster = monster;
}
public void Start() // 배틀 시작 메서드
{
while(player.Hp > 0 && monster.Hp > 0) // 한명이라도 체력이 0으로 내려가면 종료.
{
player.Action(monster); // 선공은 기본적으로 플레이어 먼저
if (monster.Hp == 0)
{
break;
}
monster.Action(player); // 몬스터 후공
}
if (player.Hp == 0) Console.WriteLine($"{monster.Name}과의 전투에서 패배하였습니다.");
else if (monster.Hp == 0) Console.WriteLine($"{monster.Name}과의 전투에서 승리하였습니다.");
else if (player.Hp == 0 && monster.Hp == 0) Console.WriteLine($"무승부로 싸움이 종료되었습니다.");
}
}
}
2) 캐릭터 구현
- 캐릭터라는 추상 클래스를 만들었고, 이 캐릭터는 Player(유저가 컨트롤 할 캐릭터)와 Monster를 모두 포함할 것이다.
- 내가 설계한 모든 캐릭터는 기본적으로 이름, 체력, 공격력, 방어력을 가지며, 데미지를 줄 수 있는 존재다.
- 이 캐릭터들은 모두 배틀 시스템에서 활용될 Action()을 가지는데 이는 Monster의 경우 공격, Player의 경우는 턴제 게임에서 흔히 볼 수 있는 행동 (전투, 아이템 사용, 도망) 등을 추가해보려고 한다.
namespace MyConsoleMapleRPG.Character
{
abstract class Character
{
private string name;
private int hp;
private int attack;
private int defense;
public Character(string name, int hp, int attack, int defense) // 캐릭터 생성자
{
Name = name;
Hp = hp;
Attack = attack;
Defense = defense;
}
public string Name // 이름 프로퍼티
{
get { return name; }
protected set { name = value; }
}
public int Hp // 체력 프로퍼티
{
get { return hp; }
protected set
{
if (value <= 0) hp = 0;
else hp = value;
}
}
public int Attack // 공격력 프로퍼티
{
get { return attack; }
protected set
{
if (attack <= 0) attack = 1;
else attack = value;
}
}
public int Defense // 방어력 프로퍼티
{
get { return defense; }
protected set
{
if (defense <= 0) defense = 1;
else defense = value;
}
}
public virtual void TakeDamage(int damage) // 모든 Character는 Damage라는 것을 줄 수 있다.
{
}
public abstract void Action(Character target); // 모든 Character는 Action을 상속받아 처리한다. (Battle시 사용)
}
}
3) 플레이어, 몬스터 분리
namespace MyConsoleMapleRPG.Character
{
internal abstract class Player : Character
{
public Player() : base("기본 캐릭터", 50, 10, 5) { }
public Player(string name) : base(name, 50, 10, 5) { }
public Player(string name, int hp) : base(name, hp, 10, 5) { }
public Player(string name, int hp, int attack, int defense) : base(name, hp, attack, defense) { }
public override void Action(Character target) // 추후 변경
{
Console.WriteLine($"{Name}이 {target.Name}을 공격합니다.");
target.TakeDamage(Attack);
}
public abstract void UseSkill(Character target);
}
}
namespace MyConsoleMapleRPG.Character
{
internal class Monster : Character
{
public Monster() : base("기본 몬스터", 50, 10, 5) { }
public Monster(string name) : base(name, 50, 10, 5) { }
public Monster(string name, int hp) : base(name, hp, 10, 5) { }
public Monster(string name, int hp, int attack, int defense) : base(name, hp, attack, defense) { }
public override void Action(Character target) // 추후 변경
{
Console.WriteLine($"{Name}이 {target.Name}을 공격합니다.");
target.TakeDamage(Attack);
}
}
}
- 아직 이 쪽에 대한 구체적인 시나리오는 나오지 않았다.
- 기본적인 구상을 잡아 놓은 상태에서 어떻게 뻗어나갈지를 더 생각해봐야 할 것 같다.
- 다만 Playerble Character는 전사, 마법사 클래스로 할 예정이고, 스킬 셋도 그것으로 가져올 예정이다.
4) 콘솔 렌더링 분리
using System.Drawing;
namespace MyConsoleMapleRPG.UI
{
internal static class ConsoleRenderer
{
public static void DrawWindow(int x, int y, int width, int height, string title = "") // 창 밖의 틀 표현
{
Console.SetCursorPosition(x, y);
Console.Write("┌");
for (int i = 0; i < width - 2; i++)
{
Console.Write("─");
}
Console.Write("┐");
for (int row = 1; row < height - 1; row++)
{
Console.SetCursorPosition(x, y + row);
Console.Write("│");
for (int col = 0; col < width - 2; col++)
{
Console.Write(" ");
}
Console.Write("│");
}
Console.SetCursorPosition(x, y + height - 1);
Console.Write("└");
for (int i = 0; i < width - 2; i++)
{
Console.Write("─");
}
Console.Write("┘");
if (title != "")
{
Console.SetCursorPosition(x + 3, y);
Console.Write(title);
}
}
public static void DrawImageAsAscii(string Path, int targetWidth, int x = 0, int y = 0)
{
using Bitmap original = new Bitmap(Path);
using Bitmap bitmap = new Bitmap(original, new Size(32, 22)); // 가로, 세로
for (int row = 0; row < bitmap.Height; row++)
{
Console.SetCursorPosition(x, y + row);
for (int col = 0; col < bitmap.Width; col++)
{
Color color = bitmap.GetPixel(col, row);
if (color.A == 0) // 투명셀 무시
{
Console.SetCursorPosition(x + col + 1, y + row);
continue ;
}
Console.Write($"\x1b[48;2;{color.R};{color.G};{color.B}m ");
}
}
Console.Write("\x1b[0m");
}
}
}
- 이전 프로젝트를 진행할 때는 처음에 분리 안하고 나중에 분리해 놓는 경우가 많았다.
- 이번에는 그냥 미리 분리 해놨고, 가능하면 콘솔 화면 렌더링은 이 클래스만 담당하게 하는 것이 목적이다.
- DrawWindows 는 화면의 겉 부분을 구현하는 쪽이고, DrawImageAsASCII는 이미지 파일의 픽셀 값을 가져와서 화면에 직접 찍는 방식이다. (도트를 찍을 때 그것과 유사하다.)
5) 게임 컨트롤러 처리
using MyConsoleMapleRPG.Enums;
using MyConsoleMapleRPG.UI;
using MyConsoleMapleRPG.UI.Map;
namespace MyConsoleMapleRPG.Systems
{
internal class GameController
{
private readonly MainMenu mainMenu = new MainMenu();
public void Run()
{
ConsoleSetting.Initialize();
while (true)
{
MainMenuResult result = mainMenu.Show(); // 메뉴 창 선택
switch(result)
{
case MainMenuResult.StartGame:
StartGame();
break;
case MainMenuResult.LoadGame:
// LoadGame();
break;
case MainMenuResult.Credits:
// ShowCredits();
break;
case MainMenuResult.Exit:
return ;
}
}
}
private void StartGame()
{
CharacterSelectScreen characterSelectScreen = new CharacterSelectScreen();
int selectedJob = characterSelectScreen.Show();
if (selectedJob == -1)
{
return;
}
MapScreen mapscreen = new MapScreen();
mapscreen.Show();
}
}
}
- 게임의 시작에서 종료까지 여기서 담당할 것이다.
- 다만, 기존 오목처럼 룰과 관련된 정보를 이곳에 포함 시켜야 할지 아니면, 별도로 나눠서 판단할 지에 대해서는 아직 조금 더 고민이 필요하다.
3. 진행 사항
1) 기본 화면 구현

- MapleStory의 로고를 아스키 아트로 변환하는 사이트를 통해서 아스키 아트로 변환해주었다.
- 아스키 아트의 변환은 해당 사이트를 이용하였다.
링크 : https://wepplication.github.io/tools/asciiArtGen/
아스키아트 변환 사이트
텍스트 및 이미지(URL,파일첨부)를 아스키코드로 만드는 아스키아트 생성 사이트
wepplication.github.io

- 원래는 캐릭터를 넣고 싶었지만, 인게임에서 활용 중인 도트가 상당히 커서 콘솔 화면에 담으려면 게임 화면이 상당히 커야 할 것 같아 해당 직업을 잘 나타내 주는 무기(검, 스태프)로 대체하였다.
4. 진행 회고
아직 정리가 되지 않은 부분이 많아서 많이 난잡하다. 기본 틀은 잡아놨으니까 여기서 어떻게 추가해야할지 생각해 봐야겠다.
'Projects > My Turn Based Console RPG' 카테고리의 다른 글
| [My Turn Based Console RPG] Day 5. 프로젝트 최종 정리 (0) | 2026.05.19 |
|---|---|
| [My Turn Based Console RPG] Day 5. 저장 / 불러오기 기능 추가 (0) | 2026.05.19 |
| [My Turn Based Console RPG] Day 4. 아이템, 상점, 인벤토리 기능 구현 (0) | 2026.05.18 |
| [My Turn Based Console RPG] Day 3. 배틀 시스템 구현 및 기능 단위 클래스 분리 (0) | 2026.05.17 |
| [My Turn Based Console RPG] Day 2. 직업 선택 페이지 & 기본 UI & 맵 관련 기능 구현 (0) | 2026.05.16 |