วิธีสร้างแผนที่โลกหกเหลี่ยมใน PHP จากฐานข้อมูลสำหรับเกมกลยุทธ์ที่ใช้เบราว์เซอร์


28

ฉันพยายามสร้างแผนที่โลกแบบหกเหลี่ยมสำหรับเกมวางกลยุทธ์บนเบราว์เซอร์ PHP ของฉัน ฉันสร้างตารางในฐานข้อมูลของฉันด้วยข้อมูลต่อแถว: id, type, x, y และครอบครอง โดยที่ type เป็นชนิดของไพ่ที่กำหนดไว้ในตัวเลข ตัวอย่างเช่น 1 คือหญ้า แผนที่มีขนาด 25 x 25

ฉันต้องการวาดแผนที่จากฐานข้อมูลด้วยไทล์ที่สามารถคลิกได้และความเป็นไปได้ในการนำทางแผนที่ด้วยลูกศร ฉันไม่รู้จริง ๆ เกี่ยวกับวิธีการเริ่มต้นด้วยสิ่งนี้และความช่วยเหลือใด ๆ จะได้รับการชื่นชม

คำตอบ:


38

* แก้ไข: แก้ไขข้อผิดพลาดในจาวาสคริปต์ที่ทำให้เกิดข้อผิดพลาดใน firefox *

แก้ไข: เพิ่งเพิ่มความสามารถในการปรับสเกล hexes ให้กับซอร์สโค้ด PHP ขนาดเล็ก 1/2 อันหรือจัมโบ้ 2x ขึ้นอยู่กับคุณ :)

ฉันไม่แน่ใจว่าจะเขียนสิ่งนี้ทั้งหมดอย่างไร แต่พบว่ามันง่ายกว่าที่จะเขียนโค้ดสำหรับตัวอย่างสดเต็มรูปแบบ หน้า (ลิงก์และแหล่งที่มาด้านล่าง) สร้าง hexmap แบบไดนามิกด้วย PHP และใช้ Javascript เพื่อจัดการการคลิกแผนที่ คลิกที่ hex เน้น hex

แผนที่ถูกสร้างขึ้นแบบสุ่ม แต่คุณควรจะสามารถใช้รหัสของคุณเองแทนเพื่อเติมแผนที่ มันถูกแทนด้วยอาร์เรย์ 2 มิติอย่างง่ายโดยที่แต่ละองค์ประกอบอาร์เรย์ถือประเภทของภูมิประเทศที่มีอยู่ในฐานสิบหกนั้น

คลิกฉันเพื่อลองตัวอย่างแผนที่ Hex

หากต้องการใช้งานให้คลิกที่เลขฐานสิบหกเพื่อไฮไลต์

ตอนนี้มันกำลังสร้างแผนที่ 10x10 แต่คุณสามารถเปลี่ยนขนาดแผนที่ใน PHP ให้เป็นขนาดที่คุณต้องการ ฉันยังใช้ชุดไพ่จากเกมของ Wesnoth เป็นตัวอย่าง พวกเขามีความสูง 72x72 พิกเซล แต่แหล่งที่มายังช่วยให้คุณกำหนดขนาดของไทล์ฐานสิบหกของคุณ

ฐานสิบหกจะถูกแสดงด้วยภาพ PNG ที่มีพื้นที่ "นอกฐานสิบหก" ตั้งค่าเป็นแบบโปร่งใส ในการวางตำแหน่ง hex แต่ละตัวฉันใช้ CSS เพื่อตั้งค่าตำแหน่งสัมบูรณ์ของไทล์แต่ละอันคำนวณโดยพิกัดกริดของ hex แผนที่นั้นอยู่ใน 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

