Lag en Mekanisk Snake Med Inverse Kinematics

Create en Mekanisk Snake Med Inverse Kinematikk
3
Del
to
Del

Dette Cyber ​​mandag Envato Tuts + kurs vil bli redusert til bare $ 3. . Ikke gå glipp av

Tenk deg en kjede av partikler animere i symfoni sammen: Et tog i bevegelse som alle tilkoblede avdelinger følge etter; et dukketeater dans som sin herre trekker sin string; selv armene, når foreldrene dine holder hendene som de lede deg i en kveldstur. Movevment krusninger ned fra siste node til opprinnelsen, følge til begrensninger som det går. Dette er inverse kinematikk plakater (IK), en matematisk algoritme som beregner nødvendige bevegelser. Her vil vi bruke den til å lage en slange som er litt mer avansert enn den fra Nokia spill.




Endelig resultat Forhåndsvisning

La oss ta en ser på det endelige resultatet vi skal jobbe mot. Trykk og hold opp, venstre og høyre piltaster for å gjøre det flytte



Trinn 1:. Sammenhengene i en kjede

En kjede er konstruert av noder. Hver node representerer et punkt i kjeden hvor overføring og rotasjon kan skje. I IK kjeden, krusninger bevegelse ned i revers fra siste node (siste barn) til den første noden (root node) i motsetning til Forward Kinematikk (FK) der kinematikk traversere fra rotnoden til det siste barnet.

Alle kjedene begynner med rotnoden. Dette rotnoden er konstituert forelder til som et nytt barn node er vedlagt. I sin tur, blir dette første barn vil moder den andre barn i kjeden, og dette gjentas inntil den siste barnet blir tilsatt. Animasjonen nedenfor viser et slikt forhold



Trinn 2:. Remembering relasjoner

IKshape klassen implementerer tanken om en node i vår kjede. Forekomster av IKshape klasse huske sine overordnede og underordnede noder, med unntak av rotnoden som ikke har foreldrenoden og den siste node som ikke har noen barn node. Nedenfor er de private eiendommer i IKshape
private Var childNode. IKshape, privat Var parentNode: IKshape; privat Var vec2Parent: Vector2D;

accessors av disse eiendommene er vist som nedenfor:
offentlig funksjon satt IKchild ( childSprite: IKshape): void {childNode = childSprite;} offentlig funksjon får IKchild (): IKshape {return childNode} offentlig funksjon satt IKparent (parentSprite: IKshape): void {parentNode = parentSprite;} offentlig funksjon får IKparent (): IKshape { returnere parentNode;}



Trinn 3: Vector fra barn til Parent

Du kan legge merke til at denne klassen gjør lagre en Vector2D som peker fra barn node til foreldrenoden. Begrunnelsen for denne retningen skyldes bevegelse strømmer fra barn til foreldre. Vector2D brukes fordi størrelsen og retningen av vektoren som peker fra barn til forelder vil bli manipulert ofte mens gjennomføringsmåten til en IK kjede. Dermed holde styr på slike data er nødvendig. Nedenfor er metoder for å maniplate vektor mengder for IKshape
offentlig funksjon calcVec2Parent (): void {var xlength. Number = parentNode.x - this.x; Var ylength: Number = parentNode.y - this.y; vec2Parent = new Vector2D (xlength, ylength);} offentlig funksjon setVec2Parent (vec: Vector2D): void {vec2Parent = vec.duplicate ();} offentlig funksjon getVec2Parent (): Vector2D {return vec2Parent.duplicate ();} offentlig funksjon getAng2Parent (): Antall {return vec2Parent.getAngle ();}



Trinn 4: Tegning Node

Sist men ikke minst, trenger vi en metode for å trekke vår form. Vi skal tegne et rektangel for å representere hver node. Imidlertid kan noen andre preferanser settes i ved å overstyre trekningen metoden her. Iv følger et eksempel på en klasse styrer standard trekningen metoden, Ball klasse. (En rask bytte mellom figurene vil bli demonstrert på slutten av denne opplæringen.) Med dette full vi etableringen av Ikshape klassen
beskyttet funksjon draw (): void {var col. Number = 0x00FF00; Var w: Number = 50; Var h: Number = 10; graphics.beginFill (col); graphics.drawRect (w /2, -h /2, w, h); graphics.endFill ();}



Trinn 5: IK Chain

