👨🏫 프로젝트 소개

- SilverTown Utopia는 은퇴를 꿈꾸는 늙고 지친 용사의 실버타운 입주를 향한 마지막 모험을 그린 로그라이크 액션 게임이다.
- ‘궁수의 전설’을 재해석하여, 절차적 맵 생성과 회피 중심의 전투 패턴을 구현하고자 했다.
- 특히, 매번 새로운 전투 경험을 제공하기 위해 맵 구성과 전투 패턴에 변화를 주는 액션성에 중점을 두었다.
🔗 깃허브 링크
🎥 게임 영상
⏲️ 플로우 차트

⏲️ 개발기간
- 2025.07.29(월) ~ 2025.08.06(화)
📚 기술스택
- Language: C#
- Engine: Unity 2022.3.17f
- Version Control: GitHub
🧑🤝🧑 팀 역할 담당
- 팀장 박용규
일정 관리, 리소스 관리, 버전 관리, 아트워크 작업, BGM 및 효과음 적용, 게임 연출 - 프로그래머 이원진(본인)
랜덤 맵 생성 시스템, BT 시스템, 와이어 액션, 플레이어 / 보스 작업, 메인UI, 카드 선택 UI,
카드 업그레이드 시스템, 카메라 시스템, 스킬 - 스크린 스킬 등 - 프로그래머 문장원
플레이어 스킬패턴, 몬스터 스킬패턴 - 기획 박기훈
시스템 기획, 콘텐츠 기획, 레벨 디자인, 연출 기획, UI 수정,
💜 주요기능
1. 절차적 맵 생성
- 충돌 배치와 MST(Minimum Spanning Tree)를 접목해, 매번 다른 형태의 맵을 자동으로 생성하도록 구성했다.

2. 몬스터 - Behavior Tree 기반 AI
- 몬스터가 상황에 따라 패턴을 변경하도록 구현해, 단순한 반복 공격 대신 다양한 회피 루트를 유도하는 전투를 구성했다.
- 보스 바하뮤트 BT

