2019年末特別企画: シューティングゲーム ポラックスを工学?する - Engineering analysis for POLLUX -

ゲーセンミカド さんのシューティング動画でおなじみ? 韓流シューティングゲーム「ポラックス POLLUX」。 動画内でも触れられているように数々の不可解な挙動がある。 それらについてソフトウェア・エンジニアリング的に 「どういった設計・実装をすればそういう挙動になるのか?」 勝手に妄想してみようという無責任極まりない企画。

なので、「こんなの工学じゃない」みたいなマジレスは全力却下なのでw

前提

ポラックスとは?

  • NTC という韓国メーカーが作ったアーケード向け縦スクロールシューティングゲーム
    • Copyright 表記は1991となっているが、日本に入ってきた時期はもう少しあとの模様
    • 普通に説明がつかない挙動がいくつかある (後述)

本記事の筆者について

  • 職業ソフトウェアエンジニア歴22年、ゲーム開発経験はほぼなし
    • 8bit PC の BASIC でプログラム書いてから、だと35年くらい
    • ゲーム開発は仕事としては経験なし (学生時代に 8bit, 16bit PC で趣味としてはやっていたが)
    • デバイスドライバーとかよりは、 MW 〜 アプリ層の経験が圧倒的に多い
    • プラットフォーム的には 16bit 〜 64bit, RTOS〜Window〜LinuxUNIXクラウド、組み込み〜ゲーム機(開発してたのは非ゲーム)〜PC〜HPC〜サーバーソリューションと守備範囲は広め
    • 言語的には C++ が圧倒的に経験多い、あとは C、shell、JAVAOctavePython、Shader Language、OpenCL、…

参考資料

ポラックスの不可解な挙動まとめ

  • 自機のショットをパワーアップさせたり 3way に変更すると、サブウエポンのミサイルの発射数が目に見えて少なくなる (発射間隔が長くなる)
  • 敵や敵弾の多い場面で1P側のボム発射ボタンおよび2P側のレバー右が効かなくなる
    • 「ボムは出るまで押せ」「ボムに連射つけて欲しい」
  • ハードウェア連射装置を使うと、敵が多くいる場面で自機の弾が出なくなる
    • ゲーム内でアイテムと取ると適用されるソフトウェア連射は問題ない
  • ボス敵が突然固くなり数分間攻撃を当て続けても倒せなくなる (普段はもちろんもっと短い)
  • ブラックホールアイテムが一定時間経つと画面最下段まで移動(スクロール)する

その他

ゲームプレイ自体に直接影響するわけではないが、不可解な事象たちや特徴的なゲームシステムなど

  • 微妙に音痴で短いフレーズが延々ループする BGM
  • 毒々しい配色・デザインの敵たち
  • 他の弾と見分けがつかないデザイン・弾道からの突然変化球する敵弾
  • プレイヤーは「POLLUX と戦うこと」を命ぜられている
  • 一定距離にある敵弾を吸い込むブラックホールが時々登場する
    • 吸い込まれて軌道が変わった敵弾に自機が当たっても平気

メインショットをパワーアップしたり 3way にすると、サブウエポンのミサイルの発射数が目に見えて少なくなる (発射間隔が長くなる)

  • ノーマル + ミサイル + ソフトウェア連射
    • メインショットが画面上に4発(|| で一発と数えて) + ミサイルが8発 (4組)
      f:id:minosjp:20191231192208p:plain
      ノーマル + ミサイル + ソフトウェア連射
  • 3way + ミサイル + ソフトウェア連射
    • メインショットが画面上に11 or 12発(|| で一発と数えて) + ミサイルが3 or 4発 (2組) 出ている
      f:id:minosjp:20191231195424p:plain
      3way + ミサイル + ソフトウェア連射 その1
  • 上記2つは敵の有無など条件が違うので正確な比較ではなく以下妄想であることは最初に断っておく
    • メインショットがノーマルのときは、連射に合わせて正しく弾が出ているように見える
    • メインショットが 3way のときは、メインショットは連射に合わせて正しく出ているように見えるが、ミサイルは連射2回につき1回しか出ていないように見える
    • 上記以外の 3way 時にはミサイルの出がもっと悪いときもあるように見える
    • 何が「ミサイルの出」を制約しているのか…
    • 次の画像は連射に対してメインショットもミサイルも出ていない瞬間があるように見える (自機の真ん前にある自弾のさらに前の弾との間隔が空いてる)
      f:id:minosjp:20191231202548p:plain
      3way + ミサイル + ソフトウェア連射 その2
    • メインショットとミサイルをあわせた画面内にある自機の弾数に上限があるのでは…
      • 優先的に出るのはメインショット…??
      • ゲームフレームごとにやってるだろう発射処理擬似コードで書くとこんな感じか…
