Как создать карту шестиугольника в PHP из базы данных для браузерной стратегии

28

Я пытаюсь создать шестиугольную карту мира для моей стратегической игры на основе браузера PHP. Я создал таблицу в своей базе данных со следующими данными в строке: id, type, x, y и busy. Где тип - это вид плиток, которые определены в числах. Например, 1 - трава. Сама карта 25 х 25.

Я хочу нарисовать карту из базы данных с кликабельными плитками и возможностью перемещаться по карте со стрелками. Я действительно понятия не имею, как начать с этого, и любая помощь будет оценена.

fabianPas
источник

Ответы:

38

* Редактировать: исправлена ​​ошибка в javascript, приводившая к ошибке на firefox *

Редактировать: только что добавлена ​​возможность масштабировать гексы в исходном коде PHP. Крошечные 1/2 размера или 2x джамбо, все зависит от вас :)

Я не совсем знал, как все это написать, но обнаружил, что было проще написать код для полного живого примера. Страница (ссылка и источник ниже) динамически генерирует шестнадцатеричную карту с помощью PHP и использует Javascript для обработки щелчков карты. Нажатие на гекс выделяет гекс.

Карта генерируется случайным образом, но вы должны иметь возможность использовать свой собственный код вместо этого, чтобы заполнить карту. Он представлен простым двумерным массивом, где каждый элемент массива содержит тип местности, представленной в этом гексе.

Нажмите меня, чтобы попробовать Пример шестнадцатеричной карты

Чтобы использовать, нажмите на любой гекс, чтобы выделить его.

Прямо сейчас он генерирует карту 10х10, но вы можете изменить размер карты в PHP так, чтобы он был любым. Я также использую набор плиток из игры Wesnoth для примера. Они имеют высоту 72x72 пикселя, но источник также позволяет вам устанавливать размер ваших шестнадцатеричных плиток.

Гексы представлены изображениями в формате PNG с прозрачными областями «вне гексагона». Чтобы расположить каждый гекс, я использую CSS, чтобы установить абсолютную позицию каждой плитки, рассчитанную по координатам сетки гекса. Карта заключена в один DIV, что облегчит вам модификацию примера.

Вот полный код страницы. Вы также можете скачать демо-источник (включая все шестнадцатеричные изображения).

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

Вот скриншот с примером ...

Скриншот примера Hex Map

Определенно можно использовать некоторые улучшения. В предыдущем комментарии я заметил, что вы сказали, что знакомы с jQuery, и это хорошо. Я не использовал это здесь для простоты, но это было бы довольно полезно для использования.

Тим Холт
источник
1
слава тебе :)
Фуу
1
Определенно посмотрите на пример Фуу. Возможно, вы сможете использовать мой метод позиционирования шестнадцатеричных изображений и определения кликов в сочетании с его предложением jQuery и JSON. О, вы можете посмотреть, как я наложу блик на карту. Это просто изображение, но я установил для свойства стиля z-index более высокое число, чем для плиток, то есть оно будет нарисовано позже. Вы можете использовать ту же идею для наложения игрока, маркеров, проходящих облаков и всего, что вы хотите сделать.
Тим Холт
Эрк - не проверял это на Firefox. Я обновил код новым фрагментом кода, чтобы определить местоположение щелчка, и теперь работает в Firefox. Вот почему вы используете jQuery, так что вам не нужно беспокоиться об этом :)
Тим Холт
1
точно так же вы знаете, что в демоверсии вы используете zindex: 99 на каждом div. Это должен быть z-index: 99, но он вам не нужен.
Corymathews
@corymathews На самом деле это похоже на начало реализации, которая учитывает вещи, которые «выходят» из плиток, например дерево справа от плитки леса. Нужно изменить индекс, чтобы другие плитки не перекрывали дерево (что является текущим поведением).
Джонатан Коннелл
11

Вам следует написать небольшой механизм компоновки мозаики javascript, который отображает координаты мозаики базы данных в представление на веб-странице, поскольку это позволяет передавать время обработки ЦП на компьютер игроков. Это не сложно, и вы можете сделать это за несколько страниц кода.