3. ScriptableObject 기반 스킬 관리
- 스킬 및 패턴 데이터를 모듈화해 인스펙터에서 직접 수정 가능하도록 설계했고, 이를 통해 빠른 밸런싱과 확장성을 확보했다.
4. 카메라 시스템
- 각 방에서는 카메라가 해당 방의 범위를 벗어나지 않도록 고정했다.
- 복도에서는 플레이어를 따라 이동하도록 설정해, 방과 복도 사이에서 부드러운 전환 효과를 주도록 구성했다.
5. 와이어 액션
- 플레이어가 회피하는 이동 수단으로 와이어를 사용할 수 있도록 구현했다.
- 맵의 장애물과 상호작용 가능하다.
6. 카드 선택을 통한 업그레이드 시스템
- 몬스터 클리어 시, 무작위로 등장하는 3개의 업그레이드 카드 중 하나를 선택해 능력치 / 스킬을 강화하도록 구성했다.
🛠️ Trouble Shooting - 몬스터 대시 문제
📌 문제 배경
- 플레이어를 추적하는 몬스터에게 일직선 대시 공격을 구현하였다.
- 대시 목표는 대시 시작 시점의 플레이어 위치로 고정되며, 몬스터는 해당 위치까지 빠르게 돌진한다.
❗ 문제 상황
- 플레이어가 벽 끝에 붙어 있는 경우, 그 지점이 몬스터의 목표가 되었다.
- 하지만 몬스터는 CapsuleCollider2D를 사용하고 있어, 부피 때문에 물리적으로 해당 지점까지 도달할 수 없다.
- 그 결과, 대시가 멈추지 않고 무한히 유지되거나, 벽에 부딪히고도 튕기듯 비정상적으로 움직이는 현상이 발생한다.
✅ 요약:
콜라이더 외곽이 벽에 닿아 이동이 막혀도, 중심점이 목표 지점에 도달하지 못했기 때문에 대시가 멈추지 않는 상태가 되었다.
🧪 문제 해결을 위한 첫 번째 시도
- 처음에는 아주 간단한 방식으로 문제를 해결하고자 했다. → "대시 중 벽에 닿으면 멈춘다"
- 하지만 곧 또 다른 문제가 드러났다.
- 몬스터가 벽 근처에서 대시를 시작하고, 플레이어 또한 같은 방향의 벽 끝에 붙어 있는 상황에서, 대시를 시작하자마자 벽에 걸려 바로 멈추는 현상이 발생했다.
- 즉, 정상적인 대시조차 불가능한 경우가 생긴 것이다.
✅ 최종 해결 방법
1) 레이캐스트를 통한 정확한 충돌 판정
- 기존에는 OnCollisionStay2D() 내부에서 전달된 collision.contacts[0].normal만 사용했지만, 충돌이 모서리인 경우에는 법선 방향이 불안정하게 반환되었다.
- 이를 보완하기 위해, 레이캐스트로 몬스터 전방을 직접 감지하도록 변경했다. 충돌의 방향과 면을 정확하게 파악할 수 있어 신뢰도가 높아졌다.
RaycastHit2D hit = Physics2D.Raycast(startPos, moveDirection, rayDist, wallLayer);
Vector2 wallNormal = hit.normal;
2) 법선과 이동 방향의 각도 비교
- 벽과 충돌하더라도, 충돌 각도가 정면이 아닌 경우에는 그대로 벽을 따라 미끄러지는 방식으로 이동할 수 있도록 했다.
- 반면, **정면 충돌(각도 170도 이상)**일 경우에만 대시를 중단하도록 처리하였다.
float angle = Vector2.Angle(wallNormal, moveDirection);
if (angle > 170f)
{
prePlayerPos = transform.position; // 목표 지점 변경
return;
}
3) 슬라이딩 방향 계산
- 정면 충돌이 아닌 경우에는, 현재 대시 벡터에서 벽의 수직 성분을 제거하여, 벽을 따라 미끄러지는 새로운 이동 방향을 계산하였다.
// 빛 반사 공식을 응용했다.
Vector2 slideDirection = moveDirection - Vector2.Dot(moveDirection, wallNormal) * wallNormal;
moveDirection = slideDirection.normalized;
4) 목표 지점 보정
- 기존 목표인 prePlayerPos는 여전히 벽 근처일 수 있다.
- 따라서 현재 위치에서 슬라이딩된 방향으로 남은 거리만큼 이동한 위치로 목표 지점을 갱신하였다.
// 충돌했을 시의 위치와 목표 위치의 거리 계산
float dist = Vector2.Distance(transform.position, prePlayerPos);
// 변경된 moveDirection따라 prePlayerPos위치도 갱신
prePlayerPos = (Vector2)transform.position + moveDirection * dist;
✅ 최종 결과
- ❗ 도달 불가능한 목표에 대해 무한히 대시하는 문제 해결했다.
- ❗ 충돌이 발생해도 자연스럽게 벽을 따라 미끄러지도록 처리했다.
- ❗ 대시 시작 위치와 목표 지점이 같은 벽에 붙어 있어도 정상 작동하도록 개선했다.
✍️ 마무리
- 이 문제는 단순한 충돌 처리의 문제가 아니었다. 대시 로직과 실제 물리 환경 사이의 불일치를 어떻게 보정할 것인지에 초점을 맞춰 해결했다.
- "논리적 목표 위치"와 "물리적으로 도달 가능한 위치"는 항상 같지 않다. 이를 고려해 처리하지 않으면, 의도치 않은 반복 행동이나 비정상적 움직임이 발생할 수 있다.
'Programming > Unity 구현' 카테고리의 다른 글
| Unity 3D 팀 미니 프로젝트 - 🕹️ Followers_Project_Survival (4) | 2025.08.22 |
|---|---|
| Unity 3D 미니 프로젝트 - 🕹️ SpartaDungeon (4) | 2025.08.13 |
| Unity 2D 미니 프로젝트 - 🕹️ Sparta Metaverse (3) | 2025.07.28 |
| Unity 2D 미니 프로젝트 - 팀원 카드 매칭 게임 (3) | 2025.07.07 |
| Unity 2D 구현 - 카드 배치 연출 Ver.3 (0) | 2025.07.03 |
