Abbiamo affrontato le curve di disegno e trovato le loro radici quadratiche e cubiche, oltre a pratiche applicazioni per l'utilizzo di radici quadratiche all'interno dei giochi. Ora, come promesso, esamineremo le domande per la ricerca cubo le radici, così come i gradienti e le normali delle curve, come fare in modo che gli oggetti rimbalzino sulle superfici curve. Andiamo!
Diamo un'occhiata a un uso pratico di questa matematica:
In questa demo, la "nave" rimbalza sui bordi del SWF e della curva. Il punto giallo rappresenta il punto più vicino alla nave che si trova sulla curva. È possibile regolare la forma della curva trascinando i punti rossi e regolare il movimento della nave usando i tasti freccia.
Consideriamo lo scenario in cui un punto si trova vicino a una curva quadratica. Come calcoli la distanza più breve tra il punto e la curva?
Bene, iniziamo con il Teorema di Pitagora.
\ [
Consenti \ the \ point \ be \ (x_p, \ y_p) \\
e \ call \ the \ closest \ point \ on \ the \ curve \ (x_c, \ y_c) \\
Poi:\\
z ^ 2 = x ^ 2 + y ^ 2 \\
z ^ 2 = (x_c-x_p) ^ 2 + (y_c-y_p) ^ 2 \\
Dato \ y_c = ax_c ^ 2 + bx_c + c, \\
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
Puoi vedere che abbiamo sostituito \ (y_c \) con l'equazione quadratica. A colpo d'occhio, possiamo vedere che la più alta potenza è 4. Quindi, abbiamo a quartic equazione. Tutto quello che dobbiamo fare è trovare un punto minimo in questa equazione per darci la distanza più breve da un punto a una curva quadratica.
Ma prima, dovremo capire i gradienti su una curva ...
Prima di esaminare il problema di ridurre al minimo un'equazione quartica, proviamo a capire i gradienti di una curva. Una retta ha solo un gradiente. Ma il gradiente di una curva quadratica dipende da quale punto della curva a cui ci riferiamo. Controlla la presentazione Flash qui sotto:
Trascina i punti rossi intorno per cambiare la curva quadratica. Puoi anche giocare con la maniglia del cursore per cambiare la posizione del punto blu lungo x. Man mano che il punto blu cambia, anche il gradiente viene disegnato.
Questo è dove il calcolo sarà utile. Potresti aver intuito che la differenziazione di un'equazione quadratica ti avrebbe dato il gradiente della curva.
\ [
f (x) = ax ^ 2 + bx + c \\
\ frac df (x) dx = 2ax + b
\]
Quindi \ (\ frac df (x) dx \) è il gradiente di una curva quadratica, ed è dipendente dalla coordinata \ (x \). Bene, per fortuna abbiamo un metodo per gestire questo: DIFF1 (x: Number)
restituirà il valore dopo una singola differenziazione.
Per disegnare la sfumatura, avremo bisogno di un'equazione per rappresentare la linea, \ (y = mx + c \). Le coordinate del punto blu \ ((x_p, y_p) \) saranno sostituite in \ (x \) e \ (y \), e il gradiente della linea trovata attraverso la differenziazione andrà in \ (m \). Quindi l'intercetta y della linea, \ (c \) può essere calcolata attraverso un lavoro di algebra.
Guarda l'AS3:
var x: Number = s.value var y: Number = quadratic_equation.fx_of (s.value) point.x = x; point.y = y; / ** * y = mx + c; * c = y - mx; <== use this to find c */ var m:Number = quadratic_equation.diff1(x); var c:Number = y - m * x; graphics.clear(); graphics.lineStyle(1, 0xff0000); graphics.moveTo(0, c); graphics.lineTo(stage.stageWidth, m * stage.stageWidth + c);
Ricordare sempre l'asse y invertito dello spazio delle coordinate Flash come mostrato nell'immagine seguente. A prima vista, il diagramma a destra può sembrare un gradiente negativo, ma a causa dell'asse y invertito, in realtà è un gradiente positivo.
Lo stesso vale per il punto minimo come indicato di seguito. A causa dell'asse y invertito, il punto minimo nello spazio delle coordinate cartesiane (a (0,0)) appare come un massimo nello spazio delle coordinate Flash. Ma facendo riferimento alla posizione di origine nello spazio delle coordinate Flash relativo alla curva quadratica, in realtà è un punto minimo.
Ora diciamo che sono interessato a trovare il punto più basso di una curva - come procedo? Guarda l'immagine qui sotto (entrambe le figure si trovano nello stesso spazio di coordinate).
Per ottenere il punto minimo, equivarremo a \ (\ frac df (x) dx = 0 \), poiché per definizione stiamo cercando il punto in cui il gradiente è zero. Ma come mostrato sopra, risulta che il punto massimo su una curva soddisfa anche questa condizione. Quindi, come facciamo a discriminare tra questi due casi?
Proviamo la differenziazione del secondo grado. Ci darà il tasso di variazione del gradiente.
\ [
\ frac df (x) dx = 2ax + b \\
\ frac df ^ 2 (x) dx ^ 2 = 2a
\]
Spiegherò con riferimento all'immagine qui sotto (disegnata nello spazio delle coordinate cartesiane). Possiamo vedere che, mentre aumentiamo lungo l'asse x, il gradiente cambia da negativo a positivo. Quindi il tasso di cambiamento dovrebbe essere a positivo valore.
Possiamo anche vedere che quando \ (\ frac df ^ 2 (x) dx ^ 2 \) è positivo, c'è un punto minimo sulla curva. Al contrario, se la velocità è negativa, è presente un punto massimo.
Ora siamo pronti a risolvere il problema presentato nella Fase 1. Ricordiamo l'equazione quartica (dove il grado più alto è 4) siamo arrivati a:
\ [
z ^ 2 = (x_c-x_p) ^ 2 + [(ax_c ^ 2 + bx_c + c) -y_p] ^ 2
\]
La stessa equazione quartica, tracciata
Ricorda, siamo interessati a trovare il punto minimo su questa curva, perché il punto corrispondente sulla curva quadratica originale sarà il punto che si trova alla minima distanza dal punto rosso.
Quindi, distinguiamo la funzione quartica per ottenere il gradiente di questa curva e quindi azzeriamo il gradiente di questa funzione quartica. Vedrai che il gradiente è in realtà una funzione cubica. Riferirò i lettori interessati alla pagina di Wolfram; per questo tutorial, mi limiterò a cogliere il risultato del loro funzionamento algebrico:
\ [
\ Frac d (z ^ 2) dx =
2 (x_c-x_p) + 2 (ax_c ^ 2 + bx_c + c - y_p) (2ax_c + b) \\
\ frac d (z ^ 2) dx = 2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p- x_p) \\
Equare \ gradient \ to \ 0 \\
\ Frac d (z ^ 2) dx = 0 \\
2a ^ 2 (x_c) ^ 3 + 3ab (x_c) ^ 2 + (b ^ 2 + 2ac-2ay_p + 1) (x_c) + (bc-by_p-x_p) = 0 \\
Confronta \ con \ cubico \ equazione \\
Ax ^ 3 + Bx ^ 2 + Cx + D = 0 \\
A = 2a ^ 2 \\
B = \\ 3AB
C = b ^ 2 + 2ac-2ay_p + 1 \\
D = bc-by_p-x_p
\]
Risolvi le radici di questa (piuttosto disordinata) funzione cubica e arriveremo alle coordinate dei tre punti blu come indicato sopra.
Quindi, come possiamo filtrare i nostri risultati per il punto minimo? Ricorda dal passaggio precedente che un punto minimo ha un tasso di cambiamento positivo. Per ottenere questa velocità di cambiamento, differenziare la funzione cubica che rappresenta il gradiente. Se il tasso di cambiamento per il dato punto blu è positivo, lo è uno di i punti minimi. Ottenere il punto minimo, quello a cui siamo interessati, scegliere il punto con il più alto tasso di variazione.
Quindi ecco una implementazione esemplificativa dell'idea spiegata sopra. Puoi trascinare i punti rossi intorno per personalizzare la tua curva quadratica. Il punto blu può anche essere trascinato. Mentre sposti il punto blu, quello giallo verrà riposizionato in modo che la distanza tra i punti blu e giallo sia minima tra tutti i punti della curva.
Mentre interagisci con la presentazione Flash, potrebbero esserci delle volte in cui appaiono tre punti gialli tutti in una volta. Due di questi, sbiaditi, si riferiscono alle radici ottenute dal calcolo ma rifiutate perché non sono i punti più vicini sulla curva al punto blu.
Quindi ecco l'implementazione di ActionScript di cui sopra. Puoi trovare lo script completo in Demo2.as
.
Prima di tutto, dovremo disegnare la curva quadratica. Si noti che la matrice m2
sarà indicato per ulteriori calcoli.
funzione privata redraw_quadratic_curve (): void var cmd: Vector.= nuovo vettore. ; Var Coord: Vector. = nuovo vettore. ; // ridisegna la curva; m1 = new Matrix3d (curve_points [0] .x * curve_points [0] .x, curve_points [0] .x, 1, 0, curve_points [1] .x * curve_points [1] .x, curve_points [1] .x , 1, 0, curve_points [2] .x * curve_points [2] .x, curve_points [2] .x, 1, 0, 0,0,0,1); m2 = new Matrix3d (curve_points [0] .y, 0, 0, 0, curve_points [1] .y, 0, 0, 0, curve_points [2] .y, 0, 0, 0, 0,0,0, 1) m1.invert (); m2.append (m1); quadratic_equation.define (m2.n11, m2.n21, m2.n31); per (var i: int = 0; i < stage.stageWidth; i+=2) if (i == 0) cmd.push(1); else cmd.push(2); coord.push(i, quadratic_equation.fx_of(i)); graphics.clear(); graphics.lineStyle(1); graphics.drawPath(cmd, coord);
Ed ecco quello che implementa il concetto matematico spiegato. c1
si riferisce a un punto posizionato casualmente sul palco.
funzione privata recalculate_distance (): void var a: Number = m2.n11; var b: Number = m2.n21; var c: Number = m2.n31; / * f (x) = Axe ^ 3 + Bx ^ 2 + Cx + D * / var A: Number = 2 * a * a var B: Number = 3 * b * a var C: Number = b * b + 2 * c * a - 2 * a * c1.y +1 var D: Number = c * b - b * c1.y - c1.x quartic_gradient = new EqCubic (); quartic_gradient.define (A, B, C, D); quartic_gradient.calcRoots (); roots = quartic_gradient.roots_R; var scelto: Number = roots [0]; if (! isNaN (roots [1]) &&! isNaN (roots [2])) // calcola il gradiente e la frequenza del gradiente di tutte le radici reali var quartic_rate: Vector.= nuovo vettore. ; per (var i: int = 0; i < roots.length; i++) if (!isNaN(roots[i])) quartic_rate.push(quartic_gradient.diff1(roots[i])); else roots.splice(i, 1); //select the root that will produce the shortest distance for (var j:int = 1; j < roots.length; j++) //the rate that corresponds with the root must be the highest positive value //because that will correspond with the minimum point if (quartic_rate[j] > quartic_rate [j - 1]) chosen = roots [j]; // posiziona le radici extra nella demo position_extras (); else // rimuovi le radici extra nella demo kill_extras (); intersec_points [0] .x = select intersec_points [0] .y = quadratic_equation.fx_of (scelto);
Facciamo uso di questo concetto per rilevare la sovrapposizione tra un cerchio e una curva.
L'idea è semplice: se la distanza tra il punto blu e il punto giallo è inferiore al raggio del punto blu, abbiamo una collisione. Guarda la demo qui sotto. Gli elementi interattivi sono i punti rossi (per controllare la curva) e il punto blu. Se il punto blu è in collisione con la curva, si attenuerà un po '.
Bene, il codice è abbastanza semplice. Guarda l'origine completa in CollisionDetection.as
.
graphics.moveTo (intersec_points [0] .x, intersec_points [0] .y); graphics.lineTo (c1.x, c1.y); var distance: Number = Math2.Pythagoras (intersec_points [0] .x, intersec_points [0] .y, c1.x, c1.y) if (distanza < c1.radius) c1.alpha = 0.5; else c1.alpha = 1.0; t.text = distance.toPrecision(3);
Quindi, ora che sappiamo quando si verificherà la collisione, proviamo a programmare una risposta di collisione. Che ne dici di rimbalzare sulla superficie? Guarda la presentazione Flash qui sotto.
Puoi vedere la nave (a forma triangolare), circondata da un cerchio (blu traslucido). Una volta che il cerchio si scontra con la curva, la nave rimbalzerà sulla superficie.
Ecco il codice ActionScript per controllare la nave.
funzione pubblica CollisionDetection2 () / ** * Istanziazione della nave e la sua area circolare blu-ish * / ship = new Triangle (); addChild (nave); ship.x = Math.random () * stage.stageWidth; ship.y = stage.stageHeight * 0.8; c1 = new Circle (0x0000ff, 15); addChild (c1); c1.alpha = 0,2; / ** * Velocità della nave * / velo = new Vector2D (0, -1); updateShip (); stage.addEventListener (KeyboardEvent.KEY_DOWN, handleKey); stage.addEventListener (KeyboardEvent.KEY_UP, handleKey); stage.addEventListener (Event.EXIT_FRAME, handleEnterFrame); / ** * La curva e i calcoli * / quadratic_equation = new EqQuadratic (); curve_points = nuovo vettore.; popola (curve_points, 0xff0000, 3); intersec_points = new Vector. ; popolare (intersec_points, 0xffff00, 3, false); redraw_quadratic_curve (); private function handleKey (e: KeyboardEvent): void if (e.type == "keyDown") if (e.keyCode == Keyboard.UP) isUp = true; else if (e.keyCode == Keyboard.DOWN) isDown = true; if (e.keyCode == Keyboard.LEFT) isLeft = true; else if (e.keyCode == Keyboard.RIGHT) isRight = true; if (e.type == "keyUp") if (e.keyCode == Keyboard.UP) isUp = false; else if (e.keyCode == Keyboard.DOWN) isDown = false; if (e.keyCode == Keyboard.LEFT) isLeft = false; else if (e.keyCode == Keyboard.RIGHT) isRight = false; private function handleEnterFrame (e: Event): void / ** * Controlla la grandezza * / if (isUp) velo.setMagnitude (Math.min (velo.getMagnitude () + 0.2, 3)); else if (isDown) velo.setMagnitude (Math.max (velo.getMagnitude () - 0.2, 1)); / ** * Controlla la direzione * / if (isRight) velo.setAngle (velo.getAngle () + 0.03); else if (isLeft) velo.setAngle (velo.getAngle () - 0.03); recalculate_distance (); se (distanza < c1.radius) bounce(); updateShip(); /** * Update ship's position, orientation and it's area (the blue-ish circle) */ private function updateShip():void ship.x += velo.x; ship.y += velo.y; ship.rotation = Math2.degreeOf(velo.getAngle()); c1.x = ship.x; c1.y = ship.y; if (ship.x > stage.stageWidth || ship.x < 0) velo.x *= -1; if (ship.y > stage.stageHeight || ship.y < 0) velo.y *= -1;
Puoi vedere che i controlli della tastiera stanno aggiornando i flag per indicare se i tasti sinistra, su, destra o giù vengono premuti. Questi flag verranno catturati dal gestore eventi enterframe e aggiorneranno l'entità e la direzione della nave.
Ho già coperto il calcolo vettoriale del vettore di riflessione in questo post. Qui, mi limiterò a capire come ottenere il vettore normale dal gradiente.
\ [
\ Frac df (x) dx = gradiente \\
line \ gradient = \ frac y x \\
Presuma \ gradient = 0.5 \\
y = 0,5 \\
x = 1 \\
Vettore \ di \ left \ normal =
\ begin bmatrix -1 \\ 0.5 \ end bmatrix \\
Vector \ of \ right \ normal =
\ begin bmatrix 1 \\ - 0.5 \ end bmatrix
\]
Pertanto, l'ActionScript seguente implementerà il concetto matematico spiegato nel passaggio precedente. Controlla le linee evidenziate:
private function bounce (): void var gradient: Number = quadratic_equation.diff1 (intersec_points [0] .x); var grad_vec: Vector2D = new Vector2D (1, gradiente); var left_norm: Vector2D = grad_vec.getNormal (false); var right_norm: Vector2D = grad_vec.getNormal (); var chosen_vec: Vector2D; if (velo.dotProduct (left_norm)> 0) chosen_vec = left_norm else chosen_vec = right_norm var scelta_unit: Vector2D = chosen_vec.normalise (); var proj: Number = velo.dotProduct (scelta_unità); chosen_unit.scale (-2 * proj); velo = velo.add (scelta_unità);
Bene, grazie per il tuo tempo! Se hai trovato questo utile, o hai delle domande, lascia dei commenti.