Come creare una mappa del mondo esagonale in PHP da un database per un gioco di strategia basato su browser


28

Sto cercando di creare una mappa del mondo esagonale per il mio gioco di strategia basato su browser PHP. Ho creato una tabella nel mio database con i seguenti dati per riga: id, tipo, x, y e occupato. Dove tipo è il tipo di tessere, che sono definite in numeri. Ad esempio, 1 è erba. La mappa stessa è di 25 x 25.

Voglio disegnare la mappa dal database con riquadri cliccabili e la possibilità di navigare attraverso la mappa con le frecce. Non ho davvero idea di come iniziare con questo e qualsiasi aiuto sarebbe apprezzato.

Risposte:


38

* Modifica: risolto errore in javascript che causava errore su Firefox *

Modifica: appena aggiunta la possibilità di ridimensionare gli esagoni al codice sorgente PHP. Piccoli 1/2 o jumbo 2x, dipende tutto da te :)

Non ero abbastanza sicuro di come scrivere tutto questo, ma ho scoperto che era più semplice scrivere il codice per un esempio live completo. La pagina (link e sorgente di seguito) genera dinamicamente una hexmap con PHP e utilizza Javascript per gestire i clic della mappa. Facendo clic su un esagono si evidenzia l'esagono.

La mappa viene generata casualmente, ma dovresti essere in grado di utilizzare il tuo codice invece di popolare la mappa. È rappresentato da un semplice array 2d, con ogni elemento dell'array che contiene il tipo di terreno presente in quell'esagono.

Fare clic su di me per provare l'esempio della mappa esadecimale

Per usare, fai clic su qualsiasi esagono per evidenziarlo.

In questo momento sta generando una mappa 10x10, ma puoi cambiare la dimensione della mappa nel PHP in modo che sia la dimensione che desideri. Sto anche usando un set di tessere del gioco Wesnoth per l'esempio. Hanno un'altezza di 72x72 pixel, ma la fonte ti consente anche di impostare la dimensione delle tue tessere esadecimali.

Gli esagoni sono rappresentati da immagini PNG con aree "esterne all'esagono" impostate come trasparenti. Per posizionare ogni esagono, sto usando CSS per impostare la posizione assoluta di ogni piastrella, calcolata dalla coordinata della griglia esadecimale. La mappa è racchiusa in un singolo DIV, il che dovrebbe semplificare la modifica dell'esempio.

Ecco il codice della pagina intera. Puoi anche scaricare la sorgente demo (comprese tutte le immagini esadecimali).

<?php
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// :: HEX.PHP
// ::
// :: Author:  
// ::    Tim Holt, tim.m.holt@gmail.com
// :: Description:  
// ::    Generates a random hex map from a set of terrain types, then
// ::    outputs HTML to display the map.  Also outputs Javascript
// ::    to handle mouse clicks on the map.  When a mouse click is
// ::    detected, the hex cell clicked is determined, and then the
// ::    cell is highlighted.
// :: Usage Restrictions:  
// ::    Available for any use.
// :: Notes:
// ::    Some content (where noted) copied and/or derived from other 
// ::    sources.
// ::    Images used in this example are from the game Wesnoth.
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

// --- Turn up error reporting in PHP
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

// --- Define some constants
$MAP_WIDTH = 10;
$MAP_HEIGHT = 10;
$HEX_HEIGHT = 72;

// --- Use this to scale the hexes smaller or larger than the actual graphics
$HEX_SCALED_HEIGHT = $HEX_HEIGHT * 1.0;
$HEX_SIDE = $HEX_SCALED_HEIGHT / 2;
?>
<html>
    <head>
        <title>Hex Map Demo</title>
        <!-- Stylesheet to define map boundary area and hex style -->
        <style type="text/css">
        body {
            /* 
            margin: 0;
            padding: 0;
            */
        }

        .hexmap {
            width: <?php echo $MAP_WIDTH * $HEX_SIDE * 1.5 + $HEX_SIDE/2; ?>px;
            height: <?php echo $MAP_HEIGHT * $HEX_SCALED_HEIGHT + $HEX_SIDE; ?>px;
            position: relative;
            background: #000;
        }

        .hex-key-element {
            width: <?php echo $HEX_HEIGHT * 1.5; ?>px;
            height: <?php echo $HEX_HEIGHT * 1.5; ?>px;
            border: 1px solid #fff;
            float: left;
            text-align: center;
        }

        .hex {
            position: absolute;
            width: <?php echo $HEX_SCALED_HEIGHT ?>;
            height: <?php echo $HEX_SCALED_HEIGHT ?>;
        }
        </style>
    </head>
    <body>
    <script>

