Як створити шестикутну карту світу в PHP з бази даних для браузерної стратегічної гри


28

Я намагаюся створити шестикутну карту світу для своєї стратегії на базі браузера PHP. Я створив таблицю в своїй базі даних із такими даними на рядок: id, тип, x, y та зайнятий. Де тип - це вид плитки, який визначається цифрами. Наприклад, 1 - трава. Сама карта розміром 25 х 25.

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

Відповіді:


38

* Редагувати: виправлена ​​помилка javascript, яка спричинила помилку на firefox *

Редагувати: щойно додана можливість масштабування шістнадцяткових знаків до вихідного коду PHP. Крихітні на 1/2 розміру або 2х джамбо, все залежить від вас :)

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

Карта генерується випадковим чином, але ви повинні мати можливість використовувати власний код замість того, щоб заповнити карту. Він представлений простим 2d масивом, причому кожен елемент масиву містить тип місцевості, присутній у цій шестигранній.

Клацніть мені, щоб спробувати приклад шестигранної карти

Для використання натисніть на будь-який шестигранник, щоб виділити його.

Зараз він генерує 10x10 карту, але ви можете змінити розмір карти в 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>

Ось скріншот прикладу ...

Знімок екрана з прикладу шестигранної карти

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


1
кудо тобі :)
Фуу

1
Виразно подивіться на приклад Фуу. Можливо, ви зможете використовувати мій метод позиціонування шестигранних зображень та визначення кліків у поєднанні з його пропозицією jQuery та JSON. О, ви можете подивитися на те, як я накладаю родзинку на карту. Це просто зображення, але я встановив властивість стилю z-index більшу кількість, ніж плитки - це означає, що воно буде намальовано пізніше. Ви можете використовувати ту саму ідею, щоб накласти гравця, маркери, хмари, що пропливають, про все, що б ви хотіли зробити.
Тім Холт

Ерк - не тестував його на Firefox. Я оновив код новим бітом коду, щоб визначити місце натискання і зараз працює на Firefox. Ось чому ви використовуєте jQuery, тому вам не доведеться хвилюватися з приводу цього матеріалу :)
Тім Холт

1
так що ви знаєте, в демонстрації ви використовуєте zindex: 99 на кожен div. Це має бути z-індекс: 99, але він вам не потрібен.
corymathews

@corymathews Насправді це виглядає як початок реалізації, щоб врахувати речі, які "виходять" з плиток, як дерево праворуч від лісової плитки. Для цього потрібно змінити індекс, щоб інші плитки не перекривали дерево (що є поточною поведінкою).
Джонатан Коннелл

11

Ви повинні написати невеликий механізм компонування плиток JavaScript, який відображає координати плиток бази даних у подання на веб-сторінці, оскільки це дозволяє передавати час обробки процесора на комп'ютер гравців. Це не важко зробити, і ви можете це зробити на кількох сторінках коду.

Тож по суті ви будете писати тонкий шар PHP, єдиною метою якого є передача даних про координати клієнту з вашої бази даних, бажано при відповіді на дзвінок AJAX з вашої веб-сторінки. Ви, швидше за все, використовуєте формат даних JSON для легкого розбору, і тоді генеруюча та відображаюча частина карти записується у javascript і виконується на клієнті за допомогою бібліотеки типу jQuery, як це запропонувало numo16. Цю частину зробити досить просто, і такі ж концепції застосовуються, як і в реальних програмах для ігор, тому список статей комуністичних качок пояснить вам шестигранну частину, що відображає.

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

Якщо ви застосуєте події клацання jQuery до цих дивів для обгортання зображень, ви можете зробити карту легко натиснутою, не відстежуючи позиції миші вручну, як пропонується. Створіть контейнер-поділ за допомогою відсікання переливу, щоб обрізати краї карт, щоб вони були квадратними замість шістнадцяткової плитки з нерівними лініями, щоб карта виглядала добре. :)


Велике спасибі. Я вже знайомий з jQuery, оскільки це дивовижна бібліотека! Ще раз дякую вам!
fabianPas

Однозначно використовуйте jQuery - приголомшливу мову. Фуу, твоя відповідь, безумовно, більш елегантна, ніж моя, і я б пішов шляхом, якби навести приклад більше часу. jQuery + JSON, щоб отримати дані про карту, це був би шлях.
Тім Холт

1

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


1

Боюся, що я не розмовляю PHP, тому не можу робити приклади коду. Однак ось приємний список ресурсів, який може вам допомогти. :)

Ось приємний список статей із ізометричної / шестикутної сітки про Гамедева; починаючи від способу поводження з шестикутними координатами до кешування плиток . (Звичайно, деякі речі не будуть актуальними, оскільки це здебільшого ... що це за слово? На ПК не веб-браузер.)

Що стосується графічного відображення, просто додайте прозорість до квадратного зображення шестикутної плитки.

"Клікабельний" - це щось на зразок:

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

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

Я бажаю тобі удачі. :)


Навіть в іграх на базі браузерів високо оцінюються трюки програмування низького рівня, якщо вони ще не потрібні.
Tor Valamo

1

Слідом за підходом Fuu, я отримав версію, яка працює, що спирається виключно на JavaScript і jQuery в браузері, щоб надати шестигранну карту. Зараз є функція, яка генерує випадкову структуру карти в JSON (з двох можливих плиток), більш-менш подібну:

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

... але легко уявити, як веб-сторінка видає виклик Ajax, щоб отримати таку структуру карти з сервера, а не генерувати сам код.

Код розміщено на jsfiddle , звідки ви також можете знайти посилання на публікацію в блозі, що пояснює його, та посилання github, якщо вам це цікаво.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.