포스트

TIL 2026-03-25

TIL 2026-03-25

팀프로젝트 [콘솔게임 발표 자료] 3/25~ 4/1

생성일: 2026년 4월 1일 오전 10:14

소개

팀장 - 신장식, 팀원 - 김지원, 팀원- 이승진, 팀원- 전종환

image.png

기획서

  • 던전노조(가제) 게임 기획서

    # 📌 프로젝트 개요

    항목내용
    장르로그라이크 턴제 던전 탐험 RPG
    배경중세 판타지
    플랫폼PC (콘솔/터미널 기반)
    맵 규모100 × 100 타일

    ## 레퍼런스 게임

    Shattered Pixel Dungeon

    image.png

    ## 📒 문서 모음

    spreadsheets: https://docs.google.com/spreadsheets/d/1cM5d1gMhKLpuqJOGj-DiciZgW4YiUPYYDTq7VYwd27o/edit?usp=sharing

    플로우차트.excalidraw (1)


    # 🎮 핵심 게임플레이

    ## 게임 목표

    • 마지막 층에 도착해서, 보스 쓰러뜨리기

    ## 이동 시스템

    • 이동 방식 : 상·하·좌·우 단 1칸씩 이동 (대각선 이동 없음)
    • 입력 방법 : 키보드 방향키 또는 W, A , S, D
    • 이동은 턴 기반 — 플레이어가 1칸 이동하면 몬스터도 1회 행동
    • 대쉬하여 이동시 한칸더 이동

    ## 전투 시스템

    1. 몬스터와 인접(접점) 시 전투 발생
    2. 전투 옵션 : 공격 / 아이템 사용 / 이동(도망)
    3. 명중률 : 무기별로 상이 (아래 무기 표 참고)

    ## 회복 & 보상

    • 몬스터 처치 시 아이템 드롭
    • 아이템 사용시 체력 회복
    • 레벨업 이벤트로 체력 회복 가능 (선택 사항)

    # 📈 레벨 시스템

    ## 레벨업 보너스 (매 레벨)

    레벨업마다 아래 4가지 중 1가지 선택:

    • ❤️ 최대 체력 증가 + 3
    • ⚔️ 공격력 증가 + 1
    • 🛡️ 방어력 증가 + 1
    • 💨 민첩 증가 + 1
    • 💊 현재 체력 회복 + 5

    ## 레벨업 요구 경험치

    LV.1 ~ LV.15


    20


    # 🏗️ 맵 시스템

    ## 타일 범례

    기호풀 네임의미10이내면 생성 X
    #Wall벽 (통과 불가) 
    @Player플레이어 위치X
    RRock바위 (좁은 장애물) 
    WWater물 (넓은 장애물) 
    MMonster몬스터 
    >Stair계단 (다음 층)X
    CChest보물 상자 
    EElite Monster엘리트 몬스터 
     Blank그 외 빈공간은 모두 투명 처리여기서만 생성

    ## 맵 구성

    • 전체 맵 크기 : 100 × 100 타일
    • 각 층은 방 탐색 구조 — 방마다 구조물(벽), 몬스터, 아이템 배치
    • 다음층 계단(>) 생성 로직 : 룸 List 마지막방에 생성

    # 🔄 게임 플로우차트

    image.png

https://drive.google.com/file/d/1l0a_tAmKJ3JVNhTSTlvVcLZJA4u_7FsF/view?usp=sharing

핵심 기능 요약

시스템파일핵심 역할
GameManagerGameManager/GameManager.h·cpp게임 루프, 입력→행동→렌더 총괄
InputManagerGameManager/InputManager.h·cpp키 입력 추상화 (GameAction)
BspManagerBspManager/BspManager.h·cppBSP 알고리즘 맵 자동 생성
ItemManagerItem/ItemManager.h·cpp아이템 생성 팩토리 + 인벤토리 UI
RenderRender/Render.h·cpp콘솔 렌더링 시스템 전체
SpwanManagerSpawnManager/SpawnManger.h.cpp스폰 관련 시스템

🕹️ 게임 루프 및 인풋 시스템

핵심 멤버 구조

image.png

게임 루프 흐름

image.png

📸 [게임 실행 화면 전체]

image.png


[키 바인딩 표]