function handle_map_click(event) {
    // ----------------------------------------------------------------------
    // --- This function gets a mouse click on the map, converts the click to
    // --- hex map coordinates, then moves the highlight image to be over the
    // --- clicked on hex.
    // ----------------------------------------------------------------------

    // ----------------------------------------------------------------------
    // --- Determine coordinate of map div as we want the click coordinate as
    // --- we want the mouse click relative to this div.
    // ----------------------------------------------------------------------

    // ----------------------------------------------------------------------
    // --- Code based on http://www.quirksmode.org/js/events_properties.html
    // ----------------------------------------------------------------------
    var posx = 0;
    var posy = 0;
    if (event.pageX || event.pageY) {
        posx = event.pageX;
        posy = event.pageY;
    } else if (event.clientX || e.clientY) {
        posx = event.clientX + document.body.scrollLeft
            + document.documentElement.scrollLeft;
        posy = event.clientY + document.body.scrollTop
            + document.documentElement.scrollTop;
    }
    // --- Apply offset for the map div
    var map = document.getElementById('hexmap');
    posx = posx - map.offsetLeft;
    posy = posy - map.offsetTop;
    //console.log ("posx = " + posx + ", posy = " + posy);

    // ----------------------------------------------------------------------
    // --- Convert mouse click to hex grid coordinate
    // --- Code is from http://www-cs-students.stanford.edu/~amitp/Articles/GridToHex.html
    // ----------------------------------------------------------------------
    var hex_height = <?php echo $HEX_SCALED_HEIGHT; ?>;
    x = (posx - (hex_height/2)) / (hex_height * 0.75);
    y = (posy - (hex_height/2)) / hex_height;
    z = -0.5 * x - y;
    y = -0.5 * x + y;

    ix = Math.floor(x+0.5);
    iy = Math.floor(y+0.5);
    iz = Math.floor(z+0.5);
    s = ix + iy + iz;
    if (s) {
        abs_dx = Math.abs(ix-x);
        abs_dy = Math.abs(iy-y);
        abs_dz = Math.abs(iz-z);
        if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
            ix -= s;
        } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
            iy -= s;
        } else {
            iz -= s;
        }
    }

    // ----------------------------------------------------------------------
    // --- map_x and map_y are the map coordinates of the click
    // ----------------------------------------------------------------------
    map_x = ix;
    map_y = (iy - iz + (1 - ix %2 ) ) / 2 - 0.5;

    // ----------------------------------------------------------------------
    // --- Calculate coordinates of this hex.  We will use this
    // --- to place the highlight image.
    // ----------------------------------------------------------------------
    tx = map_x * <?php echo $HEX_SIDE ?> * 1.5;
    ty = map_y * <?php echo $HEX_SCALED_HEIGHT ?> + (map_x % 2) * (<?php echo $HEX_SCALED_HEIGHT ?> / 2);

    // ----------------------------------------------------------------------
    // --- Get the highlight image by ID
    // ----------------------------------------------------------------------
    var highlight = document.getElementById('highlight');

    // ----------------------------------------------------------------------
    // --- Set position to be over the clicked on hex
    // ----------------------------------------------------------------------
    highlight.style.left = tx + 'px';
    highlight.style.top = ty + 'px';
}
</script>
<?php

