2D MMORPG 좌표 시스템에 대해서 질문드립니다.

3권에서 새로 도입된 네트웍 및 멀티플레이어 프로그로그래밍 섹션을 위한 게시판입니다.

Moderator: 류광

Locked
비회원

2D MMORPG 좌표 시스템에 대해서 질문드립니다.

Post by 비회원 »

저는 지금 좌표시스템을 이렇게 사용하고 있습니다.

1111111111111111
1111110000001111
1111111100001111
1111111111111111

이라는 파일이 있으면 이 파일을 읽어들여서
좌표의 이동 가능, 장애물 여부를 map 상에 저장합니다.
Key는 [좌표x * 1000 + 좌표y] 로 검색되도록 하고 있구요.
(map은 제가 엄청 많이 사용하다보니깐 사용방법이나, 특성등은 다 알고 있습니다.
map[key] 일 경우에는 기존에 key로 등록된 자료가 없으면 생성하거나, 이미 존재할경우 modify 되므로 첨자 사용시에는 이에 유의해야하는 점등은 기본적으로 알고 있습니다.)

그리고 주인공 캐릭터가 맵에 처음 뜬 이후, 이동을 할때마다
이동 된 범위내에서 새로 업데이트된 범위를 하나씩 검색해서 오브젝트를 띄우는 방식을 사용하고 있습니다.
(캐릭터의 시야가 10일때, 캐릭터가 우측으로 1칸 이동하면 위 아래로 -10~ +10 범위에 대해서
주변의 오브젝트를 새로 업데이트하는 방식이죠.
아래 예제 있습니다.)

저는 이렇게 사용하는데 큰 문제 있을까요?
지금가지는 문제가 없는데 오브젝트 개체수를 늘리면 그만큼 루프횟수가 늘어나니깐 문제가 될거 같은데;;;

고수님들은 어떤 방식을 쓰시는지 궁금합니다.

많은 질책과 조언좀 날려주세요...



