Domanda

Ho scritto un'applicazione java abbastanza semplice che ti consente di trascinare il mouse e in base alla lunghezza del trascinamento del mouse che hai fatto, lancerà una palla in quella direzione, facendo rimbalzare i muri mentre procede.

Ecco uno screenshot rapido:
alt text http://img222.imageshack.us/img222/3179/ballbouncemf9.png

Ciascuno 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
    }
}

Funziona benissimo. Tutte le palle rimbalzano da una parete all'altra.

Tuttavia, ho deciso che voglio essere in grado di includere gli effetti della gravità. So che gli oggetti accelerano verso la 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 con questo non ha avuto l'effetto desiderato che volevo.

Idealmente, Vorrei essere in grado di aggiungere un po 'di effetto gravità a questo programma e consentire anche alle palle di rimbalzare alcune volte prima di sistemarsi sul terreno. "

Come posso creare questo effetto di gravità elastico che rimbalza? Come devo manipolare i vettori di velocità della palla su ogni gradino? Che cosa si deve fare quando colpisce il "terreno" così posso permettergli di rimbalzare di nuovo, ma un po 'più breve rispetto alla volta precedente?

Qualsiasi aiuto è apprezzato nel indicarmi la giusta direzione.


Grazie per i commenti a tutti! Funziona già alla grande!

Nel mio passaggio () sto aggiungendo una costante di gravità al mio yVector come suggerito dalla gente 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 / rotolare sul pavimento. Suppongo che questo sia perché sto semplicemente prendendo una percentuale (90%) dei loro vettori ogni rimbalzo e non è mai veramente zero. Dovrei aggiungere un segno di spunta che se xVector diventa un certo valore assoluto dovrei semplicemente cambiarlo in zero?

È stato utile?

Soluzione

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

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. Come se colpisce yVector = -500, quando inverti il ??segno, rendilo +480 invece di +500. Probabilmente dovresti fare la stessa cosa con xVector per impedire che rimbalzi da un lato all'altro.

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

Edit3: A proposito della cosa che rotola sul fondo per sempre - A seconda di quanto sono alti i tuoi numeri, potrebbe essere una delle due cose. O i tuoi numeri sono grandi e sembra che ci voglia un'eternità per finire, oppure 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).

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

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

Altri suggerimenti

Quando le palline rotolano tutte sul terreno, sì, controlla se la velocità è al di sotto di un certo valore minimo e, in tal caso, impostala su zero. Se osservi la fisica dietro questo tipo di movimento idealizzato e ti confronti con ciò che accade nel mondo reale, vedrai che una singola equazione non può essere utilizzata per spiegare il fatto che una palla reale smette di muoversi.

A proposito, quello che stai facendo è chiamato il metodo Euler per l'integrazione numerica. Va così:

  • Inizia con le equazioni cinematiche del movimento:
    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 xey sono posizione, vx e vy sono velocità, ax e ay sono accelerazione e t è tempo. x0, y0, vx0 e vy0 sono i valori iniziali. Questo descrive il movimento di un oggetto in assenza di qualsiasi forza esterna.

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

  • Ora aggiungi l'attrito dell'aria: poiché è una sfera sferica, possiamo supporre che abbia un coefficiente di attrito c. In genere ci sono due scelte su come modellare l'attrito dell'aria. Può essere proporzionale alla velocità o al quadrato della velocità. Usiamo il quadrato:
    ax = -c vx ^ 2
    ay = -c
    vy ^ 2 - 9.8
    Poiché l'accelerazione ora dipende dalla velocità, che non è costante, dobbiamo integrarci. Questo è male, perché non c'è modo di risolverlo a mano. Dovremo integrarci numericamente.

  • Facciamo passi temporali discreti, dt. Per il metodo di Eulero, sostituiamo semplicemente tutte le occorrenze di t nelle equazioni precedenti con dt e usiamo il valore del precedente timestep al posto dei valori iniziali, x0, y0, ecc. Quindi ora le nostre equazioni sembrano così (in pseudocodice) :

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

    // Aggiorna accelerazione
    ax = -c vxold ^ 2;
    ay = -c
    vyold ^ 2 - 9.8;

    // Aggiorna velocità
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // Aggiorna posizione
    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 timestep più grandi, l'errore aumenta, quindi se vogliamo modellare accuratamente come si muoverebbe una palla reale, dovremmo usare valori molto piccoli per dt, che causerebbero problemi di precisione su un computer. Per risolverlo, ci sono tecniche più complicate. Ma se vuoi solo vedere un comportamento che assomiglia a gravità e attrito allo stesso tempo, allora il metodo di Eulero è ok.

