본문 바로가기

개발 프로젝트/Win32 - VampireSurvivor 모작

로그라이크 게임 구현 2주차 (1)

이번 글에선 이중 연결리스트로 몬스터를 구현한 것에대해 적겠다.

 

struct strMonster
{
    strMonster* next;
    strMonster* prev;
    short int monsterCount;  // 몬스터 식별 번호 
    short int monsterCategory;      //몬스터 종류 번호 (무슨 비트맵을 사용하는가 , 어떤 움직임을 보이는가)
    POINT monsterPos;          // 몬스터 좌표
};

strMonster* strMonsterList = NULL;

 

strMonster* strMonsterInit(strMonster* tmp)
{
    tmp = new strMonster;                     
    tmp->prev = NULL;
    tmp->next = NULL;
    tmp-> monsterCount = 0;
    tmp->monsterCategory = 0;
    tmp->monsterPos = { 10,10 };
    return tmp;
}

strMonster* strMonsterGetData(strMonster* head , int newMonsterCount)
{
    strMonster* cur_node = head;
    for (int i = 0; i < newMonsterCount - 1; i++) {
        cur_node = cur_node->next;
        if (cur_node == head)
            break;
    }
    if (cur_node == NULL) {
        return 0;
    }
    return cur_node;
}

strMonster* strMonsterInsert(strMonster* head, short int newMonsterCount, short int newMonsterCategory, POINT newMonsterPos)
{
    if (head == NULL) {
        head = strMonsterInit(head);
        head->monsterCount = newMonsterCount;
        head->monsterCategory = newMonsterCategory;
        
        head->monsterPos = newMonsterPos;
        head->next = head;
        head->prev = head;
        return head;
    }

    strMonster* last_node = head->prev;
    strMonster* new_node = NULL;
    new_node = strMonsterInit(new_node);

    last_node->next = new_node;
    new_node->prev = last_node;
    new_node->next = head;
    head->prev = new_node;

    new_node->monsterCount = newMonsterCount;
    new_node->monsterCategory = newMonsterCategory;
    new_node->monsterPos = newMonsterPos;

    
    return head;
}



strMonster* strMonsterRemove(strMonster* head, int index)
{
    strMonster* cur_node = head;
    while (index > 0) {
        cur_node = cur_node->next;
        if (cur_node == head)
            break;
    }
    if (index)
        return head;
    if (cur_node->prev == cur_node->next) {
        return strMonsterInit(head);
    }
    cur_node->prev->next = cur_node->next;
    cur_node->next->prev = cur_node->prev;
    if (cur_node == head) {
        head = head->next;
    }
    delete cur_node;
    return head;
}

void strMonsterDestroyNode(strMonster* head)
{
    strMonster* destroy = head -> next;
    if (destroy == head) {
        delete destroy;
        destroy = 0;
        return;
    }
    strMonster* temp = destroy;
    while (destroy != NULL) {
        temp = temp->next;
        delete destroy;
        destroy = 0;
        destroy = temp;
        if (destroy == head) {
            delete destroy;
            destroy = 0;
            return;
        }
    }
}

 

코드가 긴 편이다. 그런데 자료구조들이 거의 그렇듯 이해하고나면 변형도 쉬운편이고, 작성하는 것도 어렵지 않다.

이중 연결리스트를 사용한 이유는, 몬스터가 생성하고 언젠가 없어지기도 할텐데, 없어지는 몬스터들의 노드를 효율적으로 찾기 위해서이다.

만약 죽은 몬스터가 노드의 맨 끝에 위치할 경우 단일 연결리스트에선 굉장히 비효율적이게 될 것이다 

그러나 이중 연결리스트라면 효율적이게 처리가 가능하다. 헤드 노드의 이전 노드로 찾아가면 되기 때문이다.

 

case WM_TIMER:{
        switch (wParam) {
        case TIMER_MOBCREATE: {
            static short int monsterCount = 0;
            short int monsterCategory = 1;
            POINT monsterPos;
            static std::random_device rd;               // 랜덤 디바이스 시드값 얻는 함수 (시간이 많이 걸리는편이라 static 선언함)
            std::mt19937 gen(rd());
            std::uniform_int_distribution<int> disDirection(0, 3);
            std::uniform_int_distribution<int> disPosx(-20,(rc.right - rc.left));
            std::uniform_int_distribution<int> disPosy(-20,(rc.bottom - rc.top));
            switch (disDirection(gen)) {
            case 0 : {
                    monsterPos.x = 20;
                    monsterPos.y = disPosy(gen);
                    break;
                }
            case 1: {
                monsterPos.x = disPosx(gen);
                monsterPos.y = 20;
                break;
            }
            case 2: {
                monsterPos.x = (rc.right - rc.left) - 20;
                monsterPos.y = disPosy(gen);
                break;
            }
            case 3: {
                monsterPos.x = disPosx(gen);
                monsterPos.y = (rc.bottom - rc.top) - 20;
                break;
            }

            }
            strMonsterList = strMonsterInsert(strMonsterList, monsterCount, monsterCategory, {monsterPos.x, monsterPos.y});
            monsterCount++;
            if (monsterCount > 10) {
                KillTimer(hwnd, TIMER_MOBCREATE);
            }
            break;
        }
            
        }
    }

 

위 함수는 몬스터들을 매 시간마다 무작위의 좌표에 집어넣는 타이머 함수이다.