Eenheid nu denk je met componenten

Hoewel Unity een geweldig gamedev-platform is, zal het wennen eraan een beetje eerste werk vereisen, omdat je waarschijnlijk je cognitieve tandwielen moet verplaatsen om zijn componenten gebaseerde architectuur.

Hoewel klassieke objectgeoriënteerde programmering (OOP) kan worden en wordt gebruikt, bouwt de Unity-workflow sterk op de structuur van componenten - waarvoor een op componenten gebaseerd denken vereist is. Als je bekend bent met componenten, is dat geweldig; zo niet, dat is geen probleem. Hier geef ik je een spoedcursus over componenten in Unity.

Voorbeeldafbeelding: Old Cogs van Emmanuel Huybrech.


Wat is een component?

Voordat we verder gaan met het werken en denken met componenten, laten we ervoor zorgen dat we volledig begrijpen wat ze precies zijn.

In de programmeerwereld gaan de concepten componenten en ontkoppeling hand in hand. Een component kan worden gezien als een kleiner onderdeel van een grotere machine. Elke component heeft zijn eigen specifieke taak en kan over het algemeen (en optimaal) zijn taak of doel bereiken zonder de hulp van externe bronnen. Bovendien behoren componenten zelden tot een enkele machine en kunnen ze met verschillende systemen worden gecombineerd om hun specifieke taak te volbrengen, maar bereiken ze andere resultaten als het gaat om het grotere geheel. Dit komt omdat componenten niet alleen om dat grotere plaatje geven, maar ook weet niet eens dat het bestaat.

Een klassiek voorbeeld van componenten zijn de onderdelen van een auto - maar dat is saai, want ik ben niet zo bezig met auto's. Overweeg in plaats daarvan een Xbox 360-controller. Tt heeft twee analoge sticks, verschillende knoppen, triggers, enzovoort. Niet alleen is de volledige controller zelf een component, maar elk afzonderlijk aspect van de controller is een component.

Foto door Futurilla.

De X-knop kan: ingedrukt worden; stuur de informatie dat het is ingedrukt; worden vrijgelaten; en stuur informatie weg dat het is vrijgegeven. Het heeft geen idee dat er verschillende andere knoppen naast liggen, en het maakt het ook niet uit.

De controller zelf is een component, samengesteld uit andere componenten (alle knoppen, joysticks en triggers), omdat het gegevens kan verzenden naar waar het op is aangesloten, maar het maakt niet uit wat dat object is (Xbox, pc, sommige Arduino creatie, of wat dan ook). Noch de X-knop, noch de controller zelf, moet weten welk spel je speelt, omdat het nog steeds zijn werk zal doen, ongeacht de ontvanger van zijn informatie. 

De functie van de controller is eenrichtingsverkeer en de taak zal nooit veranderen vanwege de manier waarop deze is aangesloten. Dit maakt het een succesvol onderdeel, zowel omdat het zijn werk als een stand-alone apparaat kan doen, maar ook omdat het zijn werk kan doen met meerdere apparaten.


Hoe en waarom biedt Unity de voorkeur aan componenten?

Unity is gebouwd met componenten in het achterhoofd, en dat blijkt. Een van de meest waardevolle en onderscheidende aspecten van Unity is dat het een heel visueel programma is. Ik werk nu al jaren in game-ontwikkeling, en afgezien van mijn zeer vroege Flash IDE-dagen, heb ik vooral gewerkt met PNG-sprite-sheets en een code-editor zoals FlashDevelop - die helemaal niet visueel is.

Eenheid is precies het tegenovergestelde. Met Unity kun je alles zien waar je aan werkt, en in realtime. Dit betekent dat u uw project kunt testen, uw project in een apart venster kunt bekijken, bewerkingen kunt uitvoeren op uw code of game-objecten, en deze bewerkingen live kunt zien. De hoeveelheid kracht die dit systeem geeft aan een ontwikkelaar is enorm, en is nu naar mijn mening een essentieel aspect van moderne game-ontwikkeling. Dit alles wordt mogelijk gemaakt door de op componenten gebaseerde architectuur van Unity.

De inspecteur

In het geval u onbekend bent met Unity, zal ik de inspecteur uitleggen. Dit is een paneel in Unity dat alle eigenschappen van een game-object laat zien. Als u op uw speler ter wereld klikt (tijdens runtime of eerder), kunt u alles over dat object bekijken.

