발전하는 춘배

[원고, C++/OOP] 로그라이크 게임 만들어보기 5 - 전투 : 공격 및 플레이어 사망 본문

.원고

[원고, C++/OOP] 로그라이크 게임 만들어보기 5 - 전투 : 공격 및 플레이어 사망

춘배0 2026. 2. 13. 22:30

1. 공격

이동이 가능해졌으므로 이제 전투를 해보자.

플레이어가 이동한 곳에 적이 있다면, 서로 맞딜하여 피를 깎아본다.

이를 위해 tryMoveEntity 로직에 공격 로직을 추가해준다.

때려보고 잡았으면 그 자리로 이동하고, 못 잡았으면 원래 자리에서 한대 맞는다.

  int newX = entity->getX() + x;
  int newY = entity->getY() + y;

  if (currentMap->getTile(newX, newY).props.walkable) {
    entity->move(x, y);
    Entity *target = getEntityAt(newX, newY);
    if (target != nullptr && target->getState() != EntityState::DEAD) {
      entity->attack(target);
      if (target->getState() != EntityState::DEAD) {
        target->attack(entity);
        entity->move(-x, -y);
      }
    }
  }

 

이렇게 했더니 문제 하나가 발견되었는데, 맵을 출력할 때 죽은 개체와 산 개체의 좌표가 겹칠 때 산 개체가 우선적으로 출력되어야 하는데 그런 고려가 없었다는 것이다. 반영해서 수정해준다. (x,y)의 엔티티를 찾아 리턴하는 함수인데 산 엔티티끼리는 서로 겹치지 않음이 보장되었다 치고 코드를 작성했다.

Entity *World::getEntityAt(int x, int y) {
  auto it = entities.find(currentMapId);
  if (it == entities.end())
    return nullptr;

  auto &vec = it->second;

  std::vector<Entity*> deadEntities;

  for (auto &e : vec) {
    if (e->getX() == x && e->getY() == y) {
      if (e->getState() == EntityState::DEAD)
        deadEntities.push_back(e.get());
      else
        return e.get();
    }
  } 
  if (!deadEntities.empty()) {
    return deadEntities[0];
  }
  return nullptr;
}

 

테스트: 플레이어(@) 체력은 30, 공격력은 10. 적($) 체력은 15, 공격력은 10

Enter command: s
# # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . # 
# . . @ . . . . . . . . . . # 
# . . $ . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# # # # # # # # # # # # # # # 
Enter command: s
# # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . # 
# . . @ . . . . . . . . . . # 
# . . $ . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# # # # # # # # # # # # # # # 
Enter command: s
# # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . @ . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# # # # # # # # # # # # # # # 
Enter command: d
# # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . X @ . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# # # # # # # # # # # # # # #

굿..

 

2. 사망

플레이어가 죽으면 세계는 어떻게 될까?

로그라이크이므로 World를 초기화한다. 로그라이크의 특징과는 별개로 내맘대로 정한 규칙이다. 죽으면 끝

플레이어의 죽음을 어디서 체크해야할까 고민하다가 Game의 run 루프에서 매 턴마다 체크하기로 했다. 이유는 사실 간단하게 생각해 본건데

1. World를 초기화하는데 World에서 체크하고 스스로를 초기화한다? 뭔가 이상스럽다.

2. World는 맵과 엔티티들을 관리한다. 그러므로 엔티티를 이동시키거나 그에 따른 공격까지는 처리할 수 있으나, 그 결과로 엔티티가 사망한 경우에의 로직은 World에 담기 뭣하다.

 

근데 또 2번에 대해 생각해보다가 이러면 공격을 World가 처리하는 게 맞나 싶긴 했는데 그렇다고 이것도 Game으로 올린다는 건 더 이상했다. 뭔가 이상한데 왜 이상한지 말로 설명을 못하겠을 땐 GPT형님한테 물어본다.

조언에 따라 "상태 변화"라는 측면에서 접근해본다.

플레이어나 엔티티의 좌표(상태) 변화: World에서 일어난다.

엔티티의 HP(상태) 변화: World에서 일어난다.

플레이어의 사망(상태) 변화: World에서 일어난다.

그러므로 사망 자체는 World에서 체크하지만, 그 사망에 따른 결과로 초기화는 Game에서 한다. 이를 위해 World는 플레이어의 사망 유무를 Game에게 보고하는 일종의 getter를 가져야 한다.

bool World::isPlayerDead() { return player->getState() == EntityState::DEAD; }
void Game::run() {
  isRunning = true;
  world->populateMap("map1", 15, 10);
  player= world->getPlayer();
  while (isRunning) {
    world->drawCurrentMap();
    handleInput();
    if (world->isPlayerDead()) {
      std::cout << "You died!\n";
      isRunning = false;
      break;
    }
  }
}

 

테스트:

# # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . # 
# . . $ . . . . . . . . . . # 
# . . @ . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# . . . . . . . . . . . . . # 
# # # # # # # # # # # # # # # 
Enter command: w
You died!

 

반응형