Hoi! Ik ben Jamie Fristrom van Happion Laboratories. Je kent me misschien nog wel van spellen als Die By The Sword, Spider-Man 2 en Schizoid ... allemaal, in een of andere vorm, inclusief touw. Ik heb de laatste tijd gewerkt aan een spel genaamd Energy Hook - onlangs gelanceerd op Kickstarter - waarbij het allemaal om touw gaat. (Nou, het gaat sowieso allemaal om je graviton-aangedreven grijpbalk.)
Ik ga bespreken hoe ik graag een touwzwaaiende gameplay-monteur implementeer. Toen ik aan Energy Hook begon, deed ik meestal alles met mijn eigen aangepaste code. Ik geloof dat het mogelijk is om eenvoudig naar Unity te gaan en de configureerbare joint te gebruiken om een aantal vergelijkbare dingen te doen, maar op dat moment was het niet beschikbaar. Ik ben er vrij zeker van dat dit hoe dan ook de juiste manier is geweest, omdat het me de controle over alles geeft - en ik kan het met je delen.
De fundamenten van slingeren zoals ik deed - gebruiken beperkingen - zijn eigenlijk vrij eenvoudig. (Ik hoop dat je niet teleurgesteld bent als je ziet wat er onder de motorkap zit.) Het werkt hetzelfde, of je nu een 2D-game of 3D-game maakt, het is alleen dat de vectoren anders zijn, dus ik begin met 2D en dan bespreek wat rimpels wanneer je naar drie dimensies gaat.
In dit artikel wordt er ook van uitgegaan dat je een game-engine zoals Unity gebruikt die veel van het werk voor je kan doen, zoals raycasting tegen geometrie en het oriënteren van een personage, met eenvoudige functie-aanroepen. Als u uw eigen motor gebruikt, moet u dat misschien zelf doen.
Er zijn twee manieren waarop spelsimulaties vaak werken. De meeste third person-games hebben alles ingeblikt; ingeblikte animaties en ingeblikte bewegingen, zodat alles er perfect uitziet, de voeten van het personage schuiven niet rechtstreeks uit het animatieprogramma van de animator naar ons. Het alternatief is een veel fysiekere simulatie - je simuleert fysica: versnelling, snelheid, zwaartekracht - veel first-person games doen dit, maar third-person games hebben de neiging om het te vermijden, omdat het veel gemakkelijker is om de voeten van het personage te hebben dia en de fysieke dingen komen niet overeen met de animatie.
Als je een niet-ingeblikte rope swing wilt doen (in tegenstelling tot een ingeblikte rope swing, zoals te zien in de allereerste Pitfall, of de vroege Spider-Man-games zoals Neversoft's op de PSX), is een rope-swing eigenlijk een fysieke simulatie en kan daarom misschien vrijheid en nuance hebben en het viscerale gevoel dat echte zwaartekracht en momentum je kunnen geven, dan wil je het op de tweede manier doen - laat alles een fysieke simulatie zijn en vermijd ingeblikte dingen die doden het momentum van het personage. Dan zal alles stromen.
Dat is de manier waarop ik dingen doe met Energy Hook.
Er zijn een heleboel artikelen over hoe je fysica kunt simuleren - hier is er een met een coole Flash-demo ingebouwd. Je kunt Euler-integratie of Verlet-integratie doen, het maakt niet echt uit. (Ik zal je hier niet over Euler versus Verlet leren, maar laat me zeggen dat de concepten niet zo eng zijn als ze klinken.)
Laten we aannemen dat we de boteenvoudige Euler-integratie doen. De tijdstempel van ons personage kan er als volgt uitzien, waarbij versnelling wordt bepaald door zwaartekracht en hoe je op de stok duwt:
avatar.velocity = avatar.velocity + avatar.acceleration * deltaT; avatar.position = avatar.position + avatar.velocity * deltaT;
(Kanttekening: dit is eigenlijk een ruwe benadering - je kunt een betere, minder framerate-afhankelijke benadering krijgen met avatar.position = avatar.position + (avatar.oldvelocity + avatar.velocity) * deltaT / 2.0f
, en je kunt een close-to-perfect-simulatie krijgen met zoiets - maar je spelers zullen het waarschijnlijk niet merken.)
Je game heeft waarschijnlijk een systeem om tegen de geometrie van de wereld aan te botsen. Het ziet er waarschijnlijk ongeveer zo uit:
Vector testPosition = avatar.position + avatar.velocity * deltaT; Vector kruising indien (RayCast (positie, testpositie, uit kruising)) // we gingen door een muur, laten we het personage terugtrekken, // gebruik de normaal van de muur // met een ruimte van 1 eenheid voor ademruimte testPosition = intersection + intersection.normal;
Wat gebeurt er met de snelheid van je avatar wanneer ze een obstakel raken? Het is niet logisch dat je avatar dezelfde snelheid door de muur blijft houden. Sommige games kunnen je stuiteren, gebruikmakend van de standaard van waar je elkaar hebt gekruist om je snelheid weer te geven; andere spellen kunnen je langs de muur laten glijden; anderen zullen ergens tussenin staan. Laten we eens kijken naar de code om langs een muur te glijden:
avatar.velocity = (testPosition - avatar.position) / deltaT; avatar.position = testPosition;
(Als u Verlet-integratie uitvoert, waarbij u voor elk frame de snelheid al bepaalt door alleen naar de positiegegevens te kijken, is deze stap al voor u gedaan.)
Ook zijn videogames de hacky-dingen die ze zijn, en je zult waarschijnlijk vaak merken dat de positie van je personage in bepaalde bochten ineens van het ene frame naar het volgende springt. Wanneer dit gebeurt, zal hun snelheid door het dak gaan. Mijn oplossing daarvoor is simpelweg een hack: controleer of hun snelheid te extreem wordt en repareer het als dit zo is.
Wat doet die code als je een muur schuin raakt? Het eerste frame, het verandert je snelheid drastisch als je de muur raakt, maar het duwt je nog steeds door de muur. In het volgende frame wordt je avatar weer bijgewerkt in de muur en vervolgens weer teruggeduwd, maar nu is de snelheid de nieuwe positie van de avatar, langs de muur glijdend, minus zijn oude positie, tegen de muur, dus het is evenwijdig aan de muur.
Dit is een enorme oversimplificatie van wat er in de echte wereld gebeurt wanneer een object botst met een ander, maar de meeste van je spelers zullen het niet opmerken of verzorgen.
Maar op dit moment hebben we de positie en snelheid van onze avatar met de muren en vloeren van ons spel met succes beperkt. Dus nu zijn we klaar.
Stel je nu dus voor dat je avatar al door een virtueel touw aan een virtueel punt is vastgemaakt. Wat ons betreft kunnen we dat simpelweg beschouwen als een onzichtbare ronde of bolvormige wand. We kunnen botsingen testen door te kijken of je te ver van het midden van de cirkel bent gekomen en de avatar terug naar binnen te trekken.
if (amitethered) if (testPosition - tetherPoint) .Length ()> tetherLength) // we zijn voorbij het einde van ons touw // trek de avatar weer naar binnen. testPosition = (testPosition - tetherPoint) .Normalized () * tetherLength;
Door dezelfde snelheidsaanpassing die ons langs muren deed glijden, schuiven we ook langs de binnenkant van deze virtuele cirkel of bol.
Op dit punt heb je een swingend spel. Maar er zullen waarschijnlijk een paar dingen zijn die plezier voor je spelers in de weg zitten, en dit is waar een beetje hacken de dingen beter kan maken.
Afhankelijk van je spel, en of het verondersteld wordt om een stuk touw of een elastisch web of een straal van een grijper te simuleren, kun je verschillende benaderingen hebben van speling en veerkracht. Je kunt veel dingen simuleren door de tetherLength
. Als u uit uw veerachtige web of grijpbalk wilt halen, kunt u de tetherLength
als de speler dichter bij het eindpunt komt:
tetherLength = (avatar.position - tetherPoint) .Length ();
Maar als het een niet-elastisch touw is, verlaat je de tetherLength
onaangeroerd.
Voor veerkracht, kunt u een desiredLength
dat is opgelost en een currentLength
die voortdurend probeert de desiredLength
- dit is ook handig voor onze volgende stap:
Wat als je avatar op een lage plaats begint en probeert te slingeren? Het is vrij duidelijk dat ze niet ver komen. Een snelle oplossing hiervoor is om te controleren hoe hoog de grond is waarop ze willen slingeren, en de lengte van hun ketting in te korten zodat ze de grond vrijmaken.
Je kunt het niet zomaar over één frame inkorten, want dan springen ze plotseling in de lucht - dus hier, met een desiredLength
dat is kort genoeg om de grond niet te raken en een currentLength
dat nadert snel desiredLength
zal je de gewenste klaring krijgen.
Als je avatar een menselijke figuur is, is het niet logisch dat ze constant perfect verticaal lijken als ze slingeren. Oriënteer ze zodat ze eruit zien alsof ze aan het touw hangen - dus ze zitten ondersteboven als ze een lus maken, bijvoorbeeld - zien er misschien zo uit in Unity:
Vector myUp = (avatar.position - tetherPoint); avatar.rotation = Quaternion.LookRotation (avatar.rotation.forward, myUp);
Hierdoor gaat de avatar achteruit. Je zou ook de snelheid van de avatar kunnen gebruiken om vooruit te komen (dat is wat ik doe) - of laat ze gek worden ...
In de echte wereld, touwen wikkel rond dingen. Een snelle manier om dat in je code te simuleren, is om elk frame langs het virtuele touw te laten ruisen en als het iets raakt, maak je een nieuw aanknopingspunt waar het snijdt. Dit ziet er niet goed uit als de avatar vervolgens terugzwaait op een niet-kleverig touw, maar prima voor een plakkerig web, tong of grijpbalk.
De manier waarop je touw omwikkelt, kan een grote impact hebben op je plezier. Plotseling wikkelen rond een outcropping kan de speler verrassen en gefrustreerd maken. We hadden een driestapsoplossing in Spider-Man 2: als je te dicht bij de outcropping was, zou het web breken; als je op een halve afstand zat, zou het web inpakken; en als je ver weg was, zou het internet gewoon doorgaan.
Het moeilijkste om deze 2D-monteur naar 3D te brengen, is de interface voor de speler in overweging nemen - hoe ze punten in de wereld plukken waaruit ze kunnen swingen. Verschillende spellen gebruiken verschillende methoden om zo'n punt te kiezen. Ratchet & Clank heeft vaste punten in de wereld die je kunt vastgrijpen; met de Quake grappling-hook mod en Bionic Commando: Rearmed richt je de camera op wat je wilt bevestigen; Spider-Man 2 en Energy Hook werpen stralen naar buiten ten opzichte van het personage, en waar de stralen de fysieke geometrie snijden, dat is het punt waar u zich vasthecht.
Bijna al deze methoden omvatten raycasting tegen de fysieke geometrie van de wereld, of het nu langs de lijn van de camera is of vanaf het personage tot het punt van de muisklik - de kruising van de raycast bepaalt je nieuwe tulppunt.
Hier is bijvoorbeeld een roker met muislook die goed zou kunnen zijn voor een first-person game:
RaycastHit wallData; if (Physics.Raycast (camera.position, camera.forward, out wallData, maximumTetherLength)) amitethered = true; tetherPoint = wallData.point; tetherLength = Vector3.Distance (wallData.point, avatar.position);
Tegen muren en dergelijke stoten heeft ook de neiging vaker in een 3D-spel te spelen dan in een 2D-spel, vooral als het de muren zijn waar je jezelf aan vastklampt. Er zijn een aantal dingen die je kunt doen om dit minder vervelend te maken.
Ga eropuit en maak je eigen swingende spellen en laat me weten wat je verzint! Ik kan nooit genoeg krijgen van swingende spellen.
Ik hoop dat je dit artikel nuttig hebt gevonden. Als je dat hebt, steun dan de Energy Hook Kickstarter of stem het in op Steam Greenlight. Bedankt!