Unity: hvordan lage 2d tilemap programma
for video tutorial klikk her
hvis du trenger å lage forskjellige Tilemaps av ulike størrelser sjekk ut den andre delen av denne opplæringen
Tilemap komponent ble introdusert I Unity 2017.2 og betydelig lettet 2d spill utviklingsprosessen. Med versjonen 2018.3 Isometrisk Tilemap ble introdusert gir god støtte for 2.5 D spill. Nylig hadde jeg mulighet til å jobbe med denne komponenten tett og ble utfordret med oppgaven å lage fliser programmatisk. I mitt siste spill har jeg mange nivåer, og alle har samme Tilemap-baserte brett. Men styret selv har nivå-unikt oppsett. Åpenbart, å være en profesjonell utvikler jeg ikke ønsker å lage 60 spillscener og male alle nivåer for hånd, men heller har en mekanisme for å fylle brettet med de riktige elementene avhengig av gitt innspill. Hvis du er nysgjerrig på hvordan sluttresultatet ser ut her er link til spillet.
Kildekoden er tilgjengelig På GitHub, vær så snill, finn linken på slutten av denne opplæringen.
jeg bruker den nyeste Tilgjengelige Unity 2019.2.0.1 f. For å kunne jobbe med denne opplæringen bør du ha minst versjon 2018.3. Vi vil bruke Isometrisk rutenett, men beskrevet teknikk gjelder for alle typer.
før jeg starter, anbefaler jeg sterkt å lese gjennom dette strålende Isometriske 2d-Miljøene med tilemap bloggoppføring for å få en grunnleggende forståelse Av Isometrisk Tilemap.
da vi jobber I ET 2d-miljø, bør du sette opp et nytt 2d-prosjekt (se 2DAnd3DModeSettings)
Det er 3 hovedkomponenter som kreves for å nå vårt mål: et spillbrett hvor spillet vil finne sted, et sted hvor du kan holde et nivå beskrivelse og noen kode som kobles til hverandre.
Del 1. Opprette Styret
La Oss starte med å importere nødvendige bildeelementer. Jeg bruker de samme bildene jeg brukte til spillet mitt:
vi vil holde bilder i Fliser-mappen, bare dra og slipp dem der.
merk! Du bør ha en riktig» Pixel Per Enhet » størrelse satt per hver flis bilde(for ytterligere detaljer se Isometriske 2d Miljøer med Tilemap). For disse bildene er verdien 1096.
det er en god praksis å skille forskjellige lag på en scene i dedikerte spillobjekter med beskrivende navn. Tenk deg den typiske spillskjermen som kan inneholde en spillsone, BRUKERGRENSESNITT, Annonseplasseringer og så videre.
Etter denne tilnærmingen, la oss lage et nytt tomt spillobjekt kalt GameZone
hvor brettet og alle spillelementene kan plasseres.
Nå er det på tide å lage faktiske fliser fra bildene vi importerte tidligere
Trykk Window -> 2D -> Flis Palett
når du er ferdig med maleriet, bør du manuelt komprimere tilemap-grenser. For dette må du velge Tilemap I Hierarkivisningen, trykk på en settings (gear) – knapp på tilemap-komponenten og velg «Komprimer Tilemap Bounds»
spillebrettet er klart til bruk! Vi kommer tilbake til dette i del 3.
Del 2. Nivådata
Siden vi vil gjenbruke samme scene og samme spillbrett, bør nivådataene holdes et sted. En grei løsning for det er en enkel json fil som vil beskrive hvordan hvert nivå skal bygges.
Det er viktig å forstå hva vi prøver å oppnå, derfor må jeg si noen ord om mekanikken. I spillet er det objekter som beveger seg langs banen fra start til slutt (ganske mye Som I Zuma) og spillerens mål er å ødelegge dem alle. I denne opplæringen vil vi lage denne banen som vil være unik for hvert nivå.
ok, tilbake til prosjektet.
det er flere måter å få tilgang til eksterne data i en runtime fra et skript. Her skal vi bruke Ressurser mapper
La oss lage en ny mappe-Filer-Og En Undermappe Ressurser. Det er stedet vi ønsker å beholde dataene, så opprett en ny fil-Nivåer.jens og plasser den der.
for opplæringen formål vil vi bare ha to felt for å beskrive hvert nivå:
- nummer — en int å identifisere et nivå
- path-flis – basert bane som vi ønsker å lage programmatisk. Matrise av verdier der første verdi er startpunktet og den siste verdien er sluttpunktet.
dette er filen jeg vil bruke. Ikke bekymre deg for disse verdiene i banen, vi kommer til det senere.
{
"levels":
},
{
"number": 2,
"path":
},
{
"number": 3,
"path":
}
]
}
la oss lage en annen mappe-Skript-og nå begynner kodingen endelig.
Vi vil ha tilgang til dataene fra filen i koden, derfor trenger vi en modellklasse for den. Det er på tide å lage vårt aller første skript – LevelsData
. Det er ikke ment å bli instantiert Av Enhet, så MonoBehaviour
og Start
Update
metoder bør fjernes. Fra filen ovenfor kan vi se at rotelementet er en array
nivåer hvor hvert nivå skal ha en int
feltnummer og enint array
feltbane. Ikke glem å sette merknad.
public class LevelsData
{
public LevelData levels;
public class LevelData
{
public int number;
public int path;
}
}
Fint, nå har vi filen og modellen. Neste trinn er å forvandle en til en annen. La oss lage et annet skript- GameZone
— og fest det tilGameZone
– objektet på scenen. Dette skriptet vil bli brukt senere for å sette opp hele spillbrettet.
Følg Enkelt ansvarsprinsipp la oss lage enda et skript — LevelsDataLoader
— som vil gjøre all transformasjonen. Fest den tilGameZone
– objektet også.
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);
}
}
denne klassen vil laste dataene og returnere den som en ordbok hvor nøkkelen er nivånummeret og dataene er nivådataene selv.
nå skal vi kunne få tilgang til dataene iGameZone
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!");
}
}
Bytt tilbake Til Unity, trykk på play-knappen og sjekk konsollen-du bør se meldingen » 3 nivåer har blitt lagret i ordboken!»
Del 3. Koble Styret og Data
Gratulerer, du har nådd den siste og mest interessante delen av denne opplæringen. Hvordan skal vi faktisk koble styret og dataene? Fortsett å lese for å finne det ut!
Først av alt, la oss plasserehorizontal
og start_stop
fliser opprettet i Den Første delen av opplæringen I Ressurser mappen under Fliser mappen. Legg deretter til et nytt skript — TilesResourcesLoader
— en statisk hjelpeklasse for å laste fliser fra den mappen i en kjøretid.
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));
}
}
som det siste trinnet bør vi plassere disse brikkene på brettet ved oppstart av scenen. La oss gå tilbake tilGameZone
skriptet. Først og fremst må vi simulere nivåvalget, i det virkelige spillet skjer det vanligvis når en bruker trykker på en nivåknapp. For enkelhets skyld la oss legge til et offentlig feltnivå til GameZone
og endre den verdien til 1 for start. Jeg vil vise deg det endelige skriptet først:
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, Det er mye action! La meg lede deg gjennom det.
iSetupTiles
– metoden bør vi først få tilemap selv fordi vi trenger å vite posisjonene til fliser for å endre den. For å oppnå dette bruker vi tilemap.cellBounds.allPositionsWithin
– metoden som returnerer alle posisjonene til fliser fra den aller første-i den oppgitte konfigurasjonen er det en ned-mest flis.
Se følgende bilde der hvert tall representerer indeksen ilocalTilesPositions
– listen.
Nummerert tilemapfigcaption>
husker du verdiene vi bruker i banen i Levels.json
? Som du kanskje har gjettet allerede er disse verdiene indeksene til flisene i matrisen. Alt du trenger å gjøre nå er å ha en cheatsheet bilde for å hjelpe deg å bygge nivåer. Det er den jeg har brukt under utviklingen:
Min stygge nummererte tilemap
i dette eksemplet setter vi opp en horisontal linje i banen, vennligst seSetupPath
– metoden. Nøkkeldelen er følgende sløyfe:
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last)))
{
baseLevel.SetTile(localPosition, pathHorizontalTile);
}
Her itererer vi over localTilesPositions
for å finne de som skal angi ønsket flis-horisontal i dette tilfellet.
Merk! GetRange
metoden har to parametere-indeks og telling.
for å markere start-og sluttposisjoner brukes start_stop
– flisen.
her er resultatet av vårt harde arbeid:
prøv nå å endre nivånummerfeltet til GameZone
script fra 1 til 2 eller 3, og du vil se at banen er lastet riktig for det oppgitte nivået.