2008-12-04 8 views
11

Ho scritto una semplice applicazione java che ti permette di trascinare il mouse e in base alla lunghezza del trascinamento del mouse che hai fatto, sparerà una palla in quella direzione, rimbalzando contro i muri.Come applico la gravità alla mia applicazione di palla rimbalzante?

ecco un breve screenshot:
alt text http://img222.imageshack.us/img222/3179/ballbouncemf9.png

Ognuno dei cerchi sullo schermo è un oggetto Ball. Il movimento delle palle è suddiviso in un vettore xey;

public class Ball { 
    public int xPos; 
    public int yPos; 
    public int xVector; 
    public int yVector; 

    public Ball(int xPos, int yPos, int xVector, int yVector) { 
     this.xPos = xPos; 
     this.yPos = yPos; 
     this.xVector = xVector; 
     this.yVector = yVector; 
    } 

    public void step() 
    { 
     posX += xVector; 
     posY += yVector; 

     checkCollisions(); 
    } 

    public void checkCollisions() 
    { 
     // Check if we have collided with a wall 
     // If we have, take the negative of the appropriate vector 
     // Depending on which wall you hit 
    } 

    public void draw() 
    { 
     // draw our circle at it's position 
    } 
} 

Questo funziona benissimo. Tutte le palle rimbalzano intorno e intorno da una parete all'altra.

Tuttavia, ho deciso che voglio essere in grado di includere gli effetti della gravità. So che gli oggetti accelera verso terra a 9,8 m/s, ma non so direttamente come questo dovrebbe tradursi in codice. Mi rendo conto che yVector sarà interessato, ma la mia sperimentazione non ha avuto l'effetto desiderato che volevo.

Idealmente, Vorrei poter aggiungere un po 'di effetto di gravità a questo programma e anche consentire alle sfere di rimbalzare un paio di volte prima di stabilirsi nel "terreno".

Come è possibile creare questo effetto elastico di gravità rimbalzante? Come devo manipolare i vettori di velocità della palla su ogni passo? Cosa deve essere fatto quando colpisce il "terreno" in modo che possa permettergli di rimbalzare di nuovo, ma in qualche modo più breve rispetto alla volta precedente?

Qualsiasi aiuto è apprezzato nel indicarmi la giusta direzione.


Grazie per i commenti a tutti! Sta già funzionando alla grande!

nel mio passo() sto aggiungendo un peso costante per il mio yVector come le persone suggerite e questo è il mio checkCollision():

public void checkCollision() 
{ 
    if (posX - radius < 0)    // Left Wall? 
    { 
     posX = radius;    // Place ball against edge 
     xVector = -(xVector * friction); 
    } 
    else if (posX + radius > rightBound) // Right Wall? 
    { 
     posX = rightBound - radius;  // Place ball against edge 
     xVector = -(xVector * friction); 
    } 

    // Same for posY and yVector here. 
} 

Tuttavia, le palle continueranno a scivolare/rotolo sul pavimento . Presumo questo perché sto semplicemente prendendo una percentuale (90%) dei loro vettori ogni rimbalzo e non è mai veramente zero. Dovrei aggiungere un controllo che se xVector diventa un certo valore assoluto, dovrei semplicemente cambiarlo a zero?

+1

Cosa succede se si capovolge il monitor? – BenAlabaster

+4

Commento molto valido in questi giorni con l'iPhone e tutto ... –

+0

Potresti postare il tuo codice di fine come FYI per favore? – JamesSugrue

risposta

15

Quello che devi fare è sottrarre costantemente una piccola costante (qualcosa che rappresenta i tuoi 9,8 m/s) da yVector. Quando la palla sta scendendo (yVector è già negativo), questo lo farebbe andare più veloce. Quando sale (yVector è positivo) lo rallenterebbe.

Questo non spiegherebbe l'attrito, quindi le cose dovrebbero rimbalzare praticamente per sempre.

edit1: Per tenere conto dell'attrito, ogni volta che si inverte (e si inverte il segno), abbassare leggermente il numero assoluto. Ad esempio se colpisce yVector = -500, quando inverti il ​​segno, fallo +480 invece di +500. Probabilmente dovresti fare la stessa cosa con xVector per impedirgli di saltare da una parte all'altra.

edit2: Inoltre, se si desidera che reagisca a "attrito aria", ridurre entrambi i vettori di una quantità molto piccola ad ogni regolazione.

edit3: Informazioni su cosa rotola sul fondo per sempre - A seconda di quanto alti sono i tuoi numeri, potrebbe essere una delle due cose. O i tuoi numeri sono grandi e sembra che ci vorrà un'eternità per finire, o stai arrotondando e i tuoi Vettori sono sempre 5 o qualcosa del genere. (Il 90% di 5 è 4,5, quindi può arrotondare fino a 5).

