ブラウザベースの戦略ゲームのデータベースからPHPで六角形の世界地図を作成する方法


28

PHPブラウザベースの戦略ゲームの六角形の世界地図を作成しようとしています。データベースに、行ごとにid、type、x、y、および占有データを含むテーブルを作成しました。typeはタイルの種類で、数字で定義されます。たとえば、1は草です。マップ自体は25 x 25です。

クリック可能なタイルを使用してデータベースからマップを描画し、矢印を使用してマップ内を移動できるようにします。私はこれから始める方法について本当に手がかりを持っていません、そして、どんな助けも感謝されるでしょう。

回答:


38

*編集:Firefoxでエラーを引き起こしたJavaScriptのエラーを修正*

編集:ヘックスをPHPソースコードにスケーリングする機能を追加しました。小さな1/2サイズのものまたは2xジャンボ、それはあなた次第です:)

私はこれをすべて書く方法をよく知りませんでしたが、完全な実例のコードを書く方が簡単だとわかりました。このページ(以下のリンクとソース)は、PHPで16進マップを動的に生成し、Javascriptを使用してマップクリックを処理します。ヘックスをクリックすると、そのヘックスが強調表示されます。

マップはランダムに生成されますが、マップを作成する代わりに独自のコードを使用できる必要があります。それは単純な2D配列で表され、各配列要素はそのヘックスに存在する地形のタイプを保持します。

16進マップの例を試すにはここをクリックしてください

使用するには、任意のヘックスをクリックしてハイライトします。

現在、10x10マップを生成していますが、PHPのマップサイズを任意のサイズに変更できます。また、例としてゲームWesnothのタイルのセットを使用しています。高さは72x72ピクセルですが、ソースでは16進タイルのサイズも設定できます。

ヘックスは、「ヘックスの外側」領域が透明に設定されたPNG画像で表されます。各ヘクスを配置するには、CSSを使用して、各タイルの絶対位置を16進グリッド座標で計算して設定します。マップは単一の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>

これがサンプルのスクリーンショットです...

六角マップの例のスクリーンショット

間違いなくいくつかの改善を使用できます。前のコメントで、jQueryに精通していると言っていましたが、これは良いことです。ここでは、物事を単純にするために使用しませんでしたが、使用すると非常に便利です。


1
あなたへの称賛:)
Fuu

1
間違いなくFuuの例を見てください。16進画像を配置してクリック数を決定する私の方法を、jQueryとJSONの提案と組み合わせて使用​​できるかもしれません。ああ、ハイライトを地図に重ねる方法を見るかもしれません。これは単なる画像ですが、z-indexスタイルプロパティをタイルよりも大きい数値に設定しました。つまり、後で描画されます。同じアイデアを使用して、プレイヤー、マーカー、漂流する雲など、何でもやりたいことをオーバーレイできます。
ティム・ホルト

Erk-Firefoxでテストしませんでした。クリック位置を決定するために新しいコードでコードを更新し、Firefoxで動作するようになりました。あなたはjQueryのを使用する理由あなたは:)このようなものを心配する必要はありませんので、これは、ある
ティム・ホルト

1
デモでは、各divでzindex:99を使用することを知っているだけです。これはz-index:99である必要がありますが、必要ありません。
-corymathews

@corymathews実際には、フォレストタイルの右側のツリーのように、タイルから「出てくる」ものを考慮することは実装の開始のように見えます。他のタイルがツリーに重ならないようにインデックスを変更する必要があります(これが現在の動作です)。
ジョナサンコネル

11

データベースタイル座標をWebページ上のビューにマッピングする小さなJavaScriptタイルレイアウトエンジンを作成する必要があります。これにより、CPU処理時間をプレーヤーのコンピューターに外部委託できるようになります。難しくありません。数ページのコードで実行できます。

したがって、基本的には、データベースのクライアントに座標データを配信することだけが目的で、できればWebページからのAJAX呼び出しへの応答で、PHPの薄いレイヤーを作成します。JSONデータ形式を使用して簡単に解析し、マップ生成および表示部分をjavascriptで記述し、numo16で提案されているjQueryなどのライブラリを使用してクライアントで実行する可能性があります。この部分は比較的簡単に実行でき、実際のゲームアプリケーションと同じ概念が適用されるため、共産主義者のアヒルの記事のリストで、16進表示部分について説明します。

プレーヤー画面にマップグラフィックを表示するには、すべてのマップタイルを1つのファイルに保存できるCSSスプライトテクニックを使用することをお勧めします。配置には、divにラップされたタイルイメージの絶対座標を使用します。これも、比較的配置されたコンテナーdivにあります。

これらの画像ラッピングdivにjQueryクリックイベントを適用すると、推奨されるようにマウス位置を手動で追跡することなく、マップを簡単にクリック可能にすることができます。コンテナdivをスタイルし、オーバーフロークリッピングを使用して、マップの見栄えを良くするために、ぎざぎざの線の16進タイルではなく正方形になるようにマップのエッジをトリミングします。:)


どうもありがとうございました。jQueryはすばらしいライブラリであるため、すでにjQueryに精通しています!ありがとうございました!
fabianPas

間違いなくjQuery-素晴らしい言語を使用してください。ふう、あなたの答えは私のものよりも間違いなくエレガントであり、私が例にもっと時間を与えたいなら私が行く方法だ。マップデータを取得するjQuery + JSONが最適です。
ティム・ホルト

1

私の考えでは、データベースからデータが読み込まれると、各タイルは、(x、y)ポイントで指定された位置に六角形のイメージマップを持つ正方形のイメージとして作成されます。つまり、タイルイメージを周囲の空のアルファチャネルを持つ六角形として作成する必要があります。これにより、タイルを少し重ねて、タイルをぴったりと合わせることができます。jQueryを調べて、グラフィックスとUIの側面(アニメーション、より高速で簡単なAjax、簡単なイベント処理など)を磨くのに役立ちます。


1

PHPを話せないので、コード例を書くことはできません。ただし、役立つリソースのリストを以下に示します。:)

Gamedevの等尺性/六角形のグリッド記事の素晴らしいリスト以下に示します。至るまで、六角形COORDSに対処する方法タイルをキャッシュします。(もちろん、WebブラウザではなくPC上での単語の意味がほとんどなので、一部のものは関連性がありません。)

グラフィック表示については、六角形タイルの正方形の画像に透明度を追加するだけです。

「クリック可能」は次のようになります。

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

PHPに対するユーザーイベントとデータベースフックアップの方法がどれだけあるかはわかりません。そのためには、他の言語とフレームワークを調べる必要があるかもしれません。

私はあなたの幸運を祈ります。:)


ブラウザベースのゲームでも、さらに必要ない場合は、低レベルのプログラミングトリックを高く評価しています。
TorのValamo

1

Fuuのアプローチをフォローアップすると、16進マップをレンダリングするためにブラウザーのjavascriptとjQueryのみに依存するバージョンが動作します。現在、JSONで(2つの可能性のあるタイルから)多かれ少なかれこのようなランダムなマップ構造を生成する関数があります。

var map = [["ocean、" desert "、" desert "]、[" desert、 "desert"、 "ocean"]、["ocean、" desert "、" ocean "]]

...しかし、WebページがAjax呼び出しを発行して、コード自体を生成する代わりに、サーバーからそのようなマップ構造を取得することは簡単に想像できます。

コードはjsfiddleにあり、そこから、それを説明するブログ投稿へのリンクと、興味があるならgithubリンクを見つけることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.