하아찡
[언리얼5, C++] 몬스터 어그로 본문
언리얼 버전 5.5.3
서버언어 C++로 구성했습니다.
실행화면
몬스터가 주변 플레이어를 스캔해서 몬스터별 정해진 어그로 범위안에 있을경우 제일 처음에 들어온 플레이어를 어그로를 잡게됨.
스포너 위치에서 몬스터 어그로 범위를 벗어났을경우 몬스터 어그로가 풀리게됨.
해당 어그로 코드를 Room클래스에 작업을 할까 몬스터 클래스에 작업을 할까 고민을 했는데, Room클래스쪽에서 몬스터 어그로 작업을 하게 했습니다.
각 맵별 플레이어 정보를 Room클래스에서 다루다보닌깐 해당 방식이 편하기도하고 굳이 몬스터 클래스에다가 기능을 많이 추가하는건 아닌거같았습니다.
이렇게 짜는게 맞나 싶긴한데 일단 생각나는대로 작업을 했습니다.(매번 해보지만 이게 맞나... 고민을 참 많이 하게됩니다)
추가된 Room코드입니다
void Room::ProcessMonster()
{
for (auto item : _monsters) {
MonsterRef monster = item.second;
MonsterState monsterState = monster->GetState();
if (monsterState == MonsterState::IDLE || monsterState == MonsterState::MOVE) {
// TODO 대기상태이거나 움직이는중일때만 몬스터 어그로 가능
float x = monster->posInfo->x();
float y = monster->posInfo->y();
float AggroRange = monster->GetAggroRange();
int searchRange = std::ceil(AggroRange / GRID_SIZE); // 서치범위
for (auto playerid : GetNearbyPlayers(x, y, searchRange))
{
ObjectRef player = _objects[playerid];
if(!player) continue;
if (monster->Distance2D(player->posInfo->x(), player->posInfo->y()) > AggroRange)
continue;
// TODO 해당 몬스터 근처에 사람검색 일단은 바로 잡힌사람으로 어그로
monster->SetTarget(playerid);
break; // 지금은 처음잡은사람 바로 나감
}
}
else if(monsterState == MonsterState::AGGRO || monsterState == MonsterState::ATTACK)
{
// TODO 어그로 상태이거나 공격상태일때만 확인 스폰위치에서 타겟이 거리를 벗어나면 어그로 잃음
uint64 spawnerID = monster->SpawnerID;
float x = _levelMonsterSpawnLocation[spawnerID].x;
float y = _levelMonsterSpawnLocation[spawnerID].y;
float AggroRange = monster->GetAggroRange();
uint64 TargetID = monster->GetTarget();
if (TargetID == 0) {
// TODO 타겟아이디값이없는데 상태값이 이상함으로 상태값을 돌려줌
monster->LostTarget();
}
else {
// 몬스터 거리와 스포너 거리가 어그로 범위를 벗어나서 타겟을 잃음
if (monster->Distance2D(x, y) > AggroRange)
monster->LostTarget();
}
}
if (monsterState != MonsterState::ATTACK)
{
// 일단 움직여
SendMonsterMove(monster);
}
}
}
void Room::SendMonsterMove(MonsterRef monster)
{
if (monster == nullptr) return;
uint64 objectid = monster->objectInfo->object_id();
//움직이전 데이터
RemoveMonsterToGrid(objectid, monster->posInfo->x(), monster->posInfo->y());
if (monster->GetState() == MonsterState::AGGRO)
{
// 어그로 위치로
SendBufferRef sendBuffer = monster->ServerMove(monster, _objects[monster->GetTarget()]);
// Null값으로 온거면 어그로 플레이어가 사라진거임
if(sendBuffer != nullptr)
Broadcast(sendBuffer, NotObjectID);
else
// TODO 그래서 어그로값 날려줌
monster->LostTarget();
}
else
// 어그로가없으면 랜덤으로
Broadcast(monster->ServerMove(monster, _levelMonsterSpawnLocation), NotObjectID);
//움직인후 데이터
AddMonsterToGrid(objectid, monster->posInfo->x(), monster->posInfo->y());
}
ProcessMonster 함수는 몬스터 움직임, 대기, 어그로, 공격 등등등 처리하기 위해 만들었습니다. 현재는 움직임과 대기 기능만 추가됐고 나중에 공격기능같은걸 추가하게되면 해당 함수에서 늘려나갈듯합니다.
SendMonsterMove 함수는 몬스터가 움직였을때 클라이언트에게 패킷을 보내주고, Grid값도 수정하는 역할을 하게됩니다.
작업을 하나씩하고 글을 쓰기는 참 어렵더라구요...
서버쪽 공부를하는데도 클라이언트에서 받쳐주지못하면 기능이 무용지물이다보니 언리얼도 공부하게되고 사실 글을 작성 할 시간이 부족하네요.
현재 작업으론 몬스터 어그로 추가가 됐고 공격도 추가를했지만 그냥 마우스 눌렀을때 공격범위안에 몬스터가있으면 죽는기능도 추가가 됐습니다.
로그인도 추가가 됐고~ 캐릭터 생성도 대충 UI로 캐릭터명만 입력하게 하면 생성이 되게 해놨습니다!
DB도 간단하게 작성해두고 생각보다 재밌습니다.
이제 추가해야 될것이 캐릭터가 마지막으로 종료했을때 마지막 위치를 DB에 저장하고 다음에 접속했을때 해당위치에 스폰하게 하면 될듯합니다!
현재 좌클릭 눌렀을때 기본 공격 처리입니다.
치명타 기능도 추가해보고 이것저것 다 추가해보고 있습니다.
이게 생각보다 작업이 재밌어서 이것저것 추가하다보니 몬스터 어그로도 잠깐 내려놓고 부랴부랴 작업해서 글을 작성했네요..!
'C++ > 이것저것서버테스트' 카테고리의 다른 글
[C++] 몬스터 움직임 - 2 (0) | 2025.03.17 |
---|---|
[언리얼5] 마지막 종료지점 저장 (0) | 2025.03.12 |
[언리얼5, C++] 몬스터 움직임 (0) | 2025.03.06 |
[언리얼5, C++] 몬스터 생성 (0) | 2025.03.04 |
[언리얼5, C++] 맵 이동 - 2 (0) | 2025.02.25 |