코드
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using static TimspartaBasic.Week4_TextRPGcs;
namespace TimspartaBasic
{
internal class Week4_TextRPGcs
{
//문자열 가운데 정렬 한글은 제대로 작동하지는 않는것 같다
public static void StringCenter(string str)
{
Console.WriteLine(String.Format($"{str}").PadLeft(50 - (30 - (str.Length / 2))));// 가운데정렬
}
//캐릭터 인터페이스
public interface ICharacter
{
string Name { get; set; }
int Health { get; set; }
int AttackPower { get; set; }
int Attack { get; }
bool IsDead { get; }
void TakeDamge(int damage);
}
//플레이어 클래스
public class Warrior : ICharacter
{
Random random = new Random();
public string Name { get; set; }
public int Health { get; set; }
public int AttackPower { get; set; }
public int Attack => random.Next(AttackPower - 5, AttackPower + 5);
public bool IsDead => Health <= 0;
public Warrior(string name)
{
Name = name;
Health = 100;
AttackPower = 20;
}
public void TakeDamge(int damage)
{
Health -= damage;
StringCenter($"{Name}은(는) {damage}의 피해를 입었다");
}
}
//몬스터 클래스
public class Monster : ICharacter
{
Random random = new Random();
public string Name { get; set; }
public int Health { get; set; } = 100;
public int AttackPower { get; set; }
public int Attack => random.Next(AttackPower - 10, AttackPower + 3);
public bool IsDead => Health <= 0;
public Monster(string name)
{
Name = name;
AttackPower = 10;
}
public void TakeDamge(int damage)
{
Health -= damage;
StringCenter($"{Name}은(는) {damage}의 피해를 입었다");
}
}
//아이템 인터페이스
public interface IItem
{
string Name { get; set; }
int Amount { get; set; }
//미리 정의된 델리게이트로 값을 반환하지 않는 메서드를 나타내는 델리게이트
//아이템 강화에서 사용
Action Enforce { get; set; }
void Use(Warrior warrior);
void AmountUp();
}
//체력포션 클래스
public class HealthPotion : IItem
{
public string Name { get; set; } = "체력포션";
public int Amount { get; set; } = 30;
public void Use(Warrior warrior)
{
warrior.Health += Amount;
StringCenter($"{Name}을 마셨다");
}
public Action Enforce { get; set; }
public HealthPotion()
{
//델리게이트에 람다식으로 등록
Enforce = AmountUp;
}
//아이템 강화 함수
public void AmountUp()
{
if (Name != "강화체력포션")
{
Amount += 30;
StringCenter($"{Name} 아이템이 강화되었습니다.");
Name = "강화체력포션";
}
else
{
StringCenter($"강화{Name}은 존재하지 않네요");
}
}
}
//공격력 포션 클래스
public class StrengthPotion : IItem
{
public string Name { get; set; } = "공격력포션";
public int Amount { get; set; } = 30;
public Action Enforce { get; set; }
public StrengthPotion()
{
Enforce = AmountUp;
}
public void Use(Warrior warrior)
{
warrior.AttackPower += Amount;
StringCenter($"{Name}을 마셨다");
}
public void AmountUp()
{
if (Name != "강화공격력포션")
{
Amount += 30;
StringCenter($"{Name} 아이템이 강화되었습니다.");
Name = "강화공격력포션";
}
else
{
StringCenter($"강화{Name}은 존재하지 않네요");
}
}
}
//스테이지 클래스
public class Stage
{
//델리게이트 선언
public delegate void GameEvent(ICharacter character);
//델리게이트 이벤트 등록
public event GameEvent OnCharacterDeath;
ICharacter player;
ICharacter monster;
List<IItem> rewardsItem;
//스테이지 레벨을 나타내는 변수
int level;
public Stage(ICharacter player, ICharacter monster, List<IItem> rewardsItem, int level)
{
this.player = player;
this.monster = monster;
this.rewardsItem = rewardsItem;
this.level = level;
OnCharacterDeath += StageClear; //캐릭터 죽으면 StageClear 실행
}
//스테이지 시작 함수
public void Start()
{
Console.Clear();
string stageStrMessge = $"스테이지{level} 시작!";
StringCenter(stageStrMessge);
Console.WriteLine(" ------------------------------------------------");
Thread.Sleep(1000);
Console.WriteLine();
while (!player.IsDead && !monster.IsDead)
{
Console.WriteLine($"{player.Name,8}({player.Health}) -> 공격!");
Console.WriteLine();
Thread.Sleep(1000);
monster.TakeDamge(player.Attack);
Console.WriteLine();
Thread.Sleep(1000);
if(monster.IsDead) { break; }
Console.WriteLine($"공격! <- {monster.Name}({monster.Health})".PadLeft(46));
Console.WriteLine();
Thread.Sleep(1000);
player.TakeDamge(monster.Attack);
Console.WriteLine();
Thread.Sleep(1000);
}
if (player.IsDead)
{
Console.WriteLine();
StringCenter($"{player.Name}이(가) 죽었습니다.");
Thread.Sleep(1000);
//StageClear(monster); 이렇게 선언하게 되면 의존을 하게 되서 이 다음에 나올 함수들을 계속 선언을 해야한다 하지만 델리게이트를 사용하면 플레이어가 죽었을 때 실행할 함수들을 추가만 해주면 된다
OnCharacterDeath?.Invoke(monster);
}
else if (monster.IsDead)
{
Console.WriteLine();
StringCenter($"{monster.Name}이(가) 죽었습니다.");
Thread.Sleep(1000);
//StageClear(player);
OnCharacterDeath?.Invoke(player);
}
}
//스테이지 클리어 함수
void StageClear(ICharacter character)
{
if (character == player)
{
Console.WriteLine(" ------------------------------------------------");
StringCenter("스테이지 클리어!");
Console.WriteLine();
Console.WriteLine(" 보상의 이름을 입력하여 선택하세요");
Console.WriteLine();
foreach (var item in rewardsItem)
{
StringCenter($"{item.Name} ");
}
Console.WriteLine();
Console.WriteLine(" ------------------------------------------------");
Console.Write(">> ");
string? input = Console.ReadLine();
IItem? selectedItem = rewardsItem.Find(item => item.Name == input);
if (selectedItem != null)
{
//Warrior는 곧 player니까 형변환 가능
Console.WriteLine();
selectedItem.Use((Warrior)player);
Console.WriteLine();
//아이템 강화 함수를 델리게이트를 이용하여 호출
selectedItem.Enforce();
//물약 강화
Thread.Sleep(1000);
}
else
{
StringCenter("아무것도 선택하지 않으셨습니다.");
Thread.Sleep(1000);
}
Console.WriteLine();
StringCenter("다음 스테이지로 넘어갑니다.");
Console.WriteLine();
}
else
{
StringCenter("YOU DIED");
Thread.Sleep(1000);
}
}
}
//게임을 관리하는 클래스
public class GameManager
{
Warrior player;
Monster Slime;
Monster goblin;
Monster dragon;
List<IItem> stageRewards;
//스테이지를 리스트로 관리
List<Stage> stageMap;
Stage stage1;
Stage stage2;
Stage stage3;
public GameManager()
{
player = new Warrior("Player");
Slime = new Monster("슬라임");
goblin = new Monster("고블린");
dragon = new Monster("드래곤");
stageRewards = new List<IItem> { new HealthPotion(), new StrengthPotion() };
stage1 = new Stage(player, Slime, stageRewards, 1);
stage2 = new Stage(player, goblin, stageRewards, 2);
stage3 = new Stage(player, dragon, stageRewards, 3);
stageMap = new List<Stage>();
stageMap.Add(stage1);
stageMap.Add(stage2);
stageMap.Add(stage3);
}
//게임 시작 함수
public void StageStart()
{
foreach (var stage in stageMap)
{
string startMessage = "게임을 시작합니다.";
StringCenter(startMessage);
Thread.Sleep(1000);
stage.Start();
if (player.IsDead) return;
StringCenter("아무키나 눌러서 다음 스테이지 시작");
Console.ReadKey();
}
StringCenter("Victory!!");
}
}
static void Main(string[] args)
{
Console.SetWindowSize(53, 40);
Console.CursorVisible = false;
GameManager Game = new GameManager();
Game.StageStart();
}
}
}
후기
강의내용 자체가 중요하고 어려운 내용이라서 이해하는데 오래 걸렸다
과제 구현 방법은 전체적으로는 인터페이스를 이용해서 관리하는 것으로 과제를 하면서 왜 인터페이스를 사용하는가에 대한 이해를 할 수 있었다 인터페이스를 상속받아서 클래스를 구현하게 되면 클래스가 뭔지 몰라도 인터페이스를 이용하여 접근이 가능하고 추상화를 통해 다형성을 높일 수 있다
델리게이트를 사용하는 것도 아직 이해가 부족하지만 사용해보면서 익힐 수 있었는데
과제 해설에서 나온 델리게이트를 이해하며 아이템 클래스에 아이템을 사용하면 강화를 할 수 있게 하는 함수를 연결하여
Item 강화를 해보았다
강화 함수는 반환값이 없고 매개변수도 없어서 미리 정의된 델리게이트인 Action을 사용해보았다
확실히 개인과제를 만들었을 때와는 달리 더 관리하기가 편해졌고 느슨한 결합으로 인해 각자 함수나 클래스에 대한 정보를 몰라도 사용할 수 있었다 개발을 하면서 기능을 추가할 때면 하나 고치면 다른것도 다 고치고 해야했지만 이번에는 그럴 필요가 없었다
'개발일지' 카테고리의 다른 글
[C# 문법 종합반] 5주차 과제 - 알고리즘 문제 (0) | 2023.09.03 |
---|---|
[C# 팀 프로젝트] TextRPG 확장 (0) | 2023.09.01 |
[C# 개인과제] TextRPG (0) | 2023.08.23 |
[C# 문법 종합반] 3주차 과제 - 스네이크 게임, 블랙잭 (0) | 2023.08.17 |
[C#문법종합반] 2주차 과제 - 틱택토 (feat.Console명령어) (0) | 2023.08.15 |