Als de avatar van je speler zes componenten bevat, worden deze allemaal weergegeven in een apart tabblad en is elke openbare variabele beschikbaar die je kunt zien en aanpassen. Als de avatar van je speler een inventaris heeft, zie je niet alleen dat hij een inventaris heeft, maar ook dat je de items in die inventaris kunt zien en welke index in de lijst of array van elk item inneemt. Als je een nieuw item in de game ophaalt terwijl je aan het testen bent, zie je het aan de inventaris toegevoegd worden. U kunt zelfs items uit die inventaris toevoegen of verwijderen, zodat u snel nieuwe items kunt testen, die u zelfs zou kunnen maken terwijl het spel loopt.

De Unity Inspector.

Hoewel live bewerken waanzinnig krachtig is, is dit niet echt afhankelijk van het gebruik van componenten. U kunt een script wijzigen en deze bewerkingen live weergeven, maar dat is beperkend in vergelijking met welke componenten u kunt gebruiken.

Overweeg een verticale space shooter. Wanneer u uw project test in de meeste andere omgevingen, ziet u hoe uw game speelt, notities maakt, gaat u terug naar de code en past u dingen aan, alleen om het project opnieuw te compileren en die punten te testen. Als je een live-opstelling hebt, kun je die code meteen aanpassen en die veranderingen bekijken terwijl je speelt, wat nog beter is. Dat gezegd hebbende, als je geen componenten gebruikt, zul je veel code moeten veranderen om grote effecten te zien, wat tijd kost, en dit verslaat eerder het doel van live bewerken.

Als u met componenten werkt, kunt u nieuwe componenten in twee seconden vlak toevoegen. Je kunt de kanonnen van je schip verwisselen voor de wapens die een baas gebruikt (ervan uitgaande dat je je componenten hebt geprogrammeerd om zelfstandig te werken, zoals goede componenten zouden moeten doen), je kunt je vijf-hit gezondheidssysteem veranderen in dat coole Halo-achtige oplaadschild dat je geprogrammeerd voor een ander spel. je kunt een reeks spreuken toevoegen aan een van je personages, en dat allemaal in seconden.

U hoeft geen code te wijzigen, u hoeft het niet opnieuw te compileren, u sleept eenvoudigweg en zet het neer of u selecteert het gewenste onderdeel uit een vervolgkeuzelijst en het is toegevoegd. Dat soort kracht is van onschatbare waarde voor het balanceren van games en het bespaart enorm veel tijd.


Overschakelen naar op componenten gebaseerd denken

Het moeilijkste deel van het werken met componenten is leren hoe je je projecten kunt structureren wanneer je ze gebruikt. Voor de meeste programmeurs betekent dit waarschijnlijk dat je veel meer scripts zult maken, waarbij elk van hen kleinere, specifiekere taken zal doen.

Hoe je communiceert tussen scripts is ook een behoorlijke hindernis, omdat je veel meer stukken en minder gigantische klassen hebt waar elk object weet van elk ander object. Er zijn natuurlijk manieren om dit te omzeilen, zoals statische variabelen voor kerncomponenten van je spel (spelers, score, enzovoort), maar dat werkt zelden voor alles (en wordt niet geadviseerd), en er zijn geavanceerde methoden om je componenten goed te structureren en om ontkoppeld te blijven.

Gelukkig, omdat Unity werd gebouwd met componenten in het achterhoofd, heeft het een aantal ingebouwde functies die ons helpen dit te bereiken. Er zijn functies om verwijzingen naar een specifiek onderdeel te krijgen, om alle objecten te controleren om te zien welke een specifiek onderdeel bevatten, enz. Met deze verschillende functies kunt u eenvoudig de informatie ophalen die nodig is om die magische eenrichtingsweg van kennis te creëren waar componenten kunnen communiceren met objecten die ze beïnvloeden, maar de component zelf heeft geen idee wat dat object precies is. Combineer dit met het gebruik van interfaces, en je hebt voldoende programmeerkracht om elke benadering van de materie te nemen, eenvoudig of complex.

Voorbeeld: vijandige typen

In een OOP-overervingssysteem zou je een basis kunnen hebben Vijand klasse die enige en alle functies bevat die de meeste van je vijanden zouden gebruiken, en dan zou je die kunnen uitbreiden om specifieke functionaliteit toe te voegen.

Als je ooit zo'n systeem hebt geïmplementeerd, weet je dat tussen je basis Vijand klas, en misschien je basis GameObject (of een gelijkwaardige) klasse, krijg je met veel onnodige rommel variabelen en functies die sommige klassen nodig hebben, maar velen niet. interfaces kan hierbij helpen, maar ze zijn niet altijd de oplossing.

