Simulazione della "linea di vista" con ostacoli sulla griglia 2d?


10

Si è imbattuto in un problema interessante. Devo capire come simulare la linea di vista - abbastanza semplice, solo su una griglia 2D con ostacoli. Una cella della griglia è visibile o non lo è.

Posso ottenere qualcosa di veramente rudimentale, come spargere n spazi dal giocatore o bloccare la propagazione orizzontale quando viene rilevato un ostacolo adiacente, ma non riesco a lasciarmi convivere con esso. Molte altre app stanno usando metodi più sofisticati che inclinano la linea di vista dietro gli angoli ecc. E voglio essere all'altezza.

Finora DCSS è stata la mia fonte di ispirazione quando sono sconcertato, spero di avvicinarmi a ciò che hanno: http://crawl.sz.org/ .

Qualsiasi approfondimento sarebbe apprezzato - grazie per l'aiuto!

(Perdona se questo è imbarazzantemente noobish - ha iniziato lo sviluppo del gioco solo poche settimane fa, cercando di recuperare.)


2
Quando dici "inclina la linea di vista dietro gli angoli", cosa intendi esattamente?
Djentleman,

Il meglio che posso dire è dare un'occhiata a un gioco su crawl.sz.org. Ad esempio, quando il giocatore è in piedi sotto una parete orizzontale larga 5 tessere, la linea di vista si interrompe sopra il piano orizzontale di quella parete, ma non irrealisticamente al di là di essa. Il meglio che ho potuto approssimare è mantenere la linea di vista sul piano orizzontale del muro.
CodeMoose

Risposte:


7

Ray casting è un modo molto veloce ed efficiente per determinare la linea di vista. Implica sostanzialmente l'invio di un raggio (pensalo come un laser infinito che non può essere reindirizzato) da una determinata posizione in una determinata direzione. Usando questo raggio, puoi determinare cose come quali punti si intersecano e quanto lontano era l'origine quando attraversava un certo punto.

Ad esempio, in uno scenario giocatore / nemico, il raggio potrebbe provenire dal nemico con la direzione in cui si trova la posizione del giocatore. Se il raggio si scontra con una tessera solida, il nemico non può vedere il giocatore. In caso contrario, il nemico può vedere il giocatore.

Ecco un eccellente tutorial che dovrebbe aiutare.

Puoi anche considerare l'algoritmo di linea di Bresenham (riassunto, crea linee) per qualcosa che potrebbe essere più facilmente ridimensionato in tessere.


1
Sembra la strada da percorrere, soprattutto quella di Bresenham. Grazie per l'aiuto djent!
CodeMoose

3

Ho un codice blog per calcolare la linea di vista da una mappa di altezza. Una semplice mappa piatta con ostacoli è solo una mappa di altezza molto piatta e questa implementazione è ancora completamente applicabile.

inserisci qui la descrizione dell'immagine

Qui è in C ++ e suoi O(n); se conosci l'altezza massima nella mappa, puoi tracciare una linea di scansione che non ha raggi rimanenti al di sotto di quell'altezza e iniziare:

typedef std::vector<float> visbuf_t;

inline void map::_visibility_scan(const visbuf_t& in,visbuf_t& out,const vec_t& eye,int start_x,int stop_x,int y,int prev_y) {
    const int xdir = (start_x < stop_x)? 1: -1;
    for(int x=start_x; x!=stop_x; x+=xdir) {
        const int x_diff = abs(eye.x-x), y_diff = abs(eye.z-y);
        const bool horiz = (x_diff >= y_diff);
        const int x_step = horiz? 1: x_diff/y_diff;
        const int in_x = x-x_step*xdir; // where in the in buffer would we get the inner value?
        const float outer_d = vec2_t(x,y).distance(vec2_t(eye.x,eye.z));
        const float inner_d = vec2_t(in_x,horiz? y: prev_y).distance(vec2_t(eye.x,eye.z));
        const float inner = (horiz? out: in).at(in_x)*(outer_d/inner_d); // get the inner value, scaling by distance
        const float outer = height_at(x,y)-eye.y; // height we are at right now in the map, eye-relative
        if(inner <= outer) {
            out.at(x) = outer;
            vis.at(y*width+x) = VISIBLE;
        } else {
            out.at(x) = inner;
            vis.at(y*width+x) = NOT_VISIBLE;
        }
    }
}

void map::visibility_add(const vec_t& eye) {
    const float BASE = -10000; // represents a downward vector that would always be visible
    visbuf_t scan_0, scan_out, scan_in;
    scan_0.resize(width);
    vis[eye.z*width+eye.x-1] = vis[eye.z*width+eye.x] = vis[eye.z*width+eye.x+1] = VISIBLE;
    scan_0.at(eye.x) = BASE;
    scan_0.at(eye.x-1) = BASE;
    scan_0.at(eye.x+1) = BASE;
    _visibility_scan(scan_0,scan_0,eye,eye.x+2,width,eye.z,eye.z);
    _visibility_scan(scan_0,scan_0,eye,eye.x-2,-1,eye.z,eye.z);
    scan_out = scan_0;
    for(int y=eye.z+1; y<height; y++) {
        scan_in = scan_out;
        _visibility_scan(scan_in,scan_out,eye,eye.x,-1,y,y-1);
        _visibility_scan(scan_in,scan_out,eye,eye.x,width,y,y-1);
    }
    scan_out = scan_0;
    for(int y=eye.z-1; y>=0; y--) {
        scan_in = scan_out;
        _visibility_scan(scan_in,scan_out,eye,eye.x,-1,y,y+1);
        _visibility_scan(scan_in,scan_out,eye,eye.x,width,y,y+1);
    }
}

Questo va bene, ma penso molto più di quello che cerca se ciò che vuole è qualcosa di simile al link che ha pubblicato.
Djentleman,

Molto approfondito e impressionante, ma djent è proprio fuori dal mio campo di applicazione. Grazie per il post però!
CodeMoose

@CodeMoose eh il suo codice di lavoro; basta tagliare e incollare, tradurre letteralmente in qualsiasi lingua tu stia prendendo di mira. È un'implementazione di Bresenham, eseguita in modo incrementale, quindi visita ogni piazza una sola volta. Se per ogni quadrato della griglia fai una linea di Bresenham al giocatore, la troverai enormemente più lenta.
Sarà il

Buon punto
CodeMoose
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.