如何为基于浏览器的策略游戏从数据库中用PHP创建六边形世界地图


28

我正在尝试为基于PHP浏览器的策略游戏创建六角形世界地图。我已经在数据库中创建了一个表,每行包含以下数据:id,type,x,y和占用。其中type是瓦片的种类,以数字定义。例如,1是草。地图本身为25 x 25。

我想使用可点击的图块从数据库中绘制地图,并使用箭头在地图上导航。我真的不知道如何开始此工作,任何帮助将不胜感激。

Answers:


38

*编辑:修复了导致Firefox错误的JavaScript错误*

编辑:刚刚添加了将十六进制缩放到PHP源代码的功能。微小的1/2尺寸或2x超大尺寸,一切由您决定:)

我不太确定如何将其全部编写出来,但是发现仅编写完整示例的代码会更容易。该页面(下面的链接和源代码)使用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>

这是示例的屏幕截图...

Hex Map Example Screenshot

肯定可以使用一些改进。我在之前的评论中注意到您说您熟悉jQuery,这很好。我在这里没有使用它来使事情保持简单,但是使用它将非常有用。


1
恭喜您:)
Fuu 2010年

1
绝对看看Fuu的例子。您可能可以使用我的定位十六进制图像和确定点击的方法,结合他对jQuery和JSON的建议。哦,您可能会看看我如何将高光叠加到地图上。这只是一幅图像,但是我将z-index样式属性设置为比图块更高的数字-这意味着稍后会绘制它。您可以使用相同的想法来叠加播放器,标记,飘过的云彩,以及您想做的任何事情。
蒂姆·霍尔特

Erk-未在Firefox上进行测试。我已经用新的代码更新了代码,以确定点击位置,现在可以在Firefox上使用了。这就是为什么使用jQuery的原因,所以您不必担心这些问题:)
Tim Holt 2010年

1
只是您知道在演示中,您在每个div上使用了zindex:99。这应该是z-index:99,但是您不需要它。
corymathews

@corymathews实际上,这似乎是一个实现的开始,它要考虑从“瓷砖”中出来的东西,例如森林瓷砖右侧的树。它需要更改索引,以便其他图块不与树重叠(这是当前行为)。
乔纳森·康奈尔

11

您应该编写一个小的javascript拼贴布局引擎,该引擎将数据库拼贴坐标映射到网页上的视图中,因为这使您可以将cpu处理时间外包给播放器计算机。这并不难,您可以在几页代码中完成。

因此,从本质上讲,您将编写PHP的薄层,其目的仅是将坐标数据从数据库传递到客户端,最好是响应网页上的AJAX调用。您可能会使用JSON数据格式进行轻松解析,然后使用javascript编写地图生成和显示部分,并使用numo16建议的类似jQuery的库在客户端上执行。该部分相对容易实现,并且与真实游戏应用程序中的概念相同,因此共产鸭子的文章列表将向您说明十六进制显示部分。

为了在玩家屏幕上显示地图图形,我建议您使用CSS Sprites技术,该技术可将所有地图图块存储在一个文件中。对于定位,您将对包裹在div中的图块图像使用绝对坐标,该绝对坐标再次位于相对定位的容器div中。

如果将jQuery click事件应用于这些图像包装div,则可以轻松使地图可单击,而无需按照建议手动跟踪鼠标位置。使用溢出剪裁为容器div设置样式,以将地图边缘修剪为正方形(而不是锯齿状的六边形图块),以使地图看起来更美观。:)


非常感谢你。我已经很熟悉jQuery,因为它是一个了不起的库!再次感谢你!
fabianPas 2010年

绝对使用jQuery-很棒的语言。Fuu,您的回答肯定比我的回答要优雅,如果我给这个例子更多的时间,我会走的路。jQuery + JSON获取地图数据将是一种方法。
蒂姆·霍尔特

1

我的想法是,当从数据库中读取数据时,将在(x,y)点指定的任何位置将每个图块创建为具有六边形图像图的正方形图像。这意味着您必须将瓷砖图像创建为六边形,并带有一个空的Alpha通道,以便您可以稍微重叠一下瓷砖以使它们看起来可以放在一起。您可能需要研究jQuery,以帮助完善事物的图形和UI方面(动画,更快,更简单的ajax,轻松的事件处理等)。


1

恐怕我不会讲PHP,因此无法编写代码示例。但是,这里有一个不错的资源列表,可能会对您有所帮助。:)

这是 Gamedev上等距/六边形网格文章的不错列表;从如何处理六边形坐标缓存瓷砖等等。(当然,其中一些内容是无关紧要的,因为它主要是……什么意思?在PC而非Web浏览器上。)

对于图形显示,只需将透明度添加到六角形瓷砖的正方形图像即可。

“可点击”类似于:

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

我不知道用户事件和PHP的数据库连接方式有多少,因此您可能需要研究其他语言和框架。

祝你好运。:)


即使在基于浏览器的游戏中,如果不需要更多的低级编程技巧,它们也会受到赞赏。
Tor Valamo 2011年

1

遵循Fuu的方法,我得到了一个版本的工作,该版本完全依靠浏览器中的javascript和jQuery来呈现十六进制地图。现在有一个函数可以在JSON中(从两个可能的图块中)或多或少地生成一个随机映射结构,如下所示:

var map = [[[“ ocean,” desert“,” desert“],[” desert,“ desert”,“ ocean”],[“ ocean,” desert“,” ocean“]]

...但是很容易想象让网页发出Ajax调用以从服务器获取这样的地图结构,而不是自己生成代码。

该代码位于jsfiddle上,在这里您还可以找到指向说明它的博客文章的链接,以及感兴趣的github链接。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.