Articles

unitate: cum de a crea 2D Tilemap programatic

pentru tutorial video click aici

Dacă aveți nevoie pentru a crea diferite Tilemaps de diferite dimensiuni a verifica afară de a doua parte a acestui tutorial

componenta Tilemap a fost introdus în unitate 2017.2 și a facilitat în mod semnificativ procesul de dezvoltare joc 2d. Cu versiunea 2018.3 isometric Tilemap a fost introdus oferind suport mare pentru jocuri 2.5 D. Recent am avut ocazia să lucrez îndeaproape cu această componentă și am fost provocat de sarcina de a crea plăci programatic. În ultimul meu Joc Am o mulțime de niveluri și toate au aceeași placă bazată pe Tilemap. Dar placa în sine are o configurație unică la nivel. Evident, fiind un dezvoltator profesionist, nu am vrut să creez 60 de scene de joc și să pictez toate nivelurile manual, ci mai degrabă să am un mecanism pentru a umple placa cu elementele corespunzătoare în funcție de intrarea dată. Dacă sunteți curios cum arată rezultatul final, aici este legătura cu jocul.

codul sursă este disponibil la GitHub, vă rugăm, găsiți link-ul de la sfârșitul acestui tutorial.

folosesc cea mai recentă unitate disponibilă 2019.2.0.1 f. Pentru a lucra cu acest tutorial ar trebui să aveți cel puțin versiunea 2018.3. Vom folosi grila izometrică, dar tehnica descrisă este aplicabilă oricărui tip.

înainte de a începe vă sugerez să citiți prin acest genial medii izometrice 2D cu intrare Blog Tilemap pentru a obține o înțelegere de bază a Tilemap izometrice.

deoarece lucrăm într-un mediu 2D, ar trebui să configurați un nou proiect 2D (consultați 2DAnd3DModeSettings)

există 3 componente principale necesare pentru a ne atinge obiectivul: o tablă de joc în cazul în care jocul va avea loc, un loc unde să păstreze o descriere nivel și un cod care se conectează unul la altul.

Partea 1. Crearea plăcii

să începem prin importarea activelor de imagine necesare. Voi folosi aceleași imagini am folosit pentru jocul meu:

Used to create a base tilemap level

Used as a path

Used to mark the start and end points of a calea

vom păstra imaginile în folderul Tiles, pur și simplu trageți-le și plasați-le acolo.

Placi de configurare

notă! Ar trebui să aveți o dimensiune corectă „Pixel Per unitate” setată pe fiecare imagine de țiglă (pentru detalii suplimentare, consultați medii 2D izometrice cu Tilemap). Pentru aceste imagini valoarea este 1096.

configurare imagine

este o bună practică să separați diferite straturi pe o scenă în obiecte de joc dedicate cu nume descriptive. Imaginați-vă ecranul de joc tipic care ar putea conține o zonă de joc, UI, destinații de plasare a anunțurilor și așa mai departe.

urmând această abordare, să creăm un nou obiect de joc gol numitGameZone unde ar putea fi plasate tabla și toate elementele jocului.

acum este timpul să creăm plăci reale din imaginile pe care le-am importat mai devreme

apăsați fereastra- > 2D – > paleta de plăci

paleta de plăci

vizualizarea paletei de plăci va fi deschisă. Faceți clic pe „Creați o paletă nouă” și creați o paletă:

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.

FindGameZone în vederea ierarhie, click dreapta al mouse-ului obiect 2D xtctu izometric Tilemap. Va fi creat un nou obiect de joc Grid.

dimensiunea plăcii va fi de 11×11 plăci și putem începe să pictăm. Selectați caseta instrument perie (al 4-lea element din stânga) în vizualizarea „paletă țiglă”, apoi selectați țiglă „curățare”. Pictează-l în vizualizarea scenei.

bord

după ce ați terminat cu pictura ar trebui să comprima manual limitele tilemap. Pentru aceasta trebuie să selectați Tilemap în vizualizarea ierarhie, apăsați pe un buton setări (angrenaj) de pe componenta Tilemap și selectați „comprimare limite Tilemap”

comprima Tilemap limite

bine făcut, tabla de joc este gata pentru a fi utilizate! Vom reveni la ea în partea 3.

Partea 2. Date de nivel

deoarece dorim să reutilizăm aceeași scenă și aceeași tablă de joc, datele de nivel ar trebui păstrate undeva. O soluție simplă pentru că este un fișier json simplu, care va descrie modul în care fiecare nivel ar trebui să fie construit.

este important să înțelegem ce încercăm să realizăm, de aceea trebuie să spun câteva cuvinte despre Mecanică. În joc, există obiecte care se deplasează de-a lungul calea de la început până la sfârșit (destul de mult ca în Zuma) și scopul jucătorului este de a distruge toate acestea. În acest tutorial vom crea această cale care va fi unică pentru fiecare nivel.