Vorrei stampare una dichiarazione di debug e vedere come sono i numeri di vettore. Se vanno da qualche parte intorno al 5 e rimangono lì, allora puoi usare una funzione che tronca la tua frazione a 4 invece di tornare indietro a 5. Se continua a scendere e alla fine si ferma, allora potresti dover aumentare il coefficiente di attrito .

Se non è possibile trovare una funzione di "arrotondamento" facile, è possibile utilizzare (0.9 * Vector) - 1, sottraendo 1 dall'equazione esistente dovrebbe fare la stessa cosa.

+0

@Bill K, potresti dare un'occhiata alla mia modifica e commentarla? – mmcdole

+0

@ Bill K, grazie Bill! – mmcdole

1

Ogni volta che si deve applicare gli effetti della gravità accelerando la palla nella direzione verso il basso. Come suggerito da Bill K, è semplice come fare una sottrazione dal tuo "yVector". Quando la palla colpisce il fondo, yVector = -yVector, quindi ora si muove verso l'alto ma continua ad acceleararsi verso il basso. Se vuoi che le palle smettano di rimbalzare, devi rendere le collisioni leggermente non elastiche, in pratica rimuovendo un po 'di velocità nella direzione di y-up, possibilmente invece di "yVector = -yVector", fallo "yVector = -0.9 * yVector ".

+0

Hai bisogno di perdere velocità in entrambe le dimensioni con ogni rimbalzo, non solo verticalmente con il pavimento. – erickson

+0

@erickson, volevo solo chiedere, grazie! – mmcdole

1
public void step() 
{ 
    posX += xVector; 
    posY += yVector; 

    yVector += g //some constant representing 9.8 

    checkCollisions(); 
} 

in checkCollisions(), si dovrebbe invertire e moltiplicare yVector da un numero compreso tra 0 e 1 quando rimbalza a terra. Questo dovrebbe darti l'effetto desiderato

0

Vuoi davvero simulare cosa fa la gravità - tutto ciò che fa è creare una forza che agisca nel tempo per cambiare la velocità di un oggetto. Ogni volta che fai un passo, cambi leggermente la velocità della tua palla per "tirarla" verso il fondo del widget.

Per affrontare il problema dei blocchi di palla senza attrito/rimbalzo, è necessario fare in modo che la collisione "terra" eserciti un effetto diverso dalla semplice riflessione - dovrebbe rimuovere una certa quantità di energia dalla palla, rendendola rimbalzare ad una velocità inferiore dopo che colpisce il suolo di quanto non farebbe altrimenti.

Un'altra cosa che in genere si vuole fare in questi tipi di visualizzazioni rimbalzanti è dare un po 'di frizione laterale, in modo che, quando sta colpendo il terreno tutto il tempo, alla fine si fermerà.

+0

Che cosa intendi se concedi al terreno un certo attrito laterale? – mmcdole

0

Sono d'accordo con cosa "Bill K" ha detto, e aggiungerei che se vuoi che si "stabilizzino" dovrai ridurre i vettori xey nel tempo (applicare resistenza). Dovrà essere una piccola quantità alla volta, quindi potrebbe essere necessario cambiare i vettori da int a un tipo a virgola mobile o ridurli solo di 1 ogni pochi secondi.

0

Quello che vuoi fare è cambiare i valori di xVector e yVector per simulare la gravità e l'attrito. Questo è davvero molto semplice da fare. (Necessità di modificare tutte le variabili per carri Quando arriva il momento di disegnare, proprio dietro i carri..)

Nella funzione di passaggio, dopo aver aggiornato la posizione della palla, si dovrebbe fare qualcosa di simile:

yVector *= 0.95; 
xVector *= 0.95; 
yVector -= 2.0; 

Riduce leggermente la velocità X e Y, consentendo alle sfere di fermarsi, e quindi applica una "accelerazione" costante verso il basso al valore Y, che si accumulerà più velocemente del "rallentamento" e provocherà la caduta delle sfere .

Questa è un'approssimazione di ciò che si vuole veramente fare. Quello che vuoi veramente è mantenere un vettore che rappresenti l'accelerazione delle tue palle. Ogni passo dovresti puntare sul prodotto quel vettore con un vettore di gravità costante per modificare leggermente l'accelerazione della palla. Ma penso che sia più complesso di quanto tu voglia ottenere, a meno che tu non stia cercando una simulazione fisica più realistica.

+0

Moltiplicando yVector nella funzione step, non si rappresenta un'accelerazione costante come la gravità. –

+0

Rallentando la palla in ogni passaggio, farai apparire le palline come se si muovessero attraverso qualcosa di estremamente viscoso. Un effetto più realistico sarebbe quello di perdere energia solo quando rimbalzano. – erickson

0

Cosa si deve fare quando si colpisce il "terreno" in modo che possa permettergli di rimbalzo di nuovo