if (ショットボタンが release から press 状態になった) {
  if (画面内の自弾総数が制限以内) {
    メインショット発射処理
  }
  if (ミサイル装備中 かつ 画面内の自弾総数が制限以内) {
    ミサイル発射処理
  }
}

敵や敵弾の多い場面で1P側のボム発射ボタンおよび2P側のレバー右が効かなくなる

  • 処理落ちで処理がすっ飛ばされるにしても「レバーの特定方向のみが効かない」というのは不可解だし、 1P / 2P で症状が違うのも不可解
  • 普通のSTGで「処理落ち」というとゲーム自体のフレームレートが下がって露骨にスローがかかっているような状態になるが、ポラックスはそういう感じがない
    • 規定のフレームレートに処理が間に合わなくなるとそのフレームのやりかけてる処理を全部やめてる??
  • 1P のボム発射ボタンと 2P のレバー右の症状が紐付いているのも容易な説明は思いつかない
    • GPIO とかが足りなくて ADC 使って入力を受け付けていて 1P のボム発射ボタンと 2P のレバー右が同じ入力系統になっていて過負荷で処理が打ち切られてすっ飛ばされて効かないというのは考えられるが、1P のボム発射ボタンと 2P のレバー右を同じ系統にする理由がまるで思いつかない

ハードウェア連射装置を使うと、敵が多くいる場面で自機の弾が出なくなる

  • もし、フレームレートに間に合わなくなると処理を打ち切っているせい、だとすると、レバー移動すら効かなくなったりしてもおかしくないのだがそういう挙動はなさそう…
  • ミカドさんのハードウェア連射装置は、動画中で「15連」「30連」という言葉が聞かれるように、いわゆる「シンクロ連射」だろうから VSYNC に同期しているはず
    • 読み取る側の処理が VSYNC に同期してない?

ボス敵が突然固くなり数分間攻撃を当て続けても倒せなくなる (普段はもちろんもっと短い)

  • ヒット判定で光っているようには見える
  • 「光らせるところまでは処理入れてるのに、ダメージ処理までは入らず打ち切ってる」なんてことはとても考えられない
  • ミカドさんの攻略動画は「ミサイル出なくなる」対策のため基本的にあまりパワーアップせずにボス戦に挑んでる
    • テストはパワーアップしてる状態でしかやってなくて、小さいダメージの積み重ねが正しく入らない状態になることがある?

ブラックホールアイテムが一定時間経つと画面最下段まで移動(スクロール)する

  • これこそ妥当な説明が思いつかないやつ…
  • 「画面上の絶対位置にとどまるはずのものが急に動き出す」「最下段にはとどまり続ける」という2点に分解して考えるのが良いか…??
  • 「画面上の絶対位置にとどまるはずのものが急に動き出す」
  • スクリプトなのかデータなのかわからんが、とどまる時間を有限でしか表現できないようになっていて、とりあえず最長にしてあるとかだろうか…
  • 「最下段にはとどまり続ける」
    • 自発的な移動以外では画面外に出られない (fail safe 的なコードで何となく画面下端からフレームアウト出来ずにとどまり続けている、みたいな…)

結局…

いろいろ考えてみたんだけど、どんなポカをやったらあの挙動のまま世に出てしまうほどのものが作れるのかは分からんかった(滝汗)。 ただ考えてみた結果ちょっと思い当たったのは「一般的な同期システムに則らずにゲームの1フレームを処理してるんじゃないか?」ということ。

  • 同期システムが厳密な周期を刻んでいない
  • その同期システムの中で規定している1フレーム内に処理が間に合わなかった時は、フレーム内の処理を適当に途中で打ち切ってる

とすると、何となく説明がつきそうなことが多いんだよな…。 とはいえ、これも妄想ですけどw

short の複合代入演算

-Werror=conversion  とかコンパイルオプション厳しくしちゃうと

    short n = 1;
    const short m = 2;
    
    n += m;

って通らないの!?

prog.cc: In function 'int main()':
prog.cc:10:7: error: conversion from 'int' to 'short int' may change value [-Werror=conversion]
   10 |     n += m;
      |     ~~^~~~