IKine klassen implementerer oppførsel av en IK-kjeden. Forklaring om denne klassen følger denne rekkefølgen


    Innføring i private variabler i denne klassen.
  1. Grunnleggende metoder som brukes i denne klassen.
  2. Matematisk forklaring på arbeidet i spesifikke funksjoner.

    Implementering av disse spesifikke funksjoner


    Trinn 6:. Data i en kjede

    Kode nedenfor viser IKine klasse private variabler
    . private Var IKineChain. Vector < IKshape >; //medlemmer av kjeden //Datastruktur for constraintsprivate Var constraintDistance. Vector < Number >; . //avstand mellom nodesprivate Var constraintRangeStart: Vector < Number >; //starten av rotasjons freedomprivate Var constraintRangeEnd. Vector < Number >; //slutten av rotasjons frihet



    Trinn 7: instantiate Chain

    IKine kjeden vil lagre en Sprite datatype som husker forholdet av sin foreldre og barn. Disse sprites er forekomster av IKshape. Den resulterende kjeden ser rotnoden ved indeks 0, det neste barnet ved indeks 1, ... til siste barnet i sekvensiell måte. Imidlertid er byggingen av kjeden ikke fra rot til siste barn; . det er fra siste barn til roten

    Forutsatt at kjeden er av lengde n, følger bygge denne sekvensen: n-te node, (n-1) -te node, (n-2) -te node ... 0-th node. Animasjonen nedenfor viser denne sekvensen.

    Ved oppretting av IK-kjeden, er den siste noden inn. Parent noder vil bli lagt senere. Den siste node vedlagt er roten. Koden nedenfor er metoder for IK kjeden konstruksjon, tilføye og fjerne noder til kjeden
    offentlig funksjon IKine (lastChild: IKshape, avstand: Number) {//initiere alle private variabler IKineChain = new Vector < IKshape > ()..; Number >; constraintDistance = new Vector <. (); Number >; constraintRangeStart = new Vector <. (); Number >; constraintRangeEnd = new Vector <. (); //Set begrensninger this.IKineChain [0] = lastChild; this.constraintDistance [0] = avstand; this.constraintRangeStart [0] = 0; this.constraintRangeEnd [0] = 0;} /* Metoder for å manipulere IK kjetting * /public funksjon appendNode (nodeNext: IKshape, avstand: Number = 60, angleStart: Number = -1 * Math.PI, angleEnd: Number = Math. PI): void {this.IKineChain.unshift (nodeNext); this.constraintDistance.unshift (avstand); this.constraintRangeStart.unshift (angleStart); this.constraintRangeEnd.unshift (angleEnd);} offentlig funksjon removeNode (node: Number): void {this.IKineChain.splice (node, 1); this.constraintDistance.splice (node, 1); this.constraintRangeStart.splice (node, 1); this.constraintRangeEnd.splice (node, 1);}



    Trinn 8: Få Chain Nodes

    Disse følgende metoder brukes til å hente noder fra kjeden når det er behov
    offentlig. funksjon getRootNode (): IKshape {return this.IKineChain [0];} offentlig funksjon getLastNode (): IKshape {return this.IKineChain [IKineChain.length - 1];} offentlig funksjon getNode (node: Number): IKshape {return denne .IKineChain [node];}



    Trinn 9: Begrensninger

    Vi har sett hvordan kjeden av noder blir representert i en rekke: Root node ved indeks 0, ... (n- 1) 'te node ved indeks (n-2), n-te knutepunktet ved indeks (n-1) hvor n er lengden av kjeden. Vi kan enkelt ordne våre begrensninger i en slik ordre i tillegg. Begrensninger kommer i to former: avstanden mellom nodene Hotell og grad av bøying frihet mellom nodene

    Avstand for å opprettholde mellom noder er anerkjent som et barn nodes begrensning på sin forelder. . For ordens skyld på å registrere bekvemmelighet, kan vi lagre denne verdien som constraintDistance array med indeks lik som barnet node-tallet. Merk at rotnoden har ingen foreldre. Imidlertid bør avstanden begrensning registreres ved å føye rotnoden, slik at hvis kjeden er forlenget senere, kan den nylig vedlagte "foreldre" av denne rotnoden utnytte sine data.

    Deretter vinkelen for bøying en foreldrenoden er begrenset til et område. Vi skal lagre start- og sluttpunkt for serien i constraintRangeStart og ConstraintRangeEnd array. Figuren nedenfor viser et barn node i grønne og to overordnede noder i blått. Bare den noden som er merket "OK" er tillatt fordi den ligger innenfor den vinkel begrensningen. Vi kan bruke samme tilnærming i å registrere verdier i disse arrays. Merk igjen at rotnoden vinkel begrensninger skal registreres, selv om den ikke er i bruk på grunn av lignende begrunnelse som tidligere. Plus, vinkel begrensninger gjelder ikke den siste barnet fordi vi ønsker fleksibilitet i kontroll



    Trinn 10:. Begrensninger: Komme og sette

    Metodene som følger kan være nyttig når du har igangsatt begrensninger på en node, men ønsker å endre verdien i fremtiden Twitter /* Manipulere tilsvarende begrensninger * /public funksjon getDistance (node: Number): Antall {return this.constraintDistance [node];} offentlig funksjon setDistance (. newDistance: Antall, node: Number): void {this.constraintDistance [node] = newDistance;} offentlig funksjon getAngleStart (node: Number): Antall {return this.constraintRangeStart [node];} offentlig funksjon setAngleStart (newAngleStart: Antall, node : Number): void {this.constraintRangeStart [node] = newAngleStart;} offentlig funksjon getAngleRange (node: Number): Antall {return this.constraintRangeEnd [node];} offentlig funksjon setAngleRange (newAngleRange: Antall, node: Number): void {this.constraintRangeEnd [node] = newAngleRange;}



    Trinn 11:. Lengde Constraint, Concept

    Følgende animasjonen viser beregningen av lengden begrensningen



    Trinn 12: Lengde Constraint, Formula

    I dette trinnet skal vi ta en titt på kommandoer i en metode som bidrar til å begrense avstanden mellom nodene. Legg merke til de markerte linjer. Du kan merke bare det siste barnet er påført denne begrensningen. Vel, så langt kommandoen går, dette er sant. Foreldrenodene er nødvendig for å oppfylle ikke bare lengde, men vinkel begrensninger. Alle disse er behandlet med gjennomføringen av metoden vecWithinRange ()
    . Siste barnet trenger ikke være begrenset i vinkel fordi vi trenger maksimal bøy fleksibilitet
    privat funksjon updateParentPosition (): void {for (var i: uint = IKineChain.length - 1; i > 0; I--). {IKineChain [i] .calcVec2Parent (); Var vec: Vector2D; //håndtering av siste barnet hvis (i == IKineChain.length - 1) {var ang: Number = IKineChain [i] .getAng2Parent (); vec = new Vector2D (0, 0); vec.redefine (this.constraintDistance [IKineChain.length - 1], ang); } Else {vec = this.vecWithinRange (i); } IKineChain [i] .setVec2Parent (vec); IKineChain [i] .IKparent.x = IKineChain [i] .x + IKineChain [i] .getVec2Parent () x.; IKineChain [i] .IKparent.y = IKineChain [i] .Y + IKineChain [i] .getVec2Parent () y.; }}



    Trinn 13: Angle tvang, Concept

    Først må vi regne ut vinkelen klemt mellom to vektorer, vec1 og vec2. Hvis vinkelen er ikke innenfor anstrengt rekkevidde, tildele minimum eller maksimumsgrense for vinkel. Når en vinkel er definert, kan vi beregne en vektor som er rotert fra vec1 sammen med begrensningen av avstand (magnitude).

    Følgende animasjon tilbyr et alternativ for å visualisere ideen.


    < h2> Trinn 14: Angle Constraint, Formula

    Gjennomføringen av vinkel begrensninger er like nedenfor.
    privat funksjon vecWithinRange (currentNode: Number): Vector2D {//får riktig vektorer Var child2Me: Vector2D = IKineChain [currentNode] .IKchild.getVec2Parent (); Var me2Parent: Vector2D = IKineChain [currentNode] .getVec2Parent (); //Implementere vinkel grenser begrensning Var currentAng: Number = child2Me.angleBetween (me2Parent); Var currentStart: Number = this.constraintRangeStart [currentNode]; Var currentEnd: Number = this.constraintRangeEnd [currentNode]; Var limitedAng: Number = Math2.implementBound (currentStart, currentEnd, currentAng); //Implementere avstand begrensning child2Me.setMagnitude (this.constraintDistance [currentNode]); child2Me.rotate (limitedAng); returnere child2Me}



    Trinn 15: Vinkel med beskrivelser

    Kanskje det er verdig til å gå gjennom her ideen om å få en vinkel som tolker med og mot urviseren. Vinkelen mellom to vektorer, si vec1 og vec2, kan lett oppnås fra prikk-produktet av disse to vektorer. Produksjonen vil være den korteste vinkel for å rotere vec1 til vec2. Det er imidlertid ingen oppfatningen av retning som svaret er alltid positive. Derfor modifikasjon på den vanlige produksjonen skal utføres. Før outputing vinkel, anvendes vektorprodukt mellom vec1 og vec2 for å bestemme om den aktuelle sekvensen er positiv eller negativ rotasjon og innlemmet tegnet inn i vinkel. Jeg har markert retnings funksjonen i linjer med kode under
    offentlig funksjon vectorProduct (vec2: Vector2D): Antall {return this.vec_x * vec2.y - this.vec_y * vec2.x;} offentlig funksjon angleBetween (vec2.: Vector2D): Antall {var vinkel. Number = Math.acos (this.normalise () dotProduct (vec2.normalise ())); Var vec1: Vector2D = this.duplicate (); if (vec1.vectorProduct (vec2) < 0) {vinkel * = 1; } Avkastning vinkel;}



    Trinn 16: Orientere Knuter

    noder som er bokser må være orientert i retning av sine vektorer, slik at de ser fine. Ellers vil du se en kjede som nedenfor. (Bruk piltastene til å flytte.)

    Funksjonen nedenfor implementerer riktig orientering av noder
    privat funksjon updateOrientation (): void {for (var i. Uint = 0; i < IKineChain. lengde - 1; i ++) {var orientering. Number = IKineChain [i] .IKchild.getVec2Parent () getAngle (); IKineChain [i] .rotation = Math2.degreeOf (orientering); }}



    Trinn 17: Siste Bit

    Nå som alt er satt, kan vi animere vår kjede hjelp animere (). Dette er en sammensatt funksjon å ringe til updateParentPosition () og updateOrientation (). Men før det kan oppnås, må vi oppdatere relasjoner på alle noder. Vi gjør et kall til updateRelationships (). Igjen, updateRelationships () er en sammensatt funksjon å ringe til defineParent () og defineChild (). Dette gjøres en gang, og når det er en endring i kjedestrukturen, f.eks noder legges til eller falt under kjøring



    Trinn 18:. Essential Metoder i IKine

    For å gjøre IKine klasse jobben for deg, disse er noen metoder du bør se nærmere på. Jeg har dokumentert dem i en tabellform
    MethodInput ParametersRoleIKine () lastChild. IKshape, avstand: NumberConstructor.appendNode () nodeNext: IKshape, [distanse: Antall, angleStart: Antall, angleEnd: Number] legge til noder i kjeden, definere begrensninger implementert av node.updateRelationships () NoneUpdate foreldre-barn-relasjoner for alle nodes.animate () NoneRecalculating posisjonen til alle nodene i kjeden. Må kalles hver ramme

    Merk at vinkel inngangene er i radianer ikke grader



    Trinn 19:.. Opprette en Snake

    Nå kan opprette et prosjekt i FlashDevelop. I src mappen vil du se Main.as. Dette er rekkefølgen av oppgaver du bør gjøre:


      Start kopier av IKshape eller klasser som strekker seg fra IKshape på scenen

      Initiere IKine og bruke den til å kjede seg kopier av IKshape. på scenen

      Oppdater relasjoner på alle nodene i kjeden

      Implementere brukerkontroller

      Animate


      Trinn 20...!: tegne objekter

      Object er trukket som vi konstruere IKshape. Dette gjøres i en sløyfe. Oppmerksom hvis du ønsker å endre utsiktene av tegningen til en sirkel, aktiverer kommentar på linje 56 og deaktivere kommentar på linje 57. (Du må laste ned mine kildefilene for at dette skal fungere.) Anmeldelser private funksjons drawObjects (): void {for (var i: uint = 0; i < totalNodes; i ++) {var currentObj: IKshape = new IKshape (); //Var currentObj: Ball = new Ball (); currentObj.name = & quot; b & quot; + I; addChild (currentObj); }}



      Trinn 21: Initial Chain

      Før initialisering av IKine klassen å konstruere kjeden, private variabler av Main.as er skapt
      privat Var currentChain. IKine; private Var lastNode : IKshape; private Var totalNodes: uint = 10;

      For tilfelle her, alle nodene er begrenset til en avstand på 40 mellom noder
      privat funksjon initChain (): void {this.lastNode = this.getChildByName (. & quot; b & quot; + (totalNodes - 1)) som IKshape; currentChain = new IKine (lastNode, 40); for (var i: uint = 2; i < = totalNodes; i ++) {currentChain.appendNode (this.getChildByName (& quot; b & quot; + (totalNodes - i)) som IKshape, 40, Math2.radianOf ( -30), Math2.radianOf (30)); } CurrentChain.updateRelationships (); //sentrum slange på scenen. currentChain.getLastNode () x = stage.stageWidth /2.; . currentChain.getLastNode () y = stage.stageHeight /2}



      Trinn 22:. Legg tastaturkontroller

      Neste, vi erklærer variabler som skal benyttes av våre tastatur kontroll
      privat Div leadingVec: Vector2D; private Var currentMagnitude: Antall = 0; privat Var currentAngle: Antall = 0; privat Var increaseAng: Number = 5; privat Var increaseMag: Number = 1; privat Var decreaseMag: Number = 0.8; privat Var capMag: Number = 10; private Var pressedUp: Boolean = false; privat Var pressedLeft: Boolean = false; privat Var pressedRight: Boolean = false;

      Fest på scenen hovedsløyfe og tastatur lyttere. Jeg har uthevet dem
      privat funksjon init (e: Hendelses = null). Void {removeEventListener (Event.ADDED_TO_STAGE init,); //Entry point this.drawObjects (); this.initChain (); leadingVec = new Vector2D (0, 0); stage.addEventListener (Event.ENTER_FRAME, handleEnterFrame); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKeyDown); . stage.addEventListener (KeyboardEvent.KEY_UP, handleKeyUp);}

      Skriv lytterne
      privat funksjon handleEnterFrame (e: Hendelses): void {if (pressedUp == true) {currentMagnitude + = increaseMag; currentMagnitude = Math.min (currentMagnitude, capMag); } Else {currentMagnitude * = decreaseMag; } If (pressedLeft == true) {currentAngle - = Math2.radianOf (increaseAng); } If (pressedRight == true) {currentAngle + = Math2.radianOf (increaseAng); } LeadingVec.redefine (currentMagnitude, currentAngle); Var futureX: Number = leadingVec.x + lastNode.x; Var futureY: Number = leadingVec.y + lastNode.y; futureX = Math2.implementBound (0, stage.stageWidth, futureX); futureY = Math2.implementBound (0, stage.stageHeight, futureY); lastNode.x = futureX; lastNode.y = futureY; lastNode.rotation = Math2.degreeOf (leadingVec.getAngle ()); currentChain.animate ();} private funksjon handleKeyDown (e: KeyboardEvent): void {if (e.keyCode == Keyboard.UP) {pressedUp = true; } If (e.keyCode == Keyboard.LEFT) {pressedLeft = true; } Else if (e.keyCode == Keyboard.RIGHT) {pressedRight = true; }} privat funksjon handleKeyUp (e: KeyboardEvent): void {if (e.keyCode == Keyboard.UP) {pressedUp = false; } If (e.keyCode == Keyboard.LEFT) {pressedLeft = false; } Else if (e.keyCode == Keyboard.RIGHT) {pressedRight = false; }}

      Legg merke til at jeg har brukt en Vector2D eksempel å føre slangen beveger seg rundt på scenen. Jeg har også begrenset denne vektoren innenfor grensen av scenen så det vil ikke flytte ut. Action utfører denne begrensningen er uthevet



      Trinn 23:.! Animate

      Trykk Ctrl + Enter for å se din slange animere !. Kontrollere sin bevegelse ved hjelp av piltastene.



      Konklusjon

      Denne opplæringen krever litt kunnskap i vektoranalyse. For lesere som ønsker å få et kjent titt på vektorer, har en lese på innlegget fra Daniel Sidhon. Håper dette hjelper deg i å forstå og implementere inverse kinematikk. Takk for lesing. Må slippe forslag og kommentarer som Im alltid ivrige etter å høre fra publikum. Terima Kasih. Anmeldelser