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. 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 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 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 accessors av disse eiendommene er vist som nedenfor: 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 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 IKine klassen implementerer oppførsel av en IK-kjeden. Forklaring om denne klassen følger denne rekkefølgen Kode nedenfor viser IKine klasse private variabler 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 Disse følgende metoder brukes til å hente noder fra kjeden når det er behov 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 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;} Følgende animasjonen viser beregningen av lengden begrensningen 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 () 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. Gjennomføringen av vinkel begrensninger er like nedenfor. 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 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 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 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 Merk at vinkel inngangene er i radianer ikke grader 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: 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); }} Før initialisering av IKine klassen å konstruere kjeden, private variabler av Main.as er skapt For tilfelle her, alle nodene er begrenset til en avstand på 40 mellom noder Neste, vi erklærer variabler som skal benyttes av våre tastatur kontroll Fest på scenen hovedsløyfe og tastatur lyttere. Jeg har uthevet dem Skriv lytterne 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 Trykk Ctrl + Enter for å se din slange animere !. Kontrollere sin bevegelse ved hjelp av piltastene. 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
Endelig resultat Forhåndsvisning
Trinn 1:. Sammenhengene i en kjede
Trinn 2:. Remembering relasjoner
private Var childNode. IKshape, privat Var parentNode: IKshape; privat Var vec2Parent: Vector2D;
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
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
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
Innføring i private variabler i denne klassen.
Implementering av disse spesifikke funksjoner
Trinn 6:. Data i en kjede
. 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
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
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
Trinn 10:. Begrensninger: Komme og sette
Trinn 11:. Lengde Constraint, Concept
Trinn 12: Lengde Constraint, Formula
. 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
< h2> Trinn 14: Angle Constraint, Formula
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
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
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
Trinn 18:. Essential Metoder i IKine
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
Trinn 19:.. Opprette en Snake
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
Trinn 21: Initial Chain
privat Var currentChain. IKine; private Var lastNode : IKshape; private Var totalNodes: uint = 10;
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
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;
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);}
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; }}
Trinn 23:.! Animate
Konklusjon