Swinging Physics for Player Movement (som sett i Spider-Man 2 og Energy Hook)

Hei! Jeg er Jamie Fristrom of Happion Laboratories. Du kan huske meg fra slike spill som Die By The Sword, Spider-Man 2, og Schizoid ... som i en eller annen form inkluderte tau. Jeg har nylig jobbet på et spill som heter Energy Hook - nylig lansert på Kickstarter - som handler om tau. (Vel, det handler alt om din graviton-drevne gripestråle, uansett.)


Jeg skal diskutere hvordan jeg liker å implementere en tau-svingende gameplay mekaniker. Da jeg begynte å jobbe med Energy Hook, gjorde jeg det meste med min egen kode. Jeg tror det er mulig å bare gå inn i Unity og bruke Configurable Joint til å gjøre noen svært like ting, men den gangen var den ikke tilgjengelig. Jeg er ganske sikker på at dette har vært riktig samtale, uansett fordi det gir meg kontroll over alt - og jeg kan dele det med deg.


Grunnleggende for å gjøre svingende måten jeg gjorde - ved hjelp av begrensninger - er faktisk ganske enkelt. (Håper du ikke blir skuffet når du ser hva som er under hetten.) Det fungerer uansett om du lager et 2D-spill eller 3D-spill, det er bare at vektorene er forskjellige, så jeg starter med 2D og deretter diskuter noen rynker når du går til tre dimensjoner.

Denne artikkelen forutsetter også at du bruker en spillmotor som Unity som kan gjøre mye arbeid for deg, som å rydde mot geometri og orientere et tegn, med enkle funksjonssamtaler. Hvis du bruker din egen motor, må du kanskje gjøre det matte selv.



Fysiske Simuleringer vs Canned Movement

Det finnes to måter spillsimuleringer ofte fungerer. De fleste tredjeparts spill har hermetisert alt; hermetisert animasjoner og hermetiserte bevegelser, slik at alt ser perfekt ut, tegnets føtter glir ikke, rett fra animatørens animasjonsprogram til oss. Alternativet er en mye mer fysisk simulering - du simulerer fysikk: akselerasjon, hastighet, tyngdekraften - mange førstepersonsspill gjør dette, men tredjeparts spill pleier å unngå det, fordi det er mye lettere å få karakterens føtter lysbilde og de fysiske tingene stemmer ikke overens med animasjonen.

Hvis du vil gjøre en non-canned tau swing (i motsetning til en hermetisert tau swing, sett i den aller første fallgruva, eller tidlig Spider-Man spill som Neversoft er på PSX), en tau-swing som faktisk er en fysisk simulering og kan derfor kanskje ha frihet og nyanse og den viscerale følelsen at ekte tyngdekraft og momentum kan gi deg, så vil du gjøre det på den andre måten - få alt til å bli en fysisk simulering, og unngå å gjøre hermetiske ting som dreper karakterens momentum. Så vil alt strømme.

Det er slik jeg gjør ting med Energy Hook.


Det er massevis av artikler der ute på hvordan å simulere fysikk - her er en med en kul Flash-demo innebygd. Du kan gjøre Euler-integrasjon eller Verlet-integrasjon, det spiller ingen rolle. (Jeg vil ikke lære deg om Euler vs Verlet her, men la meg bare si at konseptene ikke er så skummelt som de høres.)

La oss anta at vi gjør ben-enkle Euler-integrasjonen. Vår tegnespillets timestep kan se slik ut, hvor akselerasjon bestemmes av tyngdekraften og hvordan du skyver på pinnen:

 avatar.velocity = avatar.velocity + avatar.acceleration * deltaT; avatar.position = avatar.position + avatar.velocity * deltaT;

(Sidebeskrivelse: Dette er faktisk en grov tilnærming - du kan få en bedre, mindre frameratavhengig tilnærming med avatar.position = avatar.position + (avatar.oldvelocity + avatar.velocity) * delta / 2.0f, og du kan få en perfekt simulering med noe som dette - men spillerne dine vil nok ikke legge merke til.)

Relaterte innlegg
  • Simuler Tearable Cloth and Ragdolls Med Simple Verlet Integration
  • Slik oppretter du en tilpasset 2D-fysikkmotor

Begrense det

Ditt spill har sannsynligvis et system for å kollidere med verdens geometri. Det ser sannsynligvis ut som dette:

 Vector testPosition = avatar.position + avatar.velocity * deltaT; Vektor kryss hvis (RayCast (posisjon, testposisjon, utkryssing)) // vi gikk gjennom en vegg, la oss trekke tegnet tilbake, // ved hjelp av normal av veggen // med en 1-plass til å puste testPosition = skjæringspunktet + krysset.normalt; 

