Articles

Jednota: Jak vytvořit 2D Tilemap programově

Pro video tutorial klikněte zde

Pokud potřebujete vytvořit různé Tilemaps různých velikostí, podívejte se na druhou část tohoto tutoriálu

Tilemap složka byla zavedena v Jednotě 2017.2 a výrazně zmírnil 2D game development procesu. S verzí 2018.3 izometrické Tilemap byl představen poskytuje velkou podporu pro 2.5 D hry. Nedávno jsem měl příležitost s touto komponentou úzce spolupracovat a byl jsem vyzván úkolem programově vytvářet dlaždice. V mé poslední hře mám spoustu úrovní a všechny mají stejnou desku založenou na Tilemapu. Ale deska sama o sobě má úroveň jedinečné nastavení. Je zřejmé, že profesionální vývojář nechtěl jsem vytvořit 60 herní scény a malovat všechny úrovně tím, že straně, ale spíše mechanismus vyplnit desku s řádným prvků v závislosti na daném vstupu. Pokud jste zvědaví, jak vypadá konečný výsledek, je zde odkaz na hru.

zdrojový kód je k dispozici na Githubu, prosím, najděte odkaz na konci tohoto tutoriálu.

používám nejnovější dostupné Unity 2019.2.0.1 f. Abyste mohli pracovat s tímto tutoriálem, měli byste mít alespoň verzi 2018.3. Použijeme izometrickou mřížku, ale popsaná technika je použitelná pro jakýkoli typ.

Před zahájením doporučuji přečíst tento skvělý Izometrické 2D Prostředí s Tilemap blogu, aby si základní pochopení Izometrické Tilemap.

protože pracujeme ve 2D prostředí, měli byste nastavit nový 2D projekt (viz 2dand3dmodesettings)

k dosažení našeho cíle jsou zapotřebí 3 hlavní komponenty: hrací deska, kde se hra bude konat, místo, kde se udržet popis úrovně a nějaký kód, který spojuje jeden s druhým.

Část 1. Vytvoření desky

začněme importem potřebných obrazových aktiv. Budu používat stejné obrázky, které jsem použil pro svou hru:

Used to create a base tilemap level

Used as a path

Used to mark the start and end points of a cesta

budeme uchovávat obrázky ve složce dlaždice, jednoduše je tam přetáhněte.

Dlaždice nastavení

Poznámka! Měli byste mít správný „Pixel na jednotku“ velikost nastavena na každý obrázek dlaždice (pro další podrobnosti viz izometrické 2D prostředí s Tilemap). Pro tyto obrázky je hodnota 1096.

nastavení Obrázku

To je dobré praxe k oddělení různých vrstev na scénu do specializované herní objekty s popisnými názvy. Představte si typickou herní obrazovku, která může obsahovat herní zónu, uživatelské rozhraní, umístění reklam atd.

po tomto přístupu vytvoříme nový prázdný herní objekt s názvem GameZone, kde by mohla být umístěna deska a všechny herní prvky.

Nyní je čas k vytvoření skutečné dlaždice z obrázků jsme importovali dříve,

Tisk Okno -> 2D -> Dlaždice, Palety,

Dlaždice, Palety

Uspořádat zobrazení v Paletě, bude otevřeno. Klikněte na „Vytvořit novou paletu“ a vytvořte paletu:

New Palette

Drag and drop tile images one by one to create an actual tiles.

Tiles setup

With those tiles we can finally create the game board which will be filled with elements programmatically on a level startup later.

Najděte GameZone v zobrazení hierarchie klikněte pravým tlačítkem myši → 2D objekt → izometrická Tilemapa. Bude vytvořen nový herní objekt Grid.

velikost desky bude 11×11 dlaždic a můžeme začít malovat. Vyberte nástroj Box Brush tool (4. prvek zleva) v zobrazení „paleta dlaždic“ a poté vyberte“ vyčistit “ dlaždici. Malovat v zobrazení scény.

Rada

Poté, co jste udělal s obrazem, měli byste ručně komprimovat tilemap kroky. Pro tuto budete muset vybrat Tilemap v Hierarchii zobrazení, stiskněte tlačítko na nastavení (gear) tlačítko na Tilemap složku a vyberte možnost „Komprimovat Tilemap Kroky“