예제:)
// 새로운 범위 처리 루틴
bool WorldMap::NewRangeObjectProcess(DWORD ssid, s32 areaid, word locx, word locy, word belocx, word belocy, int range_x, int range_y, int range_type)
{
if( chmap.chmap.find(ssid) == chmap.chmap.end() ){
elog.LogOut(1,1,"<CharProcess::NewRangeObjectProcess>없는 SSID입니다.\n");
return 0;
}

//testlog.LogOut(1,1,"중심캐릭터: (%d)(%s)\n",chmap.chmap[ssid].obj, chmap.chmap[ssid].charname.c_str() );

// 캐릭터 리스트, NPC 리스트, 아이템 리스트담을 컨테이너의 주소값을 넘겨준다
CharMap::CHMAPKEYLIST otherch;
Npc::MOBLIST npcs;
Item::ITEMLOCLIST itemloclist;

// 로컬맵 포인터를 얻어온다
AREAPTR it;
LOCMAPPTR it2;
GetLocPtr(areaid, it, it2);
// 예외처리
if( !GetLocPtr(areaid, it, it2) ){
elog.LogOut(1,1,"<WorldMap::NewRangeObjectProcess>로컬맵 포인터를 얻어올 수 없습니다.\n");
return 0;
}

// 브로드 캐스팅 처리
int x = locx;
int y = locy;
int bx = belocx;
int by = belocy;
int dx = range_x;
int dy = range_y;
int distance_x = 0;
int distance_y = 0;
// 검색 범위의 기준을 잡기 위한 좌표
int fx = x;
int fy = y;
pair<WorldMap::LocalMap::LOCMAP::iterator,WorldMap::LocalMap::LOCMAP::iterator> range;
for(int i=(fx-dx); i<=(fx+dx); i++)
{
for(int j=(fy-dy); j<=(fy+dy); j++)
{
if( range_type == NEWRANGE ) // 새로운 라인의 범위일 경우만 실시, 아닐 경우에는 전체 범위를 처리한다
{
distance_x = abs( bx - i );
distance_y = abs( by - j );
if( (distance_x <= dx) && (distance_y <= dy) )
continue;
}

if( it2->second.mlocif.find(i*1000 + j) == it2->second.mlocif.end() )
continue;

// 월드아이템 리스트를 가져온다
if( items.worlditem[areaid].find(i*1000 + j) != items.worlditem[areaid].end() )
{
if( items.worlditem[areaid][i*1000 + j].size() )
{
Item::ITEM_LOC itemloc;
itemloc.item = items.worlditem[areaid][i*1000 + j].top();
itemloc.locx = i;
itemloc.locy = j;
itemloclist.push_back(itemloc);
}
}

// 캐릭터와 NPC를 가져온다
range = it2->second.locmap.equal_range(i*1000 + j);
for(WorldMap::LocalMap::LOCMAP::iterator it = range.first; it != range.second; ++ it)
{
if( it2->second.objmap.find((*it).second) == it2->second.objmap.end() )
continue;
int objtype = ObjCodeToType((*it).second);
if( objtype == CHARTYPE ){
DWORD charid = it2->second.objmap[(*it).second];
if( chmap.chmap.find(charid) == chmap.chmap.end() )
continue;
/*** Prevent Sending to Self ***/
if( charid == ssid )
continue;

otherch.push_back(charid);

// 서로를 KnownObject로 추가한다 - 여기에 배치해야 서로
chmap.chmap[ssid].knownuser[charid] = 1;
chmap.chmap[charid].knownuser[ssid] = 1;

//testlog.LogOut(1,1,"- 캐릭터: (%d)(%s)\n",(*it).second, chmap.chmap[charid].charname.c_str() );

}else if(objtype == MOBTYPE || objtype == NPCTYPE || objtype == GEMTYPE){
int mobid = it2->second.objmap[(*it).second];
if( npc.npcspawn.find(mobid) == npc.npcspawn.end() )
continue;

// 목록에 추가
npcs.push_back(mobid);
// KnownObject에 추가
npc.npcspawn[mobid].knownuser[ssid] = 1;

//testlog.LogOut(1,1,"- NPC: (%d)(%d)\n", (*it).second, npc.npcspawn[mobid].refid);

// 몬스터를 활동 상태로 변경한다
if( (npc.npcspawn[mobid].state == 1) ){
npc.npcspawn[mobid].state = 0;
npc.actmob.push(mobid);
}else if( npc.npcspawn[mobid].state == 3 ) // 사라져있는 상태라면 스킵한다
continue;
}
}
}
}

// 갯수를 파악하는건 필수임 - Error Fixed
if( otherch.size() ){
// 캐릭터 패킷 준비
word header = dp_rezen_character;
word objlen = 1;
Buffer recpack(4 + objlen*49);
recpack << header;
recpack << objlen;
psend.MakeCharPacket(ssid, &recpack);

// 패킷 전송을 한번에 처리하도록 한다
header = dp_rezen_character;
objlen = (word)otherch.size();
Buffer charpack(4 + objlen*49);
charpack << header;
charpack << objlen;
for(int i=0; i< objlen; i++){
psend.MakeCharPacket(otherch, &charpack);
// 자기 캐릭터를 타 캐릭에게 보이도록 한다
psend.SendPack(otherch, (char*)recpack.buffer, recpack.size);
}
psend.SendPack(ssid, (char*)charpack.buffer, charpack.size);
}

if( npcs.size() ){
word header = dp_respawn_npc;
word objlen = (word)npcs.size();
Buffer npcpack(4 + objlen*17);
npcpack << header;
npcpack << objlen;
for(int i=0; i< objlen; i++){
psend.MakeNpcPack(npcs, &npcpack);
}
psend.SendPack(ssid, (char*)npcpack.buffer, npcpack.size);
}

if( itemloclist.size() ){
word header = dp_put_item_on_map;
word objlen = (word)itemloclist.size();
Buffer itempack(2 + 2 + 6*objlen);
itempack << header;
itempack << objlen;
for(int i=0; i< objlen; i++){
Item::ITEM_LOC itemloc = itemloclist;
int refid = itemloc.item.refid;
string type = itemloc.item.type;
word locx = itemloc.locx;
word locy = itemloc.locy;
word shape = items.GetItemShape(refid, type);
itempack << shape;
itempack << locx;
itempack << locy;
}
psend.SendPack(ssid, (char*)itempack.buffer, itempack.size);
}

return 1;
}


// 클래스 내용

