目次

ハイハイスクールアドベンチャー Qt版

あらすじ

2019年神奈山県立ハイ高等学校は 地盤が弱く校舎の老朽化も進んだため、 とうとう廃校にする以外方法がなく なってしまった。

ところで大変な情報を手に入れた。 それは、

「ハイ高校にATOMIC BOMBが仕掛けられている。」

と、いうものだ。 どうやらハイ高が廃校になった時、 気が狂った理科の先生がATOMIC BOMBを 学校のどこかに仕掛けてしまったらしい。

お願いだ。我が母校のコナゴナになった 姿を見たくはない。 早くATOMIC BOMBを取り除いてくれ……!!

行動は英語で、“<動詞>” 或いは、“<動詞>”+“<目的語>“のように入れていただきたい。 例えば、”look room”と入れれば部屋の様子を見ることが出来るという訳だ。

それでは Good Luck!!!…………

概要

M5版を作った勢いで、SDL2版を作ろうと思ったのだが、Textureの扱いがイマイチ判然とせず、動き始めたものの表示が不規則に切り替わり、それを直すくらいならと、Qtにスイッチしたのがこのバージョンである。

入出力部以外はM5版と同じである。

ビルド

QtCreatorを使用して作成したので、QtCreatorを使ってビルドするのが簡単だが、cmakeで直接ビルドすることも可能である。 ソースはM5版同様GitHubから取得可能である。 データファイルは ~/.HHSAdv の下に展開する。

GitHubからは git clone で取得する。

$ git clone https://github.com/wildtree/qhhsadv.git

QtCreatorを使用する場合には、CMakeLists.txt がプロジェクトファイルになる。 cmakeでビルドするなら、

$ mkdir build
$ cd build
$ cmake ..
$ make

と、通常の cmakeの作法に従えばいい。

実行は qhhsadv を実行すればいい。

ハイハイスクールアドベンチャー

あれこれ

ダイアログについて

M5版とは異なり、自由度の高いダイアログなどを用意できる関係で、「ストーリー」や「このプログラムについて」などのダイアログを実装したが、スタッフクレジットが意外と長くて、環境によっては画面に収まらないことが判明。

だったら、スクロールするようにすればいいじゃんと思ったのだが、これが以外にもめんどくさかった。

スクロールバーつきのダイアログ

Qtではこの種のダイアログは QMessageBox::about() を使って表示するのだが、残念なことにスクロールバーがつくことはない。 なので、自力でそのようなダイアログをつくらなければならない。

普通に考えれば、QDialogのインスタンスを作って、そこに必要な部品をはめていけばいいのだが、うっかり、QMessageBoxクラスを継承して、うまいことごにょごにょすればいいんじゃないかと思ってしまった。

それが、間違いだったが、始めてしまったからには仕方がない。

方針としては、QMessageBoxで表示してる QLabelをスクロールバーつきにしてやればいいのだ。 そうだ、そんなに難しいことではない……と、思ってた。

ウィジェットをいくつも詰め込む場合にはレイアウトも必要になるが、今回は QLabelひとつを QScrollAreaに突っ込むだけなので大したことはない。

QScrollArea *scrollArea = new QScrollArea();
QLabel *label = new QLabel(text);
scrollArea->setWidget(label);

これを、元々表示している QLabelと入れ替えてやればいい。 QMessageBoxには QGridLayout がはりついていて、そこにQLabel がはまっているので、そのGridLayoutに対して差し替えをさせればいい。

QGridLayout *l = qobject_cast<QGridLayout*>(this->layout());
l->addWidget(scrollArea, 0, 2, 1, -1); // アプリケーションアイコンがある場合。ない場合は 2->1に置き換える 

ところが、これやったら、ダイアログの左上に出てくるだけで、狙ったところにスクロールバーはつかない。 1)

もうなんか、アイコンのあるなしで場所代わるし、レイアウト触るんじゃなくて、表示してる QLabel の中身をスクロールバーつきの QLabelに変えちゃえばいいんじゃね? ということに気づいた。 勿論、正しいやり方かどうかは知らない。 そこまでQtに詳しいわけじゃない。

    QLabel *label = this->findChild<QLabel*>("qt_msgbox_label");
    QLabel *content = new QLabel(label->text());
    content->setWordWrap(true);
    content->setOpenExternalLinks(true);
    label->clear();
    _scrollArea = new QScrollArea(label);
    _scrollArea->setWidget(content);
    _scrollArea->setWidgetResizable(true);
    _scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    _scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    _scrollArea->setMinimumSize(label->width(),label->height());
    label->setMinimumSize(label->width(),label->height());

最後、最小サイズを指定してやらないとダイアログが極小になってしまうのでこれがある意味キモ。

なお、QMessageBoxを継承した場合は void about()も定義しなおしておかないといけない。 最後の setMinimunSize()に関しては、StyleSheetでやっている例もあったが、QLabel { min-width: 300 px; min-height: 300 px; }みたいにしてしまうと、textだけでなく、アイコンの方もそのサイズにされてしまうので、アイコン付きの about()では都合が悪いのだ。

void
ScrollMessageBox::about(QWidget *parent, const QString &title, const QString &text)
{
    ScrollMessageBox *scrBox = new ScrollMessageBox(QMessageBox::Icon::Information, title, text, QMessageBox::StandardButton::Ok, parent);
    scrBox->setAttribute(Qt::WA_DeleteOnClose);
    QIcon icon = scrBox->windowIcon();
    QSize size = icon.actualSize(QSize(64, 64));
    scrBox->setIconPixmap(icon.pixmap(size));

    scrBox->exec();
}

Qt::WA_DeleteOnClose をセットしておくことで、ダイアログを閉じたら勝手に削除してくれるので staticな関数なのに、オブジェクトを返さずにすむ。 お手軽だが、こんなの、Qtのソースみないとわからない。

Qt5/Qt6の互換性

主にuConsoleでコード書いていた関係で、Qt5を使って開発したため、Qt6で動くかどうかとか全然ちゃんと調べてなかった。 DevTermの方は逆にQt6をインストールしてあったので、そっちでビルドできるかトライしたらあっさりこけた。

ただし、こけたのは QMediaPlayer 周り。

音鳴らす機能はおまけなのでなくてもいい。

とりあえず、動作チェックのために、Qt6 なら飛ばすようにした。

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
        _mp.setMedia(QUrl::fromLocalFile(files()->mp3_file(sound_names[n]).c_str()));
        _mp.setVolume(100);
        _mp.play();
#endif

ざっくり、Qt6.0.0より前のバージョンの時だけコンパイルされるようにした。

でも待って。Qt6のリファレンスにサンプルコードあるじゃん。 ってことで、速攻で Qt6用のコードも追加した。 音もちゃんと鳴った。

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
        _mp.setMedia(QUrl::fromLocalFile(files()->mp3_file(sound_names[n]).c_str()));
        _mp.setVolume(100);
        _mp.play();
#else
        _mp.setAudioOutput(&_audio);
        _mp.setSource(QUrl::fromLocalFile(files()->mp3_file(sound_names[n]).c_str()));
        _audio.setVolume(100);
        _mp.play();
#endif

ボリュームの制御が QAudioOutput に移されてて、あとは setMedia()が setSource()になっただけ。 とはいえ、違いは違い。

1)
後で考えたらもしかすると QGridLayout *l = qobject_cast<QGridLayout*>(this→widget()→layout() ); とするべきだったのかもしれない。