Enhet Nu tänker du med komponenter

Medan Unity är en fantastisk gamedev-plattform, kommer det att behövas lite av det ursprungliga arbetet, eftersom du sannolikt behöver flytta dina kognitiva kuggar för att förstå dess komponentbaserad arkitektur.

Medan klassisk objektorienterad programmering (OOP) kan användas och används, bygger Unity-arbetsflödet sig mycket på komponentstrukturen - vilket kräver komponentbaserat tänkande. Om du är bekant med komponenter, så är det bra. Om inte, det är inte ett problem. Här ska jag ge dig en kollisionskurs på komponenter i Unity.

Förhandsvisningsbild: Old Cogs av Emmanuel Huybrech.


Vad är en komponent?

Innan vi fortsätter med hur man arbetar och tänker på komponenter, låt oss se till att vi helt förstår vad de är.

I programmeringsvärlden går begreppen komponenter och avkoppling hand i hand. En komponent kan ses som en mindre bit av en större maskin. Varje komponent har sitt eget specifika jobb och kan i allmänhet (och optimalt) fullfölja sin uppgift eller syfte utan hjälp av några externa källor. Dessutom hör komponenter sällan till en enda maskin och kan förenas med olika system för att uppnå sin specifika uppgift, men uppnå olika resultat när det gäller den större bilden. Det beror på att komponenter inte bara bryr sig om den större bilden, utan också vet inte ens att det existerar.

Ett klassiskt exempel på komponenter är bitar av en bil, men det är tråkigt, eftersom jag inte är med i bilar. Tänk istället på en Xbox 360-kontroller. Tt har två analoga pinnar, olika knappar, triggers och så vidare. Inte bara är hela kontrollen själv en komponent, men varje enskild aspekt av styrenheten är en komponent.

Foto av Futurilla.

X-knappen kan: tryckas på; skicka av den information som den har tryckts på; bli släppt; och skicka information som den är släppt. Det har ingen aning om det finns olika andra knappar intill det, det bryr sig inte heller.

Självkontrollen är en komponent som består av andra komponenter (alla knappar, joysticks och triggers), eftersom det kan skicka data till vad det är anslutet till, men det bryr sig inte om vad det här objektet är (Xbox, PC, vissa Arduino skapande, eller vad som helst). Varken X-knappen eller regulatorn själv behöver veta vilket spel du spelar, eftersom det fortfarande gör sitt jobb oavsett mottagaren av sin information. 

Styrenhetens funktion är en enkelriktad gata, och uppgiften ändras aldrig på grund av vad den är ansluten till. Det gör det till en framgångsrik komponent, både för att den kan göra sitt jobb som en fristående enhet, men också för att den kan göra sitt jobb med flera olika enheter.


Hur och varför har Unity Favor Components?

Enighet byggdes med komponenter i åtanke, och det visar. En av de mest värdefulla och särskiljande aspekterna av enhet är att det är ett mycket visuellt program. Jag har jobbat i spelutveckling i flera år, och förutom mina mycket tidiga Flash IDE-dagar har jag mest arbetat med PNG-spritark och en kodredaktör som FlashDevelop-som inte är visuell alls.

Enhet är exakt motsatsen. Enhet kan du se allt du jobbar med, och i realtid. Det innebär att du kan testa ditt projekt, se ditt projekt som körs i ett separat fönster, göra ändringar i din kod eller spelobjekt och se de redigerade ändringarna. Mängden ström som detta system ger till en utvecklare är enormt och är enligt min mening en viktig aspekt av modern spelutveckling. Allt detta görs av Unitys komponentbaserade arkitektur.

Inspektören

Om du inte känner till Unity, ska jag förklara inspektören. Det här är en panel i Unity som visar alla egenskaper hos ett spelobjekt. Om du klickar på din spelare i världen (antingen vid körtid eller tidigare) kan du se allt om det objektet.

