2019年4月21日 | ブログ記事

ダンジョンエディタ開発記【新歓ブログリレー2019 44日目】

sea314

sea314です。

今回はMonsterという製作中のRPGのダンジョンエディタを0から自作した話です。
DSC_0425

経緯

プロジェクトのリーダーが作りたいダンジョンが複数あったこと、ダンジョンや画像などを暗号化したいとの意見が挙がったので、ダンジョンエディタを制作することになりました。
ゲームをwindowsとmac両方で動かすため、ゲームエンジンにOpenSiv3dを使うことになった一方で、エディタの開発は自分一人に任せてもらっていたので慣れているDXライブラリを使用することにしました。今考えればかなりアホですね。
こうして1つのプロジェクトに2つの異なるゲームエンジンを使うことになりました。

怪しい雲行き

6月はどんなものを作るかの話し合いに使い、7月から開発をはじめました。
7月中にメニューやダイアログ、暗号化の仕組みなどが実装完了していたので、夏休み中に完成しそうなんて思ってました。現実はそこまで甘くなかった。
editor1

本来、エディタだけの担当でしたが、ダンジョンのファイル形式も自作することにしたので、ゲーム側での読み込みも担当することになりました。つまり、エディタ用に書いたダンジョン読み込み処理をゲーム用にも書かなければならないことになりました。使用しているゲームエンジンが異なるので、使い回しはできません。
こうして1つの機能に2つの異なるコードを書くことになりました。

仕様が違う!

いざOpenSiv3dを使ってみると、とてもDXライブラリとの連携が取りにくいことが分かりました。前者はC++を拡張したようなライブラリなので、オブジェクト主体のコーディング、文字コードは4バイトユニコード、String型(std::stringの4バイト版)です。後者はwinAPIとC言語を拡張したようなライブラリなので、関数主体のコーディング、文字コードはShift-JISや2バイトユニコード、char*/wchar_t* 型です。
また、OpenSiv3dはまだまだ発展途上のライブラリなので、リファレンスが必要最低限もなく、また必要不可欠な機能も実装されていないなどもザラでした。リファレンスが役に立たないので、ライブラリのソースコードを見に行ってなんとか代用できそうな方法を探すことになり、かなり時間を食われました。

Game^3と工大祭

9/16のGame^3にゲームを展示することになったので、ゲーム用のダンジョン読み込みを最優先で作ることになりました。夏休み中の大部分をOpenSiv3dに費やしたので、エディタはほとんど進捗せず、ダンジョンも手入力してもらうことになりました。
結局、ダンジョンもダンジョン読み込みもGame^3には間に合いませんでした。確かダンジョン無しで戦闘シーンだけ展示したはずです。
10/6の工大祭にこそ間に合わせるため、朝から晩まで実装を続け、3日前くらいに読み込みクラスが完成しました。手入力の仮ダンジョンも完成し、無事正しく読み込めていたようです。
testdungeon

余談ですが、工大祭前1週間のプロジェクト全体でのコミット数は90くらいありました。その時の累計コミット数は270くらいだったので、1/3くらい進んだことになりますね。実際、Game^3と工大祭とでは全く別ゲー状態だったので感覚通りだと思います。

進捗無

工大祭が終わりしばらくゲームに大きな進捗を求められなくなりました。エディタに戻ってみると、なんとUIと内部データ型と暗号化しか出来ていませんでした。
エディタ用読み込みを実装するにあたり、ゲーム用に合わせて0から書き直すか、エディタもOpenSiv3dにしてしまってゲーム用のを流用するかという選択に迫られました。思ったほどエディタが進んでいなかったことと、2つの異なるコードを使い続ける面倒くささを考えて後者を選びました。もっと早く気付くべきですね。
しかし、エディタ進捗としては実質0に戻ってしまいました。

本格始動

ゲームのセーブ・ロードの実装などいろいろあって、OpenSiv3d化された新エディタの本格開発は12月からになってしまいましたが、ここからはかなりスムーズに進みました。
まず、ゲーム用読み込みクラスを微修正して流用し、ダンジョン読み込み/書き込みクラスを作りました。
OpenSiv3dにまだ実装されていないダイアログを使うためにはWinAPIに頼るしかありませんが、この2者は文字コードが異なりそのままでは使えません。そこで、文字コード変換ライブラリを作り、連携できるようにしました。他にもあると便利なクラスはWinAPIを使用して自作しました。
Siv3d_winAPI
ダイアログとかよく使う機能はOpenSiv3dの元となっているSiv3dでは実装されていたんですけどね。OpenSiv3dはまだまだ開発途上のため核の部分以外は実装が終わっていないようです。
これらにより、エディタのUIや編集画面を作るめどが立ち、開発がより進むことになります。

操作しやすく実装しやすく

設定等のダイアログについてはボタンやエディットコントロールを配置して値を入力してもらえば良いだけなので簡単ですが、ダンジョンにマップチップを配置する場面の実装はかなり悩みました。
ダイアログに座標とマップチップ名を入力してもらうのが一番実装として楽ですが、ダンジョン作成側からは最悪の操作性になってしまいます。一方でカーソルでマップチップを配置するのも、コピーや移動、配置、削除といった動作をどう操作、実装するかかが問題になります。WinAPIのメニューバーや右クリックメニュー、ボタンを使えれば楽だったんですが、このときのOpenSiv3dはウィンドウハンドルを取得する手段すら提供されていなかったので諦めました。メニューやボタンの自前実装は面倒なので。