Se si assume una collisione perfetta (cioè tutta l'energia è conservata) tutto devi invertire il segno di uno degli scalari di velocità a seconda di quale muro è stato colpito.

Ad esempio, se la palla colpisce la parete destra o sinistra Revese componente x scalare e lasciare la componente y scalare la stessa:

this.xVector = -this.xVector; 

Se la palla colpisce le pareti superiore o inferiore invertire la y scalare componenti e lasciare la componente x scalare la stessa:

this.yVector = -this.yVector; 

ma un po 'più breve poi il volta precedente?

In questo scenario un po 'dell'energia sarà perso nella collisione con la parete quindi basta aggiungere in un fattore di perdita di prendere di alcuni della velocità ogni volta che il muro è colpito:

double loss_factor = 0.99; 
this.xVector = -(loss_factor * this.xVector); 
this.yVector = -(loss_factor * this.yVector; 
13

Quando tutte le sfere rotolano sul terreno, sì, controlla se la velocità è inferiore ad un determinato valore minimo e, in tal caso, impostalo su zero. Se osservate la fisica dietro questo tipo di movimento idealizzato e confrontate con ciò che accade nel mondo reale, vedrete che una singola equazione non può essere utilizzata per spiegare il fatto che una palla vera smette di muoversi.

BTW, quello che stai facendo è chiamato il metodo Eulero per l'integrazione numerica. Va in questo modo:

  • Iniziare con le equazioni cinematiche del moto:
    x (t) = x0 + vx * t + 0.5 * ax t^2
    y (t) = y0 + VY
    t + 0.5 * ay t^2
    vx (t) = Vx0 + ax
    t
    Vy (t) = VY0 + ay * t
    dove x e Y sono posizione, vx e vy sono velocità, ax e sono accelerazione, e t è tempo. x0, y0, vx0 e vy0 sono i valori iniziali. Descrive il movimento di un oggetto in assenza di forze esterne.

  • Ora applicare gravità:
    ay = -9.8 m/s^2
    A questo punto, non c'è bisogno di fare nulla complicato. Possiamo risolvere la posizione di ogni palla usando questa equazione in qualsiasi momento.

  • Ora aggiungere l'attrito dell'aria: poiché si tratta di una sfera sferica, possiamo supporre che abbia un coefficiente di attrito c. Ci sono in genere due scelte su come modellare l'attrito dell'aria.Può essere proporzionale alla velocità o al quadrato della velocità. Usiamo la piazza:
    ax = -c VX^2
    ay = -c
    Vy^2-9,8
    Poiché l'accelerazione è ora dipende dalla velocità, che non è costante, dobbiamo integrare. Questo è male, perché non c'è modo di risolverlo a mano. Dovremo integrare numericamente.

  • Prendiamo passaggi di tempo discreti, dt. Per il metodo di Eulero, sostituiamo semplicemente tutte le occorrenze di t nelle equazioni precedenti con dt, e usiamo il valore dal timestep precedente al posto dei valori iniziali, x0, y0, ecc. Così ora le nostre equazioni assomigliano a questo (in pseudocodice) :

    // Salva valori precedenti
    xold = x;
    yold = y;
    vxold = vx;
    vyold = vy;

    // Accelerazione di aggiornamento
    ax = -c vxold^2;
    ay = -c
    vyold^2 - 9,8;

    // Velocità di aggiornamento
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // Posizione di aggiornamento
    x = xold + vxold * dt + 0,5 * ax dt^2;
    y = yold + vyold
    dt + 0,5 * ay * dt^2;

Questa è un'approssimazione, quindi non sarà esattamente corretta, ma sembrerà OK. Il problema è che per i timestep più grandi, l'errore aumenta, quindi se vogliamo modellare accuratamente come si muoverà una palla vera, dovremmo usare valori molto piccoli per dt, il che causerebbe problemi di precisione su un computer. Per risolvere ciò, ci sono tecniche più complicate. Ma se vuoi solo vedere un comportamento che assomiglia alla gravità e all'attrito allo stesso tempo, allora il metodo di Eulero è ok.

1

È un movimento balistico. Quindi hai un movimento lineare sull'asse xe un movimento accelerato uniforme sull'asse y.

L'idea di base è che l'asse y seguirà l'equazione:

y = y0 + v0 * t + (0.5)*a*t^2 

Oppure, nel codice C, per esempio:

float speed = 10.0f, acceleration = -9.8f, y = [whatever position]; 

y += speed*t + 0.5f*acceleration*t^2; 

Dove Qui uso tiem parametrizzazione. Ma è possibile utilizzare Torricelli:

v = sqrt(v0^2 + 2*acceleration*(y-y0)); 

E, su questo modello, è necessario mantenere gli ultimi valori di v e y.

Infine, ho fatto qualcosa di simile utilizzando il primo modello con dt (differenziale del tempo) fissato a 1/60 di secondo (60 FPS).

Bene, entrambi i modelli danno buoni risultati reali, ma sqrt(), ad esempio, è costoso.

Problemi correlati