Implicit integer promotion 後の代入が許せないってこと?? ぐぬぬぬ…。

git で「特定のサイトのみ proxy を通さない」設定

なんかちょっとハマったんだけど、 例によってちゃんと調べてないので鵜呑みにはしないで欲しいw

例えば

  • proxy の内側にいる
  • 普段は proxy の内側にあるオンプレミスの git サーバーを使ってる
  • 時々 GitHub にアクセスしたい

みたいなときに

git config --global http.https://github.com/.proxy http://proxy.foo.com:port

として ~/.gitconfig が

[http "https://github.com/"]
  proxy = "http://proxy.foo.com:port"

となって [http] に対する指定がない状態だと上手く動かないケースがあるようだ。 加えて

[http]
  proxy = ""

があってもダメみたい。 あまり悩む余裕もなかったのと、 オンプレミスの git サーバーが今のところ1個しかないので、

[http]
  proxy = "http://proxy.foo.com:port"

[http "オンプレミスのURL"]
  proxy = ""

という書き方にして逃げたんだけど、 どうも釈然とはしないな…。

cppcheck が template あたりで syntax error を出す(誤検知する)場合

以下、あんまりちゃんと追っかけてないので信じないで欲しいがw

[include/fixedpointnumber_conversion-priv.h:31]: (error) syntax error

特にこれ以上メッセージ出ないやつ。 どうも途中のバージョンであったバグらしくて、 結論的には cppcheck を最新にすれば治まった。

cppcheck の version OK / NG 備考 
1.72 OK 2019/11/15 現在 Ubuntu 16.04 の apt install cppcheck でインストールされるバージョン
1.82 NG 2019/11/15 現在 Ubuntu 18.04 の apt install cppcheck でインストールされるバージョン
1.89 OK 2019/11/15 現在の最新、 macOSbrew install cppcheck でインストールされるバージョン

bug report とかソースコードを少し追っかけた感じでは、 .h ファイルのようにファイル単体では C++ か C か判別つかないファイルで template というキーワードを見つけると発症するのかな…。

これを調べるためだけに Ubuntu 16.04 と 18.04 の仮想環境を用意しちまったさ…、 …とはいっても

$ mkdir -p vagrant/ubuntu/18.04
$ cd vagrant/ubuntu/18.04
$ vagrant init ubuntu/bionic64
$ vagrant up
$ vagrant ssh

とかするだけで環境用意できるなんていい時代になったよねぇ…。

キー配列の異なるクライアントからリモートデスクトップ接続

minosjp.hatenablog.com

昨日書いたことは間違っちゃいないんだが、もうちょっと続きがあることが分かった。

  • 接続先でサインアウトしておく
  • 接続元で接続時にサインインする
  • 接続元でサインアウトせずにリモートデスクトップを切断する

こうすると、このあと接続先コンソールを使うと接続元のキー配列になる。 というわけなので、ルールはごくシンプルで

  • サインインしたクライアントマシンのキー配列がサインアウトするまで使われる

ということのようだ。

英語キーボードのWindowsに日本語キーボードのWindowsからリモートデスクトップ接続するとき…

とすると…

接続先でログオンしたままのとき

  • 接続元は英語配列になる (= 接続先のキーボードに基づく配列になる = 操作しているキーボードの刻印と入力が食い違う)

接続先でログオフしてある場合

  • 接続元は日本語配列になる (= 接続元のキーボードに基づく配列になる s= 操作しているキーボードの刻印と入力が一致する)

ということのようだ…。

保存できないやりかけの作業でもない限りロフオフしろってことか…。

接続元側で一旦ロフオフ操作をしてから再接続すれば治るのかは試してない… (おうちに Windows 環境1つしかないし、 その唯一の Amazon Workspaces の VDI が Internal error で繋がらねぇ… orz)。

Windows 環境に Scoop で ImageMagick をインストールしたら…

ImageMagickconvert コマンドを使おうと思って PowerShell とかから単に convert -append foo.jpg bar.jpg baz.jpg とかすると Windows にもともと入っている CONVERT コマンド (FAT - NTFS 変換するコマンド) が起動したりするので注意。 微妙に分かりにくいエラーメッセージが出るので何が起きてるのか把握するのに10分くらいかかってしまった…。

Scoop でのインストールに限ったことじゃないのかもしれないけど…。