Ogni volta che devi applicare gli effetti della gravità accelerando la palla nella direzione verso il basso. Come ha suggerito Bill K, è semplice come fare una sottrazione dal tuo "yVector". Quando la palla colpisce il fondo, yVector = -yVector, quindi ora si sta muovendo verso l'alto ma sta ancora accelerando verso il basso. Se vuoi che le palline smettano di rimbalzare, devi rendere le collisioni leggermente anelastiche, fondamentalmente rimuovendo una certa velocità nella direzione y-up, possibilmente invece di " yVector = -yVector " ;, fallo " yVector = -0,9 * yVector " ;.

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

    yVector += g //some constant representing 9.8

    checkCollisions();
}

in checkCollisions (), dovresti invertire e moltiplicare yVector per un numero compreso tra 0 e 1 quando rimbalza sul terreno. Questo dovrebbe darti l'effetto desiderato

È un movimento balistico. Quindi hai un movimento lineare sull'asse x e 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

O, nel codice C, ad esempio:

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

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

Dove uso la parametrizzazione di tiem. Ma potresti usare 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 usando il primo modello con dt (differenziale temporale) fissato a 1/60 di secondo (60 FPS).

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

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

Al fine di affrontare il problema dei depositi di palla senza attrito / rimbalzo, è necessario effettuare la "messa a terra" la collisione esercita un effetto diverso rispetto a un semplice riflesso: dovrebbe rimuovere una certa quantità di energia dalla palla, facendola rimbalzare a una velocità inferiore dopo che colpisce il terreno di quanto non farebbe altrimenti.

Un'altra cosa che generalmente vuoi fare in questi tipi di visualizzazioni rimbalzanti è dare al terreno anche un po 'di attrito laterale, in modo che quando colpisce sempre il terreno, alla fine si fermerà.

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

Quello che vuoi fare è cambiare i valori di xVector e yVector per simulare gravità e attrito. Questo è davvero abbastanza semplice da fare. (È necessario modificare tutte le variabili in float. Quando arriva il momento di disegnare, basta arrotondare i float.)

Nella tua funzione di passaggio, dopo aver aggiornato la posizione della palla, dovresti fare qualcosa del genere:

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

Questo ridimensiona leggermente la velocità X e Y, permettendo alle tue sfere di smettere di muoversi, e quindi applica una costante "accelerazione" verso il basso; al valore Y, che si accumulerà più velocemente del "rallentamento" e far cadere le palle.

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

  

Cosa si deve fare quando colpisce il   & Quot; terra " così che posso permetterlo   rimbalzare di nuovo

Se si assume una collisione perfetta (cioè tutta l'energia viene conservata) tutto ciò che si deve fare per invertire il segno di uno degli scalari di velocità a seconda del muro colpito.

Ad esempio, se la palla colpisce le pareti destra o sinistra, ravvivare il componente scalare x e lasciare lo stesso il componente scalare y:

 this.xVector = -this.xVector;

Se la palla colpisce la parete superiore o inferiore inverte il componente scalare y e lascia lo stesso il componente scalare x:

 this.yVector = -this.yVector;
  

ma un po 'più corto del precedente   tempo?

In questo scenario parte dell'energia andrà persa nella collisione con il muro, quindi basta aggiungere un fattore di perdita per prendere parte della velocità ogni volta che il muro viene colpito:

 double loss_factor = 0.99;
 this.xVector = -(loss_factor * this.xVector);
 this.yVector = -(loss_factor * this.yVector;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top