Come implementare un raggio traente?


8

Sto lavorando a un gioco in cui il giocatore può raccogliere oggetti usando qualcosa come un raggio traente e trasportarli.

Attrarre l'oggetto verso il centro del raggio non è difficile. Ma una volta che l'oggetto è abbastanza vicino al centro, devo tenerlo lì mentre il giocatore si muove, che è ciò con cui ho problemi. Posso pensare a due modi per farlo, ed entrambi hanno problemi:

  1. Aggiorna la posizione dell'oggetto ogni volta che cambia la posizione del giocatore, mantenendolo centrato sulla trave.

  2. Aggiorna la velocità dell'oggetto per puntare direttamente verso il centro del raggio, più è lontano, più è la velocità.

Lo spostamento e la rotazione funzionano bene con entrambi gli approcci, ma la fisica è sbagliata quando l'oggetto trasportato si scontra con altri oggetti:

Con il primo approccio, la fisica viene completamente ignorata. L'oggetto trasportato spinge via qualsiasi cosa. Questo perché i cambiamenti di posizione dovrebbero essere fatti solo come parte della fisica mondiale, basata sulla velocità.

Con il secondo approccio, la fisica si comporta sostanzialmente come dovrebbe, ma reagisce in modo eccessivo. Il problema è: per mantenere l'oggetto trasportato al centro del raggio anche quando si ruota e si sposta, è necessario utilizzare valori di velocità elevata. Quindi, una volta che l'oggetto trasportato tocca un altro, ottiene troppa velocità dalla collisione.

Come posso implementarlo correttamente? La mia ipotesi migliore in questo momento è quella di andare con il secondo approccio e aggiungere una gestione speciale per gli oggetti trasportati alla fisica mondiale, riducendo la velocità a valori sani per le collisioni o quando il giocatore smette di trasportarli. Ma sembra una soluzione piuttosto inelegante.

Modifica: aggiunta di un pseudo codice per illustrare come funziona ora (sarebbe il secondo approccio sopra)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

Da quello che vedo, questo accade quando l'oggetto trasportato spinge via un altro oggetto:

  1. L'oggetto trasportato si scontra con un altro oggetto
  2. Le velocità degli oggetti sono distribuite correttamente, quindi l'oggetto trasportato viene allontanato dal centro del raggio nel processo
  3. Il codice sopra fa tornare l'oggetto trasportato al centro del raggio, con così tanta velocità che tornerà rapidamente lì
  4. Quando l'oggetto trasportato si sposta indietro verso il centro del raggio, metà della sua alta velocità viene trasferita all'altro oggetto, respingendolo violentemente. Poiché la velocità iniziale dell'oggetto trasportato sembra sana, posso immaginare che i passaggi da 2 a 4 vengano ripetuti più volte, aumentando così la velocità.

Questa sembra essere la causa. Non riesco a pensare a un modo carino per risolverlo però :(


1
Benvenuti nella complessità di un reattore a fusione Tokomak. Hai il vantaggio di dover solo costruire un modello matematico funzionante, non la bottiglia magnetica funzionante, ma la matematica è identica e non banale. Ciò che stai tentando è fattibile, ma dovrai riflettere attentamente sul tuo modello matematico prima di scrivere codice.
Pieter Geerkens,

Risposte:


1

In sostanza quello che stai cercando è far sì che l'oggetto "irradiato" si comporti esattamente come se lo afferrassi con le mani.
Un'opzione sarebbe quella di farla condividere le velocità di accelerazione dell'a / o della "mano" che lo tiene invece di regolare la sua velocità per riempire il vuoto con il centro del raggio.

Diciamo che il centro del raggio è la mano che tiene. se il tuo personaggio ruota di 90 gradi alla sua sinistra in 1 secondo, la velocità della mano sarebbe:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
fai in modo che passi il tempo trascorso del fotogramma per trovare la velocità che devi applicare al tuo oggetto. Trova la normale orizzontale alla trave per trovare il suo vettore di direzione.

Il mio punto è che non devi risolvere i problemi se provi altre implementazioni che non lo causeranno in primo luogo.

Andrei a giocare con la pistola a gravità in HL2 per trarre ispirazione dal problema, ma ho altri piani per oggi.

EDIT: mi dispiace, ho pensato che fosse per un beamgun 3D, ma è essenzialmente la stessa cosa con il 2D tranne che gli assi sono diversi (e non esiste una geometria complessa)


Dopo aver provato vari hack, questo è ciò che mi ha portato in pista, il risultato è buono. La velocità in caso di collisione è ancora un po 'troppo alta, ma penso di poterlo capire. Forse semplicemente non attirando oggetti ad alta velocità in una direzione diversa.
futlib,

Forse quando la "mano" si scontra con un muro, è possibile calcolare la posizione plausibile più vicina (che non si scontra) per la mano e utilizzarla come "mano temporanea" durante la collisione. Mi piace spesso affrontare problemi da un'altra prospettiva. Sono felice di poterti aiutare;).
Icosamuel,

4

Che ne dite di aggiungere una connessione a molla, ovvero forzare l'oggetto trasportato per tornare alla posizione di trasporto in base alla distanza, ma consentire comunque che venga spinto via da oggetti solidi (come i muri).

Regola costantemente la velocità dell'oggetto trasportato (modificando l'accelerazione in base alla posizione / distanza) per puntare verso la posizione della trave del trattore (ovvero il tuo secondo avvicinamento). Se l'oggetto viene allontanato troppo, rilasciare la connessione (e l'oggetto).

Non sono davvero sicuro del motivo per cui avresti bisogno di alte velocità. Soprattutto il caso "giocatore lascia andare" indicherebbe che la velocità di rotazione potrebbe essere troppo alta o non realistica. Inoltre, non dimenticare cose come la resistenza dell'aria e la gravità.


Modifica: considerando il codice aggiornato, il problema è piuttosto banale da trovare:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

Il problema qui è il caso in cui la distanza dell'oggetto dalla sua posizione obiettivo è costantemente troppo elevata (cioè > 10). Fintanto che questa condizione è vera, la sua velocità aumenta semplicemente ripetutamente (cioè indefinitamente).

Due possibili soluzioni per questo:

Definire una velocità massima:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Applicare una velocità fissa anziché accelerare:

object.velocity = distance.normalized() * ticks * magic_factor;

Accelerare pur essendo troppo lontano è sicuramente un approccio sbagliato qui. Il fatto di tirare una molla o un elastico: non importa se lo tieni per un secondo o un minuto. Alla fine accelera allo stesso modo (considerando che non è stato in movimento e non ci sono altre forze applicate).


È quello che ho descritto nel metodo 2, non è vero? Questa è la fisica di base della primavera AFAIK. Aggiungerò del codice alla domanda sopra per illustrarlo.
futlib

Inoltre rimosso la parte sulla velocità errata sopra, in realtà va bene, appena testato. Quindi sono solo le collisioni con altri oggetti che sono incasinate.
futlib

Sì, è essenzialmente il tuo secondo approccio. Aggiornamento della mia risposta.
Mario,

Ho provato entrambi gli approcci, ma non aiuta. Sembra che il caso magnitudo (distanza)> 10 non sia il colpevole qui. Ho provato a limitare la velocità per il caso <=, ma è il solito problema: la velocità è così bassa da far cadere l'oggetto, o così alta da respingere violentemente gli altri.
futlib

"così alto da respingere violentemente gli altri": dovresti muovere altri oggetti in base alla velocità effettiva, non alla velocità dietro le quinte (ovvero resettare la velocità a causa della collisione).
Mario,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.