// ----------------------------------------------------------------------
// --- This is a list of possible terrain types and the
// --- image to use to render the hex.
// ----------------------------------------------------------------------
    $terrain_images = array("grass"    => "grass-r1.png",
                            "dirt"     => "dirt.png",
                            "water"    => "coast.png",
                            "path"     => "stone-path.png",
                            "swamp"    => "water-tile.png",
                            "desert"   => "desert.png",
                            "oasis"    => "desert-oasis-tile.png",
                            "forest"   => "forested-mixed-summer-hills-tile.png",
                            "hills"    => "hills-variation3.png",
                            "mountain" => "mountain-tile.png");

    // ==================================================================

    function generate_map_data() {
        // -------------------------------------------------------------
        // --- Fill the $map array with values identifying the terrain
        // --- type in each hex.  This example simply randomizes the
        // --- contents of each hex.  Your code could actually load the
        // --- values from a file or from a database.
        // -------------------------------------------------------------
        global $MAP_WIDTH, $MAP_HEIGHT;
        global $map, $terrain_images;
        for ($x=0; $x<$MAP_WIDTH; $x++) {
            for ($y=0; $y<$MAP_HEIGHT; $y++) {
                // --- Randomly choose a terrain type from the terrain
                // --- images array and assign to this coordinate.
                $map[$x][$y] = array_rand($terrain_images);
            }
        }
    }

    // ==================================================================

    function render_map_to_html() {
        // -------------------------------------------------------------
        // --- This function renders the map to HTML.  It uses the $map
        // --- array to determine what is in each hex, and the 
        // --- $terrain_images array to determine what type of image to
        // --- draw in each cell.
        // -------------------------------------------------------------
        global $MAP_WIDTH, $MAP_HEIGHT;
        global $HEX_HEIGHT, $HEX_SCALED_HEIGHT, $HEX_SIDE;
        global $map, $terrain_images;

        // -------------------------------------------------------------
        // --- Draw each hex in the map
        // -------------------------------------------------------------
        for ($x=0; $x<$MAP_WIDTH; $x++) {
            for ($y=0; $y<$MAP_HEIGHT; $y++) {
                // --- Terrain type in this hex
                $terrain = $map[$x][$y];

                // --- Image to draw
                $img = $terrain_images[$terrain];

                // --- Coordinates to place hex on the screen
                $tx = $x * $HEX_SIDE * 1.5;
                $ty = $y * $HEX_SCALED_HEIGHT + ($x % 2) * $HEX_SCALED_HEIGHT / 2;

                // --- Style values to position hex image in the right location
                $style = sprintf("left:%dpx;top:%dpx", $tx, $ty);

                // --- Output the image tag for this hex
                print "<img src='$img' alt='$terrain' class='hex' style='zindex:99;$style'>\n";
            }
        }
    }

    // -----------------------------------------------------------------
    // --- Generate the map data
    // -----------------------------------------------------------------
    generate_map_data();
    ?>

    <h1>Hex Map Example</h1>
    <a href='index.phps'>View page source</a><br/>
    <a href='hexmap.zip'>Download source and all images</a>

    <!-- Render the hex map inside of a div block -->
    <div id='hexmap' class='hexmap' onclick='handle_map_click(event);'>
        <?php render_map_to_html(); ?>
        <img id='highlight' class='hex' src='hex-highlight.png' style='zindex:100;'>
    </div>

    <!--- output a list of all terrain types -->
    <br/>
    <?php 
        reset ($terrain_images);
        while (list($type, $img) = each($terrain_images)) {
            print "<div class='hex-key-element'><img src='$img' alt='$type'><br/>$type</div>";
        }
    ?>
    </body>
</html>

Ecco uno screenshot dell'esempio ...

Schermata di esempio della mappa esadecimale

Sicuramente potrebbe usare alcuni miglioramenti. Ho notato in un precedente commento che hai detto di conoscere jQuery, il che è positivo. Non l'ho usato qui per rendere le cose semplici, ma sarebbe abbastanza utile da usare.


1
complimenti a te :)
Fuu

1
Guarda sicuramente l'esempio di Fuu. Potresti essere in grado di utilizzare il mio metodo per posizionare le immagini esadecimali e determinare i clic in combinazione con il suo suggerimento di jQuery e JSON. Oh, potresti vedere come sovrappongo l'evidenziazione sulla mappa. È solo un'immagine, ma ho impostato la proprietà dello stile z-index su un numero più alto rispetto alle tessere, il che significa che verrà disegnata in seguito. Potresti usare la stessa idea per sovrapporre un giocatore, marcatori, nuvole alla deriva, su qualsiasi cosa tu voglia fare.
Tim Holt,

Erk - non l'ho provato su Firefox. Ho aggiornato il codice con un nuovo bit di codice per determinare la posizione del clic e ora funziona su Firefox. Questo è il motivo per cui usi jQuery, quindi non devi preoccuparti di queste cose :)
Tim Holt,

1
solo così sai nella demo che usi zindex: 99 su ogni div. Questo dovrebbe essere z-index: 99, ma non è necessario.
corymathews,

@corymathews In realtà sembra l'inizio di un'implementazione per tenere conto delle cose che "escono" dalle tessere, come l'albero a destra della tessera foresta. È necessario che l'indice sia cambiato in modo che le altre tessere non si sovrappongano all'albero (che è il comportamento corrente).
Jonathan Connell,

11

