오늘부터 팀원들과 함께 C# 팀 프로젝트를 진행하기로 했다.
프로젝트를 어떻게 진행할지에 대해 대략적인 구상을 하고 역할 분담을 했다. 회의 이후 C# 문자열 특강이 있어서 일단 이걸 먼저 진행하도록 하겠다.
string name = "예준";
int age = 27;
Console.WriteLine("이름: "+ name + ", 나이: " + age);
Console.WriteLine($"이름:{name}, 나이:{age}");
//사용 예시: 채팅 필터링에서 욕설이나 금칙어를 가릴 때
string testString = "야 너 바보야?";
string filtered = testString.Replace("바보", "***");
Console.WriteLine(filtered);
//출력: 야 너 ***야?
string testString = " Hello ";
string filtered = testString.Trim();
string filtered2 = testString.ToLower();
string filtered3 = testString.ToUpper();
Console.WriteLine(filtered);
Console.WriteLine(filtered2);
Console.WriteLine(filtered3);
string testString = "오늘 점심은 닭가슴살입니다.";
Console.WriteLine(testString.Contains("치킨"));
//출력: False
string testString = "yj6662@naver.com";
int atIndex = testString.IndexOf("@");
Console.WriteLine($"인덱스번호 : {atIndex}");
//출력 : 6
Split
문자열 나누기
Substring
부분 문자열 추출
StringBuilder
많은 문자열을 효율적으로 조립
StringBuilder sb = new StringBuiler();
for (int i = 0; i < 50000; i++)
{
sb AppendLine("로그 메시지" + i);
}
Console.WriteLine(sb.ToString());
string[] item = { "a", "b", "c", "d" };
Console.WriteLine(string.Join("*",item));
StartsWith/EndsWith
문자열 시작/끝 확인
Length/Insert/Remove
문자열 길이 및 삽입/삭제
Equals/Compare
문자열 비교
Regex
정규 표현식으로 고급 패턴 처리
우리 팀원들은 어떤 방향으로 게임을 구현할지를 고민해보고, 각자 역할을 분담하였다. 나는 전투기능 전반을 담당하기로 했다.
우리 팀은 일단 전체적인 게임의 틀을 구현하여 Main branch에 올리고, 각 기능을 맡은 사람이 기능별 branch를 따로 만들어 구현한 뒤, 완성된 코드를 Main branch에 merge하는 방식으로 작업을 진행하기로 했다.
기획을 담당한 팀원이 전투 기능을 기획한 바는 다음과 같았다.
전투
전투 시작 시 1~4마리 몬스터가 랜덤 스폰
몬스터는 총 3종류
LV2 미니언
HP 15
ATK 5
LV3 공허충
HP 15
ATK 9
LV5 대포 미니언
HP 25
ATK 8
내 턴에 할 수 있는 것은 공격 밖에 없음
공격
적 차례
전투 결과
public enum Monsters { Minion, VoidInsect, CannonMinion }
Monsters 열거형(enum)을 통해 몬스터의 종류를 세 가지로 정의
Monster 클래스를 통해 몬스터가 가져야 할 능력치를 설정.
public class Monster
{
public Monsters MonsterType { get; set; }
public int monMaxHp { get; set; }
public int monHP { get; private set; }
public int monAtk { get; set; }
public int monLevel { get; set; }
public bool IsAlive => monHP > 0; // IsAlive 불리언을 통해 생존 여부 확인
public Monster(Monsters type)
{
MonsterType = type;
// switch 식으로 타입별 기본 스탯 정의
(monMaxHp, monAtk, monLevel) = type switch
{
Monsters.Minion => (15, 5, 2),
Monsters.VoidInsect => (15, 9, 3),
Monsters.CannonMinion => (25, 8, 5),
};
monHP = monMaxHp;
}
public int TakeDamage(int playerAtk)
{
Random random = new Random();
// 0.9 ~ 1.1 사이의 랜덤 배율 생성
double variation = random.NextDouble() * 0.2 + 0.9;
int dmg = (int)Math.Round(playerAtk * variation);
monHP = Math.Max(monHP - dmg, 0); // HP가 0보다 작아지지 않도록 조정
return dmg;
}
}
public MonsterplayerAtk * 배율 결과를 Math.Round 하고, 그만큼 monHP에서 차감한다.BattleSystem
Start(Job Player) public static void Start(Job player)
{
var monsters = SpawnMonsters();
while (player.health > 0 && monsters.Any(m => m.IsAlive))
{
PlayerTurn(player, monsters);
if (!monsters.Any(m => m.IsAlive)) break;
EnemyTurn(player, monsters);
}
Console.Clear();
Console.WriteLine(player.health > 0 ? " 승리! " : " 패배...");
Console.ReadKey();
}
SpawnMonsters() public static Monster[] SpawnMonsters()
{
int monsterCount = _rand.Next(1, 5); // 1~4마리 랜덤 생성
Monsters[] monsterTypes = (Monsters[])Enum.GetValues(typeof(Monsters));
Monster[] monsters = new Monster[monsterCount];
for (int i = 0; i < monsterCount; i++)
{
Monsters randomType = monsterTypes[_rand.Next(monsterTypes.Length)];
monsters[i] = new Monster(randomType);
}
return monsters;
}
RenderStatus(Job player, Monster[] monsters) public static void RenderStatus(Job player, Monster[] monsters)
{
Console.Clear();
Console.WriteLine("플레이어 상태");
Console.WriteLine($"체력: {player.health}");
Console.WriteLine($"공격력: {player.atk}");
for (int i = 0; i < monsters.Length; i++)
{
var m = monsters[i];
if (m.IsAlive)
{
Console.WriteLine($"몬스터 {i + 1} ({m.MonsterType})");
Console.WriteLine($"체력: {m.monHP}/{m.monMaxHp}");
Console.WriteLine($"공격력: {m.monAtk}");
}
else
{
Console.WriteLine($"몬스터 {i + 1} ({m.MonsterType}) 사망");
}
}
}
public static void PlayerTurn(Job player, Monster[] monsters)
{
while (true)
{
RenderStatus(player, monsters);
Console.Write("공격할 몬스터 번호(0: 차례 넘기기) > ");
if (!Mathod.CheckInput(out int sel)) continue;
if (sel == 0) break;
if (sel < 1 || sel > monsters.Length || !monsters[sel - 1].IsAlive)
{
Console.WriteLine("잘못된 입력입니다."); continue;
}
var target = monsters[sel - 1];
int dmg = target.TakeDamage(player.atk);
Console.WriteLine($"→ 몬스터 {sel}번에 {dmg} 데미지! (남은 HP {target.monHP}/{target.monMaxHp})");
Console.ReadKey();
break; // 한 번 공격 후 적 차례로
}
}
`EnemyTurn(Job player, Monster[] monsters)
public static void EnemyTurn(Job player, Monster[] monsters)
{
Mathod.BufferClear();
Console.WriteLine("적 턴");
Console.WriteLine($"플레이어 체력: {player.health}\n");
for (int i = 0; i < monsters.Length; i++)
{
var m = monsters[i];
if (!m.IsAlive)
continue; //죽은 몬스터는 건너뛰기
else
{
Console.WriteLine($"몬스터 {i + 1} {m.MonsterType}의 공격");
Console.WriteLine($"→ {m.monAtk} 데미지!");
player.health -= m.monAtk;
Console.WriteLine($"남은 체력: {player.health}");
Console.WriteLine("계속하시려면 아무 키나 누르세요.");
Console.ReadKey();
if (player.health <= 0)
{
Console.WriteLine("플레이어가 사망했습니다.");
Console.WriteLine("전투가 종료됩니다.");
Console.WriteLine("게임 오버");
Console.WriteLine("다시 시작하려면 아무 키나 누르세요.");
Console.ReadKey();
return; // 플레이어가 사망하면 전투 종료
}
}
}
}
오늘 구현은 여기까지 하도록 하겠다. 내일은 몬스터의 층 별 스탯 강화나 보스 몬스터와 같은 추가 기능을 구현해 볼 생각이다.