Komprimovat Tilemap Hranice

výborně, herní deska je připravena k použití! Vrátíme se k tomu v části 3.

Část 2. Data úrovně

protože chceme znovu použít stejnou scénu a stejnou herní desku, data úrovně by měla být někde uchovávána. Přímočarým řešením je jednoduchý soubor json, který bude popisovat, jak by měla být každá úroveň postavena.

je důležité pochopit, čeho se snažíme dosáhnout, proto musím říci pár slov o Mechanice. Ve hře jsou objekty, které se pohybují podél cesty od začátku až do konce (skoro jako v Zuma) a cílem hráče je zničit všechny z nich. V tomto tutoriálu vytvoříme tuto cestu, která bude jedinečná pro každou úroveň.

dobře, zpět k projektu.

existuje několik způsobů, jak získat přístup k externím datům za běhu skriptu. Zde budeme používat složky zdrojů

Pojďme vytvořit novou složku-soubory – a podsložku zdroje. To je místo, kde chceme, aby data, takže vytvořit nový soubor-úrovně.json a umístěte ji tam.

Pro výukové účely, budeme mít pouze dvě pole k popisu každé úrovni:

  • číslo — int identifikovat úroveň
  • cesta — tile-based cestu, kterou chceme vytvořit programově. Pole hodnot, kde první hodnota je počáteční bod a poslední hodnota je koncový bod.

Toto je soubor, který budu používat. Nedělejte si starosti s těmito hodnotami v path, přijdeme k tomu později.

{
"levels":
},
{
"number": 2,
"path":
},
{
"number": 3,
"path":
}
]
}

vytvoříme další složku-skripty – a nyní se kódování konečně spustí.

chceme přistupovat k datům ze souboru v kódu, proto pro něj potřebujeme třídu modelu. Je čas vytvořit náš úplně první skript – LevelsData. To není chtěl být vytvořena Jednota, takže MonoBehaviourStartUpdate metody by měly být odstraněny. Ze souboru výše můžeme vidět, že kořenový element je array úrovní, kde každá úroveň by měla mít jeden int číslo pole a jeden int array polní cestu. Nezapomeňte také vložit anotace.


public class LevelsData
{
public LevelData levels;
public class LevelData
{
public int number;
public int path;
}
}

pěkné, nyní máme soubor a model. Dalším krokem je transformace jednoho do druhého. Vytvoříme další skript – GameZone – a připojíme jej k objektu GameZone na scéně. Tento skript bude později použit k nastavení celé herní desky.

postupujte podle zásady jednotné odpovědnosti pojďme vytvořit další skript – LevelsDataLoader – který provede veškerou transformaci. Připojte jej také k objektu GameZone.

public class LevelsDataLoader : MonoBehaviour
{
private const string LevelsPath = "Levels";
public Dictionary<int, LevelsData.LevelData> ReadLevelsData()
{
var jsonFile = Resources.Load(LevelsPath, typeof(TextAsset)) as TextAsset;
if (jsonFile == null)
{
throw new ApplicationException("Levels file is not accessible");
}
var loadedData = JsonUtility.FromJson<LevelsData>(jsonFile.text);
return loadedData.levels.ToDictionary(level => level.number, level => level);
}
}

Tato třída načte data a vrátit je jako slovník, kde klíčem je číslo úrovně a dat je na úrovni dat sám.

Nyní bychom měli mít přístup k datům ve skriptu GameZone.

public class GameZone : MonoBehaviour
{
private Dictionary<int, LevelsData.LevelData> _levelsData;
private LevelsDataLoader _dataLoader;private void Awake()
{
_dataLoader = GetComponent<LevelsDataLoader>();
}private void Start()
{
_levelsData = _dataLoader.ReadLevelsData();Debug.Log(_levelsData.Count + " levels have been stored in the dictionary!");
}
}

přepněte zpět na Unity, stiskněte tlačítko play a zkontrolujte konzolu – měli byste vidět zprávu “ 3 úrovně byly uloženy ve slovníku!“

Část 3. Připojení desky a dat

