Como criar um mapa-múndi hexagonal em PHP a partir de um banco de dados para um jogo de estratégia baseado em navegador


28

Estou tentando criar um mapa-múndi hexágono para o meu jogo de estratégia baseado em navegador PHP. Eu criei uma tabela no meu banco de dados com os seguintes dados por linha: id, tipo, x, ye ocupado. Onde type é o tipo de blocos, que são definidos em números. Por exemplo, 1 é grama. O mapa em si é 25 x 25.

Quero desenhar o mapa do banco de dados com blocos clicáveis ​​e a possibilidade de navegar pelo mapa com setas. Eu realmente não tenho idéia de como começar com isso e qualquer ajuda seria apreciada.

Respostas:


38

* Edit: Corrigido erro no javascript que causava erro no firefox *

Edit: acabou de adicionar a capacidade de escalar hexágonos no código fonte do PHP. Pequenos tamanhos 1/2 ou 2x jumbo, tudo depende de você :)

Eu não tinha muita certeza de como escrever tudo isso, mas achei mais fácil escrever o código para um exemplo completo ao vivo. A página (link e fonte abaixo) gera dinamicamente um hexmap com PHP e usa Javascript para manipular cliques no mapa. Clicar em um hexágono destaca o hexágono.

O mapa é gerado aleatoriamente, mas você deve poder usar seu próprio código para preencher o mapa. É representado por uma matriz 2D simples, com cada elemento da matriz segurando o tipo de terreno presente nesse hexágono.

Clique em mim para experimentar o Exemplo de Mapa Hex

Para usar, clique em qualquer hexágono para destacá-lo.

No momento, ele está gerando um mapa de 10x10, mas você pode alterar o tamanho do mapa no PHP para o tamanho que desejar. Também estou usando um conjunto de peças do jogo Wesnoth, por exemplo. Eles têm 72x72 pixels de altura, mas a fonte também permite definir o tamanho dos seus blocos hexadecimais.

Os hexágonos são representados por imagens PNG com as áreas "fora do hex" definidas como transparentes. Para posicionar cada hexadecimal, estou usando CSS para definir a posição absoluta de cada bloco, calculada pela coordenada da grade hexadecimal. O mapa é colocado em uma única DIV, o que deve facilitar a modificação do exemplo.

Here is the full page code. You can also download the demo source (including all hex images).

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

Aqui está uma captura de tela do exemplo ...

Captura de tela de exemplo de mapa hexadecimal

Definitivamente poderia usar algumas melhorias. Notei em um comentário anterior que você disse que estava familiarizado com o jQuery, o que é bom. Eu não o usei aqui para manter as coisas simples, mas seria bastante útil.


1
parabéns para você :)
Fuu

1
Definitivamente, olhe o exemplo de Fuu. Você pode usar meu método de posicionamento de imagens hexadecimais e determinação de cliques combinados com a sugestão de jQuery e JSON. Ah, você pode ver como eu sobreponho o destaque no mapa. É apenas uma imagem, mas eu configurei a propriedade de estilo do índice z para um número maior que os blocos - o que significa que ela será desenhada mais tarde. Você pode usar a mesma idéia para sobrepor um jogador, marcadores, nuvens flutuando, sobre qualquer coisa que você queira fazer.
Tim Holt

Erk - não testou no Firefox. Atualizei o código com um novo código para determinar o local do clique e agora funciona no Firefox. É por isso que você usa jQuery, assim você não precisa se preocupar com essas coisas :)
Tim Holt

1
só para você saber na demonstração, você usa o zindex: 99 em cada div. Este deve ser o z-index: 99, mas você não precisa dele.
21711 CorelDRAGONO

@corymathews Na verdade, parece que o início de uma implementação leva em consideração coisas que saem dos ladrilhos, como a árvore à direita do ladrilho da floresta. Ele precisa que seu índice seja alterado para que outros blocos não se sobreponham à árvore (que é o comportamento atual).
Jonathan Connell

11

Você deve escrever um pequeno mecanismo de layout de bloco javascript que mapeie as coordenadas do bloco de banco de dados em uma exibição na página da Web, pois isso permite terceirizar o tempo de processamento da CPU para o computador dos jogadores. Não é difícil de fazer e você pode fazê-lo em poucas páginas de código.

