Chapter 4. Text RPG
카테고리: C Sharp
태그: C Sharp Programming
인프런에 있는 Rookiss님의 강의 Part1: C# 기초 프로그래밍 입문 를 듣고 정리한 필기입니다. 😀
👱♀️ 직업 고르기
using System;
namespace CSharp
{
class Program
{
enum ClassType
{
None = 0,
Knight = 1,
Archer = 2,
Mage = 3
}
static ClassType ChooseClass()
{
Console.WriteLine("직업을 선택하세요");
Console.WriteLine("[1] 기사");
Console.WriteLine("[2] 궁수");
Console.WriteLine("[3] 법사");
ClassType choice = ClassType.None;
string input = Console.ReadLine();
switch (input)
{
case "1":
choice = ClassType.Knight;
break;
case "2":
choice = ClassType.Archer;
break;
case "3":
choice = ClassType.Mage;
break;
}
return choice;
}
static void Main(string[] args)
{
while (true)
{
ClassType choice; // 초기화 하지 않으면 자동으로 첫 번째 상수인 ClassType.None 으로 초기화 된다.
choice = ChooseClass(); // static함수라 클래스 이름으로 호출하는 Program.ChooseClass(); 와도 같다.
if (choice != ClassType.None) // 함수 내부에서 switch 문에 걸리지 않았다면, 즉 123 입력을 하지 않았다면 다시 반복. 제대로 123 중 하나 입력 했다면 직업 선택 그만함.
break;
}
}
}
}
ChooseClass()
가 일반 멤버 함수 였다면- 같은 Program 클래스 내에 있더라도 메인 함수에서
ChooseClass();
이렇게 호출하는건 불가능하다.- 왜냐면 메인 함수는 static 함수이기 때문에 어디서든 Program 객체 생성 없이 바로 Program 이름으로 호출이 가능한데 ChooseClass()는 일반 멤버 함수이므로 객체 생성이 있어야하지만 가능하기 때문이다.
- 따라서 Program 타입의 인스턴스를 만들고 인스턴스를 통해 호출해야 한다.
- 메인 함수 같은 static 함수가 아닌 같은 일반 멤버 함수 내에서 호출된거라면 같은 클래스니까
ChooseClass();
호출 가능
- 같은 Program 클래스 내에 있더라도 메인 함수에서
ChooseClass()
가 static 함수여서- 같은 static 함수인
ChooseClass()
도 객체 생성이 필요 없기 때문에 그냥 메인 함수 내에서ChooseClass();
이렇게 호출이 가능한 것이다. - 정석대로 클래스 이름 호출로
Program.ChooseClass();
이렇게도 가능하다. 근데 같은 클래스 내부니까 클래스 이름 생략 가능.
- 같은 static 함수인
👩🏼 플레이어 생성
class Program
{
struct Player
{
public int hp;
public int attack;
public ClassType type;
}
static void CreatePlayer(ClassType choice, out Player player)
{
player.type = choice;
// 기사(100, 10) 궁수(75, 12) 법사(50, 15)
switch (choice)
{
case ClassType.Knight:
player.hp = 100;
player.attack = 10;
break;
case ClassType.Archer:
player.hp = 75;
player.attack = 12;
break;
case ClassType.Mage:
player.hp = 50;
player.attack = 15;
break;
default:
player.hp = 0;
player.attack = 0;
break;
}
}
static void Main(string[] args)
{
while (true)
{
// 직업 고르기
ClassType choice;
choice = ChooseClass(); // Program.ChooseClass();
if (choice != ClassType.None)
{
// 플레이어 캐릭터 생성
Player player;
CreatePlayer(choice, out player);
Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
}
}
}
}
- 플레이어 특성 구조체로 관리
hp
,attack
만 사용했지만mp
,region
등등 많은 특성이 생길 수 있다.- 그래서 일일이 다 변수로 선언하기 보단 구조체로 묶어서 관리하는게 좋다.
- 구조체 변수들도 접근 지정자가 있다. 지정 안해주면 디폴트로
private
이 되서player.hp
이렇게 호출할 수가 없다. - 구조체 매개 변수를
out
으로 하면 구조체의 모든 멤버 변수들을 전부 다 빠짐 없이 write 해야 한다.out
은 엄격하기 때문에 case 에 걸리지 않아 write 되지 않을 것도 생각해서인지 모든 case에 대해 write 해주지 않으면 컴파일 오류 난다.default
에도 꼭 write 해주어야 한다.
Player player;
CreatePlayer(choice, out player);
아무런 초기화 되어 있지 않던 player
가 CreatePlayer 함수 호출이 끝난 후 멤버 변수들이 전부 다 설정된다! 초기화 안되 있었던 상태니, 즉 메모리 할당이 되어 있지 않은 상태라 ref
는 쓸 수 없다. out
은 메모리 할당 안되어 있는 변수를 참조하는 것이라도 함수 내에서 write 할 것이라는게 보장 되어 있기 때문에 문제 되지 않는다.
👩🏼 몬스터 생성
enum MonsterType
{
None = 0,
Slime = 1,
Orc = 2,
Skeleton = 3
}
struct Monster
{
public int hp;
public int attack;
}
static void CreateRandomMonster(out Monster monster)
{
Random rand = new Random();
int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴
switch(randMonster)
{
case (int)MonsterType.Slime:
Console.WriteLine("슬라임이 스폰 되었습니다!");
monster.hp = 20;
monster.attack = 2;
break;
case (int)MonsterType.Orc:
Console.WriteLine("오크가 스폰 되었습니다!");
monster.hp = 40;
monster.attack = 4;
break;
case (int)MonsterType.Skeleton:
Console.WriteLine("스켈레톤이 스폰 되었습니다!");
monster.hp = 30;
monster.attack = 3;
break;
default:
monster.hp = 0;
monster.attack = 0;
break;
}
}
static void EnterField()
{
Console.WriteLine("필드에 접속했습니다.");
// 랜덤으로 1~3 몬스터 중 하나를 리스폰
Monster monster;
CreateRandomMonster(out monster);
}
static void EnterGame()
{
while(true)
{
Console.WriteLine("마을에 접속했습니다.");
Console.WriteLine("[1] 월드로 간다.");
Console.WriteLine("[2] 로비로 돌아가기.");
String input = Console.ReadLine();
switch (input)
{
case "1":
EnterField();
break;
case "2":
return;
}
}
}
static void Main(string[] args)
{
while (true)
{
// 직업 고르기
ClassType choice;
choice = ChooseClass(); // Program.ChooseClass();
if (choice != ClassType.None)
{
// 플레이어 캐릭터 생성
Player player;
CreatePlayer(choice, out player);
Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
// 게임 시작! 몬스터 생성 및 전투
EnterGame();
}
}
}
}
static void CreateRandomMonster(out Monster monster)
{
Random rand = new Random();
int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴
switch(randMonster)
{
case (int)MonsterType.Slime:
randMonster
는int
형이기 때문에 case문에서(int)MonsterType.Slime
이렇게 형변환 해주어야 한다.- monster 구조체의 모든 멤버 변수들을 전부 다 write 해야 한다. write 되지 않을 케이스가 있어서도 안된다.
static void EnterGame()
{
while(true)
{
Console.WriteLine("마을에 접속했습니다.");
Console.WriteLine("[1] 월드로 간다.");
Console.WriteLine("[2] 로비로 돌아가기.");
String input = Console.ReadLine();
switch (input)
{
case "1":
EnterField();
break;
case "2":
return;
}
}
}
2를 선택하면 로비로 돌아가게 하고 싶다. 즉, 다시 직업을 고르도록 Main 함수로 돌아가고 싶다. 따라서 case "2"
의 경우엔 break
가 아닌 그냥 return
을 해주어 아예 EnterGame 함수를 종료시킨다.
👩🏼 전투
static void Fight(ref Player player, ref Monster monster)
{
while(true)
{
// 플레이어가 몬스터 공격
monster.hp -= player.attack;
if (monster.hp <= 0)
{
Console.WriteLine("승리했습니다!");
Console.WriteLine($"남은 체력 : {player.hp}");
break;
}
// 몬스터 반격
player.hp -= monster.attack;
if (player.hp <= 0)
{
Console.WriteLine("패배했습니다!");
break;
}
}
}
static void EnterField(ref Player player)
{
Console.WriteLine("필드에 접속했습니다.");
// 랜덤으로 1~3 몬스터 중 하나를 리스폰
Monster monster;
CreateRandomMonster(out monster);
Console.WriteLine("[1] 전투 모드 돌입");
Console.WriteLine("[2] 일정 확률로 마을로 도망");
string input = Console.ReadLine();
if (input == "1")
{
Fight(ref player, ref monster);
}
else if (input == "2")
{
// 33 %
Random rand = new Random();
int randValue = rand.Next(0, 101);
if (randValue <= 33)
{
Console.WriteLine("도망치는데 성공했습니다!");
}
else
{
Fight(ref player, ref monster);
}
}
}
static void EnterGame(ref Player player)
{
while(true)
{
Console.WriteLine("마을에 접속했습니다.");
Console.WriteLine("[1] 월드로 간다.");
Console.WriteLine("[2] 로비로 돌아가기.");
String input = Console.ReadLine();
switch (input)
{
case "1":
EnterField(ref player);
break;
case "2":
return;
}
}
}
static void Main(string[] args)
{
while (true)
{
// 직업 고르기
ClassType choice;
choice = ChooseClass(); // Program.ChooseClass();
if (choice == ClassType.None)
continue;
// 플레이어 캐릭터 생성
Player player;
CreatePlayer(choice, out player);
Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
// 게임 시작! 몬스터 생성 및 전투
EnterGame(ref player);
}
}
}
Fight 함수에서 player
구조체가 필요하기 때문에 인수로 전달 받아야 한다. Call by Value로 해도 나쁘지 않지만, EnterGame 👉 EnterField 👉 Figt 이 순으로 호출되며 player
를 전달해야 하기 때문에 그냥 계속 해서 사본 만드는 과정을 거치기 보다는 사본 생성 과정 없이 ref
로 원본 player
를 참조할 수 있도록 하였다.
Player player;
CreatePlayer(choice, out player);
위의 과정으로 player
는 현재 메모리가 할당 되어 있는 상태기 때문에 ref
참조로 넘길 수 있다.
👩🦰 전체 코드
using System;
namespace CSharp
{
class Program
{
enum ClassType
{
None = 0,
Knight = 1,
Archer = 2,
Mage = 3
}
struct Player
{
public int hp;
public int attack;
public ClassType type;
}
enum MonsterType
{
None = 0,
Slime = 1,
Orc = 2,
Skeleton = 3
}
struct Monster
{
public int hp;
public int attack;
}
static ClassType ChooseClass()
{
Console.WriteLine("직업을 선택하세요");
Console.WriteLine("[1] 기사");
Console.WriteLine("[2] 궁수");
Console.WriteLine("[3] 법사");
ClassType choice = ClassType.None;
string input = Console.ReadLine();
switch (input)
{
case "1":
choice = ClassType.Knight;
break;
case "2":
choice = ClassType.Archer;
break;
case "3":
choice = ClassType.Mage;
break;
}
return choice;
}
static void CreatePlayer(ClassType choice, out Player player)
{
player.type = choice;
// 기사(100, 10) 궁수(75, 12) 법사(50, 15)
switch (choice)
{
case ClassType.Knight:
player.hp = 100;
player.attack = 10;
break;
case ClassType.Archer:
player.hp = 75;
player.attack = 12;
break;
case ClassType.Mage:
player.hp = 50;
player.attack = 15;
break;
default:
player.hp = 0;
player.attack = 0;
break;
}
}
static void CreateRandomMonster(out Monster monster)
{
Random rand = new Random();
int randMonster = rand.Next(1, 4); // 1 ~ 3 중 랜덤 정수 리턴
switch(randMonster)
{
case (int)MonsterType.Slime:
Console.WriteLine("슬라임이 스폰 되었습니다!");
monster.hp = 20;
monster.attack = 2;
break;
case (int)MonsterType.Orc:
Console.WriteLine("오크가 스폰 되었습니다!");
monster.hp = 40;
monster.attack = 4;
break;
case (int)MonsterType.Skeleton:
Console.WriteLine("스켈레톤이 스폰 되었습니다!");
monster.hp = 30;
monster.attack = 3;
break;
default:
monster.hp = 0;
monster.attack = 0;
break;
}
}
static void Fight(ref Player player, ref Monster monster)
{
while(true)
{
// 플레이어가 몬스터 공격
monster.hp -= player.attack;
if (monster.hp <= 0)
{
Console.WriteLine("승리했습니다!");
Console.WriteLine($"남은 체력 : {player.hp}");
break;
}
// 몬스터 반격
player.hp -= monster.attack;
if (player.hp <= 0)
{
Console.WriteLine("패배했습니다!");
break;
}
}
}
static void EnterField(ref Player player)
{
Console.WriteLine("필드에 접속했습니다.");
// 랜덤으로 1~3 몬스터 중 하나를 리스폰
Monster monster;
CreateRandomMonster(out monster);
Console.WriteLine("[1] 전투 모드 돌입");
Console.WriteLine("[2] 일정 확률로 마을로 도망");
string input = Console.ReadLine();
if (input == "1")
{
Fight(ref player, ref monster);
}
else if (input == "2")
{
// 33 %
Random rand = new Random();
int randValue = rand.Next(0, 101);
if (randValue <= 33)
{
Console.WriteLine("도망치는데 성공했습니다!");
}
else
{
Fight(ref player, ref monster);
}
}
}
static void EnterGame(ref Player player)
{
while(true)
{
Console.WriteLine("마을에 접속했습니다.");
Console.WriteLine("[1] 월드로 간다.");
Console.WriteLine("[2] 로비로 돌아가기.");
String input = Console.ReadLine();
switch (input)
{
case "1":
EnterField(ref player);
break;
case "2":
return;
}
}
}
static void Main(string[] args)
{
while (true)
{
// 직업 고르기
ClassType choice;
choice = ChooseClass(); // Program.ChooseClass();
if (choice == ClassType.None)
continue;
// 플레이어 캐릭터 생성
Player player;
CreatePlayer(choice, out player);
Console.WriteLine($"HP{player.hp}, Attack{player.attack}");
// 게임 시작! 몬스터 생성 및 전투
EnterGame(ref player);
}
}
}
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기