結局、操作性を優先してマウスだけでマップチップのコピー、移動、配置、削除ができるような操作方法にしました。
カーソルが空のときはクリックでカーソルに移動、右クリックでカーソルにコピー、
カーソルに何か掴んでいるときはクリックで配置、右クリックでカーソルから削除
といった感じです。

仮完成

春休みに入ってしばらくした頃には、一応マップの編集機能を備えたエディタが仮完成しました。
editor2_v2.1-1
リーダーに使ってもらったところ、いくつか要望をもらいました。

重ね置き対応

ダンジョンデータはマップチップの2次元配列、カーソルはマップチップそのもので管理していました。なので、ダンジョンデータをマップチップ配列の2次元配列(つまり3次元配列)化して、同じ座標への再配置を追加配置するように変更しました。

struct MapChipInfo;

Grid<MapChipInfo> dungeonData;
// = vector<vector<MapChipInfo>> dungeonData;
MapChipInfo cursorData;

struct MapChipInfo;

Grid<Array<MapChipInfo>> dungeonData;
// = vector<vector<vector<MapChipInfo>>> dungeonData;
MapChipInfo cursorData;

ファイルへの読み込み/書き込みにも変更が入りました。元々1つの座標ごとに','で区切っていたので、同じ座標に複数枚配置する時の区切りは半角スペースにしました。元々の仕様では半角スペースを完全に無視するようになっていたので、新しいバージョンで作成したダンジョンを前のバージョンでは読み込めなくなりました。

マップチップの分割対応

配布されているマップチップは、例えば16×16ドットのマップチップが縦横10枚づつ並べて160×160ドットの1枚の画像として配布されていたりします。これをペイントツールで全部分割してファイル出力していたそうです。
これを自動化するのはOpenSiv3dの機能を使うとすぐにできました。ただ、分割数が多いと処理している間エディタが使用不能になりフリーズしてしまうので、疑似マルチスレッド化して対応しました。

複数同時編集対応

struct MapChipInfo;

Grid<Array<MapChipInfo>> dungeonData;
// = vector<vector<vector<MapChipInfo>>> dungeonData;
MapChipInfo cursorData;

struct MapChipInfo;

Grid<Array<MapChipInfo>> dungeonData;
// = vector<vector<vector<MapChipInfo>>> dungeonData;
Grid<Array<MapChipInfo>> cursorData;
// = vector<vector<vector<MapChipInfo>>> cursorData;

editor2_MapChipEdit

ついに完成

こうしていくつか追加機能を実装し、バグを修正してようやくエディタが完成しました。
editor2

3月の終わりにやっと完成というのはとんでもなく遅いですし、実質工大祭後に作り始めているのもかなりの戦犯ですね。でも、ダンジョンは後からどんどん追加していけるので、なんとか自分の役目は果たせたかなと思っています。
付けたかった機能は大体付けれたので自分としては満足しています。

感想

エディタやファイル形式は0から作らないほうがいいですね。既存のエディタを使用してファイル形式だけコンバートしたり、エディタ自作するにしても既存のファイル形式を採用したりした方がいいです。もし、ファイルもエディタもすべて自作するなら、エディタ担当とファイル担当とで2人に分けないと私みたいに長期休み中ずっとプログラミングしてたりすることになるかもしれません。
また、作り始める前によく話し合い、後で大きな仕様変更せずに済むようにするべきですね。エディタは担当一人だったので大きな仕様変更をすることが出来ましたが、チームで開発していると開発者全員に迷惑がかかります。というかもうあんな大きな変更したくない。
開発言語やライブラリを選ぶ時に、自分が慣れているかどうかよりもその開発に適しているかどうかを基準にしたほうが多分いいです。慣れていないなら勉強して慣れればいいだけですが、開発に適していないとどんどん大変になっていく気がします。

最後に

新歓記事というより自己満足の活動報告記事になってしまってしまいましたが、どうだったでしょうか?

明日はeiya君の記事です。お楽しみに!

この記事を書いた人
sea314

Monsterのエディタ&ゲーム本体のファイル入出力系を担当しています。 C/C++を主に使っています。 「しーさんいちよん」と読みます

この記事をシェア

このエントリーをはてなブックマークに追加

関連する記事

2019年4月26日
traPへの入部、ありがとうございました!
Yosotsu
2019年4月25日
本【新歓ブログリレー2019 48日目】
xecua
2019年4月24日
gnuplotで遊ぼう
mds_boy
2019年4月22日
アセンブリを読んでみよう【新歓ブログリレー2019 45日目】
eiya
2019年4月19日
ScratchでABCのD問題を解いてみた
kwfumou
2019年4月18日
自分に合ったマウス選び【新歓ブログリレー2019 41日目】
maigo_mayoigo

活動の紹介

カテゴリ

タグ