Generazione di unità in un mondo creato dal rumore di Perlin?


16

Ci sono alcuni problemi che ho riscontrato nel mio gioco basato sul rumore Perlin. Dai un'occhiata allo screenshot allegato qui sotto.

inserisci qui la descrizione dell'immagine

Le aree bianche che vedi sono pareti e le aree nere sono percorribili. Il triangolo nel mezzo è il giocatore.

Ho implementato la fisica in questo gioco disegnandolo su una trama (pixel bianchi o neri) e poi ottenendolo dalla CPU.

Tuttavia, ora ho un problema diverso a portata di mano. Voglio che le unità (o i brividi, qualunque cosa tu le chiami) si generino costantemente, ai margini dello schermo. Il punto qui è che nel gioco finale, ci sarà una "nebbia di guerra" che non consente al giocatore di vedere così lontano.

Ho pensato che potevo semplicemente scansionare i pixel sul bordo dello schermo e vedere se la loro trama fisica era nera, e quindi generare le cose in modo casuale lì. Tuttavia, se dai una seconda occhiata allo screenshot, c'è (nell'angolo in alto a sinistra) un esempio di dove non vorrei che i brividi si generassero (dal momento che non sarebbero in grado di raggiungere il giocatore da lì) .

È possibile in qualche modo fare in modo che la GPU determini questi punti di spawn per me o in qualche modo diverso? Ho pensato di creare dei vettori tra il punto proposto sul bordo dello schermo e il giocatore, e poi di seguirlo ogni 10 voxel, e vedere se un muro si scontra, prima di spawnare un'unità lì.

Tuttavia, la soluzione sopra proposta potrebbe richiedere un uso eccessivo della CPU.

Qualche suggerimento in merito?

Nota 1 Per le unità generate, non voglio usare alcuna forma di pathfinding per evitare collisioni con le pareti mentre queste unità corrono verso il giocatore. Pertanto, le unità devono spawnare sul bordo dello schermo, in un punto in cui camminare in linea retta verso il giocatore non si scontrerebbe con nessun muro.


1
La mappa si sposta con il giocatore o il giocatore si sposta sulla mappa? Cioè, la mappa cambierà? Altrimenti, suggerirei di compilare tutti i punti non raggiungibili alla generazione, in modo da non doverti preoccupare di loro.
dlras2,

Se il giocatore si sta muovendo, le unità avranno bisogno di un metodo di ricerca del percorso. Se vuoi aree concave, avrai questo problema e dovrai fornire una soluzione per l'unità in movimento che cerca di raggiungere un giocatore in movimento ... ovvero la ricerca del percorso.
Blau,

1
Oppure, per mettere il commento di Blau in un altro modo: la tua domanda non ha di fatto una risposta valida (a parte il banale caso di una mappa senza piastrelle / pixel) se il giocatore può muoversi. Richiede ancora che tu definisca cosa intendi con "linea retta" per avere una risposta se il giocatore è fermo.
Martin Sojka,

Risposte:


3

