Вводная: имеем карту типа Azeroth, где позиция xy = 0:0 посредине. В итоге координаты от -HALF_MAP до +HALF_MAP. 64х64 тайла которые в wdt карты помечены как отсутствующие или в наличии. В тайле 16х16 чанков, каждый делится на сабчанки, где собственно уже есть реальные координаты. Вкратце константы.
Код:
TILE_SIZE = 533.33333;
TILES_COUNT = 64;
MAX_TILE = 63;
HALF_MAP = 32*TILE_SIZE;
INSTANCE_QUARTER_MAP = 8*TILE_SIZE;
CHUNK_COUNT = 16;
TILE_CHUNK_COUNT = 256;
MAX_CHUNK = 255;
CHUNK_SIZE = TILE_SIZE / CHUNK_COUNT;
DCHUNK_SIZE = CHUNK_SIZE*2;
SUB_CHUNK_FULL_COUNT = 17;
SUB_CHUNK_LINE_COUNT = 8;
SUB_CHUNK_POINT_COUNT = 9;
SUB_CHUNK_SIZE = CHUNK_SIZE / SUB_CHUNK_LINE_COUNT;
HALF_SUB_CHUNK_SIZE = SUB_CHUNK_SIZE/2;
Сначала находим в каком тайле и чанке x и y и выходим если координаты хзгде. Абсолютная позиция от края карты.
Код:
absX := HALF_MAP - objPos.pos.x;absY := HALF_MAP - objPos.pos.y;
tileX := trunc(absX / TILE_SIZE);diffX := absX - tileX * TILE_SIZE;
tileY := trunc(absY / TILE_SIZE);diffY := absY - tileY * TILE_SIZE;
chunkX := trunc(diffX / CHUNK_SIZE);chunkDiffX := diffX - chunkX * CHUNK_SIZE;
chunkY := trunc(diffY / CHUNK_SIZE);chunkDiffY := diffY - chunkY * CHUNK_SIZE;
if OrdinalMapsArray[objPos.mapId, tileY, tileX] = nil then
exit;
Далее смотрим не попали ли в воду, м2, вмо и т.п., в конце-концов определились что все-таки на карте. Имеем в каждом сабчанке 9х9 точек, что дает нам кучу квадратов. Благодаря второй сетке 8х8 каждый квадрат получает точку в центре и мы имеем уже 4 полигона, что ближе к тригонометрии. Делением получаем тангенс угла, по которому сразу понятно в какой из 4-х полигонов попали. А там уже дело техники: точка сопряжения прямой с полигоном, откуда и имеем Z.
Код:
{ chunk }
tan1 := subChunkDiffY / subChunkDiffX;
tan2 := subChunkDiffY / (SUB_CHUNK_SIZE - subChunkDiffX);
v3.x := HALF_SUB_CHUNK_SIZE; v3.y := HALF_SUB_CHUNK_SIZE;
v3.z := lpChunkMap.Z[subChunkX + subChunkY * SUB_CHUNK_FULL_COUNT + SUB_CHUNK_POINT_COUNT];
p1.x := subChunkDiffY; p1.y := subChunkDiffX; p1.z := 0;
p2.x := subChunkDiffY; p2.y := subChunkDiffX; p2.z := 100;
if tan2 >= 1 then begin
if tan1 >= 1 then begin //right
v1.x := SUB_CHUNK_SIZE; v1.y := 0;
v2.x := SUB_CHUNK_SIZE; v2.y := SUB_CHUNK_SIZE;
v1.z := lpChunkMap.Z[(subChunkX + 1) + subChunkY * SUB_CHUNK_FULL_COUNT];
v2.z := lpChunkMap.Z[(subChunkX + 1) + (subChunkY + 1) * SUB_CHUNK_FULL_COUNT];
norm := Normal([v1, v2, v3]);
mapPositionResult.tilt := abs(Normalize(norm).z);
dV := IntersectionPoint(p1, p2, norm, PlaneDistance(norm, v3));
result := baseZ + dV.z;
end
else begin //bottom
v1.x := SUB_CHUNK_SIZE; v1.y := SUB_CHUNK_SIZE;
v2.x := 0; v2.y := SUB_CHUNK_SIZE;
v1.z := lpChunkMap.Z[(subChunkX + 1) + (subChunkY + 1) * SUB_CHUNK_FULL_COUNT];
v2.z := lpChunkMap.Z[subChunkX + (subChunkY + 1) * SUB_CHUNK_FULL_COUNT];
norm := Normal([v1, v2, v3]);
mapPositionResult.tilt := abs(Normalize(norm).z);
dV := IntersectionPoint(p1, p2, norm, PlaneDistance(norm, v3));
result := baseZ + dV.z;
end;
end
else
if tan1 >= 1 then begin //top
v1.x := 0; v1.y := 0;
v2.x := SUB_CHUNK_SIZE; v2.y := 0;
v1.z := lpChunkMap.Z[subChunkX + subChunkY * SUB_CHUNK_FULL_COUNT];
v2.z := lpChunkMap.Z[(subChunkX + 1) + subChunkY * SUB_CHUNK_FULL_COUNT];
norm := Normal([v1, v2, v3]);
mapPositionResult.tilt := abs(Normalize(norm).z);
dV := IntersectionPoint(p1, p2, norm, PlaneDistance(norm, v1));
result := baseZ + dV.z;
end
else begin //left
v1.x := 0; v1.y := SUB_CHUNK_SIZE;
v2.x := 0; v2.y := 0;
v1.z := lpChunkMap.Z[subChunkX + (subChunkY + 1) * SUB_CHUNK_FULL_COUNT];
v2.z := lpChunkMap.Z[subChunkX + subChunkY * SUB_CHUNK_FULL_COUNT];
norm := Normal([v1, v2, v3]);
mapPositionResult.tilt := abs(Normalize(norm).z);
dV := IntersectionPoint(p1, p2, norm, PlaneDistance(norm, v2));
result := baseZ + dV.z;
end
Код собственно не трогал со времен 1.хх. Не думаю что сильно поменялось. (Edit: эта часть не поменялась и z считается быстро, если не делать для всех мобов одновременно динамически вычисляя передвижения каждые 0.2 ярда как я когда-то делал)