Então, essencialmente, você escreverá uma fina camada de PHP, cujo único objetivo é fornecer dados de coordenadas para o cliente a partir do seu banco de dados, de preferência em resposta à chamada AJAX da sua página da web. Você provavelmente usaria um formato de dados JSON para facilitar a análise e, em seguida, a parte geradora e de exibição do mapa seria escrita em javascript e executada no cliente usando uma biblioteca como jQuery, conforme sugerido por numo16. Esta parte é relativamente fácil de fazer e os mesmos conceitos se aplicam aos aplicativos de jogos reais, de modo que a lista de artigos dos patos comunistas explicará a parte de exibição hexadecimal.

Para a exibição de gráficos de mapa na tela de jogadores, recomendo que você use a técnica CSS Sprites que permite armazenar todos os blocos de mapa em um único arquivo. Para o posicionamento, você usaria coordenadas absolutas para a imagem do bloco envolvida em uma div, que novamente está em uma div de contêiner relativamente posicionada.

Se você aplicar eventos de clique do jQuery a essas divs de agrupamento de imagens, poderá tornar o mapa clicável facilmente sem precisar rastrear manualmente as posições do mouse, conforme sugerido. Estilize a div do contêiner com um recorte de estouro para aparar as bordas do mapa para serem quadradas, em vez dos ladrilhos hexagonais de linhas irregulares para deixar o mapa com uma aparência agradável. :)


Muito obrigado. Eu já estou familiarizado com o jQuery, pois é uma biblioteca incrível! Obrigado de novo!
fabianPas

Definitivamente use jQuery - linguagem incrível. Fuu, sua resposta é definitivamente mais elegante que a minha e do jeito que eu iria se desse o exemplo mais tempo. jQuery + JSON para obter dados do mapa seria o caminho a percorrer.
Tim Holt

1

Penso que, à medida que os dados forem lidos no banco de dados, cada bloco será criado como uma imagem quadrada com um mapa de imagem hexagonal em qualquer posição especificada pelo seu ponto (x, y). O que significa que você precisará criar suas imagens de blocos como hexágonos com um canal alfa vazio ao redor, para que você possa sobrepor seus blocos um pouco para que pareçam se encaixar. Você pode procurar no jQuery para ajudar a aperfeiçoar os gráficos e o lado da interface do usuário (animação, ajax mais rápido e fácil, manipulação fácil de eventos, etc.).


1

Receio não falar PHP, então não posso fazer exemplos de código. No entanto, aqui está uma boa lista de recursos que podem ajudá-lo. :)

Aqui está uma boa lista de artigos de grade isométrica / hexagonal em Gamedev; variando de como lidar com cordas hexagonais a cache de blocos . (É claro que algumas das coisas não serão relevantes, pois é principalmente ... qual é a palavra? Em um PC, não em um navegador da web.)

Quanto à exibição gráfica, basta adicionar transparência a uma imagem quadrada de um bloco hexagonal.

'Clicável' seria algo como:

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

Eu não tenho idéia de quanto a maneira como os eventos do usuário e a conexão do banco de dados com o PHP têm, então você pode ter que procurar outras linguagens e estruturas para isso.

Eu te desejo sorte. :)


Mesmo em jogos baseados em navegador, truques de programação de baixo nível são apreciados, se não forem necessários ainda mais.
Tor Valamo 13/10

1

Seguindo a abordagem de Fuu, eu tenho uma versão funcionando que se baseia puramente em javascript e jQuery no navegador para renderizar o mapa hexadecimal. No momento, há uma função que gera uma estrutura de mapa aleatória no JSON (de dois possíveis blocos) mais ou menos assim:

var map = [["oceano," deserto "," deserto "], [" deserto, "deserto", "oceano"], ["oceano," deserto "," oceano "]]

... mas é fácil imaginar que a página da Web emita uma chamada Ajax para obter essa estrutura de mapa de um servidor em vez de gerar o próprio código.

O código está no jsfiddle , de onde você também pode encontrar um link para uma postagem no blog explicando-o e um link no github, se estiver interessado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.