🗓️ 오늘 하루 일정
✅ 오전
- 09:00 ~ 13:00 : 프로젝트 작업
- 직업별 스킬 시스템 구조 리팩토링 (SkillSet 인터페이스 기반)
- UseSkill 메서드 구조 변경 및 범위 공격 구현
- 장착 시스템 FinalAtk, FinalDef 구조 반영 확인
- 회피 확률 → 민첩 기반으로 재설계 (DodgeChance 동적 계산)
🍽️ 점심시간
- 13:00 ~ 14:00 : 점심시간
✅ 오후
- 14:00 ~ 18:00 : 팀 프로젝트 발표 준비
- 발표용 코드 정리 및 구조 설명 작성
- 몬스터 다수 공격 시 HP 변화 출력 개선 (prevHpDict)
- 트러블슈팅 정리
- 직업 클래스 구조 단순화 이슈 → 해결 과정
- 퀘스트 클래스 역할 분리 이슈 → 해결 과정
- 발표용 PPT 구성 요소 정리 및 작성 시작
🍽️ 저녁시간
- 18:00 ~ 19:00 : 저녁시간
✅ 저녁
- 19:00 ~ 21:00 : 발표용 트러블슈팅 작성 & 마무리 피드백
- 발표용 설명 작성: 다형성, 체력 추적, 스킬 범위 공격 등
- 퀘스트 시스템 구조 변경 경험 정리
- 아이템 리스트 구조 변경 내용 보완
✅ 오늘 학습 키워드
- SkillSet 인터페이스 기반 직업 다형성
- 범위 스킬 및 피해 대상 추적 출력
- FinalAtk, FinalDef 개념으로 능력치 분리
- DodgeChance = 민첩 기반 회피 확률 구현
- 발표용 트러블슈팅 작성
- 퀘스트 클래스 역할 분리 (Manager, UI, 데이터)
✅ 오늘 학습 한 내용을 나만의 언어로 정리하기
🎯 직업 스킬 구조의 리팩토링
- 기존에는 Job 클래스별로 스킬을 따로 정의했는데, 유지보수가 어렵고 중복이 많았음.
- SkillSet 인터페이스를 정의하고, 각 직업이 이를 상속해 고유 스킬을 구현하게 변경함.
- UseSkill(int index, Player, List<Monster>, Monster mainTarget) 시그니처로 통일하여, 단일/다중 타겟 모두 처리 가능하게 함.
- 이로써 다형성을 통한 유연한 스킬 시스템을 완성했고, 전사, 마법사, 궁수, 도적, 해적 모두 동일한 틀 안에서 관리 가능해짐.
🌀 몬스터 다수 공격 처리
- prevHpDict = monsterSpanwed.ToDictionary(m => m, m => m.Hp) 를 활용해,
- 스킬 사용 전 몬스터들의 체력을 저장 → 공격 후 변화가 있는 몬스터만 출력함.
- 이 과정은 DisplayHpInfo 함수를 통해 시각적으로 체력 감소 효과를 보여줌.
- 피해를 입은 대상만 출력되므로 플레이어의 인식이 명확해지고 몰입도 증가.
🛡️ 능력치 구조 정리
- 기존에는 Atk, Def 등 수치가 장비 장착 시 바로 바뀌어 관리가 어려웠음.
- Atk + ExtraAtk = FinalAtk, Def + ExtraDef = FinalDef 구조로 분리.
- 덕분에 장착 효과와 기본 능력치를 분리해 관리할 수 있게 되었고,
- 장착/해제/판매 시에도 안정적으로 적용됨.
🧠 회피 확률 계산 방식 개선
- 기존에는 DodgeChance가 고정값이었는데, 민첩 Dex 수치를 기반으로 동적으로 계산하도록 변경.
- 예: DodgeChance = Math.Min(50, Dex / 2) 처럼 민첩 수치가 높을수록 회피율도 증가하도록 설정함.
- 이로 인해 능력치가 전투에 직접적인 영향을 주는 구조로 개선됨.
🧩 학습하며 겪었던 문제점 & 에러
① 문제정의: 직업 클래스를 Job으로 나누니 코드가 너무 복잡해졌다
🧩 문제 상황
- 처음에는 직업마다 Job 클래스를 만들어 공격력, 방어력, 스킬 등을 직접 구현했다.
- 예를 들어 WarriorJob, MageJob, ThiefJob 같은 식으로 클래스가 각각 존재했음.
- 직업이 늘어날수록 코드가 급격히 길어지고, 스킬을 사용하는 방식이 서로 달라지다 보니 호출부에서 일일이 분기 처리해야 했다.
🧪 시도
// 예전 방식 - Job마다 스킬을 따로 정의하고 조건문으로 분기
if (player.Job == "전사")
WarriorSkill.UseSkill(index, player, monster);
else if (player.Job == "마법사")
MageSkill.UseSkill(index, player, monster);
🛠 해결 방법
- SkillSet 인터페이스를 만들고, 각 직업 스킬 클래스에 상속시켰다.
- 이제 직업에 따라 스킬 클래스만 다르게 생성하면, 호출부는 공통된 방식으로 처리 가능하다.
✅ 개선된 코드
// 인터페이스 기반 구조
public interface SkillSet
{
List<string> SkillNames { get; }
void UseSkill(int index, Player player, List<Monster> monsters, Monster mainTarget);
}
// 전사 스킬 예시
public class WarriorSkill : SkillSet
{
public List<string> SkillNames => new() { "강타", "회전베기", "분쇄", "더블스트라이크" };
public void UseSkill(int index, Player player, List<Monster> monsters, Monster mainTarget)
{
// 스킬 사용 로직 통합
}
}
// 호출 시
SkillSet skillSet = new WarriorSkill(); // 직업에 따라 바꿔주기만 하면 됨
skillSet.UseSkill(index, player, monsters, target);
💡 새롭게 알게 된 점
- 인터페이스를 활용하면 다형성을 통해 중복을 없애고 구조를 단순화할 수 있다.
- 앞으로는 Job 자체를 코드로 관리하지 않고 스킬/능력치 중심으로 설계하는 게 유지보수에 더 좋다.
② 문제정의: 퀘스트 클래스를 하나로 처리했더니 역할이 꼬이고 유지보수가 힘들었다
🧩 문제 상황
- 퀘스트 진행, UI 출력, 퀘스트 상태 갱신 등을 전부 Quest.cs 하나에서 처리하고 있었음.
- 클래스가 너무 커져서 어떤 코드가 어떤 역할을 하는지 찾기 어렵고, 테스트하기도 힘들었음.
🧪 시도
// 문제였던 코드
public class Quest
{
public string Title;
public string Info;
public int TargetCount;
public int CurrentCount;
public bool IsAccepted;
public bool IsCompleted;
public void DisplayQuestUI() { ... } // UI 코드
public void UpdateQuestProgress(string monsterName) { ... } // 로직 코드
}
🛠 해결 방법
- 역할을 나누어 클래스를 3개로 분리
- Quest.cs: 퀘스트 데이터만 담당 (제목, 설명, 진행률 등)
- QuestManager.cs: 진행, 완료 체크 등의 로직만 담당
- QuestUI.cs: 퀘스트 출력 전용 클래스
✅ 개선된 구조
// Quest.cs - 데이터만 담당
public class Quest
{
public string Title;
public string Info;
public int TargetCount;
public int CurrentCount;
public bool IsAccepted;
public bool IsCompleted;
}
// QuestManager.cs
public static class QuestManager
{
public static void CheckKill(string monsterName, Player player)
{
foreach (var quest in player.Quests)
{
if (quest.IsAccepted && !quest.IsCompleted && quest.Title.Contains(monsterName))
{
quest.CurrentCount++;
if (quest.CurrentCount >= quest.TargetCount)
quest.IsCompleted = true;
}
}
}
}
// QuestUI.cs - 출력 전용
public static class QuestUI
{
public static void DisplayQuestList(List<Quest> quests)
{
foreach (var quest in quests)
{
Console.WriteLine($"🧾 {quest.Title} - {quest.CurrentCount}/{quest.TargetCount}");
}
}
}
💡 새롭게 알게 된 점
- 클래스를 기능별로 나누면, 역할이 명확해지고 코드를 찾기도 쉽고 디버깅도 수월하다.
- *단일 책임 원칙(SRP)**의 중요성을 몸으로 체감했다.
📝 메모
- 점점 코드가 커질수록 책임 분리와 구조화가 중요하다는 걸 실감했다.
- 내가 만든 구조를 다시 뜯어보면서 "내가 이걸 왜 이렇게 했지?" 싶은 게 많았는데,
- 하나씩 이유를 찾고 개선해 나가면서 기분이 좋았다!
- 발표를 준비하면서 나도 몰랐던 내 코드의 장점을 알게 되어 더 자신감이 생겼다!
이제 예비군도 끝나서그런지 오늘 팀프로젝트 하는데 너무 즐거웠다ㅠㅜ.. 공부가 이렇게 재밌었지.. 항상 새롭게 느껴지고.. 오류 생길때는 살짝 답답할때도 있지만 하루하루 즐기면서 개발 잘 해보자ㅏ 오예ㅖ!~!!
'스파르타 코딩클럽 > TIL작성' 카테고리의 다른 글
| 내일배움캠프 17일차 TIL [TopDown게임 제작] (3) | 2025.07.22 |
|---|---|
| 내일배움캠프 16일차 TIL [팀프로젝트 발표 + 유니티 개발] (2) | 2025.07.21 |
| 내일배움캠프 13일차 TIL [예비군 훈련 + 팀프로젝트 직업 밸런스+스킬 추가] (1) | 2025.07.16 |
| 내일배움캠프 12일차 TIL [예비군 훈련 + 팀프로젝트 퀘스트내용 개발] (0) | 2025.07.15 |
| 내일배움캠프 11일차 TIL [예비군 훈련 + 팀프로젝트 클래스 구조 설계] (0) | 2025.07.15 |