C'è un algoritmo abbastanza utile per questo lavoro, molto più efficiente dell'algoritmo flood in questa situazione (la sua complessità è il runtime è proporzionale alla dimensione del confine piuttosto che all'area allagata): è l'algoritmo dei quadrati in marcia. Il concetto è semplice: inizia dalla posizione dei giocatori (punto medio dello schermo), scegli una direzione e cammina fino a trovare un muro. Quando ti scontri con il muro, inizi l'algoritmo: scegli un orientamento (su o giù) e inizi a marciare sul confine di quest'area, evidenziando i pixel. Questo ti dà il confine interno per l'area consentita. Successivamente, controlli semplicemente se i punti candidati per le unità di generazione si trovano su questo confine.

Questo è il principio che dovresti seguire per eseguire il confine:

http://it.wikipedia.org/wiki/File:Marchsquares.png (meh non riesco ancora a postare le foto)

Descrizione di Wikipedia (anche se ha molta più complessità perché è usata pensando ad altre applicazioni):

http://en.wikipedia.org/wiki/Marching_squares


10

Effettua un riempimento inondazione dalla posizione del giocatore; ogni area "allagata" è quindi un'area di gioco valida e tutte le altre sono muri.

EDIT: per quanto riguarda il requisito aggiuntivo "raggiungibile in linea retta", tenere presente che in uno spazio discreto , è necessario definirlo ulteriormente. Ad esempio, tutti i percorsi di cui sopra potrebbero essere una "linea retta" valida in un tale ambiente, e in un punto o nell'altro li ho visti tutti usati in un gioco:

varianti "linea retta"

In particolare, la maggior parte di questi non sono commutativi , il che significa solo che puoi raggiungere B da A in una "linea retta" non significa che puoi anche raggiungere A da B; né è necessariamente vero il contrario.

Inoltre, c'è la domanda su come gestire il movimento diagonale se uno o entrambi i punti "laterali" sono bloccati.


Questo può essere implementato interamente in HLSL?
Mathias Lykkegaard Lorenzen,

@Mathias Lykkegaard Lorenzen: Sì, in dubbio facendo ogni passo dell'algoritmo come pixel shader e rendering tra due target di trama, ad esempio, ma ... perché ? Probabilmente avrai comunque bisogno delle informazioni dell'algoritmo sulla CPU, almeno per trovare il percorso.
Martin Sojka,

1
@Mathias Lykkegaard Lorenzen: Tuttavia, è leggermente diverso da quello che hai chiesto. In questo caso: come si definisce una "linea retta", dato lo schema di partizionamento dello spazio discreto?
Martin Sojka,

2
anche se non vuoi usare il pathfinding, chiedendo alla cpu di fare il lavoro di alluvione è possibile, ricorda che devi solo chiamare alluvione una volta e poi avrai una trama di 3 colori che definisce muro, spazi liberi e spazi generabili. per la trama 4096x4096 ci vorrà meno di un secondo perché la cpu completi il ​​lavoro di alluvione.
Ali1S232

1
Il punto è che devi eseguire questo riempimento di inondazioni solo una volta, e anche se il tuo terreno cambia durante il gioco, devi solo aggiornare le sezioni interessate e far scorrere le inondazioni che è incredibilmente veloce.
TravisG,

1

Che ne dici di lasciare che accadano gli spawn? Non vedo alcun problema particolare in questo.


E se si generano dietro un muro? Come li faresti raggiungere il giocatore?
Mathias Lykkegaard Lorenzen,

1
potrebbe essere un problema se il gioco ha uno scenario per uccidere tutti i nemici e genera 50 nemici, ma alcuni sono stati generati dietro il muro. Il giocatore non sarebbe in grado di uccidere i nemici e lo scenario non finirà.
Lie Ryan,

1
Altre unità potrebbero non essere ancora in grado di raggiungere il giocatore a seconda di come si muove dopo che sono state generate, in entrambi i casi dovrai annullare la rigenerazione di alcune unità.
aaaaaaaaaaaa

la nebbia della guerra coprirà le spawn scadenti
KRB

1
Ciò non funzionerà comunque quando il giocatore si muove.
aaaaaaaaaaaa

1

se per te è importante solo segnare punti con una linea retta valida per il giocatore puoi usare un algoritmo come il seguente (è un codice c ++), consuma più del normale diluvio. potrebbero esserci alcuni bug minori (sarò felice se qualcuno li correggerà) dal momento che non ho testato il codice da solo ma avrai l'idea.

void straightlineFill(Point startPoint, Texture target)
{
    queue<Point> pointQueue;
    for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(startPoint.x + dx, startPoint.y + dy));
    while (!pointQueue.empty())
    {
        point front = pointQueue.front();
        pointQueue.pop();
        if (target.pixelAt(front) == COLOR_SPAWNABLE||
            target.pixelAt(front) == COLOR_WALL||
            target.pixelAt(front) == COLOR_NOT_SPAWNABLE)
                continue;
        taraget.setPixelAt(front, colorFilled); 
        for (int dx = -1;dx <=1;dx ++)
            for(int dy = -1;dy <=1;dy++)
                if(dx != 0 && dy != 0)
                    pointQueue.push(point(front.x + dx, front.y + dy));
        // up until now was the normal floodfill code
        // and here is the part that will do the real straight line checking

        // lineDX & lineDY will keep how much the line we are checking is skewed
        int lineDX = front.x - startPoint.x;
        int lineDY = front.y - startPoint.y;

        // step will show us how much we have to travel to reach each point of line
        point step;
        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDX < 0)
                step = point(-1,0);
            if (lineDX == 0)
                if (lineDY < 0)
                    step = point(0,-1);
                else
                    step = point(0,1);
            if (lineDX > 0)
                step = point(1,0);
        }

        if (abs(lineDX) < abs(lineDY))
        {
            if (lineDY < 0)
                step = point(0,-1);
            if (lineDY == 0)
                if (lineDX < 0)
                    step = point(-1,0);
                else
                    step = point(1,0);
            if (lineDY > 0)
                step = point(0,1);
        }

        // moved will keep how much we have traveled so far, it's just some value that will speed up calculations and doesn't have any mathematical value
        point moved = 0;

        // spawnable will keep if the current pixel is a valid spawnpaint

        bool spawnable = true;

        // now we will travel on the straight line from player position to the edges of the map and check if whole line is valid spawn points or not.

        while (target.isInside(front))
        {
            front = front + step;
            moved = moved + point(step.x * lineDX, step.y * lineDY);
            if (step.x != 0 && moved.x < moved.y)
            {
                moved.x = moved.x - lineDY * sign(lineDY);
                front.y = front.y + sign(lineDY);
            }
            if (step.y != 0 && moved.y < moved.x)
            {
                moved.y = moved.y - lineDX * sign(lineDX);
                front.x = front.x + sign(lineDX);
            }

            if (target.getPixelAt(front) == COLOR_WALL)
                spawnable = false;

            if (spawnable)
            {
                target.setPixelAt(front,COLOR_SPAWNABLE);
                for (int dx = -1;dx <=1;dx ++)
                    for(int dy = -1;dy <=1;dy++)
                        if(dx != 0 && dy != 0)
                            pointQueue.push(point(front.x + dx, front.y + dy));             
            }
            else
            {
                target.setPixelAt(front,COLOR_NOT_SPAWNABLE);
            }
        }
    }
}

