100日後に完成するゲームシステム 57日目「マウス座標からマップアドレスを取得するロジック」

2021年3月25日

テクノロジー 特集

eyecatch convertを実行した時に、revertを用意したくなる性分の、弓削田です。 ゲームマップの管理機能を構築していた時に、これまでマップ上でオブジェクトを配置したり、 キャラクターを動かしたりしているのは、マップアドレスを基準にして行ってきたんですが、 管理画面で、マップのコマをクリックして、そのタイル画像を切替えるという処理を実装したいと考えて、 実際にプログラミングをしてみたところ、 これが結構難しかったので、そのロジックを書き残しておこうと思います。

マップアドレスと画面座標の関係

ちょっとわかりにくいかもしれないので、用語の解説をしておきます。 画面座標というのは、ゲーム画面上の左上を(0,0)としたいわゆるpositionと言われる座標の事です。 マップアドレスというのは、このゲーム内で、マップの四角形で形成された、縦横のコマ数の事で、 例えば、このマップは、4x4の構成で、見た目で一番上に当たる部分が、(0,0)の値になります。 したがって、マップアドレスをかぶせて表示すると、このようになります。 このことは、このブログのシリーズの初期に説明はしてあるので、詳しく見たい人は、下記リンクを御覧ください。 https://myntinc.com/?b=47 これらの画像は、菱形の四角形が交互に配置されている状態なんですが、例えば、マップアドレスが、1x1の画像は、次のような位置に配置されています。 赤い枠の部分が、対象の画像1枚分なんですが、きれいに格子状に並んでいるわけではなく、互い違いに配置されて少し複雑ですよね。 これを画面座標でクリックした時にどのマップアドレスがクリックされたのかを判定する処理を今回は作りたいと思います。

今回の問題点

それぞれの画像タグでクリックイベントを取得すればいいかと思う人もいるかもしれませんが、 2つの問題があり、そう単純ではありません。 1つ目は、画像が重なり合っていて、クリックした時に、想定した画像が選択されない。 2つ目は、そもそも今回のマップ表示は、canvasで表示しているので、画像は1枚1枚が描画されていますが、タグはcanvas1つなので、 それぞれのアドレスでイベントを持つことはできません。 なので、画面クリックした座標情報を、マップアドレスに変換する必要があるんですね。

マップアドレスを画面座標に変換する方法

ちなみにですが、マップアドレスから、画面に配置されている画像のアドレスを取得するのは、 比較的簡単にできます。 上記URLの以前記事に、プログラムを掲載してありますが、 まず、必要な情報としては、 ・画像1枚あたりのサイズ ・マップアドレスの縦横それぞれの最大数(上記画像では、横の最大数:4、縦の最大数:4)という情報 これを、縦横の2つのネストのfor文で、1枚ずつの画像を座標を取得して配置していくわけですが、 それで次の公式で、座標を算出できます。
x : マップアドレスの横値 y : マップアドレスの縦値 w : 画像の横サイズ h : 画像の縦サイズ 横 : ((マップアドレス縦の最大値 - 1) * (w / 2)) + (x * (w / 2)) - (y * (w / 2)); 縦 : (y * (h / 2)) + (x * (h / 2));

画面座標(クリックしたポイント)に該当するマップアドレスを取得する方法

さて、それでは、問題の座標からマップアドレスを取得する方式ですが、 数学計算で、対角線判定をしてドット単位で判定するという方式もありますが、 今回は、処理速度も考慮して、ある程度の誤差があっても、違和感なく動作するということで、行った方法です。 まず、簡単に説明すると、
・それぞれのマップアドレスの画像の中心点座標の一覧を用意して、(固定なので事前に配列で保持しておく) ・クリックした画面座標と一番近い画像を対象とする。
こうすることで、とりあえず、クリックした箇所の画像が選ばれる感覚は得られます。 ただ、際どい境目などでとなりの画像が選ばれてしまうという事はありますが、比較的いい感じに動作します。 そうして、次のソースが、今回対応したプログラムです。 // 送り値のposは画面座標 // tile値は、画像1枚のサイズ(x,y) // map_address_arrは、マップアドレスのそれぞれの中心座標 function get_map_address(pos){ for(let geo in map_address_arr){ if(Math.abs(map_address_arr.x - pos.x) <= (tile.x / 2) && Math.abs(map_address_arr.y - pos.y - (tile.y / 4)) <= (tile.y / 2)){ return { x : map_address_arr.x - (tile.x / 2), y : map_address_arr.y - (tile.y / 2), geo : geo }; } } } クォータービューの悲しい実態ですが、似たような処理を作る人の参考になれば幸いです。 でわ。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。