Cara membuat peta dunia segi enam di PHP dari database untuk game strategi berbasis browser

28

Saya mencoba membuat peta dunia segi enam untuk game strategi berbasis browser PHP saya. Saya telah membuat tabel di database saya dengan data berikut per baris: id, ketik, x, y dan ditempati. Di mana tipe adalah jenis ubin, yang didefinisikan dalam angka. Misalnya, 1 adalah rumput. Peta itu sendiri adalah 25 x 25.

Saya ingin menggambar peta dari database dengan ubin yang dapat diklik dan kemungkinan untuk menavigasi peta dengan panah. Saya tidak benar-benar tahu bagaimana memulai dengan ini dan bantuan apa pun akan dihargai.

fabianPas
sumber

Jawaban:

38

* Edit: Memperbaiki kesalahan dalam javascript yang menyebabkan kesalahan pada firefox *

Sunting: baru saja menambahkan kemampuan untuk menskalakan heks ke kode sumber PHP. Tiny 1/2 ukuran atau 2x jumbo, terserah Anda :)

Saya tidak begitu yakin bagaimana cara menuliskan ini semua, tetapi ternyata lebih mudah hanya menulis kode untuk contoh langsung lengkap. Halaman (tautan dan sumber di bawah) secara dinamis menghasilkan hexmap dengan PHP dan menggunakan Javascript untuk menangani klik peta. Mengklik pada hex menyoroti hex.

Peta dibuat secara acak, tetapi Anda harus dapat menggunakan kode Anda sendiri untuk mengisi peta. Ini diwakili oleh array 2d sederhana, dengan masing-masing elemen array memegang jenis terrain yang ada di hex tersebut.

Klik saya untuk mencoba Contoh Peta Hex

Untuk menggunakan, klik hex apa saja untuk menyorotnya.

Saat ini sedang menghasilkan peta 10x10, tetapi Anda dapat mengubah ukuran peta di PHP menjadi ukuran apa pun yang Anda inginkan. Saya juga menggunakan satu set ubin dari game Wesnoth sebagai contoh. Ketinggiannya 72x72 piksel, tetapi sumbernya juga memungkinkan Anda mengatur ukuran ubin hex Anda.

Heks diwakili oleh gambar PNG dengan area "di luar heks" ditetapkan sebagai transparan. Untuk memposisikan setiap hex, saya menggunakan CSS untuk mengatur posisi absolut setiap ubin, dihitung oleh koordinat hex grid. Peta terlampir dalam DIV tunggal, yang seharusnya memudahkan Anda untuk memodifikasi contoh.

Ini adalah kode halaman penuh. Anda juga dapat mengunduh sumber demo (termasuk semua gambar hex).

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

Berikut adalah screenshot dari contoh ...

Contoh Screenshot Hex Map

Pasti bisa menggunakan beberapa perbaikan. Saya perhatikan dalam komentar sebelumnya Anda mengatakan Anda terbiasa dengan jQuery, yang bagus. Saya tidak menggunakannya di sini untuk membuat hal-hal sederhana, tetapi itu akan sangat berguna untuk digunakan.

Tim Holt
sumber
1
kudos to you :)
Fuu
1
Lihat contoh Fuu dengan jelas. Anda mungkin dapat menggunakan metode saya untuk memposisikan gambar hex dan menentukan klik dikombinasikan dengan sarannya tentang jQuery dan JSON. Oh, Anda mungkin melihat bagaimana saya overlay sorot ke peta. Ini hanya sebuah gambar, tapi saya telah menetapkan properti gaya indeks-z ke angka yang lebih tinggi dari ubin - artinya gambar tersebut diambil nanti. Anda bisa menggunakan ide yang sama untuk melapisi pemain, spidol, awan melayang, tentang apa pun yang ingin Anda lakukan.
Tim Holt
Erk - tidak mengujinya di Firefox. Saya telah memperbarui kode dengan sedikit kode baru untuk menentukan lokasi klik & sekarang berfungsi di Firefox. Inilah sebabnya mengapa Anda menggunakan jQuery, jadi Anda tidak perlu khawatir tentang hal ini :)
Tim Holt
1
supaya Anda tahu di demo Anda menggunakan zindex: 99 pada setiap div. Ini harus menjadi indeks-z: 99, tetapi Anda tidak memerlukannya.
corymathews
@corymathews Sebenarnya ini seperti awal implementasi untuk memperhitungkan hal-hal yang 'keluar' dari ubin, seperti pohon di kanan ubin hutan. Perlu indeks itu diubah sehingga ubin lain tidak tumpang tindih dengan pohon (yang merupakan perilaku saat ini).
Jonathan Connell
11

Anda harus menulis mesin tata letak ubin javascript kecil yang memetakan koordinat petak basis data ke tampilan di halaman web, karena ini memungkinkan Anda melakukan outsourcing waktu pemrosesan cpu ke komputer pemain. Ini tidak sulit untuk dilakukan dan Anda dapat melakukannya dalam beberapa halaman kode.