행동
W / S / A / D 및 방향키이동 (상/하/좌/우)
1공격 (전투 중)
2도망 (전투 중)
` (백틱)대쉬 활성화
i인벤토리 열기/닫기
Space아이템 사용
h도움말
q종료

[타일 타입]

타일문자설명
Player@플레이어 위치
Floor``빈 공간 (이동 가능)
Wall#벽 (이동 불가)
Stair>계단 (다음 층)
ChestC보물 상자
MonsterM일반 몬스터
EliteMonsterE엘리트 몬스터
BossB보스

🌲 맵 자동 생성 시스템 (BSP 알고리즘)

BSP 알고리즘 원리

BSP(Binary Space Partitioning)는 공간을 재귀적으로 두 영역으로 계속 분할하는 알고리즘입니다.

1
2
3
4
5
6
7
[전체 맵 공간]
        │
   ┌────┴────┐          ← 좌/우 (또는 상/하) 랜덤 분할
   │         │
 ┌─┴─┐     ┌─┴─┐        ← 다시 분할
 │   │     │   │
[방] [방] [방] [방]     ← 더 이상 분할 불가 → 각 영역에 방 하나씩 생성
  1. 분할 — 공간이 최소 크기보다 크면 랜덤 방향(가로/세로)으로 절반씩 분할
  2. 방 생성 — 더 이상 분할할 수 없는 말단 영역(leaf)마다 랜덤 크기의 방 조각
  3. 복도 연결 — 트리를 거슬러 올라가며 형제 노드의 방 중심을 Z자형 복도로 연결
  4. 복도 확장 — 1칸 너비 복도를 4방향 팽창시켜 3칸 너비로 확장
  5. 방 타입 배정 — 첫 방 = Start, 마지막 방 = Stair, 나머지는 랜덤으로 Elite/Treasure/Normal 배정

BSP 생성 파이프라인

1
2
3
4
5
6
7
8
9
10
Generate(map, params)
  │
  ├─ 1. map.Fill(Wall)          ← 전체 Wall로 초기화
  ├─ 2. BuildTree()             ← 파티션 재귀 분할 (BSP 트리 생성)
  ├─ 3. CarveRooms()            ← leaf 노드마다 랜덤 크기 방을 Floor로 조각
  ├─ 4. BuildRoomSnapshot()     ← 방 타일 스냅샷 저장 (복도/방 구분용)
  ├─ 5. ConnectTree()           ← Z자형 복도 중심선(1칸) 연결
  ├─ 6. DilateCorridor()        ← 복도를 4방향 팽창 → 3칸 너비로 확장
  ├─ 7. MarkDebugRoomWalls()    ← 방 경계 벽 색상 구분용 마킹
  └─ 8. AssignRoomTypes()       ← 방 타입 랜덤 배정

복도 구현 로직 상세

1단계 — HCorridor / VCorridor (복도 중심선 그리기)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 수평 복도: 지정한 행(row)에서 startCol ~ endCol 구간을 Floor으로 채움
void HCorridor(Map& map, int startCol, int endCol, int row)
{
    for (int col = min(startCol,endCol); col <= max(startCol,endCol); ++col)
        map.SetTile(col, row, Tile::Floor);
}

// 수직 복도: 지정한 열(col)에서 startRow ~ endRow 구간을 Floor으로 채움
void VCorridor(Map& map, int col, int startRow, int endRow)
{
    for (int row = min(startRow,endRow); row <= max(startRow,endRow); ++row)
        map.SetTile(col, row, Tile::Floor);
}

2단계 — ConnectTree (Z자형 복도 연결)

모든 연결은 H복도 → V복도 → H복도 (Z자형) 또는 반대 순서로 연결됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[수직 분할: 좌||우]

  좌방 중심  spine열  우방 중심
      *──────── |           ← H복도
                |
                |───────*  ← V복도 → H복도

[수평 분할: 상=하]

  위방 중심
      *
      |             ← V복도
      |────────*  ← H복도
                |
                *   ← V복도
  아래방 중심

3단계 — DilateCorridor (1칸 → 3칸 확장)

1
2
3
4
5
6
7
8
9
 [확장 전]     [확장 후]

  # # # #      # # # #
  #   # #      #     #
  # # # #  →   #     #
  # # # #      #     #
  # # # #      # # # #

  중심선 1칸    주도 3칸
  • 복도 중심선(Floor이면서 방이 아닌 타일)을 먼저 탐지
  • 해당 타일의 4방향 좌표를 물리 먼저 수집 후 일괄 Floor 변환

📸 [BSP 맵 생성 결과 화면]

bsp.gif


🖥️ 렌더 시스템

레이아웃 구현


🖼️ UI 및 그래픽

기본적인 배경 UI

image.png

ex) 예시 이미지

image.png

ex) 예시 이미지

image.png

1
2
3
4
5
6
7
8
┌────────────────────────┬──────────┬────────────────┐
│                        │  Player  │                │
│          Map           │   Info   │      Log       │
│      (뷰포트 기반)      │──────────│   (게임 로그)  │
│                        │  Info 2  │                │
├────────────────────────┴──────────┴────────────────┤
│  [help:h]  [inven:i]  [move:WASD]  [quit:q]        │
└────────────────────────────────────────────────────┘

📸 [전체 UI 레이아웃 화면]

image.png

📸 [RenderInfo HP바]

image.png


🎒 아이템 시스템

image.png

아이템 구현 목록

코드이름효과사용 가능 상황
101회복 물약체력 10 회복전투 / 비전투
102랜덤 이동 물약맵 내 랜덤 Floor로 순간이동비전투 중
103투명화 아이템5턴 동안 몬스터 탐지 회피비전투 중
105부활 토큰사망 시 최대체력 50%로 자동 부활전투 / 비전투

📸 [인벤토리 UI 화면]

image.png

📸 [아이템 획득 → 인벤토리 → 사용 흐름 캡처 삽입]

image.png


🏭 스폰 시스템

이름스폰 위치
플레이어첫번째 방
계단마지막 방
상자랜덤
몬스터첫번째 방과 마지막을 제외한 방
엘리트 몬스터엘리트방, 상자방
보스마지막 스테이지
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class SpawnManager
{
private:
    SpawnManager();
    SpawnManager(const SpawnManager&) = delete;
    SpawnManager& operator=(const SpawnManager&) = delete;

    std::vector<Monster*> activeMonsters;
    int stage;

public:
    static SpawnManager* GetInstance()
    {
        static SpawnManager instance;
        return &instance;
    }
    ~SpawnManager();

    const std::vector<Monster*>& GetActiveMonsters() const; // 저장 및 조회
    void SpawnMonstersInRooms(Map& _map, const std::vector<Room>& _rooms, int _stage);
    void UpdateCleanup(Map& _map);
    void SpawnPlayerInRooms(Map& _map, const std::vector<Room>& _roomList, Player* player);
    void SpawnObjectInRooms(Map& _map, const std::vector<Room>& _roomList);
    Monster* GetMonsterAt(int targetX, int targetY);

		// 스테이지 별 등장 몬스터 구성이 다름
    void Stage1(Map& _map, const std::vector<Room>& _rooms);
    void Stage2(Map& _map, const std::vector<Room>& _rooms);
    void Stage3(Map& _map, const std::vector<Room>& _rooms);
    void Stage4(Map& _map, const std::vector<Room>& _rooms);
};

📸 [스폰 결과 화면]

image.png


👾 몬스터 시스템

몬스터 종류

스탯고블린오크미믹골렘 
최대 체력10152050 
공격력3579 
방어력04710 
민첩3355 
경험치5577 
   (상자로 위장)  
 엘리트_고블린엘리트_오크엘리트_미믹엘리트_골렘강철_골렘 (보스)
최대 체력15223060200
공격력58101320
방어력06101420
민첩447710
경험치881010100

몬스터 랜덤 이동

플레이어가 1턴을 사용하면 각 몬스터들도 1턴을 받아 이동합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void Monster::Move(int _dx, int _dy, Map& _map)
{
    int nextX = x + _dx;
    int nextY = y + _dy;

    // 현재 위치와 다음 위치가 모두 유효해야 동작
    if (_map.InBounds(nextX, nextY) && _map.GetTile(nextX, nextY) == Tile::Floor)
    {
        // 이전 위치 타일 정리
        _map.SetTile(x, y, Tile::Floor);

        x = nextX;
        y = nextY;

        // 자신의 상태(Elite 여부)에 맞는 타일 배치
        if (isElite)
            _map.SetTile(x, y, Tile::EliteMonster);
        else
            _map.SetTile(x, y, Tile::Monster);
    }
}

void Monster::UpdateAI(Map& _map)
{
    // IDLE 상태일 때만 랜덤 이동
    if (state != MonsterState::IDLE) return;

    // 30% 확률로 이동
    if (rand() % 100 > 30) return;

    int direction = rand() % 4;
    int dx = 0, dy = 0;

    switch (direction)
    {
    case 0: dy = -1; break; // 위
    case 1: dy = 1;  break; // 아래
    case 2: dx = -1; break; // 왼쪽
    case 3: dx = 1;  break; // 오른쪽
    }

    if (_map.GetTile(x + dx, y + dy) == Tile::Floor)
        this->Move(dx, dy, _map);
}

📸 [랜덤 이동 화면]

제목 없음.gif

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.