Laten we nu eens naar dezelfde opstelling kijken, maar denken met componenten. Je hebt nog steeds verschillende vijanden, die allemaal veel gemeenschappelijke functionaliteit delen, maar die allemaal unieke kenmerken hebben.

De eerste stap is om alle functionaliteit in stukken te breken. Dat zou je kunnen denken gezondheid hebben en stervende maken deel uit van hetzelfde systeem, maar zelfs dat voorbeeld kan worden opgedeeld in een onderdeel van het zorgstelsel en een onderdeel van het doodssysteem. Dit komt omdat uw gezondheid uw gezondheid is, en niets meer. Wanneer het nul bereikt, is het niet aan het gezondheidszorgsysteem om te beslissen wat er vervolgens gebeurt, het is alleen aan het gezondheidssysteem om te weten dat het in feite nul is. Andere systemen, zoals een doodssysteem, kunnen deze informatie lezen en vervolgens kiezen om te doen wat ze willen.

Misschien zal het doodssysteem een ​​nieuwe vijand spawnen (denk aan een grote vijand die in stukken breekt); misschien zal het een power-up laten vallen; misschien voegt het een explosie-effect toe aan het scherm. Ongeacht wat er gebeurt, maakt het gezondheidssysteem daar geen deel van uit, en dit is hoe we schone en nuttige componenten maken.

Wanneer we aan beweging denken, denken we misschien dat alle bewegingen in één script moeten zijn. Maar sommige vijanden in sommige games kunnen niet lopen; ze kunnen alleen springen. Sommige vijanden kunnen lopen, maar kunnen niet springen. Als we aan deze dingen denken, zien we waar componenten kunnen en moeten bestaan. Als het gaat om mobiliteit, kunnen we de sprong scheiden van lopen of rennen, afzonderlijk vliegen, enzovoort. Zo krijgen we schonere code en meer veelzijdigheid.

Componenten maken onze code netter (geen onnodige variabelen en functies), maar ze maken het proces om vijanden te maken veel flexibeler en aangenamer. Met elk stuk vijandige functionaliteit ingesteld als een component, kunnen we aspecten van vijanden slepen en neerzetten, en zien hoe ze zich gedragen - en zelfs in realtime, als we Unity gebruiken.

Laten we aannemen dat alle volgende kenmerken al zijn geprogrammeerd. Tijdens ons creatieproces van de vijand, zouden we drie blanco vijanden kunnen creëren, allemaal lege prefabs in Unity. We kunnen dan een gezondheidssysteem, een systeem voor het verplaatsen van voorwerpen en een doodssysteem slepen, omdat we weten dat al onze vijanden, ongeacht hun verschillen, gezondheid zullen hebben, zullen sterven en een voorwerp zullen laten vallen. We kunnen alle drie prefabs in één keer selecteren, vervolgens deze componenten slepen en neerzetten in het Inspector-paneel en alle drie tegelijkertijd bijwerken.

Vervolgens weten we dat één vijand kan vliegen, dus we selecteren die ene vijand en slepen een vliegend component erop. Een ander kan een doelwit in het spel herkennen en erop schieten, dus gooien we op a schieten op doelwit componentscript. De derde kan een barrière werpen die alle aanvallen voor een korte duur blokkeert, dus gooien we de barrière bestanddeel.

We hebben nu drie unieke vijanden, die allemaal bepaalde componenten delen, maar allemaal ook componenten hebben die alleen op hen van toepassing zijn. Het leuke is dat het maken van deze vijanden eenvoudig is en experimenteren met nieuwe variaties net zo eenvoudig is als slepen en neerzetten. Wil je een vliegende vijand, met een barrière, die een vijand kan aanvallen en ernaar schieten? Sleep alle bovenstaande componenten naar een enkele vijand en je hebt precies dat!


Conclusie

Denken met componenten is misschien niet eenvoudig, maar het heeft zeker voordelen. Je blijft zowel componenten als overerving gebruiken in je toekomstige programmering, maar het is uiterst waardevol om je overvloed aan manieren om hetzelfde probleem te benaderen uit te breiden door verschillende perspectieven te zien.

Als het gaat om Unity, kun je de methode gebruiken die je wilt, maar componenten zijn absoluut favoriet en het is gewoon niet logisch om te vechten tegen een dergelijke verbazingwekkende workflow. Eenheid leren en mijn manier van denken om met componenten te werken veranderen, was een redelijk moeilijk obstakel om te overwinnen, maar nu ik hier ben, zie ik mezelf niet snel terugkomen.