3ヶ月半ぶりとなってしまいましたが、Cities Box.cppの開発日記を再開したいと思います。
前回は昔過ぎてうろ覚えですが、UbuntuでCities Boxが動いたよ!というご報告だったと思います。

今回は第14回、主に再設計構想と道路タイプのオブジェクト同士の接続についての話題です。

再設計構想ってなんだっけ?

構想を発表してからだいぶ時間が経ってしまったのでもう一度説明します。
まず背景として、2017年以降HSP3で作成していたCities BoxをC++に移植しようと、一昨年の11月末からC++版Cities Boxを作成し始めたわけですが…
あまりにもHSP3時代に作った適当なシステムをそっくりそのままC++に移植してしまったがために、ソースは見ずらいわ、動作は非効率だわ、拡張性はないと、開発しづらい上にバグが起きやすいシステムでした。

そこで今年の3月に打ち出したのがCities Boxの再設計構想です。
主に以下の点において、将来の拡張性を重視して設計の見直しを行います。

  • タイル主体からオブジェクト主体へ
  • レイヤ構造の導入
  • クラスの分割
  • 上記変更に伴うアドオンデータとセーブデータの仕様変更

以下に、各項目について簡単に説明します。

タイル主体からオブジェクト主体へ

「タイル主体」が従来、「オブジェクト主体」が再設計後の仕組みです。
まず、Cities Boxにおける「タイル」と「オブジェクト」の用語の意味について説明しますと、

  • タイル:マップ上の1マスのこと。
  • オブジェクト:マップ上に設置する建築物のこと。

という意味です。

「タイル主体」というのは、ゲーム上でマップをタイル単位で管理することです。
例えば、マップ座標(10, 10)のタイル上にはサイズ(2, 2)のオブジェクトの(1,1)マス目が存在していて…といった具合に、それぞれのタイルにどのオブジェクトの何マス目が存在しているか、という情報をマップデータとして記録していました。
これの何が問題かと言うと、同一のオブジェクトであろうとタイル1つ1つが独立して記録されているので、あるタイル上で起こった変化(建築物の設置や除去など)が周囲にどのように影響するか、という計算が複雑で、なおかつバグの原因になることが多々ありました。

そこで、再設計に伴いオブジェクト主体に切り替えました。
「オブジェクト主体」というのは、ゲーム上でマップをオブジェクト単位で管理することです。
例えば、サイズ(2,2)のオブジェクトはマップ座標(10, 10)を起点にして存在する、と言った具合に、マップ上に存在するあらゆるオブジェクトをオブジェクト単位でマップデータに記録していきます。おそらく3Dゲームのマップデータの構造に近いかなと思います。
タイルはあくまでもオブジェクトのポインタを格納するためのクラスに過ぎず、そのためオブジェクト単位での変化(建築物の設置や除去など)に柔軟に対応できます。
また、C++の最大の特徴とも言えるオブジェクト指向を存分に活用しており、開発する上で拡張や改善がしやすいという利点があります。

レイヤ構造の導入

レイヤというのは層のことです。
複数のアドオン画像を重ね合わせることで、時間帯や季節に応じてオブジェクトの様々な装飾の表現やアニメーション表示が可能になります。
レイヤ構造は配列によって表され、発生条件と照らし合わせて表示するレイヤを自動的に選択します。
例えば夜であれば、基礎となるレイヤ(建物の絵)の上に照明がついた夜間用のマスク画像レイヤを表示することで夜景を演出します。

0defd59e40a79b8f11e3a5b11a52fb0d-1024x795

クラスの分割

C++におけるクラスを分割し、機能を分担させます。
これまではデータマップを保持するCityMapクラスに多数の機能が集中していましたが、この度タイルの情報を保持するTileクラス、オブジェクトの情報を保持するObjectクラス、そしてマップ全体の処理を司るCityMapクラスに分割しました。
また、アドオンのデータを保持するAddonクラスは、アドオンの特定のタイプを保持・表示するAddonTypeクラス、特定のレイヤ構造を保持・表示するAddonLayerクラス、アドオン全体の処理を司るAddonクラスに分割しました。
これにより開発の上で拡張しやすくなります。

上記変更に伴うアドオンデータとセーブデータの仕様変更

そのままの意味です。
再設計に伴い、扱いやすい形にアドオンデータとセーブデータの仕様を変更しました。

現在の状況

再設計構想の8割くらいは完了しています。
一番の難所だと思っていた道路の接続についても、先日対応しました。
あとはバグの修正と、レイヤ構造におけるレイヤの充実やアニメーション表示への対応くらいです。

新しく実装した機能

道路タイプのオブジェクトの敷設

道路タイプのオブジェクトというのは、道路、線路、水路、滑走路と誘導路など、複数のタイル上のオブジェクトが繋がり合うことで成立するオブジェクトのことです。
従来も道路タイプのオブジェクトの敷設には対応していましたが、これはあくまでも「タイル主体」でした。
再設計に伴い「オブジェクト主体」に切り替えるため、オブジェクト単位での道路同士の接続に対応させました。
先述の通り、この実装が一番の難所だと思われる工程で、実際一番の難所でした。

基本的に内部の動作が変化しただけなので外見上はあまり変化はありませんが、オブジェクト主体にしたことで動作はかなり効率良くなっているはずです。

一つだけ見た目上の動作も変化した点があります。それは、「隣接したタイルに勝手に接続しなくなった」ことです。
どういうことかと言うと、従来はマップ上で隣接した道路同士は勝手に接続するような構造になっていました。
スクリーンショット 2021-09-01 22.28.37 これはなぜかと言うと、タイル主体であったがために、各タイルにおける道路の接続状態を保持する仕組みが存在せず、便宜上隣接する全ての道路と接続せざるを得なかったわけです。

今回の修正に伴い、各タイルにおける道路オブジェクト間での接続・未接続の状態が保持できるようになったため、「隣接しつつも接続はしていない」道路が引けるようになりました。
例えばこのように、並行した道路の敷設が可能になりました。
スクリーンショット 2021-09-01 22.30.09

また、こんな自動車教習所の道路みたいな曲がり道も作れるようになりました。
E92smnBVgAEZKk-

線路や水路でも同様のことが可能です。
スクリーンショット 2021-09-01 2.36.06
ただし、水路は現時点ではまだ実装が完了しておらず、「運河の敷設は可能だけど海や湖のような大きな水場は作れない」という状態です。

現時点での問題点

  • 水路における実装の未完成
  • 道路タイプのオブジェクトにおいて許可されていない状態での敷設が可能になっている(→フリーズして強制終了してしまう)
  • 以前のマップデータが読み込めない(今後対応予定)
  • 再設計前に建てた既存の道路タイプのオブジェクトについて、その接続関係などを取得する機能が存在しない
  • レイヤ構造における未実装機能の多さ(現時点で夕方レイヤと夜間レイヤのみ対応)
  • 道路と線路、水路と道路、水路と線路が交差できない(故に踏切や橋が作れない)

などです。

今後の展望

  • 上記の問題点を修正する。修正でき次第α版を公開する。
  • 道路の経路探索を可能にする。
  • 車や電車、歩行者の表示に対応する。
  • 建築物に様々な効果を追加する(電力、水道、下水道、移動通信、フリーWi-Fi、テレビやラジオ放送など)
  • その他諸々

開発版について

従来と同じく、GitHubのYotioSoft/Cities-Box.cppのdevelopブランチにて、最新の開発版が入手可能です。
ただし、上記の通りバグが多数存在しますのでご了承ください。
また、現時点では基本的に付属のサンプルのマップデータ以外は読み込めません。