Bir tarayıcı tabanlı strateji oyunu için bir veritabanından PHP'de altıgen bir dünya haritası nasıl oluşturulur


28

PHP tarayıcı tabanlı strateji oyunum için altıgen bir dünya haritası oluşturmaya çalışıyorum. Veritabanımda, her satırda aşağıdaki verilerden oluşan bir tablo oluşturdum: id, type, x, y ve busy. Tip, sayılarla tanımlanmış olan fayans türüdür. Örneğin, 1 çimdir. Haritanın kendisi 25 x 25.

Harita üzerinde tıklanabilir fayanslarla veri tabanını ve harita üzerinde oklarla gezinme olanağını çizmek istiyorum. Bununla nasıl başlayacağınıza dair hiçbir fikrim yok ve herhangi bir yardım için teşekkür ederim.

Yanıtlar:


38

* Düzenleme: javascript'te firefox'ta hataya neden olan hata düzeltildi *

Düzenleme: sadece PHP kaynak koduna hexes ölçekleme yeteneği eklendi. Küçük 1/2 boy olanlar veya 2x jumbo, hepsi size kalmış :)

Bunların hepsini nasıl yazacağımdan emin değildim, ancak tam bir canlı örnek için kodu yazmanın daha kolay olduğunu gördüm. Sayfa (aşağıdaki link ve kaynak) dinamik olarak PHP ile bir hexmap oluşturur ve harita tıklamalarını işlemek için Javascript'i kullanır. Bir altıgen üzerine tıklayarak altıgen vurgulamaktadır.

Harita rastgele oluşturulur, ancak haritayı doldurmak için kendi kodunuzu kullanabilmelisiniz. Basit bir 2d dizisi ile temsil edilir, her bir dizi elemanı o hex'te mevcut arazi tipini tutar.

Hex Harita Örneği'ni denemek için bana tıklayın

Kullanmak için vurgulamak için herhangi bir hex'e tıklayın.

Şu anda 10x10'luk bir harita oluşturuyor ancak PHP'deki harita boyutunu istediğiniz herhangi bir boyuta getirebilirsiniz. Ben de örneğin Wesnoth oyunundan bir dizi fayans kullanıyorum. Bunlar 72x72 piksel yüksekliğindedir, ancak kaynak altıgen fayanslarınızın boyutunu da ayarlamanıza izin veriyor.

Altıgenler, saydam olarak ayarlanmış "altıgenlerin dışındaki" alanlarla PNG görüntüleri ile temsil edilir. Her altıgeni konumlandırmak için, her döşemenin mutlak konumunu ayarlamak için altıgen ızgara koordinatı tarafından hesaplanan CSS kullanıyorum. Harita örneği değiştirmenizi kolaylaştıracak tek bir DIV içine yerleştirilmiştir.

İşte tam sayfa kodu. Demo kaynağını da indirebilirsiniz (tüm onaltılık görüntüler dahil).

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

İşte örneğin bir ekran görüntüsü ...

Hex Map Örnek Ekran Görüntüsü

Kesinlikle bazı iyileştirmeler kullanabilirdi. Daha önce yaptığım bir yorumda, jQuery'e aşina olduğunuzu söylediğinizi fark ettim, bu iyi. Burada işleri basit tutmak için kullanmadım, ama kullanımı oldukça yararlı olurdu.


1
sana kudos :)
Fuu

1
Fuu'nun örneğine kesinlikle bakın. Onaltılık görüntüleri konumlandırma ve jQuery ve JSON önerisiyle birlikte tıklamaları belirleme yöntemimi kullanabilirsin. Ah, vurguyu haritaya nasıl yerleştirdiğime bakabilirsiniz. Bu sadece bir görüntü, ama z-index style özelliğini döşemelerden daha yüksek bir sayıya ayarladım - yani daha sonra çizilir. Aynı fikri, bir oyuncuyu, belirteçleri, sürüklenen bulutları, yapmak istediğiniz her şeyi örtmek için kullanabilirsiniz.
Tim Holt

Erk - Firefox'ta test etmedi. Tıklama konumunu belirlemek için kodu şimdi yeni bir kod bitiyle değiştirdim ve şimdi Firefox'ta çalışıyor. Bu yüzden jQuery kullanıyorsunuz, bu yüzden bu konuda endişelenmenize gerek yok :)
Tim Holt

1
sadece demoda bildiğiniz için zindex: 99'u her divde kullanıyorsunuz. Bu z-index: 99 olmalıdır, fakat buna ihtiyacınız yok.
corymathews

@corymathews Aslına bakarsanız, orman döşemesinin sağındaki ağaç gibi döşemelerden 'çıkanları' dikkate almak için bir uygulamanın başlangıcı gibi görünüyor. Dizinin değiştirilmesine ihtiyaç duyuyor, böylece diğer fayanslar ağacı örtüşmüyor (şu anki davranış budur).
Jonathan Connell

11