bine, înapoi la proiect.

există mai multe modalități de accesare a datelor externe într-un timp de rulare dintr-un script. Aici vom folosi folderele de resurse

să creăm un nou folder — fișiere — și un sub-folder resurse. Acesta este locul pe care dorim să păstreze datele, astfel încât a crea un nou fișier — niveluri.json și puneți-l acolo.

în scopul tutorialului vom avea doar două câmpuri pentru a descrie fiecare nivel:

  • număr — un int pentru a identifica un nivel
  • cale — calea bazată pe țiglă pe care dorim să o creăm programatic. Matrice de valori în cazul în care prima valoare este punctul de pornire și ultima valoare este punctul final.

acesta este fișierul pe care îl voi folosi. Nu vă faceți griji cu privire la aceste valori în path, vom ajunge la ea mai târziu.

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

să creăm un alt folder — Scripturi — și acum codificarea începe în sfârșit.

vrem să accesăm datele din fișierul din cod, de aceea avem nevoie de o clasă de model pentru aceasta. Este timpul să creăm primul nostru script — LevelsData. Nu este menit să fie instanțiat de unitate, deci MonoBehaviour și StartUpdate metodele ar trebui eliminate. Din fișierul de mai sus putem vedea că elementul rădăcină este un array de niveluri în care fiecare nivel ar trebui să aibă un int număr de câmp și un int array cale de câmp. De asemenea, nu uitați să puneți adnotare.


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

frumos, acum avem fișierul și modelul. Următorul pas este de a transforma unul în altul. Să creăm un alt script – GameZone — și să-l atașăm la obiectul GameZone de pe scenă. Acest script va fi folosit mai târziu pentru a seta tabla de joc întreg.

urmați principiul responsabilității unice să creăm încă un alt script — LevelsDataLoader — care va face toată transformarea. Atașați-l și la obiectulGameZone.

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);
}
}

această clasă va încărca datele și le va returna ca un dicționar în care cheia este numărul de nivel și datele sunt datele de nivel în sine.

acum ar trebui să putem accesa datele dinGameZone script.

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!");
}
}

reveniți la unitate, apăsați butonul De redare și verificați consola — ar trebui să vedeți mesajul „3 niveluri au fost stocate în dicționar!”

Partea 3. Conectând placa și datele

Felicitări, ați ajuns la ultima și cea mai interesantă parte a acestui tutorial. Cum vom conecta de fapt placa și datele? Continuați să citiți pentru a afla!

în primul rând, să plasămhorizontal șistart_stop Dale create în prima parte a tutorialului în folderul resurse sub folderul Dale. Apoi adăugați un nou script – TilesResourcesLoader — o clasă de ajutor static pentru a încărca dale din acel folder într-un timp de rulare.

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));
}
}

ca ultim pas ar trebui să plasăm acele plăci pe tablă la pornirea scenei. Să ne întoarcem laGameZone script. În primul rând trebuie să simulăm selecția nivelului, în jocul real se întâmplă de obicei ori de câte ori un utilizator apasă un buton de nivel. Din motive de simplitate, să adăugăm un nivel de câmp public la GameZone și să-l schimbăm valoarea la 1 Pentru start. Vă voi arăta mai întâi scenariul final:

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, asta este multă acțiune! Lasă-mă să te plimb prin ea.

înSetupTiles metoda ar trebui să obținem mai întâi tilemap-ul în sine, deoarece trebuie să cunoaștem pozițiile plăcilor pentru ao schimba. Pentru a realiza acest lucru, folosim metoda tilemap.cellBounds.allPositionsWithin care returnează toate pozițiile plăcilor începând de la prima — în configurația dată este o țiglă în jos.

consultați următoarea imagine în care fiecare număr reprezintă indexul din listalocalTilesPositions.

tilemap numerotat

vă amintiți valorile pe care le folosim în calea din Levels.json? După cum probabil ați ghicit deja aceste valori sunt indicii plăcilor din matrice. Tot ce trebuie să faceți acum este să aveți o imagine cheatsheet pentru a vă ajuta să construiți niveluri. Acesta este cel pe care l-am folosit în timpul dezvoltării:

my ugly numerotate tilemap

în acest exemplu stabilim o linie orizontală în cale, vă rugăm să consultați metoda SetupPath. Partea cheie este următoarea buclă:

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

aici vom itera pestelocalTilesPositions pentru a găsi cele pentru a seta țiglă dorită — orizontală în acest caz.

notă! GetRange metoda are doi parametri — index și număr.

pentru a marca pozițiile de început și de sfârșit ale căii, se utilizează placastart_stop.

iată rezultatul muncii noastre grele:

rezultat final

încercați acum să schimbați câmpul Număr nivel al GameZone script de la 1 la 2 sau 3 și veți vedea că calea este încărcată corect pentru nivelul dat.

după aceea