class WorldMap
{
public:
int mfile_amt;

class LocalMap // 로컬맵 클래스
{
public:
int max_x;
int max_y;
string fname;
char area_mode;

typedef map<int, int> MAPLOCINFO;
MAPLOCINFO mlocif;
typedef map<string, char> FRIENDMAPLIST;
FRIENDMAPLIST mfriend_list;
typedef map<word, DWORD> OBJMAP; // obj 코드별로 구간을 나눠서 다룰것 (로컬맵상에 모든 오브젝트 리스트)
OBJMAP objmap;
typedef multimap<int, word> LOCMAP; // 로컬맵 내 일정좌표상에 존재하는 오브젝트 리스트
LOCMAP locmap;
typedef stack<int> STACK;
typedef map<int, STACK> LOCITEM;
LOCITEM locitem;
typedef queue<int> OBJCODELIST;
OBJCODELIST chobjcode;
OBJCODELIST mobobjcode;
OBJCODELIST npcobjcode;
// 분신과 AI 오브젝트 영역 추가
OBJCODELIST bsobjcode;
OBJCODELIST sumobjcode;
OBJCODELIST gateobjcode;
OBJCODELIST gemobjcode;


LocalMap(string s);

LocalMap& operator = (const LocalMap& A)
{
if ( this == &A )
return (*this);
max_x = A.max_x;
max_y = A.max_y;
fname = A.fname;
mlocif = A.mlocif;
mfriend_list = A.mfriend_list;
area_mode = A.area_mode;
objmap = A.objmap;
locmap = A.locmap;
locitem = A.locitem;
chobjcode = A.chobjcode;
mobobjcode = A.mobobjcode;
npcobjcode = A.npcobjcode;
bsobjcode = A.bsobjcode;
sumobjcode = A.sumobjcode;
gemobjcode = A.gemobjcode;
gateobjcode = A.gateobjcode;
return (*this);
}
};

typedef map<string, LocalMap> WORLDMAPLIST;
typedef WORLDMAPLIST::iterator LOCMAPPTR;
WORLDMAPLIST wml;

WorldMap();
~WorldMap();

struct area
{
string areaname;
string mapname;
string mfilename;
unsigned short x1;
unsigned short y1;
int restart_area;
bool can_save_gate;
bool can_use_gate;
bool can_pk;
bool can_guild_battle;
bool can_use_chat;
};
typedef map<int, struct area> AREAMAP;
typedef AREAMAP::iterator AREAPTR;
AREAMAP art;
typedef multimap<string, int> AREAGROUP;
AREAGROUP arp;

bool GetLocPtr(int areaid, AREAPTR &ar_it, LOCMAPPTR &lm_it);
bool AllocObjCode(s32 areaid, int obj_type, int optype, word &obj);
byte DecideAhead(word sub_x, word sub_y, word ob_x, word ob_y);
void GetLocbyAhead(word &subx, word &suby, byte ahead);
int ChangeLocState(LOCMAPPTR it2, word locx, word locy, int flag);
bool DelLocObject(LOCMAPPTR &it2, word obj, word locx, word locy);
bool DelObject(LOCMAPPTR &it2, word obj);
void GetNearChar(CharMap::CHMAPKEYLIST &otherch, int areaid, word locx, word locy, int flag, DWORD except_id);
void GetNewLineChar(CharMap::CHMAPKEYLIST &otherch, int areaid, word b_x, word b_y, word move_x, word move_y);
bool RandomSelectLoc(LOCMAPPTR it2, word &locx, word &locy, word range_x, word range_y);
bool GetRangeObjectEx(s32 areaid, LOCMAPPTR &it2, CharMap::CHMAPKEYLIST *otherch, bool get_char, DWORD except_ssid, CharMap::CHMAPKEYLIST *npcs, bool get_mob, int except_mobid, bool get_npc, Item::ITEMLOCLIST *itemloclist, bool get_item, word locx, word locy, word belocx, word belocy, int range_type, int range_x, int range_y);
bool NewRangeObjectProcess(DWORD ssid, s32 areaid, word locx, word locy, word belocx, word belocy, int range_x, int range_y, int range_type);
bool BeforeRangeObjectProcess(DWORD ssid, s32 areaid, word locx, word locy, word belocx, word belocy, int range_x, int range_y, int range_type);
bool NPCNewRangeObjectProcess(int mobid, word locx, word locy, word belocx, word belocy, int range_x, int range_y, int range_type);
bool NPCBeforeRangeObjectProcess(int mobid, word locx, word locy, word belocx, word belocy, int range_x, int range_y, int range_type);
bool GetObject(s32 areaid, word locx, word locy, word belocx, word belocy, byte ahead, int range_type, CharMap::CHMAPKEYLIST &char_tar, CharMap::CHMAPKEYLIST &mob_tar);
bool CheckRangeNearVector(int cur_x, int cur_y, int locx, int locy, int blocx, int blocy, double range);
int ObjCodeToType(word obj);
bool ChangeLocation(DWORD objectid, int objtype, int movetype, word before_locx, word before_locy, word locx, word locy);
bool GetInverseLoc(s32 areaid, word sub_x, word sub_y, word ob_x, word ob_y, word &m_to_x, word &m_to_y);
bool MapObjBlownAway(DWORD objectid, int objtype, word sub_x, word sub_y);

};