Om din spelare avatar har sex komponenter på dem, kommer varje lista att listas i en separat flik och varje offentlig variabel är tillgänglig för att du ska kunna se och tweak. Om din spelare avatar har en inventering ser du inte bara att han har en inventering, du kan också se objekten i den inventeringen och vilket index i listan eller arrayen varje enskild artikel upptar. Om du hämtar ett nytt objekt i spelet medan du testar, ser du det lagt till i inventerings-live. Du kan till och med lägga till eller ta bort objekt från den inventeringen, vilket gör att du snabbt kan testa nya objekt som du även skulle kunna skapa medan spelet går.

Enhetsinspektören.

Medan levande redigering är galen kraftfull, är det inte helt beroende av användningen av komponenter. Du kan ändra ett skript och se de redigeringar som reflekteras live, men det är begränsande jämfört med vilka komponenter du kan göra.

Tänk på en vertikal rymdskytt. När du testar ditt projekt i de flesta andra miljöer ser du hur ditt spel spelar, tar anteckningar och går tillbaka till koden och tweak saker, bara för att kompilera projektet igen och testa de här poängen. Om du har en levande inställning kan du tweak den koden i flyg och se de förändringarna medan du spelar, vilket är ännu bättre. Med det sagt, om du inte använder komponenter måste du byta mycket kod för att se några större effekter, vilket tar tid, och det här slår ihop syftet med live redigering.

Om du arbetar med komponenter kan du lägga till nya komponenter på två sekunder platt. Du kan byta ut dina skepps vapen för pistolerna som en chef använder (förutsatt att du programmerade dina komponenter för att fungera som de ska), kan du byta ditt femstegs hälsosystem till den svala Halo-liknande laddningsskölden du programmerad för ett annat spel. Du kan lägga till en rad spells till en av dina karaktärer, och alla på några sekunder.

Du behöver inte ändra någon kod, du behöver inte kompilera, helt enkelt dra och släpp eller välj önskad komponent från en rullgardinsmeny, och den läggs till. Den typen av makt är ovärderlig för spelbalansering, och det sparar enorma mängder tid.


Byter till komponentbaserad tänkande

Den svåraste delen om att arbeta med komponenter är att lära sig att strukturera dina projekt när de används. För de flesta programmerare betyder det troligtvis att du kommer att skapa många fler skript, där varje gör mindre, mer specifika uppgifter.

Hur du kommunicerar mellan skript är också en anständig hinder, eftersom du får mycket mer bitar och färre jätteklasser där varje objekt känner till varje annat objekt. Det finns uppenbarligen vägar runt det här, som statiska variabler för spelets huvudkomponenter (spelare, poäng osv.), Men det fungerar sällan för allt (och rekommenderas inte), och det finns avancerade metoder för att ordentligt strukturera dina komponenter , och förblir avkopplad.

Lyckligtvis, eftersom Unity byggdes med komponenter i åtanke, har det ett antal inbyggda funktioner som hjälper oss att uppnå detta. Det finns funktioner för att få referenser till en viss komponent, för att kontrollera alla objekt för att se vilka innehåller en specifik komponent etc. Med dessa olika funktioner kan du enkelt hämta den information som behövs för att skapa den magiska envägsgatan av kunskap där komponenter kan kommunicera med föremål som de påverkar, men själva komponenten har ingen aning om vad exakt det objektet är. Kombinera detta med hjälp av gränssnitt, och du har tillräckligt med programmeringskraft för att ta något tillvägagångssätt, enkelt eller komplext.

Exempel: Fientliga typer

I ett OOP-arvsystem kan du ha en bas Fiende klass som innehöll alla funktioner som de flesta av dina fiender skulle använda, och då kan du förlänga det för att lägga till specifik funktionalitet.

Om du någonsin faktiskt har implementerat ett sådant system är du medveten om det mellan din bas Fiende klass, och kanske din bas GameObject (eller motsvarande) klass hamnar du ihop med mycket onödig rodnad från att ha variabler och funktioner som vissa klasser behöver, men många gör inte. gränssnitt kan hjälpa till med detta, men de är inte alltid lösningen.