สามารถใช้การปรับปรุงบางอย่างได้อย่างแน่นอน ฉันสังเกตเห็นในความคิดเห็นก่อนหน้านี้ที่คุณบอกว่าคุณคุ้นเคยกับ jQuery ซึ่งเป็นสิ่งที่ดี ฉันไม่ได้ใช้ที่นี่เพื่อทำให้สิ่งต่าง ๆ เรียบง่าย แต่มันก็มีประโยชน์ที่จะใช้


1
ขอชื่นชมคุณ :)
Fu

1
ดูตัวอย่างของ Fuu อย่างแน่นอน คุณอาจจะสามารถใช้วิธีการวางตำแหน่งภาพเลขฐานสิบหกของฉันและพิจารณาการคลิกรวมกับคำแนะนำของ jQuery และ JSON โอ้คุณอาจดูว่าฉันวางไฮไลต์ไว้บนแผนที่อย่างไร มันเป็นเพียงแค่ภาพ แต่ฉันได้ตั้งค่าคุณสมบัติของดัชนี z เป็นจำนวนที่สูงกว่าไทล์ - หมายถึงมันจะถูกวาดในภายหลัง คุณสามารถใช้แนวคิดเดียวกันนี้ในการซ้อนทับผู้เล่นเครื่องหมายเมฆลอยผ่านเกี่ยวกับสิ่งที่คุณต้องการทำ
Tim Holt

Erk - ไม่ได้ทดสอบบน Firefox ฉันได้อัปเดตโค้ดด้วยโค้ดใหม่เพื่อกำหนดตำแหน่งคลิก & ตอนนี้ใช้ได้กับ Firefox นี่คือเหตุผลที่คุณใช้ jQuery เพื่อให้คุณไม่ต้องกังวลเกี่ยวกับสิ่งนี้ :)
ทิมโฮลท์

1
เพื่อให้คุณรู้ในการสาธิตคุณใช้ zindex: 99 ในแต่ละ div นี่ควรเป็นดัชนี z: 99 แต่คุณไม่ต้องการมัน
corymathews

@corymathews จริง ๆ แล้วดูเหมือนว่าการเริ่มต้นของการนำไปใช้จะคำนึงถึงสิ่งที่ 'ออกมา' ของกระเบื้องเช่นต้นไม้ทางด้านขวาของแผ่นกระเบื้องป่า ต้องมีการเปลี่ยนแปลงดัชนีเพื่อให้ไทล์อื่นไม่ทับต้นไม้ (ซึ่งเป็นพฤติกรรมปัจจุบัน)
Jonathan Connell

11

คุณควรเขียนเอ็นจินเลย์เอาต์ javascript ขนาดเล็กที่แมปพิกัดของไทล์ฐานข้อมูลลงในมุมมองบนหน้าเว็บเพราะสิ่งนี้จะช่วยให้คุณใช้เวลาประมวลผล cpu กับคอมพิวเตอร์ผู้เล่น มันไม่ยากที่จะทำและคุณสามารถทำได้ในโค้ดไม่กี่หน้า

ดังนั้นโดยพื้นฐานแล้วคุณจะต้องเขียนเลเยอร์ PHP ที่มีจุดประสงค์เพื่อส่งข้อมูลพิกัดไปยังไคลเอนต์จากฐานข้อมูลของคุณโดยเฉพาะการตอบสนองต่อการโทร AJAX จากหน้าเว็บของคุณ คุณน่าจะใช้รูปแบบข้อมูล JSON เพื่อการแยกวิเคราะห์อย่างง่ายจากนั้นการสร้างแผนที่และการแสดงส่วนจะถูกเขียนด้วย javascript และดำเนินการกับไคลเอนต์ที่ใช้ไลบรารีเช่น jQuery ตามที่แนะนำโดย numo16 ส่วนนี้ค่อนข้างง่ายในการทำและใช้แนวคิดเดียวกันกับในแอปพลิเคชั่นเกมจริงดังนั้นรายชื่อบทความของคอมมิวนิสต์จะอธิบายให้คุณเห็นส่วนที่เป็นเลขฐานสิบหก