Hva skjer med avatarens hastighet når de treffer et hinder? Det er ikke fornuftig for avataren din å opprettholde samme hastighet gjennom veggen. Noen spill kan sprette deg, ved hjelp av det normale av hvor du krysset for å reflektere hastigheten din; Andre spill kan få deg til å glide langs veggen; andre vil være et sted i mellom. La oss se på kode for å glide langs en vegg:

 avatar.velocity = (testposisjon - avatar.position) / deltaT; avatar.position = testposisjon;

(Hvis du gjør Verlet-integrasjon, hvor hver ramme du allerede bestemmer hastighet bare ved å se på tidligere posisjondata, er dette trinnet allerede gjort for deg.)

Også videogames er de hakkede tingene de er, du vil sannsynligvis ofte finne ut at karakteren din snaps plutselig fra en ramme til den neste i visse hjørnesaker. Når dette skjer, vil deres hastighet gå gjennom taket. Min løsning for det er rett og slett et hack: Sjekk om deres hastighet blir for ekstreme og fikse det hvis det gjør det.

Hva gjør den koden når du treffer en vegg i en vinkel? Den første rammen, det endrer hastigheten dramatisk når du treffer veggen, men den skyver deg fremover gjennom veggen. Den neste rammen, din avatar blir oppdatert til veggen igjen, og trykkes deretter ut igjen, men nå er hastigheten avatarens nye posisjon, gled langs veggen minus sin gamle posisjon mot veggen, så den er parallell med veggen.

Dette er en enorm oversimplification av hva som foregår i den virkelige verden når et objekt kolliderer med en annen, men de fleste av spillerne dine vil ikke legge merke til eller bryr seg.

På dette punktet har vi med hell begrenset vår avatars posisjon og hastighet med veggene og gulvene i spillet vårt. Så nå er vi klare.

Begrensning med tetter i stedet for vegger

Så nå forestill deg at avataren din allerede er festet av et virtuelt tau til et virtuelt punkt. Så vidt vi er bekymret, kan vi bare betrakte det som en usynlig sirkulær eller sfærisk vegg. Vi kan teste kollisjonen ved å se om du har kommet for langt unna sirkelens senter, og dra avbilderen tilbake.

 Hvis (amITethered) if (testPosition - tetherPoint) .Length ()> tetherLength) // vi er forbi slutten av tauet vårt // trekk avstanden tilbake. testPosition = (testPosition - tetherPoint) .Normalisert tetherLength; 

Nå, samme hastighetsjustering som fikk oss til å glide langs vegger, vil også få oss til å glide langs innsiden av denne virtuelle sirkelen eller sfæren.


Swinging Physics: Falling with a Tether - Ramme 1
Swinging Physics: Falling with a Tether - Frame 2
Swinging Physics: Falling with a Tether - Ramme 3

Noen subtilitet og Nuance

På dette punktet har du et svingende spill. Men det kommer nok til å være noen ting om det som kommer i veien for moro for spillerne dine, og det er her en liten hacking kan gjøre ting bedre.

Slack, Springiness

Avhengig av spillet ditt, og om det skal simulere en lengde av tau eller en elastisk nett eller en gripestråle, kan du ha forskjellige tilnærminger til slakk og fjærhet. Du kan simulere mange ting ved å finjustere tetherLength. Hvis du vil ta slakk ut av din fjærkende web eller grapple beam, kan du forkorte tetherLength som spilleren kommer nærmere tether-punktet:

 tetherLength = (avatar.position - tetherPoint) .Lengde ();

Men hvis det er et nonelastisk tau, vil du forlate tetherLength urørt.

For springiness kan du ha en desiredLength det er fast og a currentLength som kontinuerlig forsøker å nærme seg desiredLength - Dette vil også være nyttig for vårt neste trinn:

Men jeg vil ikke treffe bakken!

Hva om din avatar starter på et lavt sted og prøver å svinge? Det er ganske tydelig at de ikke kommer langt unna. En rask løsning for det er å sjekke hvor høyt over bakken det punktet de ønsker å svinge fra er, og forkort lengden på deres tetting slik at de vil rydde bakken.

Du kan ikke bare forkorte den over en ramme skjønt, for da vil de plutselig snuble inn i luften - så her, ha en desiredLength det er kort nok til ikke å røre bakken og a currentLength som nærmer seg raskt desiredLength vil få deg klaring du vil ha.