Nu, låt oss ta en titt på samma inställning, men tänker på komponenter. Du har fortfarande olika fiender, som alla delar en hel del gemensam funktionalitet, men var och en har unika egenskaper.

Det första steget är att bryta all funktionalitet i bitar. Man kan tro det ha hälsa och döende är en del av samma system, men även det exemplet kan brytas upp i en hälso-systemkomponent och en dödssystemkomponent. Detta beror på att din hälsa är din hälsa, och ingenting mer. När det når noll, är det inte upp till hälsosystemet att bestämma vad som händer nu, det är bara upp till hälsosystemet att veta att det i själva verket är noll. Andra system, ett sådant dödsystem, kan läsa denna information, och sedan välja att göra som de vill.

Kanske kommer dödsystemet att krossa en ny fiende (tänk på en stor fiende som går i bitar); kanske kommer det att släppa en power-up; kanske kommer det att lägga till en explosionseffekt på skärmen. Oavsett vad som händer är hälso- och sjukvården inte en del av det, och så gör vi rena och användbara komponenter.

När vi tänker på rörelse kan vi tro att all rörelse måste vara i ett enda skript. Men vissa fiender i vissa spel kan inte gå; de kan bara hoppa. Vissa fiender kan gå, men kan inte hoppa. Att tänka på dessa saker är hur vi upptäcker var komponenter kan och borde existera. När det gäller rörlighet kan vi skilja hoppet från att gå eller springa, separera flygning och så vidare gör det oss renare kod och mer mångsidighet.

Komponenter gör vår kod snätare (inga onödiga variabler och funktioner), men de gör också processen att skapa fiender mycket mer flexibelt och roligt. När varje fiendsfunktionalitet är uppbyggd som en komponent kan vi dra och släppa aspekter av fiender och se hur de beter sig - och även i realtid om vi använder Unity.

Låt oss anta att alla följande egenskaper redan är programmerade. Under vår fiendskapsprocess kunde vi skapa tre tomma fiender, som bara är tomma prefabs i Unity. Vi kan sedan dra på ett hälsosystem, ett droppsystem och ett dödsystem, eftersom vi vet att alla våra fiender, oavsett skillnader, kommer att ha hälsa, dö och släppa ett föremål. Vi kan faktiskt välja alla tre prefabs samtidigt, dra sedan och släpp dessa komponenter i inspektionspanelen och uppdatera alla tre samtidigt.

Därefter vet vi att en fiende kommer att kunna flyga, så vi väljer den ene fienden och dra en flygande komponent på den. En annan kan upptäcka ett mål i spelet och elda på det, så vi kasta på en eld på målet komponent script. Den tredje kan kasta upp ett hinder som blockerar alla attacker under en kort varaktighet, så vi kasta på barriär komponent.

Vi har nu tre unika fiender, alla delar vissa komponenter, men alla har även komponenter som bara gäller dem. Den roliga delen är att det är enkelt att göra dessa fiender, och att experimentera med nya variationer är lika lätt som att dra och släppa. Vill du ha en flygande fiende, med en barriär som kan rikta en fiende och eld på den? Dra alla ovanstående komponenter till en enda fiende, och du har bara det!


Slutsats

Att tänka på komponenter kanske inte är lätt, men det har säkert fördelarna. Du fortsätter att använda båda komponenterna och arvet i din framtida programmering, men det är extremt värdefullt att utöka din mängd olika sätt att närma sig samma problem genom att se olika perspektiv.

När det gäller Unity, kan du använda vilken metod du föredrar, men komponenter är definitivt favoriserade, och det är inte meningsfullt att bekämpa ett så fantastiskt arbetsflöde. Att lära sig enighet och byta ut mitt sätt att tänka på att arbeta med komponenter har varit ett måttligt svårt hinder att övervinna, men nu när jag är här ser jag inte mig själv återvända snart.