Playing Rundt med Elastic Kollisjoner
Del
Del
to
Del
Dette Cyber mandag Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av.
Dette innlegget er en del av en serie som heter You Do The Math.Creating generativ kunst med HYPEEuclidean vektorer i Flash
I denne opplæringen vil vi lage et spill hvor målet er å hindre at andre objekter fra kollidere med markøren. Vi vil ikke bruke Flash innebygde hitTestObject () metoder; i stedet vil vi skrive våre egne dueller rutiner.
publiseres Tutorial
Hver noen uker, vi besøker noen av våre lesernes favoritt innlegg fra hele historien til området. Denne opplæringen ble første gang utgitt i februar 2011.
Endelig resultat Forhåndsvisning
La oss ta en titt på det endelige resultatet vi skal jobbe mot:
Trinn 1: Begynn
Opprett en ny Flash-fil (Actionscript 3.0)
sette scenen dimensjoner til 500x500px og FPS til 32.
Trinn 2: The Ball Class
Denne klassen vil inneholde alle data relatert til en ball. En ball har en _mass, en _radius, en _xSpeed og en _ySpeed. Så vi vil gjøre en eiendom for hver. I konstruktøren passerer vi masse, vinkelen og hastigheten på ballen. Fordi klassen vil være knyttet til en skjerm objekt kan vi hente radien for vår ball ved å dividere bredden på displayet objektet ved 2. _xSpeed og _ySpeed kan beregnes ved hjelp av enkle sinus- og cosinusfunksjoner.
Pakke { import flash.display.Stage import flash.display.Sprite import flash.events.Event public class Ball strekker Sprite {private Var _radius: Antall = 0 private Var _mass: Antall = 0 private Var _xSpeed: Antall = 0 private Var _ySpeed: Antall = 0 offentlig funksjon Ball (masse: Number = 10,0, vinkel: Number = Math.PI, hurtighet: Number = 10,0): void {this.mass = masse this._radius = this.width /2 this.xSpeed = fart * Math .sin (vinkel) this.ySpeed = fart * Math.cos (vinkel)}}}
For mer informasjon om disse trigonometriske tak i Math.sin () og Math.cos () funksjoner, se denne Quick Tips. Anmeldelser
Trinn 3: Tilby Getters og settere
I vår Ball klasse gir vi kundeskaffere og settere for våre eiendommer
offentlig funksjon får radius (). Number {return this._radius} offentlige funksjon satt masse (masse: Number): void {this._mass = masse} offentlig funksjon får masse (): Antall {return this._mass} offentlig funksjon satt xSpeed (xSpeed: Number): void {this._xSpeed = xSpeed} public funksjonen får xSpeed (): Antall {return this._xSpeed} offentlig funksjon satt ySpeed (ySpeed: Number): void {this._ySpeed = ySpeed} offentlig funksjon får ySpeed (): Antall {return this._ySpeed}
Trinn 4: Oppdatering Funksjon
Denne funksjonen oppdaterer x og y egenskapene til vår ball i henhold til _xSpeed og _ySpeed. Vi vil implementere denne funksjonen i vår Ball klasse
offentlig funksjon oppdatering (). Void {this.x + = _xSpeed this.y + = _ySpeed}
Trinn 5: Fullført Class Anmeldelser
Vi skal gjøre ferdig vår Ball klasse i dette trinnet
pakke {import flash.display.Stage import flash.display.Sprite import flash.events.Event public class Ball strekker Sprite {private Var _radius. Number = 0 private Var _mass: Antall = 0 private Var _xSpeed: Antall = 0 private Var _ySpeed: Antall = 0 offentlig funksjon Ball (masse: Number = 10,0, vinkel: Number = Math.PI, hurtighet: Number = 10,0): void {dette .mass = masse this._radius = this.width /2 this.xSpeed = fart * tak i Math.sin (vinkel) this.ySpeed = fart * Math.cos (vinkel)} offentlig funksjon får radius (): Antall {return dette. _radius} offentlig funksjon satt masse (masse: Number): void {this._mass = masse} offentlig funksjon får masse (): Antall {return this._mass} offentlig funksjon satt xSpeed (xSpeed: Number): void {this._xSpeed = xSpeed} offentlig funksjon får xSpeed (): Antall {return this._xSpeed} offentlig funksjon satt ySpeed (ySpeed: Number): void {this._ySpeed = ySpeed} offentlig funksjon får ySpeed (): Antall {return this._ySpeed} offentlig funksjon oppdatering (): void {this.x + = _xSpeed this.y + = _ySpeed}}}
Trinn 6: Skjerm objekter for vår Ball Class
I kildefiler inkludert jeg en starter FLA som inneholder alle bibliotek elementene du trenger. Du kan tegne dem selv hvis du vil, selvfølgelig. Sørg for at FLA har følgende visningsobjekter:
der:
v1 = endelige xSpeedBall en
v2 = endelige xSpeedBall to
m1 = masse ball 1
m2 = masse ball 2
u1 = utgangshastighet ball 1
u2 = utgangshastighet ball 2
y-hastigheter don 't endre som det er en 1D kollisjon.
Med disse formlene vi kan beregne xSpeed og ySpeed av hver ball.
Nå whe har den nye x- og y-hastigheter i vår rotert koordinatsystemet. Det siste trinnet er å konvertere alt tilbake til en normal koordinatsystem. Vi bruker Math.PI /2 fordi vinkelen mellom xSpeed og ySpeed må alltid være 90 grader (pi /2 radianer)
privat funksjon doCollision. (Ball1: Ball, ball2: Ball): void {var xDist: Number = ball1 .x - ball2.x Var yDist: Number = ball1.y - ball2.y Var collisionAngle: Number = Math.atan2 (yDist, xDist) Var magBall1: Number = Math.sqrt (ball1.xSpeed * ball1.xSpeed + ball1. ySpeed * ball1.ySpeed) Var magBall2: Number = Math.sqrt (ball2.xSpeed * ball2.xSpeed + ball2.ySpeed * ball2.ySpeed) Var angleBall1: Number = Math.atan2 (ball1.ySpeed, ball1.xSpeed) Var angleBall2 : Number = Math.atan2 (ball2.ySpeed, ball2.xSpeed) Var xSpeedBall1: Number = magBall1 * Math.cos (angleBall1-collisionAngle) Var ySpeedBall1: Number = magBall1 * tak i Math.sin (angleBall1-collisionAngle) Var xSpeedBall2: Number = magBall2 * Math.cos (angleBall2-collisionAngle) Var ySpeedBall2: Number = magBall2 * tak i Math.sin (angleBall2-collisionAngle) Var finalxSpeedBall1: Number = ((ball1.mass-ball2.mass) * xSpeedBall1 + (ball2.mass + ball2.mass ) * xSpeedBall2) /(ball1.mass + ball2.mass) Var finalxSpeedBall2: Number = ((ball1.mass + ball1.mass) * xSpeedBall1 + (ball2.mass-ball1.mass) * xSpeedBall2) /(ball1.mass + ball2 .mass) Var finalySpeedBall1: Number = ySpeedBall1 Var finalySpeedBall2: Number = ySpeedBall2 ball1.xSpeed = Math.cos (collisionAngle) * finalxSpeedBall1 + Math.cos (collisionAngle + Math.PI /2) * finalySpeedBall1 ball1.ySpeed = tak i Math.sin ( collisionAngle) * finalxSpeedBall1 + tak i Math.sin (collisionAngle + Math.PI /2) * finalySpeedBall1 ball2.xSpeed = Math.cos (collisionAngle) * finalxSpeedBall2 + Math.cos (collisionAngle + Math.PI /2) * finalySpeedBall2 ball2.ySpeed = tak i Math.sin (collisionAngle) * finalxSpeedBall2 + tak i Math.sin (collisionAngle + Math.PI /2) * finalySpeedBall2}
For å finne mer informasjon om elastiske kollisjoner ta en titt på hoomanr.com.
Trinn 18: endOfGame () Funksjon
Dette er kjørt når spillet slutter
privat funksjon endOfGame (). void {tmr.stop () Mouse.show () stage.removeEventListener (MouseEvent.MOUSE_MOVE , updatePlayerBall) stage.removeEventListener (Event.ENTER_FRAME, gameLoop) while (eballs.length > 0) {TweenMax.to (eballs [0], 0,5, {scaleX: 0, Scaley: 0, lette: Bounce.easeOut}) eballs.splice (0,1)} TweenMax.to (ballspiller, 0,5, {scaleX: 0, Scaley: 0, letthet: Bounce.easeOut})}
Først av alt vi stoppe tidtakeren. Vi viser musa igjen. Neste vi fjerne både MOUSE_MOVE og ENTER_FRAME hendelsen lyttere. Til slutt gjør vi alle ballene på scenen usynlig
Trinn 19:. CheckBounds () Funksjon
Denne funksjonen gjør at ballene bo inne i spillet skjermen. Så hvis ballen treffer øvre eller nedre side, reversere vi ySpeed. hvis ballen treffer venstre eller høyre side av skjermen vi reversere xSpeed. Den bruker samme logikk til ballen dueller funksjon for å sjekke om kantene på ballen treffer en kant av skjermen
private funksjons checkBounds. (Ball: ball): void {if ((ball.x + ball.radius) > stage.stageWidth) {ball.x = stage.stageWidth - ball.radius ball.xSpeed * = -1} if ((ball.x - ball.radius) < 0) {ball.x = 0 + ball. radius ball.xSpeed * = -1} if ((ball.y + ball.radius) > stage.stageHeight) {ball.y = stage.stageHeight - ball.radius ball.ySpeed * = - 1} if ((ball .Y - ball.radius) < 0) {ball.y = 0 + ball.radius ball.ySpeed * = - 1}}
Trinn 20: The Complete Application Class
har fullført vår Application klasse. Vi har nå et fungerende spill !!!
pakke {import flash.display.Sprite; import flash.display.Graphics; import flash.events.Event; import flash.events.TimerEvent; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.utils.Timer; import flash.ui.Mouse; import com.greensock.TweenMax; importere com.greensock.easing. *; public class Søknad strekker Sprite {private Var ballspiller: Ball; private Var eballs: Array; private Var TMR: Timer; private Var poengsum: Score; offentlig funksjon Application (): void {init (); } Private funksjon init (): void {ballspiller = new PlayerBall (); eballs = new Array (); TMR = new Timer (10); poengsum = new Score (); stage.align = "TL"; stage.scaleMode = "noScale"; Mouse.hide (); setBackground (); score.x = stage.stageWidth /2; score.y = stage.stageHeight /2; stage.addChild (score); stage.addEventListener (MouseEvent.MOUSE_MOVE, updatePlayerBall); stage.addChild (ballspiller); tmr.addEventListener (TimerEvent.TIMER, updateTime); stage.addEventListener (MouseEvent.CLICK, startGame); } Private funksjon setBackground (): void {var Type: String = "radial"; Var farger: Array = [0xFFFFFF, 0xcccccc]; Var alfaer: Array = [1,1]; Var forholdstall: Array = [0255]; Var MATR: Matrix = new Matrix (); matr.createGradientBox (stage.stageWidth, stage.stageHeight, Math.PI /2, 0, 0); //SpreadMethod vil definere hvordan gradient spres. Notat!!! Flash bruker standardsortimentet til å representere strenger Var sprMethod: String = "pad"; //Start Gradietn og passerer våre variabler til det Var sprite: Sprite = new Sprite (); //Lagre typing + økning ytelse gjennom lokal referanse til et grafikkobjekt Var g: Grafikk = sprite.graphics; g.beginGradientFill (type, farger, Alpha, prosenter, matrise, sprMethod); g.drawRect (0,0, stage.stageWidth, stage.stageHeight); stage.addChild (sprite); } Private funksjon updatePlayerBall (e: MouseEvent): void {ballPlayer.x = mouseX; ballPlayer.y = mousey; } Private funksjon updateTime (e: Timerevent): void {score.txtScore.text = String (((tmr.currentCount * tmr.delay) /1000) .toFixed (2)); if ((tmr.currentCount * tmr.delay)% 5000 == 0) {addBall (); }} Privat funksjon startGame (e: MouseEvent): void {stage.removeEventListener (MouseEvent.CLICK, startGame); addBall (); addBall (); addBall (); tmr.start (); stage.addEventListener (Event.ENTER_FRAME, gameLoop); } Private funksjon addBall (): void {var ball: ball = new Ball (10, Math.random () * Math.PI * 2,5); ball.x = Math.random () * stage.stageWidth; ball.y = Math.random () * stage.stageHeight; ball.alpha = 0; stage.addChild (ball); TweenMax.to (ball, 0.5, {alfa: 1}); TweenMax.to (ball, 0, {forsinkelse: 1, onComplete: function (): void {eballs.push (ball)}}); } Private funksjon gameLoop (e: Hendelses): void {for (var i: uint = 0; i < eballs.length, jeg ++) {for (var j: uint = i + 1, j < eballs.length; j ++ ) {if (kollisjon (eballs [i], eballs [J])) {doCollision (eballs [i], eballs [j]); }} If (kollisjon (eballs [i], ballspiller)) {endOfGame (); gå i stykker; } Eballs [i] .update (); checkBounds (eballs [i]); }} Privat funksjon kollisjon (ball1: Ball, ball2: Ball): Boolean {var xDist: Number = ball1.x - ball2.x; Var yDist: Number = ball1.y - ball2.y; Var Dist: Number = Math.sqrt (xDist * xDist + yDist * yDist); if (Dist < = ball1.radius + ball2.radius) {if (ball1.x < ball2.x) {ball1.x - = 2; ball2.x + = 2; } Else {ball1.x + = 2; ball2.x - = 2; } If (ball1.y < ball2.y) {ball1.y - = 2; ball2.y + = 2; } Else {ball1.y + = 2; ball2.y - = 2; }} Tilbake Dist < = ball1.radius + ball2.radius; } Private funksjon doCollision (ball1: Ball, ball2: Ball): void {var xDist: Number = ball1.x - ball2.x; Var yDist: Number = ball1.y - ball2.y; Var collisionAngle: Number = Math.atan2 (yDist, xDist); Var magBall1: Number = Math.sqrt (ball1.xSpeed * ball1.xSpeed + ball1.ySpeed * ball1.ySpeed); Var magBall2: Number = Math.sqrt (ball2.xSpeed * ball2.xSpeed + ball2.ySpeed * ball2.ySpeed); Var angleBall1: Number = Math.atan2 (ball1.ySpeed, ball1.xSpeed); Var angleBall2: Number = Math.atan2 (ball2.ySpeed, ball2.xSpeed); Var xSpeedBall1: Number = magBall1 * Math.cos (angleBall1 - collisionAngle); Var ySpeedBall1: Number = magBall1 * tak i Math.sin (angleBall1 - collisionAngle); Var xSpeedBall2: Number = magBall2 * Math.cos (angleBall2 - collisionAngle); Var ySpeedBall2: Number = magBall2 * tak i Math.sin (angleBall2 - collisionAngle); Var finalxSpeedBall1: Number = ((ball1.mass-ball2.mass) * xSpeedBall1 + (ball2.mass + ball2.mass) * xSpeedBall2) /(ball1.mass + ball2.mass); Var finalxSpeedBall2: Number = ((ball1.mass + ball1.mass) * xSpeedBall1 + (ball2.mass-ball1.mass) * xSpeedBall2) /(ball1.mass + ball2.mass); Var finalySpeedBall1: Number = ySpeedBall1; Var finalySpeedBall2: Number = ySpeedBall2; ball1.xSpeed = Math.cos (collisionAngle) * finalxSpeedBall1 + Math.cos (collisionAngle + Math.PI /2) * finalySpeedBall1; ball1.ySpeed = tak i Math.sin (collisionAngle) * finalxSpeedBall1 + tak i Math.sin (collisionAngle + Math.PI /2) * finalySpeedBall1; ball2.xSpeed = Math.cos (collisionAngle) * finalxSpeedBall2 + Math.cos (collisionAngle + Math.PI /2) * finalySpeedBall2; ball2.ySpeed = tak i Math.sin (collisionAngle) * finalxSpeedBall2 + tak i Math.sin (collisionAngle + Math.PI /2) * finalySpeedBall2; } Private funksjon endOfGame (): void {tmr.stop (); Mouse.show (); stage.removeEventListener (MouseEvent.MOUSE_MOVE, updatePlayerBall); stage.removeEventListener (Event.ENTER_FRAME, gameLoop); while (eballs.length > 0) {TweenMax.to (eballs [0], 0,5, {scaleX: 0, Scaley: 0, letthet: Bounce.easeOut}); eballs.splice (0,1); } TweenMax.to (ballspiller, 0,5, {scaleX: 0, Scaley: 0, letthet: Bounce.easeOut}); } Private funksjon checkBounds (ball: ball): void {if ((ball.x + ball.radius) > stage.stageWidth) {ball.x = stage.stageWidth - ball.radius; ball.xSpeed * = 1; } If ((ball.x - ball.radius) < 0) {ball.x = 0 + ball.radius; ball.xSpeed * = 1; } If ((ball.y + ball.radius) > stage.stageHeight) {ball.y = stage.stageHeight - ball.radius; ball.ySpeed * = 1; } If ((ball.y - ball.radius) < 0) {ball.y = 0 + ball.radius; ball.ySpeed * = 1; }}}}
Konklusjon
Det var det for denne opplæringen. Selvfølgelig kan du legge til muligheten for å starte spillet, men det bør ikke være for vanskelig. Denne grunnleggende eksempel på elastiske kollisjoner kan brukes til større spill som biljard spill eller lignende.
Jeg håper du likte denne opplæringen, takk for lesing!