Comment créer une carte du monde hexagonale en PHP à partir d'une base de données pour un jeu de stratégie basé sur un navigateur

28

J'essaie de créer une carte du monde hexagonale pour mon jeu de stratégie basé sur un navigateur PHP. J'ai créé une table dans ma base de données avec les données suivantes par ligne: id, type, x, y et occupées. Où type correspond au type de tuiles, qui sont définies en nombre. Par exemple, 1 est l'herbe. La carte elle-même est de 25 x 25.

Je veux dessiner la carte de la base de données avec des tuiles cliquables et la possibilité de naviguer à travers la carte avec des flèches. Je n'ai pas vraiment d'indice sur la façon de commencer avec cela et toute aide serait appréciée.

fabianPas
la source

Réponses:

38

* Modifier: Correction d'une erreur en javascript qui provoquait une erreur sur Firefox *

Edit: vient d'ajouter la possibilité de redimensionner les hexs au code source PHP. De minuscules 1/2 ou 2x jumbo, tout dépend de vous :)

Je ne savais pas trop comment mettre tout cela par écrit, mais j'ai trouvé qu'il était plus facile d'écrire simplement le code pour un exemple en direct complet. La page (lien et source ci-dessous) génère dynamiquement une hexmap avec PHP et utilise Javascript pour gérer les clics sur la carte. Cliquer sur un hexagone le met en surbrillance.

La carte est générée de manière aléatoire, mais vous devriez pouvoir utiliser votre propre code à la place pour remplir la carte. Il est représenté par un simple tableau 2D, chaque élément du tableau contenant le type de terrain présent dans cet hex.

Cliquez-moi pour essayer l'exemple de carte hexadécimale

Pour l'utiliser, cliquez sur n'importe quel hex pour le mettre en surbrillance.

En ce moment, il génère une carte 10x10, mais vous pouvez changer la taille de la carte en PHP pour qu'elle soit de la taille que vous souhaitez. J'utilise également un ensemble de tuiles du jeu Wesnoth pour l'exemple. Ils mesurent 72 x 72 pixels, mais la source vous permet également de définir la taille de vos tuiles hexagonales.

Les hexs sont représentés par des images PNG avec des zones "hors de l'hex" définies comme transparentes. Pour positionner chaque hex, j'utilise CSS pour définir la position absolue de chaque tuile, calculée par les coordonnées de la grille hexadécimale. La carte est incluse dans un seul DIV, ce qui devrait vous permettre de modifier plus facilement l'exemple.

Voici le code de la page complète. Vous pouvez également télécharger la source de démonstration (y compris toutes les images hexadécimales).

<?php
// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// :: HEX.PHP
// ::
// :: Author:  
// ::    Tim Holt, [email protected]
// :: 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>

Voici une capture d'écran de l'exemple ...

Capture d'écran de l'exemple de carte hexadécimale

Pourrait certainement utiliser certaines améliorations. J'ai remarqué dans un commentaire précédent que vous aviez dit que vous connaissiez jQuery, ce qui est bien. Je ne l'ai pas utilisé ici pour garder les choses simples, mais ce serait assez utile à utiliser.

Tim Holt
la source
1
bravo à vous :)
Fuu
1
Regardez certainement l'exemple de Fuu. Vous pourrez peut-être utiliser ma méthode de positionnement d'images hexadécimales et de détermination des clics combinée à sa suggestion de jQuery et JSON. Oh, vous pourriez voir comment je superpose le point culminant sur la carte. Ce n'est qu'une image, mais j'ai défini la propriété de style z-index sur un nombre plus élevé que les tuiles - ce qui signifie qu'elle est dessinée plus tard. Vous pouvez utiliser la même idée pour superposer un joueur, des marqueurs, des nuages ​​à la dérive, sur tout ce que vous voudriez faire.
Tim Holt
Erk - ne l'a pas testé sur Firefox. J'ai mis à jour le code avec un nouveau morceau de code pour déterminer l'emplacement du clic et fonctionne maintenant sur Firefox. C'est pourquoi vous utilisez jQuery, vous n'avez donc pas à vous soucier de ce genre de choses :)
Tim Holt
1
juste pour que vous sachiez dans la démo que vous utilisez zindex: 99 sur chaque div. Cela devrait être z-index: 99, mais vous n'en avez pas besoin.
corymathews
@corymathews En fait, cela ressemble au début d'une implémentation pour prendre en compte les choses qui «sortent» des tuiles, comme l'arbre à droite de la tuile forêt. Il a besoin que son index soit changé pour que les autres tuiles ne chevauchent pas l'arbre (ce qui est le comportement actuel).
Jonathan Connell
11

