Forum Média Blog Kontakt Presskit

Mapové souřadnice

2020-01-31

Ahoj a vítejte u dalšího blogu z vývoje Nebuchadnezzar. Tentokrát se podíváme na jádro každé izometrické hry - na její mapu a konkrétně na souřadnicové systémy a na to jaký používáme my.

Typická izometrická hra se odehrává na čtverečkové mapě. Existují dva základní systémy souřadnic pro takovéto mapy. První z nich je "Staggered" a druhý "Dimond". V Nebuchadnezzar používáme první jmenovaný přestože vypadá na první pohled složitěji. Máme k tomu však několik dobrých důvodů. Pojďme se na ně podívat.

Staggered vs Diamon přehled.

Od začátku vývoje Nebuchadnezzar jsme věděli, že kvůli zachování imerze chceme aby mapa vždy vyplňovala celou obrazovku a měla tedy obdélníkový tvar. Pokud jsou všechny dílky v rozsahu (0 - [šířka mapy], 0 - [výška mapy]), tak Staggered mapa má právě obdélníkový tvar, zatímco diamond bude mít tvar kosodélníku a celou obrazovku by tedy nevyplňovala.

Pokud se všechny souřadnice nachází v takovémto rozsahu můžeme mít všechny dílky mapy uložené v maticové datové struktuře, která bude poskytovat velmi rychlý datový přístup k libovolnému dílku. To oceníme hlavně při renderování, kdy potřebujeme rychle procházet mnoho souřadnic v každém framu.

Staggered vs Diamon v (0 - [šířka mapy], 0 - [výška mapy]) rozsahu.

S tím se pojí další podobná výhoda. Jelikož renderování je jedna z výpočetně nejnáročnějších částí běhu hry, chceme renderovat pouze to nejnutnější, což typicky znamená pouze to co se v daném okamžiku nachází na hráčově monitoru. A právě ve Staggered systému se obdélníkový výřez obrazovky bude vždy nacházet na souřadnicích v rozsahu ([x1] - [x2]([y1] - [y2])). Díky tomu můžeme při renderování efektivně procházet pouze tuto podmatici souřadnic. Povšimněte si, že v Diamond souřadnicích není možné takový jednoduchý rozsah určit.

Staggered vs Diamon ve výřezu obrazovky.

Samozřejmě má tento systém i své nevýhody. Hlavní z nich je špatná interpretovatelnost samotných souřadnic. Což je problém třeba při pohybu objektu. Pokud se chceme pohybovat ve čtyřech hlavních směrech, tak v Diamond souřadnicích je výpočet sousední souřadnice velmi jednoduchý. Stačí přičíst nebo odečíst jedničku v [x] nebo [y] ose. Tím dostaneme všechny čtyři směry. Ve Staggered to tak jednoduché není a výpočet se navíc liší podle toho jestli jsme na liché nebo sudé řádce.

Obecně většina souřadnicových výpočtů na Staggered systému je složitější. Třeba výpočet vzdálenosti dvou bodů (při omezení pohybu na výše zmíněné čtyři směry) nebo dokonce rotace celého systému. Což je sám problém, který by vystačil na samostatný blog. Nakonec jsme skončili s tím, že máme převodní funkce mezi oběma systémy a na složitější výpočty můžeme použít ten, který je výhodnější.

Staggered vs Diamon při výpočtu sousedního dílku.

S přibývajícím vývojem rostl i počet souřadnicových operací, které jsme potřebovali. Přestože žádný z těchto výpočtů není nijak složitý, tak může být obtížné je všechny správně vymyslet a otestovat. Níže proto naleznete ukázky z našich zdrojových kódů s různými souřadnicovými operacemi. Můžete je použít ve svém projektu nebo si na nich třeba jen otestovat prostorovou orientaci.

Příště se podíváme ještě na další typ souřadnic který používáme a na některé další operace. Třeba velmi důležité převody mezi prostorem obrazovky a prostorem hry. Doufám, že jste se dozvěděli něco nové a zajímavého o tom, jak fungují izometrické hry a nebojte ze zeptat v komentářích kdyby jste měli jakékoliv otázky. Naschle příště.

Souřadnice sousedního dílku v daném směru.

/**
 * @brief Get neighbor tile in the given direction.
 *
 * @param pos Cooridinates of origin tile.
 * @param direction Neighbor direction in range <0, 7> for NE, E, SE, S, SW, W, NW, N.
 */
N_Point Tile_Scene::neighbor(const N_Point pos, const int direction) {
  const int x = pos.x;
  const int y = pos.y;

  switch (direction) {
    case 0:
      return {x + std::abs(y % 2), y - 1};
    case 2:
      return {x + std::abs(y % 2), y + 1};
    case 4:
      return {x - std::abs((y + 1) % 2), y + 1};
    case 6:
      return {x - std::abs((y + 1) % 2), y - 1};
    case 1:
      return {x + 1, y};
    case 3:
      return {x, y + 2};
    case 5:
      return {x - 1, y};
    case 7:
      return {x, y - 2};
  }

  n_assert_msg("Invalid parameters.");
  return N_Point();
}

Směr mezi dvěma sousednímy dílky.

/**
 * @brief Get direction between two neighboring tiles.
 *
 * @param from Cooridinates of origin tile.
 * @param to Cooridinates of destination tile.
 * 
 * @return Direction in range <0, 7> for NE, E, SE, S, SW, W, NW, N.
 */
int Tile_Scene::direction(const N_Point from, const N_Point to)
{
  const int x = to.x - from.x;
  const int y = to.y - from.y;

  if ((x == std::abs(from.y % 2)) && (y == -1)) {
    return 0;
  }
  else if ((x == std::abs(from.y % 2)) && (y == 1)) {
    return 2;
  }
  else if ((x == -std::abs((from.y + 1) % 2)) && (y == 1)) {
    return 4;
  }
  else if ((x == -std::abs((from.y + 1) % 2)) && (y == -1)) {
    return 6;
  }
  else if ((x == 1) && (y == 0)) {
    return 1;
  }
  else if ((x == 0) && (y == 2)) {
    return 3;
  }
  else if ((x == -1) && (y == 0)) {
    return 5;
  }
  else if ((x == 0) && (y == -2)) {
    return 7;
  }

  n_assert_msg("Invalid parameters.");
  return 0;
}

Převod mezi staggered a diamond systémem.

/**
 * @brief Get diamond coordinates for given staggered coordinates.
 *
 * @param pos Staggered coordinates.
 */
N_Point Tile_Scene::to_diamond(const N_Point pos)
{
  return {((pos.y + 1) / 2) + pos.x + (((pos.y + 1) % 2) * (pos.y < 0)), (pos.y / 2) - pos.x + ((pos.y % 2) * (pos.y < 0))};
}


/**
 * @brief Get staggered coordinates for given diamond coordinates.
 *
 * @param pos Diamond coordinates.
 */
N_Point Tile_Scene::from_diamond(const N_Point pos)
{
  if (pos.x % 2) {
    return {(pos.x / 2) - ((pos.y - (pos.y < 0)) / 2) + ((pos.x % 2) * (pos.x < 0)), pos.x + pos.y};
  }
  else {
    return {(pos.x / 2) - ((pos.y + (pos.y > 0)) / 2) + ((pos.x % 2) * (pos.x < 0)), pos.x + pos.y};
  }
}
Diskutujte o tomto příspěvku na našem fóru nebo redditu.

Add Nebuchadnezzar to wishlist

DO STEAM OBCHODU

Aktuální blog

Zůstaňte v kontaktu