이 코드는 온라인 게임 서버에서 몬스터 AI가 플레이어를 추적하고 공격하는 길찾기 기능을 포함하고 있으며, 클라이언트와 서버 간 동기화를 수행하는 기능을 가지고 있다.
또한, 게임의 주요 오브젝트(Monster, Player)를 관리하는 GameRoom이 존재하며, 여기서 몬스터와 플레이어의 상태를 지속적으로 갱신하는 역할을 수행한다.
서버-클라이언트 동기화
Broadcast() 메서드를 사용하여 서버에서 모든 클라이언트에게 몬스터의 이동 정보를 전송.길찾기 알고리즘 (A* 기반 휴리스틱 탐색)
FindPath() 함수에서 길찾기를 수행하며, 우선순위 큐(priority_queue) 를 사용해 최적 경로를 탐색함.CanGo() 함수를 활용해 몬스터가 이동할 수 있는지 체크.게임 오브젝트 관리
GameRoom에서 Player와 Monster를 관리.FindClosestPlayer()를 통해 가장 가까운 플레이어를 탐색하여 타겟으로 설정.UpdateIdle(), UpdateMove(), UpdateSkill()을 통해 몬스터의 상태 전이(State Transition) 관리.Monster.h : 몬스터 클래스 선언#pragma once
#include "GameObject.h"
class Monster : public GameObject
{
using Super = GameObject;
public:
Monster();
virtual ~Monster() override;
virtual void Update();
private:
virtual void UpdateIdle();
virtual void UpdateMove();
virtual void UpdateSkill();
private:
uint64 _waitUntil = 0; // 대기 시간 (밀리초 단위)
weak_ptr<Player> _target; // 목표 플레이어 (스마트 포인터 사용)
};
Monster 클래스는 GameObject 를 상속받는다._waitUntil: 몬스터가 특정 행동 후 대기해야 하는 시간 (GetTickCount64() 를 통해 체크)_target: 몬스터가 추적하는 플레이어를 weak_ptr<Player> 로 저장 (메모리 관리 최적화)Update(): 몬스터의 상태에 따라 UpdateIdle(), UpdateMove(), UpdateSkill() 을 실행.Monster.cpp : 몬스터 로직 구현Monster::Monster()
{
info.set_name("MonsterName");
info.set_hp(50);
info.set_maxhp(50);
info.set_attack(5);
info.set_defence(0);
}
Monster::~Monster()
{
}
Monster::Update() : 상태 업데이트void Monster::Update()
{
switch (info.state())
{
case IDLE:
UpdateIdle();
break;
case MOVE:
UpdateMove();
break;
case SKILL:
UpdateSkill();
break;
}
}
IDLE, MOVE, SKILL 상태에 따라 각각 다른 행동을 수행.Monster::UpdateIdle() : 타겟 탐색 및 길찾기void Monster::UpdateIdle()
{
if (room == nullptr)
return;
// 가장 가까운 플레이어 찾기
if (_target.lock() == nullptr)
_target = room->FindClosestPlayer(GetCellPos());
PlayerRef target = _target.lock();
if (target)
{
Vec2Int dir = target->GetCellPos() - GetCellPos();
int32 dist = abs(dir.x) + abs(dir.y);
if (dist == 1)
{
SetDir(GetLookAtDir(target->GetCellPos()));
SetState(SKILL, true);
_waitUntil = GetTickCount64() + 1000; // 1초 대기 후 공격
}
else
{
vector<Vec2Int> path;
if (room->FindPath(GetCellPos(), target->GetCellPos(), OUT path))
{
if (path.size() > 1)
{
Vec2Int nextPos = path[1];
if (room->CanGo(nextPos))
{
SetDir(GetLookAtDir(nextPos));
SetCellPos(nextPos);
_waitUntil = GetTickCount64() + 1000;
SetState(MOVE, true);
}
}
else
SetCellPos(path[0]);
}
}
}
}
FindClosestPlayer(): 가장 가까운 플레이어를 찾음.FindPath(): 길찾기 알고리즘을 수행하여 최적 경로를 계산.SetState(SKILL): 근접하면 공격 상태로 변경.SetCellPos(): 이동 가능한 경우 다음 위치로 이동.FindPath() : 길찾기 알고리즘 구현bool GameRoom::FindPath(Vec2Int src, Vec2Int dest, vector<Vec2Int>& path, int32 maxDepth)
{
priority_queue<PQNode, vector<PQNode>, greater<PQNode>> pq;
map<Vec2Int, int32> best;
map<Vec2Int, Vec2Int> parent;
int32 cost = abs(dest.y - src.y) + abs(dest.x - src.x);
pq.push(PQNode(cost, src));
best[src] = cost;
parent[src] = src;
Vec2Int front[4] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
while (!pq.empty())
{
PQNode node = pq.top();
pq.pop();
if (best[node.pos] < node.cost)
continue;
if (node.pos == dest)
break;
for (int32 dir = 0; dir < 4; dir++)
{
Vec2Int nextPos = node.pos + front[dir];
if (!CanGo(nextPos))
continue;
int32 newCost = abs(dest.y - nextPos.y) + abs(dest.x - nextPos.x);
if (best.find(nextPos) != best.end() && best[nextPos] <= newCost)
continue;
best[nextPos] = newCost;
pq.push(PQNode(newCost, nextPos));
parent[nextPos] = node.pos;
}
}
path.clear();
Vec2Int pos = dest;
while (pos != parent[pos])
{
path.push_back(pos);
pos = parent[pos];
}
std::reverse(path.begin(), path.end());
return true;
}
priority_queue 를 사용하여 우선 탐색.맨해튼 거리(Manhattan Distance) 를 기반으로 최단 경로 계산.CanGo() 를 통해 이동 가능 여부 확인.GameRoom::Broadcast() : 서버-클라이언트 동기화void GameRoom::Broadcast(SendBufferRef& sendBuffer)
{
for (auto& item : _players)
{
item.second->session->Send(sendBuffer);
}
}
SendBufferRef 를 사용하여 모든 클라이언트에게 몬스터의 상태를 동기화.