1

Puoi riempire la mappa con colori che rappresentano aree convesse ..., in questo modo puoi generare la tua unità se si trova nella stessa area. Oppure puoi cercare più facilmente le aree raggiungibili.

Questi sono dati statici in modo da poterlo precalcolare.

dovevi riempire l'immagine trovando punti in cui c'è un passaggio da convesso a convesso, visivamente sembra facile da trovare, hai due situazioni:

  1. Orizzontale: dove l'arancione diventa blu.
  2. Verticale: dove il rosso cambia di verde e arancione.

inserisci qui la descrizione dell'immagine


Questo non funziona Guarda in basso a destra, in particolare il blob nell'area rossa. È interamente convesso, quindi non è necessario apportare un altro colore, ma chiaramente non esiste alcuna linea retta dalla parte inferiore del rosso sul bordo destro alla parte più destra del rosso sul bordo inferiore.
Loren Pechtel,

@Loren Pechtel questo è fatto a mano, hai ragione, c'è un errore lì, è colpa mia, ma puoi capire che è la stessa situazione che passa dalla transizione arancione a blu.
Blau,

@Loren Pechtel, ricorda che l'obiettivo è evitare di spawnare in aree come il giallo. Questo metodo assicura che se rilasci un nemico nella stessa area che contiene il giocatore, questo è raggiungibile. Certo, può essere difficile da implementare, ma l'idea è valida.
Blau,

La tua revisione non aiuta. Due punti su una curva convessa non avranno mai una linea retta legale tra loro, punto. Più divisioni non aiuteranno.
Loren Pechtel,

si prega di controllare la definizione di convesso riferendosi ad aree o set di punti ... en.wikipedia.org/wiki/Convex
Blau

1

Ecco qualcosa che ho effettivamente usato nel mio gioco (mondo generato dal rumore 2d simplex, quasi esattamente come il tuo) - Raggi. Inizia dal giocatore, determina un orientamento (casuale se vuoi) e prosegui lungo quella linea finché non colpisci qualcosa (bordo dello schermo O asteroide). Se colpisci il bordo dello schermo (e non un asteroide / macchia bianca), allora sai che c'è una linea dritta e aperta dal bordo dello schermo al giocatore. Quindi genera un mostro nel punto in cui colpisci. Se colpisci un asteroide, esegui nuovamente il test.


0

Un'altra soluzione (non GPU) che è possibile utilizzare è la ricerca di percorsi. Prima di disegnare la mappa, trova percorso da ogni potenziale punto di spawn su ciascun bordo della mappa e vedi se c'è un percorso verso il centro. Un * percorso di ricerca è abbastanza OK in termini di costi / prestazioni, ma puoi farlo prima che inizi il gioco se questo è un problema.

Qualsiasi punto di spawn che non ha un percorso può essere inserito in un elenco di esclusione; o viceversa (qualsiasi punto con un percorso può essere inserito in un elenco di inclusione).

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.