Dovresti scrivere un piccolo motore di layout dei riquadri javascript che associ le coordinate dei riquadri del database in una vista sulla pagina Web, poiché ciò consente di esternalizzare il tempo di elaborazione della CPU sul computer dei giocatori. Non è difficile da fare e puoi farlo in poche pagine di codice.

Quindi essenzialmente scriverai un sottile strato di PHP il cui unico scopo è quello di fornire i dati di coordinate al client dal tuo database, preferibilmente in risposta alla chiamata AJAX dalla tua pagina web. Probabilmente useresti un formato di dati JSON per un facile analisi, e quindi la generazione della mappa e la visualizzazione della parte verrebbero scritte in javascript ed eseguite sul client usando una libreria come jQuery come suggerito da numo16. Questa parte è relativamente facile da fare e si applicano gli stessi concetti come nelle applicazioni di gioco reali, quindi l'elenco di articoli di anatre comuniste ti spiegherà la parte che visualizza esadecimale.

Per la visualizzazione della grafica della mappa sullo schermo dei giocatori, ti consiglio di utilizzare la tecnica CSS Sprites che ti consente di memorizzare tutte le tessere della mappa in un singolo file. Per il posizionamento dovresti usare le coordinate assolute per l'immagine della piastrella avvolta in un div, che di nuovo si trovano in un div contenitore posizionato in modo relativo.

Se applichi eventi click jQuery a questi div di wrapping di immagini puoi rendere facilmente cliccabile la mappa senza dover tracciare manualmente le posizioni del mouse come suggerito. Modella il div del contenitore con un ritaglio di troppo pieno per tagliare i bordi della mappa in modo che siano quadrati anziché i riquadri esagonali della linea frastagliata per rendere la mappa piacevole. :)


Grazie mille. Conosco già jQuery in quanto è una libreria fantastica! Grazie ancora!
fabianPas,

Usa sicuramente jQuery - un linguaggio fantastico. Fuu, la tua risposta è sicuramente più elegante della mia e del modo in cui andrei se dovessi dare all'esempio più tempo. jQuery + JSON per ottenere i dati della mappa sarebbe la strada da percorrere.
Tim Holt,

1

Il mio pensiero è che quando i dati vengono letti dal database, ogni riquadro verrà creato come un'immagine quadrata con una mappa immagine esagonale in qualsiasi posizione specificata dal punto (x, y). Ciò significa che dovrai creare le immagini delle tue tessere come esagoni con un canale alfa vuoto circostante, in modo da poter sovrapporre un po 'le tue tessere per farle apparire insieme. Potresti voler esaminare jQuery per aiutare a perfezionare la grafica e il lato dell'interfaccia utente (animazione, ajax più veloce e più facile, facile gestione degli eventi, ecc.).


1

Temo di non parlare in PHP, quindi non posso fare esempi di codice. Tuttavia, ecco un buon elenco di risorse che potrebbero aiutarti. :)

Ecco una bella lista di articoli a griglia isometrica / esagonale su Gamedev; che vanno da come gestire i cordoni esagonali alle tessere di cache . (Certo, alcune delle cose non saranno rilevanti dal momento che è principalmente ... qual è la parola? Su un PC non un browser web.)

Per quanto riguarda la visualizzazione grafica, aggiungi semplicemente trasparenza all'immagine quadrata di una piastrella esagonale.

'Clickable' sarebbe qualcosa del tipo:

if mouse button down on app:  
take screen coordinates of mouse  
Compare to screen coordinates of tiles

Non ho idea di quanto abbiano a disposizione gli eventi degli utenti e il collegamento del database con PHP, quindi potresti dover cercare in altre lingue e framework.

Vi auguro buona fortuna. :)


Anche nei giochi basati su browser, sono apprezzati i trucchi di programmazione di basso livello, se non ancora necessari.
Tor Valamo,

1

Seguendo l'approccio di Fuu, ho una versione funzionante che si basa esclusivamente su javascript e jQuery nel browser per eseguire il rendering della mappa esadecimale. In questo momento esiste una funzione che genera una struttura di mappa casuale in JSON (tra due possibili riquadri) più o meno come questa:

var map = [["ocean," desert "," desert "], [" desert, "desert", "ocean"], ["ocean," desert "," ocean "]]

... ma è facile immaginare che la pagina Web emetta una chiamata Ajax per ottenere una tale struttura di mappe da un server invece di generare il codice stesso.

Il codice è su jsfiddle , da dove puoi anche trovare un link a un post sul blog che lo spiega, e un link github se sei interessato.

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.