Veri tabanı döşeme koordinatlarını web sayfasındaki görünüme eşleyen küçük bir javascript döşeme düzeni motoru yazmalısınız, çünkü cpu işlem süresini oyuncu bilgisayarına dış kaynak olarak verir. Bunu yapmak zor değil ve birkaç sayfa kodda yapabilirsiniz.

Bu nedenle, temel olarak, yalnızca web sitenizden AJAX çağrısına yanıt olarak istemciye veri tabanınızdan müşteriye koordinat verilerini sunmak olan ince bir PHP katmanı yazacaksınız. Büyük olasılıkla kolay ayrıştırma için bir JSON veri formatı kullanıyor olacaksınız ve harita üreten ve görüntüleyen kısım javascript ile yazılacak ve numo16 tarafından önerildiği gibi jQuery gibi bir kütüphane kullanarak istemcide yürütülecektir. Bu bölümün yapılması nispeten kolaydır ve gerçek oyun uygulamalarında olduğu gibi aynı kavramlar uygulanır, böylece komünist ördekler yazı listesi size altıgen gösterme bölümünü açıklar.

Harita grafiklerini oynatıcılar ekranında görüntülemek için, tüm harita döşemelerinizi tek bir dosyada saklamanızı sağlayan CSS Sprites tekniğini kullanmanızı tavsiye ederim . Konumlandırma için, yine nispeten konumlandırılmış bir konteyner div'inde bulunan bir div içine sarılmış karo görüntüsü için mutlak koordinatlar kullanırsınız.

JQuery click olaylarını bu görüntü sarma divlerine uygularsanız, fare konumlarını önerilen şekilde manuel olarak takip etmek zorunda kalmadan haritayı kolayca tıklanabilir hale getirebilirsiniz. Haritanın güzel görünmesi için harita kenarlarını pürüzlü çizgi altıgen fayans yerine kare olacak şekilde kırpmak için konteyner divini taşma klipsiyle stillendirin. :)


Çok teşekkür ederim. Zaten inanılmaz bir kütüphane olduğu için jQuery ile tanıştım! Tekrar teşekkürler!
fabianPas

Kesinlikle jQuery - harika bir dil kullanın. Fuu, cevabın benimkinden ve örneğe daha fazla zaman verecek olsaydım gideceğim tarzdan kesinlikle daha zarif. Harita verilerini almak için jQuery + JSON gitmenin yolu olacaktır.
Tim Holt

1

Düşüncelerim, veriler veritabanından okunduğunda, her bir döşemenin (x, y) noktanız tarafından belirtilen pozisyonda herhangi bir konumda altıgen bir görüntü eşlemeli bir kare görüntü olarak yaratılacağıdır. Bu, döşeme görüntülerinizi çevreleyen boş alfa kanalına sahip altıgenler olarak oluşturmanız gerekir; böylece döşemelerinizi birbirine uyacak şekilde biraz üst üste getirebilirsiniz. Grafiklerin ve nesnelerin UI tarafının parlatılmasına yardımcı olmak için jQuery'e bakmak isteyebilirsiniz (animasyon, daha hızlı ve daha kolay ajax, kolay olay işleme vb.).


1

Korkarım PHP konuşamıyorum, kod örnekleri yapamıyorum. Ancak, size yardımcı olabilecek kaynakların güzel bir listesi. :)

Gamedev'de izometrik / altıgen ızgaralı makalelerin güzel bir listesi ; arasında değişen altıgen coords nasıl başa kadar önbelleğe fayans . (Tabii ki, bazı şeylerle ilgili olmayacak çünkü çoğunlukla ... kelime nedir? Bir bilgisayarda bir web tarayıcısı değil.)

Grafiksel ekrana gelince, altıgen döşemenin kare görüntüsüne saydamlık eklemeniz yeterli.

'Tıklanabilir' gibi bir şey olurdu:

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

Kullanıcı olaylarının ve PHP'nin veritabanına bağlanmasının ne kadar olduğu hakkında hiçbir fikrim yok, bu yüzden bunun için başka dillere ve çerçevelere bakmanız gerekebilir.

Sana şans diliyorum. :)


Tarayıcı tabanlı oyunlarda bile, daha fazla ihtiyaç duyulmadığında düşük seviyeli programlama püf noktaları kabul edilir.
Tor Valamo

1

Fuu'nun yaklaşımını takiben, altıgen haritasını oluşturmak için tarayıcıda sadece javascript ve jQuery'ye dayanan bir sürüm çalışmasına sahibim. Şu anda JSON'da rastgele bir harita yapısı oluşturan (iki olası döşemeden) aşağı yukarı aşağıdakine benzer bir işlev var:

var map = [["okyanus," çöl "," çöl "], [" çöl, "çöl", "okyanus"], ["okyanus," çöl "," okyanus "]]

... fakat web sayfasının, kodun kendisini oluşturmak yerine bir sunucudan böyle bir harita yapısını almak için Ajax çağrısı yaptığını hayal etmek kolaydır.

Kod, jsfiddle'da açıklanmıştır; buradan, onu açıklayan bir blog yazısına bir bağlantı ve ilgileniyorsanız bir github bağlantısı bulabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.