Vous devez écrire un petit moteur de disposition de tuiles javascript qui mappe les coordonnées des tuiles de la base de données dans une vue sur la page Web, car cela vous permet d'externaliser le temps de traitement du processeur à l'ordinateur du joueur. Ce n'est pas difficile à faire et vous pouvez le faire en quelques pages de code.

Donc, essentiellement, vous allez écrire une fine couche de PHP dont le seul but est de fournir des données de coordonnées au client à partir de votre base de données, de préférence en réponse à l'appel AJAX de votre page Web. Vous utiliserez probablement un format de données JSON pour une analyse facile, puis la partie de génération et d'affichage de la carte sera écrite en javascript et exécutée sur le client à l'aide d'une bibliothèque comme jQuery comme suggéré par numo16. Cette partie est relativement facile à faire et les mêmes concepts s'appliquent que dans les applications de jeu réel, donc la liste d'articles sur les canards communistes vous expliquera la partie d'affichage hexadécimale.

Pour l'affichage des graphiques de carte sur l'écran des joueurs, je vous recommande d'utiliser la technique CSS Sprites qui vous permet de stocker toutes vos tuiles de carte dans un seul fichier. Pour le positionnement, vous utiliseriez des coordonnées absolues pour l'image de tuile enveloppée dans un div, qui se trouvent à nouveau dans un div de conteneur relativement positionné.

Si vous appliquez des événements de clic jQuery à ces divs d'habillage d'image, vous pouvez rendre la carte cliquable facilement sans avoir à suivre manuellement les positions de la souris comme suggéré. Stylez la div du conteneur avec un écrêtage de débordement pour couper les bords de la carte pour qu'ils soient carrés au lieu des tuiles hexagonales de ligne dentelée pour rendre la carte belle. :)

Fuu
la source
Merci beaucoup. Je connais déjà jQuery car c'est une bibliothèque incroyable! Merci encore!
fabianPas
Utilisez certainement jQuery - un langage génial. Fuu, votre réponse est certainement plus élégante que la mienne et la façon dont j'irais si je donnais plus de temps à l'exemple. jQuery + JSON pour obtenir des données cartographiques serait la voie à suivre.
Tim Holt
1

Mes pensées sont que lorsque les données sont lues dans la base de données, chaque tuile sera créée comme une image carrée avec une carte d'image hexagonale dans la position spécifiée par votre point (x, y). Ce qui signifie que vous devrez créer vos images de tuiles sous forme d'hexagones avec un canal alpha vide environnant, afin que vous puissiez chevaucher un peu vos tuiles pour les faire sembler s'emboîter. Vous voudrez peut-être examiner jQuery pour aider à peaufiner le côté graphique et l'interface utilisateur des choses (animation, ajax plus rapide et plus facile, gestion facile des événements, etc.).

numo16
la source
1

J'ai peur de ne pas parler PHP, donc je ne peux pas faire d'exemples de code. Cependant, voici une belle liste de ressources qui peuvent vous aider. :)

Voici une belle liste d'articles de grille isométrique / hexagonale sur Gamedev; allant de la façon de traiter les coordonnées hexagonales à mise cache des tuiles . (Bien sûr, certaines choses ne seront pas pertinentes car c'est surtout ... quel est le mot? Sur un PC pas un navigateur Web.)

Quant à l'affichage graphique, ajoutez simplement de la transparence à une image carrée d'une tuile hexagonale.

«Cliquable» serait quelque chose comme:

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

Je n'ai aucune idée de la quantité d'événements utilisateur et de la connexion de bases de données à PHP, vous devrez donc peut-être chercher d'autres langages et cadres pour cela.

Je te souhaite bonne chance. :)

Le canard communiste
la source
Même dans les jeux basés sur un navigateur, des astuces de programmation de bas niveau sont appréciées, si elles ne sont pas encore plus nécessaires.
Tor Valamo
1

Suite à l'approche de Fuu, j'ai une version qui fonctionne uniquement sur javascript et jQuery dans le navigateur pour rendre la carte hexadécimale. À l'heure actuelle, il existe une fonction qui génère une structure de carte aléatoire en JSON (sur deux tuiles possibles) plus ou moins comme ceci:

var map = [["océan," désert "," désert "], [" désert, "désert", "océan"], ["océan," désert "," océan "]]]

... mais il est facile d'imaginer que la page Web émette un appel Ajax pour obtenir une telle structure de carte à partir d'un serveur au lieu de générer le code lui-même.

Le code est sur jsfiddle , d'où vous pouvez également trouver un lien vers un article de blog l'expliquant, et un lien github si vous êtes intéressé.

Tim Gilbert
la source