Gratulujeme, dosáhli jste poslední a nejzajímavější části tohoto tutoriálu. Jak vlastně propojíme desku a data? Pokračujte ve čtení a zjistěte to!

za Prvé, pojďme se místo horizontalstart_stop dlaždice vytvořil v první části tutoriálu na složku Prostředky pod Dlaždice složky. Poté přidejte nový skript – TilesResourcesLoader – statická pomocná třída pro načtení dlaždic z této složky za běhu.

public static class TilesResourcesLoader
{
private const string PathHorizontal = "horizontal";
private const string StartStop = "start_stop"; public static Tile GetPathHorizontalTile()
{
return GetTileByName(PathHorizontal);
} public static Tile GetStartStopTile()
{
return GetTileByName(StartStop);
} private static Tile GetTileByName(string name)
{
return (Tile) Resources.Load(name, typeof(Tile));
}
}

jako poslední krok bychom měli tyto dlaždice umístit na desku při spuštění scény. Vraťme se ke skriptu GameZone. Nejprve musíme simulovat výběr úrovně, ve skutečné hře se to obvykle stane, kdykoli uživatel stiskne tlačítko úrovně. Pro zjednodušení přidejme úroveň veřejného pole na GameZone a změníme hodnotu na 1 pro start. Nejprve vám ukážu konečný skript:

public class GameZone : MonoBehaviour
{
public int Level; private const int FieldLineSize = 11;
private const int FieldTotalTiles = FieldLineSize * FieldLineSize;
private Dictionary<int, LevelsData.LevelData> _levelsData; private void Start()
{
_levelsData = GetComponent<LevelsDataLoader>().ReadLevelsData();
SetupTiles();
} private void SetupTiles()
{
var baseLevel = GetComponentsInChildren<Tilemap>(); var localTilesPositions = new List<Vector3Int>(FieldTotalTiles);
foreach (var pos in baseLevel.cellBounds.allPositionsWithin)
{
Vector3Int localPlace = new Vector3Int(pos.x, pos.y, pos.z);
localTilesPositions.Add(localPlace);
} SetupPath(localTilesPositions, baseLevel);
} private void SetupPath(List<Vector3Int> localTilesPositions, Tilemap baseLevel)
{
var path = _levelsData.path;
var pathHorizontalTile = TilesResourcesLoader.GetPathHorizontalTile();
var first = path.First();
var last = path.Last();
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
} var startStopTile = TilesResourcesLoader.GetStartStopTile();
baseLevel.SetTile(localTilesPositions, startStopTile);
baseLevel.SetTile(localTilesPositions, startStopTile);
}
}

Wow, to je hodně akce! Provedu tě tím.

v metodě SetupTiles bychom měli nejprve získat samotnou tilemapu, protože potřebujeme znát pozice dlaždic, abychom ji mohli změnit. K dosažení tohoto cíle používáme metodu tilemap.cellBounds.allPositionsWithin, která vrací všechny pozice dlaždic od první — v dané konfiguraci je to dlaždice nejvíce dolů.

viz následující obrázek, kde každé číslo představuje index v seznamu localTilesPositions.

Číslované tilemap

Pamatuješ si hodnoty používáme v cestě v Levels.json? Jak jste již možná uhodli, tyto hodnoty jsou indexy dlaždic v poli. Vše, co musíte udělat, je mít obrázek cheatsheet, který vám pomůže při vytváření úrovní. To je ten, který jsem používal během vývoje:

Můj ošklivý číslované tilemap

V tomto příkladu jsme nastavení horizontální linii v cestě, naleznete v SetupPath metoda. Klíčovou částí je následující smyčky:

foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
}

Tady jsme iteraci localTilesPositions najít ty, pro nastavení požadované dlaždice — horizontální, v tomto případě.

Poznámka! GetRange metoda má dva parametry-index a počet.

pro označení počáteční a koncové polohy cesty se používá dlaždice start_stop.

Zde je výsledek naší tvrdé práce:

Konečný výsledek

Nyní zkuste změnit úroveň pole číslo z GameZone skript od 1 do 2 nebo 3, a uvidíte, že cesta je správně vložen na dané úrovni.

poté