Unity: miten luoda 2D Tilemap ohjelmallisesti
video tutorial klikkaa tästä
Jos haluat luoda erilaisia Tilemapeja erikokoisia katso tämän opetusohjelman toinen osa
Tilemap komponentti otettiin käyttöön Unity 2017.2: ssa ja helpotti merkittävästi 2D-pelin kehitysprosessia. Version 2018.3 myötä otettiin käyttöön isometrinen Tilemap, joka tarjoaa suuren tuen 2.5 D-peleille. Viime aikoina minulla oli mahdollisuus työskennellä tämän komponentin tiiviisti ja haastoi tehtävän luoda laatat ohjelmallisesti. Viimeisessä pelissäni minulla on paljon tasoja ja kaikissa niissä on sama Tilemap-pohjainen lauta. Mutta aluksella itsessään on tason ainutlaatuinen setup. Ilmeisesti, koska ammatillinen kehittäjä en halunnut luoda 60 pelin kohtauksia ja maalata kaikki tasot käsin, vaan on mekanismi täyttää aluksella asianmukaiset elementit riippuen annetusta panoksesta. Jos olet utelias, miten lopputulos näyttää tässä on linkki peliin.
lähdekoodi löytyy GitHubista, katso linkki tämän opetusohjelman lopusta.
käytän viimeisintä saatavilla olevaa Unity 2019.2.0.1 f. Jotta voisit työskennellä tämän opetusohjelman kanssa, sinulla pitäisi olla vähintään versio 2018.3. Käytämme isometristä ruudukkoa, mutta kuvattu tekniikka soveltuu mihin tahansa tyyppiin.
ennen kuin aloitan, suosittelen vahvasti lukemaan tämän loistavan isometrisen 2D-ympäristöjen läpi Tilemap-blogimerkinnällä saadakseni perustiedot isometrisestä Tilemapista.
koska työskentelemme 2D-ympäristössä, sinun tulisi perustaa uusi 2D-projekti (katso 2dand3dmodesettings)
tavoitteen saavuttamiseksi tarvitaan 3 pääkomponenttia: pelilauta, jossa peli tapahtuu, paikka, jossa pidetään tasokuvaus ja jokin koodi, joka yhdistää toisiinsa.
Osa 1. Levyn luominen
aloitetaan tuomalla tarvittavat kuvavarat. Käytän samoja kuvia kuin pelissäni.:
pidämme kuvat laatat-kansiossa, yksinkertaisesti vetämällä ja pudottamalla ne sinne.
Huom! Sinulla pitäisi olla oikea ”Pixel Per Unit” – kokojoukko jokaista laattakuvaa kohti (lisätietoja on Isometrisissä 2D-ympäristöissä Tilemapilla). Näiden kuvien arvo on 1096.
on hyvä käytäntö erottaa näyttämöllä olevat eri kerrokset omiksi pelikohteiksi, joilla on kuvailevat nimet. Kuvittele tyypillinen pelinäyttö, joka saattaa sisältää pelialueen, käyttöliittymän, mainosten sijoittelut ja niin edelleen.
tätä lähestymistapaa noudattaen luodaan uusi tyhjä pelikohde, jonka nimi on GameZone
, johon laudan ja kaikki pelielementit voisi sijoittaa.
nyt on aika luoda varsinaisia laattoja aiemmin tuomistamme kuvista
paina ikkunaa -> 2D -> Laattapaletti
kun olet tehnyt maalauksen, sinun tulee pakata käsin tilemapin rajoja. Tätä varten on valittava Tilemap Hierarkianäkymässä, painettava Tilemap-komponentin asetuspainiketta (gear) ja valittava ”Compress Tilemap Bounds”
hyvin tehty, pelilauta on valmis käyttöön! Palaamme siihen osassa 3.
Osa 2. Tason tiedot
koska haluamme käyttää samaa kohtausta ja samaa pelilautaa uudelleen, tason tiedot tulisi säilyttää jossain. Yksinkertainen ratkaisu tähän on yksinkertainen JSON-tiedosto, joka kuvaa, miten kukin taso olisi rakennettava.
on tärkeää ymmärtää, mitä yritämme saavuttaa, siksi minun on sanottava muutama sana mekaniikasta. Pelissä on esineitä, jotka liikkuvat polun rinnalla alusta loppuun (melko lailla kuten Zumassa) ja pelaajan tavoitteena on tuhota ne kaikki. Tässä opetusohjelmassa luomme tämän polun, joka on ainutlaatuinen kullekin tasolle.
okei, takaisin projektiin.
on olemassa useita tapoja, miten käyttää ulkoista dataa suoritusajassa komentosarjasta. Tässä käytämme Resurssikansioita
Let ’ s create a new folder — Files — and a sub-folder Resources. Se on paikka, jossa haluamme säilyttää tiedot, joten luo uusi tiedosto-tasot.JSON ja laita se tuonne.
opetustarkoituksiin meillä on vain kaksi kenttää kuvaamaan kutakin tasoa:
- numero — int tason tunnistamiseksi
- polku — laattapohjainen polku, jonka haluamme luoda ohjelmallisesti. Array arvoja, joissa ensimmäinen arvo on alkupiste ja viimeinen arvo on päätepiste.
tätä tiedostoa aion käyttää. Älä murehdi niitä arvoja polulla, tulemme siihen myöhemmin.
{
"levels":
},
{
"number": 2,
"path":
},
{
"number": 3,
"path":
}
]
}
luodaan toinen kansio — skriptit — ja nyt koodaus vihdoin alkaa.
haluamme päästä käsiksi koodin tiedoston tietoihin, joten tarvitsemme sille malliluokan. On aika luoda aivan ensimmäinen skripti – LevelsData
. Sitä ei ole tarkoitettu ykseyden instantioimaksi, joten MonoBehaviour
ja Start
Update
menetelmät tulisi poistaa. Yllä olevasta tiedostosta voimme nähdä, että juurielementti on array
tasoista, joissa jokaisella tasolla tulee olla yksi int
kenttäluku ja yksi int array
kenttäpolku. Myös, älä unohda laittaa huomautus.
public class LevelsData
{
public LevelData levels;
public class LevelData
{
public int number;
public int path;
}
}
kiva, nyt on tiedosto ja malli. Seuraava askel on muuttaa toinen toisekseen. Luodaan toinen skripti – GameZone
— ja liitetään se GameZone
objekti paikalle. Tätä komentosarjaa käytetään myöhemmin koko pelilaudan setupointiin.
seuraa yhden vastuun periaatetta luodaanpa vielä toinen skripti — LevelsDataLoader
— joka tekee kaiken muutoksen. Liitä se myös GameZone
objektiin.
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);
}
}
Tämä luokka lataa tiedon ja palauttaa sen sanakirjana, jossa avain on tasoluku ja tieto itse tasotieto.
nyt pitäisi päästä käsiksi GameZone
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!");
}
}
vaihda takaisin Unityyn, paina toistopainiketta ja tarkista konsoli — sinun pitäisi nähdä viesti ”3 tasoa on tallennettu sanakirjaan!”
Osa 3. Kun yhdistät taulun ja tiedot
Onnittelut, olet saavuttanut tämän opetusohjelman viimeisen ja kiinnostavimman osan. Miten liitämme taulun ja datan? Jatka lukemista saadaksesi sen selville!
ensinnäkin sijoitetaan horizontal
ja start_stop
opetusohjelman ensimmäisessä osassa luodut laatat Resurssikansioon laattojen kansion alle. Lisää sitten uusi skripti— TilesResourcesLoader
– staattinen apuluokka, jolla voit ladata laatat kyseisestä kansiosta suorituksen aikana.
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));
}
}
viimeisenä vaiheena pitäisi laittaa nuo laatat taululle kohtauksen käynnistyessä. Palataan GameZone
skriptiin. Ensinnäkin meidän täytyy simuloida tason valinta, oikeassa pelissä se tapahtuu yleensä aina, kun käyttäjä painaa tason painiketta. Lisätään yksinkertaisuuden vuoksi julkinen kenttätaso GameZone
ja muutetaan se arvoksi 1 aloitusta varten. Näytän ensin lopullisen käsikirjoituksen:
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);
}
}
Vau, onpa paljon toimintaa! Anna kun selitän.
SetupTiles
menetelmässä pitäisi ensin saada itse tilemerkki, koska sen muuttamiseksi pitää tietää laattojen paikat. Tämän saavuttamiseksi käytämme tilemap.cellBounds.allPositionsWithin
— menetelmää, joka palauttaa kaikki laattojen asennot alkaen ensimmäisestä-annetussa konfiguraatiossa se on alaspäin eniten laatta.
katso seuraava kuva, jossa jokainen numero edustaa localTilesPositions
– listan indeksiä.
Muistatko arvot, joita käytämme polullaLevels.json
? Kuten ehkä arvata jo nämä arvot ovat indeksit laatat array. Kaikki mitä sinun tarvitsee tehdä nyt on cheatsheet kuva auttaa rakentamaan tasoja. Se on yksi Olen käyttänyt kehityksen aikana:
tässä esimerkissä asetamme vaakasuoran viivan polulle, katsoSetupPath
menetelmä. Keskeinen osa on seuraava silmukka:
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
}
tässä iteroidaan localTilesPositions
, jotta löydetään ne, joilla haluttu laatta asetetaan vaakatasoon tässä tapauksessa.
Huom! GetRange
menetelmällä on kaksi muuttujaa — indeksi ja lukumäärä.
polkujen alku-ja loppupisteiden merkitsemiseen käytetään start_stop
– laattaa.
tässä on kovan työmme tulos:
yritä nyt muuttaaGameZone
script 1: stä 2: een tai 3: een ja näet, että polku on ladattu oikein annetulle tasolle.