สำหรับการแสดงกราฟิกแผนที่บนหน้าจอผู้เล่นฉันขอแนะนำให้คุณใช้เทคนิคCSS Spritesที่ให้คุณจัดเก็บไทล์แผนที่ทั้งหมดของคุณในไฟล์เดียว สำหรับการวางตำแหน่งคุณจะต้องใช้พิกัดแบบสัมบูรณ์สำหรับภาพย่อยที่อยู่ใน div ซึ่งอีกครั้งจะอยู่ใน div container ที่ค่อนข้างอยู่ในตำแหน่ง

หากคุณใช้เหตุการณ์การคลิก jQuery กับ div การตัดรูปภาพเหล่านี้คุณสามารถทำให้แผนที่สามารถคลิกได้ง่ายโดยไม่ต้องติดตามตำแหน่งเมาส์ตามที่แนะนำ จัดสไตล์ div คอนเทนเนอร์ด้วยการตัดล้นเพื่อตัดขอบแผนที่ให้เป็นรูปสี่เหลี่ยมแทนที่จะเป็นรูปหกเหลี่ยมของเส้นขรุขระที่ทำให้แผนที่ดูดี :)


ขอบคุณมาก. ฉันคุ้นเคยกับ jQuery แล้วเพราะเป็นห้องสมุดที่น่าทึ่ง! ขอขอบคุณอีกครั้ง!
fabianPas

ใช้ jQuery อย่างแน่นอน - ภาษาที่ยอดเยี่ยม คำตอบของคุณสวยกว่าของฉันอย่างแน่นอนและวิธีที่ฉันจะไปถ้าฉันให้เวลากับตัวอย่างมากขึ้น jQuery + JSON เพื่อรับข้อมูลแผนที่จะเป็นวิธีไป
Tim Holt

1

ความคิดของฉันคือเมื่อข้อมูลถูกอ่านจากฐานข้อมูลแต่ละไทล์จะถูกสร้างเป็นรูปสี่เหลี่ยมจัตุรัสที่มีแผนที่รูปหกเหลี่ยมในตำแหน่งใด ๆ ที่ระบุโดยจุด (x, y) ของคุณ ซึ่งหมายความว่าคุณจะต้องสร้างภาพเรียงต่อกันเป็นรูปหกเหลี่ยมที่มีช่องอัลฟาว่างเปล่าโดยรอบเพื่อให้คุณสามารถซ้อนทับแผ่นกระเบื้องของคุณเล็กน้อยเพื่อให้ปรากฏเข้าด้วยกัน คุณอาจต้องการค้นหา jQuery เพื่อช่วยขัดเกลากราฟิกและด้าน UI ของสิ่งต่าง ๆ (ภาพเคลื่อนไหว ajax ที่รวดเร็วและง่ายขึ้นจัดการเหตุการณ์ได้ง่ายและอื่น ๆ )


1

ฉันกลัวว่าฉันจะไม่พูด PHP ดังนั้นฉันจึงไม่สามารถทำตัวอย่างโค้ดได้ อย่างไรก็ตามนี่คือรายการแหล่งข้อมูลที่อาจช่วยคุณได้ :)

นี่คือรายการที่ดีของบทความกริด isometric / hexagonal บน Gamedev; ตั้งแต่วิธีการจัดการกับ coords หกเหลี่ยมที่จะกระเบื้องแคช (แน่นอนว่าบางสิ่งไม่เกี่ยวข้องเนื่องจากส่วนใหญ่เป็น ... คำว่าอะไรบนพีซีไม่ใช่เว็บเบราว์เซอร์)

สำหรับการแสดงผลกราฟิกเพียงเพิ่มความโปร่งใสให้กับภาพสี่เหลี่ยมจัตุรัสของรูปหกเหลี่ยม

'คลิกได้' จะเป็นสิ่งที่ต้องการ:

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 หากคุณสนใจ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.