So erstellen Sie eine Hexagon-Weltkarte in PHP aus einer Datenbank für ein browserbasiertes Strategiespiel


28

Ich versuche eine Hexagon-Weltkarte für mein PHP-Browser-basiertes Strategiespiel zu erstellen. Ich habe in meiner Datenbank eine Tabelle mit den folgenden Daten pro Zeile erstellt: id, type, x, y und busy. Wobei Typ die Art der Kacheln ist, die in Zahlen definiert sind. Zum Beispiel ist 1 Gras. Die Karte selbst ist 25 x 25.

Ich möchte die Karte aus der Datenbank mit anklickbaren Kacheln zeichnen und die Möglichkeit haben, mit Pfeilen durch die Karte zu navigieren. Ich habe keine Ahnung, wie ich damit anfangen soll, und jede Hilfe wäre willkommen.

Antworten:


38

* Bearbeiten: Fehler in Javascript behoben, der Fehler bei Firefox verursachte *

Bearbeiten: Es wurde die Möglichkeit hinzugefügt, Hexes im PHP-Quellcode zu skalieren. Winzige 1/2 Größe oder 2x Jumbo, es liegt ganz bei dir :)

Ich war mir nicht ganz sicher, wie ich das alles schreiben sollte, fand es aber einfacher, den Code für ein vollständiges Live-Beispiel zu schreiben. Die Seite (Link und Quelle unten) generiert dynamisch eine Hexmap mit PHP und verwendet Javascript, um Kartenklicks zu verarbeiten. Ein Klick auf ein Hex hebt das Hex hervor.

Die Karte wird nach dem Zufallsprinzip generiert, aber Sie sollten stattdessen Ihren eigenen Code verwenden können, um die Karte zu füllen. Es wird durch ein einfaches 2d-Array dargestellt, wobei jedes Array-Element den in diesem Hex vorhandenen Geländetyp enthält.

Klicken Sie auf mich, um das Hex-Kartenbeispiel zu testen

Klicken Sie zur Verwendung auf ein beliebiges Hex, um es zu markieren.

Momentan wird eine 10x10-Karte erstellt, aber Sie können die Kartengröße in PHP so ändern, dass sie jeder gewünschten Größe entspricht. Für das Beispiel verwende ich auch eine Reihe von Kacheln aus dem Spiel Wesnoth. Sie haben eine Höhe von 72 x 72 Pixel, aber mit der Quelle können Sie auch die Größe Ihrer Hex-Kacheln festlegen.

Die Sechsecke werden durch PNG-Bilder dargestellt, wobei Bereiche "außerhalb des Sechsecks" als transparent festgelegt sind. Um jedes Hex zu positionieren, verwende ich CSS, um die absolute Position jeder Kachel festzulegen, die anhand der Hex-Gitterkoordinate berechnet wird. Die Karte ist in einem einzigen DIV enthalten, was Ihnen das Ändern des Beispiels erleichtern soll.

Hier ist der ganzseitige Code. Sie können auch die Demo-Quelle herunterladen (einschließlich aller Hex-Bilder).

<?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>

Hier ist ein Screenshot des Beispiels ...

Hex Map Beispiel Screenshot

Könnte auf jeden Fall einige Verbesserungen gebrauchen. Ich habe in einem früheren Kommentar bemerkt, dass Sie gesagt haben, Sie seien mit jQuery vertraut, was gut ist. Ich habe es hier nicht verwendet, um die Dinge einfach zu halten, aber es wäre ziemlich nützlich, es zu verwenden.


1
Ein
Fuu

1
Schauen Sie sich auf jeden Fall Fuus Beispiel an. Möglicherweise können Sie meine Methode zum Positionieren von Hex-Bildern und zum Bestimmen von Klicks in Kombination mit seinem Vorschlag von jQuery und JSON verwenden. Oh, vielleicht schaust du dir an, wie ich das Highlight auf die Karte lege. Es ist nur ein Bild, aber ich habe die Z-Index-Stileigenschaft auf eine höhere Zahl als die Kacheln festgelegt - das heißt, sie wird später gezeichnet. Sie könnten die gleiche Idee verwenden, um einen Player, Markierungen, vorbeiziehende Wolken über alles zu legen, was Sie tun möchten.
Tim Holt

Erk - hat es nicht auf Firefox getestet. Ich habe den Code mit einem neuen Code-Bit aktualisiert, um die Position des Klicks zu bestimmen. Funktioniert jetzt in Firefox. Aus diesem Grund verwenden Sie jQuery, damit Sie sich keine Sorgen machen müssen :)
Tim Holt

1
Nur damit Sie in der Demo wissen, dass Sie für jedes Div den Z-Index: 99 verwenden. Dies sollte der Z-Index sein: 99, aber Sie brauchen ihn nicht.
Corymathews

@corymathews Eigentlich sieht es nach dem Beginn einer Implementierung aus, Dinge zu berücksichtigen, die aus den Kacheln herauskommen, wie der Baum rechts neben der Waldkachel. Der Index muss geändert werden, damit andere Kacheln den Baum nicht überlappen (wie es derzeit der Fall ist).
Jonathan Connell

11