По сути, вы будете писать тонкий слой PHP, единственной целью которого является доставка координатных данных клиенту из вашей базы данных, предпочтительно в ответ на вызов AJAX с вашей веб-страницы. Скорее всего, вы будете использовать формат данных JSON для простого анализа, и тогда часть, генерирующая и отображающая карту, будет написана на javascript и выполнена на клиенте с использованием библиотеки, подобной jQuery, как это предложено в numo16. Эта часть сравнительно проста, и применимы те же понятия, что и в реальных игровых приложениях, поэтому список статей «Коммунистические утки» объяснит вам часть отображения гексагона.

Для отображения графики карты на экране проигрывателя я бы порекомендовал использовать технику CSS Sprites , которая позволяет хранить все плитки карты в одном файле. Для позиционирования вы должны использовать абсолютные координаты для мозаичного изображения, заключенного в div, которые снова находятся в относительно позиционированном контейнере div.

Если вы примените события кликов jQuery к этим div-элементам обтекания изображений, вы сможете легко сделать карту кликабельной без необходимости вручную отслеживать положения мыши, как это предлагается. Стилизируйте контейнерный блок с помощью переполнения, чтобы обрезать края карты, чтобы они были квадратными, а не шестигранными плитками с неровными линиями, чтобы карта выглядела красиво. :)

Fuu
источник
Большое спасибо. Я уже знаком с jQuery, так как это потрясающая библиотека! Спасибо еще раз!
fabianPas
Обязательно используйте jQuery - потрясающий язык. Фуу, твой ответ определенно более элегантен, чем мой, и я бы пошёл так, как если бы мне пришлось уделить примеру больше времени. jQuery + JSON для получения картографических данных - это то, что нужно.
Тим Холт
1

Я думаю, что когда данные считываются из базы данных, каждая плитка будет создаваться в виде квадратного изображения с гексагональной картой изображений в любом положении, указанном вашей точкой (x, y). Это означает, что вам нужно будет создавать свои мозаичные изображения в виде шестиугольников с окружающим пустым альфа-каналом, так что вы можете немного перекрывать свои мозаичные изображения, чтобы они выглядели вместе. Возможно, вы захотите взглянуть на jQuery, чтобы помочь отточить графические аспекты и аспекты пользовательского интерфейса (анимация, более быстрый и легкий ajax, удобная обработка событий и т. Д.).

numo16
источник
1

Боюсь, я не говорю на PHP, поэтому я не могу делать примеры кода. Тем не менее, вот хороший список ресурсов, которые могут вам помочь. :)

Вот хороший список статей изометрической / гексагональной сетки на Gamedev; начиная от того, как обращаться с гексагональными координатами, до кэширования тайлов . (Конечно, некоторые вещи не будут актуальны, так как это в основном ... что за слово? На ПК, а не в веб-браузере.)

Что касается графического отображения, просто добавьте прозрачность к квадратному изображению шестиугольной плитки.

«Clickable» будет что-то вроде:

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

Я понятия не имею о том, как много пользовательских событий и подключение базы данных к PHP имеет, поэтому вам, возможно, придется поискать другие языки и платформы для этого.

Желаю тебе удачи. :)

Коммунистическая утка
источник
Даже в браузерных играх ценятся хитрости программирования низкого уровня, если не нужны даже больше.
Тор Валамо
1

Следуя подходу Фуу, у меня есть работающая версия, которая полагается исключительно на javascript и jQuery в браузере для отображения шестнадцатеричной карты. Прямо сейчас есть функция, которая генерирует случайную структуру карты в JSON (из двух возможных плиток) примерно так:

var map = [["океан," пустыня "," пустыня "], [" пустыня, "пустыня", "океан"], ["океан," пустыня "," океан "]]

... но легко представить, что веб-страница выдает Ajax-вызов, чтобы получить такую ​​структуру карты с сервера, а не генерировать сам код.

Код работает на jsfiddle , откуда вы также можете найти ссылку на пост в блоге, объясняющий это, и ссылку на github, если вам интересно.

Тим Гилберт
источник