이번 글에선 이중 연결리스트로 몬스터를 구현한 것에대해 적겠다.
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;
}
}
}
위 함수는 몬스터들을 매 시간마다 무작위의 좌표에 집어넣는 타이머 함수이다.
'개발 프로젝트 > Win32 - VampireSurvivor 모작' 카테고리의 다른 글
로그라이크 게임 구현 - Context based steering algorithm을 활용한 움직임 구현 (0) | 2022.04.07 |
---|---|
로그라이크 게임 구현 2주차 (2) (0) | 2022.04.07 |
로그라이크 게임 구현 1주차 (5) (0) | 2022.03.24 |
로그라이크 게임 구현 1주차(4) (0) | 2022.03.24 |
로그라이크 게임 구현 1주차 (3) (0) | 2022.03.24 |