Sie sollten eine kleine JavaScript-Kachel-Layout-Engine schreiben, die die Datenbankkachelkoordinaten in einer Ansicht auf der Webseite abbildet, da Sie so die CPU-Verarbeitungszeit auf den Player-Computer auslagern können. Es ist nicht schwer zu tun und Sie können es in wenigen Seiten Code tun.

Im Grunde schreiben Sie also eine dünne PHP-Schicht, deren einziger Zweck darin besteht, dem Client Koordinatendaten aus Ihrer Datenbank zu übermitteln, vorzugsweise als Antwort auf einen AJAX-Aufruf von Ihrer Webseite. Sie würden wahrscheinlich ein JSON-Datenformat verwenden, um das Parsen zu vereinfachen, und dann würde der Kartengenerierungs- und -anzeigeteil in Javascript geschrieben und auf dem Client unter Verwendung einer Bibliothek wie jQuery ausgeführt, wie von numo16 vorgeschlagen. Dieser Teil ist relativ einfach zu machen und es gelten die gleichen Konzepte wie in echten Spieleanwendungen, so dass die Artikelliste der kommunistischen Enten Ihnen den hexadezimalen Anzeigeteil erklären wird.

Für die Anzeige von Kartengrafiken auf dem Player-Bildschirm empfehle ich die Verwendung der CSS-Sprites- Technik, mit der Sie alle Kartenkacheln in einer einzigen Datei speichern können. Für die Positionierung würden Sie absolute Koordinaten für das Kachelbild verwenden, das in ein div eingebunden ist. Diese befinden sich wiederum in einem relativ positionierten container div.

Wenn Sie jQuery-Klickereignisse auf diese Bildumbruch-Divs anwenden, können Sie die Karte einfach anklickbar machen, ohne die Mauspositionen wie vorgeschlagen manuell verfolgen zu müssen. Gestalten Sie das Container-Div mit einem Überlauf-Clipping, um die Kartenränder so zu kürzen, dass sie statt der gezackten Hex-Linienfliesen quadratisch sind, damit die Karte gut aussieht. :)


Vielen Dank. Ich bin bereits mit jQuery vertraut, da es eine erstaunliche Bibliothek ist! Danke nochmal!
FabianPas

Verwenden Sie auf jeden Fall jQuery - tolle Sprache. Fuu, deine Antwort ist definitiv eleganter als meine und der Weg, den ich gehen würde, wenn ich dem Beispiel mehr Zeit geben würde. jQuery + JSON, um Kartendaten zu erhalten, wäre der richtige Weg.
Tim Holt

1

Ich bin der Meinung, dass beim Einlesen der Daten aus der Datenbank jede Kachel als quadratisches Bild mit einer hexagonalen Imagemap an der von Ihrem (x, y) -Punkt angegebenen Position erstellt wird. Das bedeutet, dass Sie Ihre Kachelbilder als Sechsecke mit einem umlaufenden leeren Alphakanal erstellen müssen, damit Sie Ihre Kacheln ein wenig überlappen können, damit sie zusammenpassen. Möglicherweise möchten Sie sich mit jQuery befassen, um die Grafiken und die Benutzeroberfläche (Animation, schnellere und einfachere Ajax-Funktion, einfache Ereignisbehandlung usw.) zu verbessern.


1

Ich spreche leider kein PHP und kann daher keine Codebeispiele erstellen. Hier ist jedoch eine schöne Liste von Ressourcen, die Ihnen helfen können. :)

Hier ist eine schöne Liste von isometrischen / hexagonalen Gitterartikeln auf Gamedev; angefangen vom Umgang mit hexagonalen Koordinaten bis hin zum Zwischenspeichern von Kacheln . (Natürlich sind einige Dinge nicht relevant, da es sich meistens um ... was ist das Wort? Auf einem PC kein Webbrowser.)

Fügen Sie für die grafische Anzeige einfach Transparenz zu einem quadratischen Bild einer hexagonalen Kachel hinzu.

"Klickbar" wäre so etwas wie:

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

Ich habe keine Ahnung, wie viel in Bezug auf Benutzerereignisse und die Anbindung von Datenbanken an PHP steckt, daher müssen Sie sich möglicherweise andere Sprachen und Frameworks dafür ansehen.

Ich wünsche Ihnen Glück. :)


Sogar in browserbasierten Spielen werden Low-Level-Programmier-Tricks geschätzt, wenn sie nicht noch mehr benötigt werden.
Tor Valamo

1

Im Anschluss an Fuus Ansatz habe ich eine funktionierende Version, die sich ausschließlich auf Javascript und jQuery im Browser stützt, um die Hex-Karte zu rendern. Momentan gibt es eine Funktion, die in JSON eine zufällige Kartenstruktur erzeugt (aus zwei möglichen Kacheln), die ungefähr so ​​aussieht:

var map = [["Ozean", "Wüste", "Wüste"], ["Wüste", "Ozean"], ["Ozean", "Wüste", "Ozean"]

... aber es ist leicht vorstellbar, dass die Webseite einen Ajax-Aufruf ausgibt, um eine solche Kartenstruktur von einem Server abzurufen, anstatt den Code selbst zu generieren.

Der Code ist auf jsfiddle verfügbar . Dort finden Sie auch einen Link zu einem Blog-Beitrag, der ihn erklärt, und einen Github-Link, wenn Sie interessiert sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.