이전 TextRPG와 다르게 객체지향으로 설계
- 코드가 깔끔해짐
- 확장성이 좋음
TextRPG에서는 절차지향으로의 제약사향이 있었음
1. 게임이 로비 -> 타운 -> 필드순으로 규칙적으로 진행
순서를 무조건 가정하고 진행했음
로비 다음은 타운이고, 타운 다음은 필드라고 생각했음(절차지향)
객체지향에서는 순서를 가정하지 않고 개발함, 다음에 갈 곳을 정해주기만 하면 됨
Game 매니저 클래스를 만들고 mode 맴버 변수를 활용하여 switch 안에서 각각의 케이스만 추가해주면 된다
먄약, 기획자가 로비에서 캐릭터를 고르고 바로 필드로 가게 해달라고 한다면
객체지향에서는 다음에 갈 곳만 mode 값으로 지정해주면 됨
로직
Main() 함수에서 while문으로 Game 클래스의 Process() 함수를 호출
계속 반복되고 switch 안에서 맴버 mode를 통해 어떻게 동작할지 달라짐
맴버 mode가 값이 변경된다면 switch 안에서 다르게 동작할 것임
파일 구성
- Creature.cs
클래스 : Creature
Player와 Monster의 최상위 클래스 Creature
필드 : type(Player or Monster), hp, attack
함수 : SetInfo(hp, attack), GetHp(), GetAttack(), isDead(), OnDamage()
- Game.cs
클래스 : Game
게임진행 및 전반적인 사항 진행
필드 : mode(None, Lobby, Town, Field), player, monster, rand
함수 : Process(), ProcessLobby(), ProcessTown(), ProcessField(), TryEscape(), ProcessFight(), CreateRandomMonster()
- Monster.cs
클래스 : Monster(Creature 상속)
몬스터
필드 : type(None, Slime, Orc, Skeleton)
함수 : GetMonsterType()
클래스 : Slime(Monster 상속)
슬라임
클래스 : Orc(Monster 상속)
오크
클래스 : Skeleton(Monster 상속)
스켈레톤
- Player.cs
클래스 : Player(Creature 상속)
몬스터
필드 : type(None, Knight, Archer, Mage)
함수 : GetPlayerType()
클래스 : Knight(Monster 상속)
기사
클래스 : Archer(Monster 상속)
궁수
클래스 : Mage(Monster 상속)
마법사
- Program.cs
Main() 함수로 진행
Game 클래스로 객체 생성하여 Process() 함수 호출하여 게임 진행
전체 코드
- Creature.cs
namespace Rookiss_CSharp
{
public enum CreatureType // 크리처가 플레이어인지, 몬스터 타입인지
{
None,
Player = 1,
Monster = 2
}
// Creature : 살아있는 생물
class Creature
{
protected CreatureType type;
protected int hp = 0;
protected int attack = 0;
protected Creature(CreatureType type)
{
this.type = type;
}
public void SetInfo(int hp, int attack)
{
this.hp = hp;
this.attack = attack;
}
public int GetHp() { return hp; }
public int GetAttack() { return attack; }
public bool isDead() { return hp <= 0; }
public void OnDamaged(int damage)
{
hp -= damage;
if (hp < 0)
hp = 0;
}
}
}
- Game.cs
namespace Rookiss_CSharp
{
public enum GameMode
{
None,
Lobby,
Town,
Field
}
// 게임진행 및 전반적인 사항 진행
class Game
{
private GameMode mode = GameMode.Lobby;
private Player player = null;
private Monster monster = null;
private Random rand = new Random();
public void Process()
{
switch (mode)
{
case GameMode.Lobby:
ProcessLobby();
break;
case GameMode.Town:
ProcessTown();
break;
case GameMode.Field:
ProcessField();
break;
}
}
private void ProcessLobby()
{
Console.WriteLine("직업을 선택하세요");
Console.WriteLine("[1] 기사");
Console.WriteLine("[2] 궁수");
Console.WriteLine("[3] 법사");
string input = Console.ReadLine();
switch (input)
{
case "1":
player = new Knight();
mode = GameMode.Town;
break;
case "2":
player = new Archer();
mode = GameMode.Town;
break;
case "3":
player = new Mage();
mode = GameMode.Town;
break;
}
}
private void ProcessTown()
{
Console.WriteLine("마을에 입장했습니다.");
Console.WriteLine("[1] 필드로 가기");
Console.WriteLine("[2] 로비로 돌아가기");
string input = Console.ReadLine();
switch (input)
{
case "1":
mode = GameMode.Field;
break;
case "2":
mode = GameMode.Lobby;
break;
}
}
private void ProcessField()
{
Console.WriteLine("필드에 입장했습니다.");
Console.WriteLine("[1] 싸우기");
Console.WriteLine("[2] 일정 확률로 마을 돌아가기");
CreateRandomMonster();
string input = Console.ReadLine();
switch (input)
{
case "1":
ProcessFight();
break;
case "2":
TryEscape();
break;
}
}
private void TryEscape()
{
int randValue = rand.Next(0, 101); // 0 ~ 100
if (randValue < 33) // 33% 미만이면 마을 돌아가기
{
mode = GameMode.Town;
}
else // 그 외에는 실패 : 싸우기
{
ProcessFight();
}
}
private void ProcessFight()
{
while (true)
{
int damage = player.GetAttack();
monster.OnDamaged(damage);
if (monster.isDead())
{
Console.WriteLine("승리했습니다.");
Console.WriteLine($"남은 체력: {player.GetHp()}");
break;
}
damage = monster.GetAttack();
player.OnDamaged(damage);
if (player.isDead())
{
Console.WriteLine("패배했습니다.");
mode = GameMode.Lobby;
break;
}
}
}
private void CreateRandomMonster()
{
int randValue = rand.Next(0, 3); // 0, 1, 2
switch (randValue)
{
case 0:
monster = new Slime();
Console.WriteLine("슬라임이 생성되었습니다.");
break;
case 1:
monster = new Orc();
Console.WriteLine("오크가 생성되었습니다.");
break;
case 2:
monster = new Skeleton();
Console.WriteLine("스켈레톤이 생성되었습니다.");
break;
}
}
}
}
- Monster.cs
namespace Rookiss_CSharp
{
public enum MonsterType
{
None = 0,
Slime = 1,
Orc = 2,
Skeleton = 3
}
class Monster : Creature
{
protected MonsterType type = MonsterType.None;
protected Monster(MonsterType type) : base(CreatureType.Monster)
{
this.type = type;
}
public MonsterType GetMonsterType() { return type; }
}
class Slime : Monster
{
public Slime() : base(MonsterType.Slime)
{
SetInfo(10, 10);
}
}
class Orc : Monster
{
public Orc() : base(MonsterType.Orc)
{
SetInfo(20, 15);
}
}
class Skeleton : Monster
{
public Skeleton() : base(MonsterType.Skeleton)
{
SetInfo(15, 25);
}
}
}
- Player.cs
namespace Rookiss_CSharp
{
public enum PlayerType
{
None = 0,
Knight = 1,
Archer = 2,
Mage = 3
}
class Player : Creature
{
// 네이밍 컨벤션(naming Convention)
// 맴버 변수명 명명 규칙이 프로젝트마다 굉장히 다름(_변수명으로 하는 곳도 있고 맴버 변수라고 m_변수명 하는 곳도 있음)
// _변수명할시 _로 보면 내부 변수임을 확인이 용이함
// protected PlayerType _type;
protected PlayerType type = PlayerType.None;
protected Player(PlayerType type) : base(CreatureType.Player) // 외부에서 Player 자체를 생성할 수 없도록(외부에서 파생 클래스로 생성해야함) protected로 하고, PlayerType을 무조건 가지도록 생성자 파라미터 받음
{
// _type = type;
this.type = type;
}
public PlayerType GetPlayerType() { return type; }
}
class Knight : Player
{
public Knight() : base(PlayerType.Knight)
{
SetInfo(100, 10);
}
}
class Archer : Player
{
public Archer() : base(PlayerType.Archer)
{
SetInfo(75, 12);
}
}
class Mage : Player
{
public Mage() : base(PlayerType.Mage)
{
SetInfo(50, 15);
}
}
}
- Program.cs
namespace Rookiss_CSharp
{
class Program()
{
static void Main(string[] args)
{
// C++은 .h include 해줘야 하는데, C#은 동일한 네임스페이스를 사용한다면 타 .cs 파일에 있어도 불러올 수 있음
// Player player = new Knight();
Game game = new Game();
while (true)
{
game.Process();
}
}
}
}
출처
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문| Rookiss - 인프런 강
현재 평점 4.9점 수강생 6,987명인 강의를 만나보세요. 기초 프로그래밍 지식이 없는 사람들을 위한 C# 프로그래밍 기초 강의입니다. 문법 암기 위주의 수업이 아니라, 최대한 필요한 부분만을 요
www.inflearn.com
45강 TextRPG2
'C# > 기본 문법' 카테고리의 다른 글
| C# 배열 연습문제 (0) | 2025.11.18 |
|---|---|
| C# 배열 (0) | 2025.11.13 |
| C# 접근 제한자 , 어셈블리란? (0) | 2025.11.05 |
| C# 문자열 둘러보기 (0) | 2025.11.05 |
| C# 다형성 (0) | 2025.11.05 |