// 정의 부분

WorldMap::WorldMap()
{
const char delimiters[] = ".";
char *token;

int i=0;
_chdir("./map/");
struct _finddata_t c_file;
long hFile;
hFile = (long)_findfirst( "*.*", &c_file);
while(! _findnext( hFile, &c_file ) )
{
if(i)
{
string file_name = c_file.name;
if(!(token = strtok (c_file.name, delimiters)))
ErrorHandling(1, 1, "WorldMap::WorldMap", "Pointer Error", "파일이름이 없습니다");

string mapname = token;
//LocalMap tmp = new LocalMap(file_name.c_str());
LocalMap tmp = LocalMap(file_name.c_str());
wml.insert(make_pair(mapname, tmp));
}
i++;
}
_findclose( hFile );
_chdir("..");

mfile_amt = i-1;

cout << "Map Zone Amount: " << mfile_amt << endl;
}

WorldMap::LocalMap::LocalMap(string s) : fname(s)
{
ifstream InputFile(fname.c_str(),ios::in);
if(InputFile == NULL)
ErrorHandling(1, 1, "WorldMap::GetLocPtr", "Pointer Error", "파일이름이 없습니다");

string stline;
int i = 0, j = 0;
for(; ; j++)
{
if( j > 999 )
ErrorHandling(1, 1, "WorldMap::GetLocPtr", "size Error", "x-loc is over 999");

if( getline(InputFile, stline)== NULL )
break;

char* lm = (char*)stline.c_str();
if( lm[0] == '#')
{
j--;
const char delimiters[] = " #=";
char* token;
if(!(token = strtok (lm, delimiters)))
ErrorHandling(1, 1, "WorldMap::LocalMap::LocalMap", "Pointer Error", "키워드가 없습니다");

string keyword = token;
if(!(token = strtok (NULL, delimiters)))
ErrorHandling(1, 1, "WorldMap::LocalMap::LocalMap", "Pointer Error", "밸류가 없습니다");

string value = token;

if( keyword == "area_type" ){
if( value == "safe" )
area_mode = 0;
else
area_mode = 1;
}else if( keyword == "friend_map" )
mfriend_list[value] = 1;
}
else
{
int size = stline.size();
for(i = 0 ; i< size; i++)
{
if( i > 999 )
ErrorHandling(1, 1, "WorldMap::GetLocPtr", "size Error", "y-loc is over 999");

if( lm == '0' )
mlocif[j*1000 + i] = 0;
else
mlocif[j*1000 + i] = 1;
}
}
stline.erase();
}
InputFile.close();

max_x = j;
max_y = i;

for(int i = 1; i<= 0x7fff; i++)
chobjcode.push(i);
for(int i = 0x8000; i< 0xA710; i++)
mobobjcode.push(i);
for(int i = 0xA710; i< 0xBA98; i++)
npcobjcode.push(i);
for(int i = 0xBA98; i< 0xCE20; i++)
gemobjcode.push(i);
for(int i = 0xCE20; i< 0xD9D8; i++)
gateobjcode.push(i);
for(int i = 0xD9D8; i< 0xE590; i++)
sumobjcode.push(i);
for(int i = 0xE590; i< 0xF148; i++)
bsobjcode.push(i);
}
비회원

음...

Post by 비회원 »

Observer, Strategy 패턴을 사용해보시는건 어떨까요??

맵에서는 섹션 별로만 Observer 패턴으로 객체들을 관리하고 객체들은 Strategy 패턴으로 맵의 섹션을 통하여 서로 메세지를 통신하는 것이지요.
Locked