Avataren er oppe

Hvis avataren din er en humanistisk figur, gir det ikke stor mening for dem å vises konstant perfekt vertikal som de svinger. Orienter dem slik at de ser ut som de henger fra tauet - så de er opp ned hvis de gjør en løkke, for eksempel - kan se slik ut i Unity:

 Vector myUp = (avatar.position - tetherPoint); avatar.rotation = Quaternion.LookRotation (avatar.rotation.forward, myUp);

Dette vil la avatar svinge bakover. Du kan også bruke avatars hastighet for deres fremover (det er det jeg gjør) - eller la dem spinne galskapt ...

Wrapping Around Things

I den virkelige verden vikler tauene rundt ting. En rask måte å simulere det i koden din er å raykast langs det virtuelle tauet hver ramme, og hvis det treffes noe, må du lage et nytt vedlegg der det krysser. Dette vil ikke se riktig hvis avataren da svinger tilbake på et ustoppet tau, men det er greit for en klebrig nett, tunge eller gripebjelke.

Tweaking måten tauet ditt kan ha stor innflytelse på moro. Plutselig innpakning rundt en outcropping kan ta spilleren overraskende og gjøre dem frustrert. Vi hadde en tre-trinns løsning i Spider-Man 2: Hvis du var for nær outcropping, ville Internettet bryte; Hvis du var i mellomavstand, ville weben pakke inn; og hvis du var langt borte, ville nettverket bare gå gjennom.


Betraktninger for 3D

Den vanskeligste tingen om å bringe denne 2D-mekanikeren til 3D overveier grensesnittet til spilleren - hvordan de velger poeng i verden å svinge fra. Ulike spill bruker forskjellige metoder for å plukke et slikt punkt. Ratchet & Clank har faste poeng i verden du kan gripe; med Quake grappling-krok mod og Bionic Commando: Rearmed du peker kameraet til det du vil feste til; Spider-Man 2 og Energy Hook kastes ut i forhold til tegnet, og hvor strålene krysser fysisk geometri som er punktet der du legger til.


Nesten alle disse metodene involverer raykasting mot verdens fysiske geometri, enten det er langs linjen i kameraet eller fra tegnet til museklikkpunktet - krysset av raycasten bestemmer ditt nye tetherpoint.

For eksempel, her er en museklikk raycast som kan være bra for et førstepersons spill:

 RaycastHit wallData; hvis (Physics.Raycast (camera.position, camera.forward, out wallData, maximumTetherLength)) amITethered = true; tetherPoint = wallData.point; tetherLength = Vector3.Distance (wallData.point, avatar.position); 

Bumping Into Stuff

Dumping i vegger og lignende har også en tendens til å skje mye oftere i et 3D-spill enn et 2D-spill, spesielt når det er veggene du knytter deg til. Det er en rekke ting du kan gjøre for å gjøre dette mindre irriterende.

  • La spilleren styre i luften: fysikk har ikke mye å si om dette, men det føles riktig. Det er en av tingene vi gjorde i Spider-Man 2, og hvorfor får du en jetpack i Energy Hook.
  • Spill en kul animasjon: ha avataren gjøre en eller annen form for flip eller spin når de treffer en vegg, og så føles det mindre som en feil og mer som "Jeg mente å gjøre det!"
  • Hold dem fra veggen: Ultimate Spider-Man gjorde dette - hvis du kom for nær en vegg, ville den forsiktig skyve deg bort fra den. Det så litt rart ut hvis du slipper staven og bare lar avataren din henge der - de ville flyte vekk fra veggen i en merkelig vinkel - men for resten av spillet var det en forbedring. (Du kan muligens ha det beste fra begge verdener ved å bare skyve vekk hvis karakteren går en viss hastighet?)
  • Belønne dem for å ikke slå veggen i første omgang: Dette er Energy Hook-strategien - lær spilleren å unngå å treffe vegger ved å belønne dem for rene svinger. På den ene siden oppmuntrer de dem til å svinge pent; På den annen side, når de slår på veggen, er det så mye mer frustrerende, fordi de mister stilpoengene sine.


Så hva venter du på?

Gå ut og lag dine egne svingende spill, og la meg få vite hva du kommer med! Jeg kan aldri få nok av svingende spill.

Jeg håper du har funnet denne artikkelen nyttig. Hvis du har, kan du støtte Energy Hook Kickstarter eller stemme på Steam Greenlight. Takk!