Jadi pada dasarnya Anda akan menulis lapisan tipis PHP yang tujuannya hanya untuk mengirimkan data koordinat ke klien dari basis data Anda, lebih disukai sebagai respons terhadap panggilan AJAX dari halaman web Anda. Anda mungkin akan menggunakan format data JSON untuk penguraian yang mudah, dan kemudian bagian pembuatan dan tampilan peta akan ditulis dalam javascript, dan dieksekusi pada klien menggunakan perpustakaan seperti jQuery seperti yang disarankan oleh numo16. Bagian ini relatif mudah dilakukan dan konsep yang sama berlaku seperti dalam aplikasi game nyata sehingga daftar artikel bebek komunis akan menjelaskan kepada Anda bagian tampilan hex.

Untuk menampilkan grafik peta pada layar pemain, saya sarankan Anda menggunakan teknik CSS Sprite yang memungkinkan Anda menyimpan semua ubin peta Anda dalam satu file. Untuk penentuan posisi, Anda akan menggunakan koordinat absolut untuk gambar ubin yang dibungkus dengan div, yang lagi-lagi berada dalam wadah yang diposisikan relatif.

Jika Anda menerapkan acara klik jQuery pada div pembungkus gambar ini, Anda dapat membuat peta dapat diklik dengan mudah tanpa harus melacak posisi mouse secara manual seperti yang disarankan. Gaya wadah div dengan kliping melimpah untuk memangkas tepi peta menjadi persegi bukannya ubin hex garis bergerigi untuk membuat peta tampak bagus. :)

Fuu
sumber
Terima kasih banyak. Saya sudah terbiasa dengan jQuery karena ini adalah perpustakaan yang luar biasa! Terima kasih lagi!
fabianPas
Jelas menggunakan jQuery - bahasa yang luar biasa. Fuu, jawaban Anda jelas lebih elegan dari pada saya dan cara saya akan pergi jika saya memberi contoh lebih banyak waktu. jQuery + JSON untuk mendapatkan data peta akan menjadi cara untuk pergi.
Tim Holt
1

Pikiranku adalah bahwa ketika data dibaca dari database, setiap ubin akan dibuat sebagai gambar persegi dengan peta gambar heksagonal di posisi apa pun yang ditentukan oleh titik (x, y) Anda. Yang berarti Anda harus membuat gambar ubin Anda sebagai segi enam dengan saluran alfa kosong di sekitarnya, sehingga Anda dapat sedikit menumpuk ubin Anda agar terlihat cocok satu sama lain. Anda mungkin ingin melihat jQuery untuk membantu memoles sisi grafis dan UI (animasi, ajax lebih cepat dan lebih mudah, penanganan acara yang mudah, dll.).

numo16
sumber
1

Saya khawatir saya tidak bisa berbahasa PHP, jadi saya tidak bisa membuat contoh kode. Namun, berikut adalah daftar sumber daya yang bagus yang dapat membantu Anda. :)

Berikut adalah daftar artikel grid isometrik / heksagonal yang bagus di Gamedev; mulai dari cara berurusan dengan heksagonal coaching hingga caching tiles . (Tentu saja, beberapa hal tidak akan relevan karena kebanyakan ... apa kata? Pada PC bukan browser web.)

Sedangkan untuk tampilan grafis, cukup tambahkan transparansi ke gambar persegi ubin heksagonal.

'Dapat diklik' akan menjadi seperti:

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

Saya tidak tahu berapa banyak dalam hal peristiwa pengguna dan menghubungkan-ke-PHP dengan database, jadi Anda mungkin harus melihat ke dalam bahasa lain dan kerangka kerja untuk itu.

Semoga beruntung. :)

Bebek Komunis
sumber
Bahkan dalam game berbasis browser, trik pemrograman tingkat rendah dihargai, jika tidak diperlukan lebih banyak lagi.
Tor Valamo
1

Menindaklanjuti pendekatan Fuu, saya telah mendapatkan versi yang berfungsi yang sepenuhnya bergantung pada javascript dan jQuery di browser untuk membuat peta hex. Saat ini ada fungsi yang menghasilkan struktur peta acak di JSON (dari dua ubin yang mungkin) kurang lebih seperti ini:

var map = [["samudra," gurun "," gurun "], [" padang pasir, "gurun", "samudra"], ["samudra," gurun "," samudra "]]

... tapi mudah untuk membayangkan memiliki halaman web mengeluarkan panggilan Ajax untuk mendapatkan struktur peta seperti itu dari server alih-alih menghasilkan kode itu sendiri.

Kode di jsfiddle , dari mana Anda juga dapat menemukan tautan ke posting blog yang menjelaskannya, dan tautan github jika Anda tertarik.

Tim Gilbert
sumber