.mjtの日記復帰計画 RSSフィード Twitter

2018-04-17

[] Irrlicht EngineをXbox OneのUWPに移植してみた

以前用意したビルドシステムを整理してゲームエンジンであるIrrlichtをXboxOneに移植してみた。

D

Irrlicht( http://irrlicht.sourceforge.net/ )は2000年代前半から開発されているゲームエンジンで、最近DX8のサポートは切ったもののソフトウェアレンダリング、スレッド不要といった古いハードウェア向けの特徴を色濃く残している。残念ながら商用ゲームでの大きなdesign winは2010年のOctodad以降無いようだが、シーンローダのような必要最低限の機能は備えている。

今回はそのソフトウェアレンダラの活用を目指して、XboxOneに移植してみた。が、FullHDを描画するにはちょっとパフォーマンス的に厳しいという事情が有り一旦OpenGL ESバックエンドを使用してANGLE上で動作させた。

移植/ビルド戦略

Irrlichtにもプラットフォームレイヤは存在するが、今回は移植層としては完全にSDL2に依存し、SDL2プラットフォーム一般で使用できるようにIrrlicht側を改造する方針を取った。SDL2側のプラットフォーム層はそれなりに使用実績もありある程度動作に確証があったのと、Win32ネイティブ版と可能な限りコードを共通化したかったため。

ビルドは今回もOSライブラリ以外の全てを静的リンクする方針としている。これはVisual Studioのgraphics debuggerを使用する上でDLL側に描画を追い出すと何故かあんまり安定しなかったのと、最終手段としてのLTCG(Link Time Code Generation -- いわゆるリンク時最適化)の可能性を残しておきたかったことに因る。

描画にはANGLEを使用する。Google本家のANGLEは今年に入ってからGLES1の実装が始まっているものの、まだ実用できる段階には無いのでGLES2フロントエンドを使用している。残念ながらGoogle本家のANGLEは依然UWPビルドが壊れているのでビルドシステム側でMSの実装を元に修正している。

SDL2では、SDL2の各APIを静的リンクするのを明示的に禁止しており、通常はSDL2を共有ライブラリとしてビルドするように要求している。このため、ソースコードを騙す方向で#defineを入れ https://github.com/okuoku/sdl2-static-cmake/blob/ee04bc39448c086d723ecc338e65c8cb81b81f2d/CMakeLists.txt#L28 ている。

Irrlichtの側はGLESブランチを使用している。GLESブランチは2008年から開発されているものの依然trunk側にはマージされておらず、trunk側のコードではGLES1/2バックエンドは使用できない。

ビルドには今回もCMakeを使用している。Irrlicht側のブランチ事情もあり、今回はIrrlichtのMakefileをCMake上でパースしてビルド用のデータを生成する( https://github.com/okuoku/irrlicht-cmake/blob/ffc3bf2e950e5ab6e81f744cac345507887240a0/CMakeLists.txt#L40 )方針としている。これを行うことで、ファイル構成が異なるtrunkとOpenGLESブランチの両方を1つのCMakeLists.txtでサポートすることができる。当然、Irrlicht側のMakefileの書き方には依存してしまうが。。

SDL2サポートはIrrlichtに元々存在するSDL1サポートを元にSDL2向けに改造している。IrrlichtではOpenGLコンテキストの管理を自前で実施している(CEGLManager、CWGLManager 等)が、これはSDL上では不要なので特に何もせずflipのみを行うCSDLContextManager( https://github.com/okuoku/irrlicht-generic-sdl/blob/af608e1362f7d50fce3846cf43f023fc9cbd6680/source/Irrlicht/CIrrDeviceSDL2.cpp#L38 )を用意している。

IrrlichtはSDLをサポートしているものの、特に純粋なSDLプラットフォームというのを想定していないため、GENERIC_SDL1_PLATFORMとGENERIC_SDL2_PLATFORMを導入し、可能な限りSDLの移植層を使用したい場合はそちらを定義するというルールにした。UWPはWin32のある種サブセットであるため、いくつかのWin32にあった機能が欠けている。

パッケージング

パッケージにファイルアセットを含める上手い方法はよくわからなかった。CMakeでは.appxmanifestやリソースファイルもexecutableのソースとして追加する方向としており、更にそれらのソースコードプロパティを適切に設定することでリソースファイルをアセットとしてパッケージに含めることができるようになる。

これらのドキュメンテーションは見当らなかったが、CMakeのテストにはUWPアプリをビルドするVSWinStorePhone( https://gitlab.kitware.com/cmake/cmake/tree/ccd17a557cbf8ada18207a72eea78d2adcc9d752/Tests/VSWinStorePhone )があり、それを参考にした。

2018-04-03

[] さよならcall/ccとwith-exception-handlerやguardへの置き換えの検討

yuniではfull-continuationの提供を止めることにした。単純な理由はKawaがこれを提供していないことだが、じゃあこれに代えるプリミティブを何にするのかという点がちょっと悩ましい。

call/ccに対する批判はOlegのページが詳しい( http://okmij.org/ftp/continuations/against-callcc.html )。要するに現実的なプログラムで使うのは難しいという話で、実際継続を介したポートI/OFFIは難しい。yuniやその前身で書かれたSchemeプログラムはそれなりの量が有るが、全てone-shotな継続しか使用しておらず、full-continuationが真に必要なケースは無い。

Rubyはcall/ccを1.9でContinuationライブラリに移し、

非推奨になりました。代わりにfiberを使ってください。

と地獄のようなことが書かれている(Fiberって継続の替わりになるの?)。

yuniも同様にcall/ccを(yuni continuation)あたりのライブラリに追い出してオプショナルにすることを考える。ただ、今のところ、yuniがサポートするScheme処理系で"call/ccが無いことを前提にパフォーマンスメリットを提供している処理系"は存在しないようだ。

with-exception-handlerとguardのセマンティクス

当のKawaはcall/ccをJavaの例外で実装している。

Kawa continuations are implemented using Java exceptions, and can be used to prematurely exit (throw), but not to implement co-routines (which should use threads anyway).

また、KawaはguardなどのSchemeの例外機構をJavaの例外機構にマップしている。

The Scheme exception model is implemented on top of the Java VM’s native exception model where the only objects that can be thrown are instances of java.lang.Throwable. Kawa also provides direct access to this native model, as well as older Scheme exception models.

実際、C++JavaScriptのように言語のコア機能として例外機構を実装しているケースは多いため、これらと良くマッチするならばSchemeの例外機構はプリミティブとして適切だろう。ただちょっと難しいのは、Schemeの例外機構のセマンティクスをより理解して進めないと危ない気がしている。...つまりyuniの場合10近くあるScheme処理系でちゃんと挙動が一致するのかどうかを比較的真面目に気にする必要が出てくることになる。

(正直、Schemeの例外手続きの使いかたを覚えるのが面倒だからcall/ccで書かれているコードがそれなりにあるので、それらをguardなりwith-exception-handlerなりに書き換えたときに挙動を変えない自信が現状あんまり無い。)

Kawaの場合、guardはwith-excpetion-handlerより制約のあるプリミティブで、guardの方が効率的としている:

Performance note: Using guard is moderately efficient: there is some overhead compared to using native exception handling, but both the body and the handlers in the cond-clause are inlined.

(with-exception-handlerはlambdaの分のコストがある -- はずなんだけど上手いこと逆コンパイルできなかったので調査できていない。ToDo。)

ただ、通常の処理系ではwith-exception-handlerをプリミティブとして使用している。例えば、Gaucheのpull request https://github.com/shirok/Gauche/pull/335 は興味深い調整で、Gaucheに元から有ったSRFI-18の with-exception-handler とR7RSの同名手続きの挙動差への言及がある。

guardとwith-exception-handlerはどちらも例外ハンドラを表現するが、その差については最終的なコミットのドキュメントに要約されている:

(SRFI-18のwith-exception-handlerについて、)

例外がraiseやerrorで通知されると、投げられたコンディションを引数としてhandlerが呼び出し元と全く同じ動的環境で呼び出されます。つまり、handler中でraiseを呼び出すと、再びhandlerが呼ばれます。また、handlerから戻ると、制御はraiseの呼び出し元に戻ります。

この振る舞いはSRFI-18により定義されました。これは、この手続きが例外制御の最もプリミティブな構成要素になることを意図しています。例外処理中にアクティブなハンドラを切り替えたければ、自分でそう書く必要があります。

通常、例外処理中に例外が発生したら、それは「外側の」ハンドラで処理したいでしょう。そういった典型的な使い方には、guardを使ってください。この手続きはあくまで、例外処理の最も低層にアクセスしたい時のみ使うとよいでしょう。

R7RSにも同名の手続きがありますが、一つだけ違いがあります。R7RS版はhandlerを呼び出す前に現在の例外ハンドラを一つ「外側」の例外ハンドラに置き換えます。R7RSのwith-exception-handlerの説明はR7RS base libraryを参照してください。

このR7RS(= SRFI-34)とSRFI-18の差は https://github.com/shirok/Gauche/commit/234d0ef154ade1983283ece93f52d462d7833ca4#diff-fb36b06efa4fdb8b419faa6d294f0e0aR350 のコードでSRFI-18 with-exception-handlerを元にr7rs:with-exception-handlerを定義することで吸収している。

Gaucheでは例外ハンドラを戻す挙動をwith-exception-handler内に実装しているが、SRFI-34ではraiseの内部に実装しているという微妙な違いがある。

;; (注: raiseの内部で呼ばれているのはwith-exception-handler"s"であることに注意。
;;      参照実装はwith-exception-handlerを実装するために、例外スタックを直接
;;      設定できるwith-exception-handlersを定義して使っている)
(define (raise obj)
  (let ((handlers *current-exception-handlers*))
    (with-exception-handlers (cdr handlers)  ;; ★ 例外スタックから現在のハンドラを除く
      (lambda ()
        ((car handlers) obj)
        (error "handler returned"
               (car handlers)
               obj)))))

guardの実装にcall/ccが必要な問題

では処理系はwith-exception-handlerさえ提供すれば十分なのかと言うと、実はそうでもないという問題がある。guardの実装にはcall/ccが必要になる

(define-syntax guard
  (syntax-rules ()
    ((guard (var clause ...) e1 e2 ...)
     ((call/cc ;; ★ guardの動的環境のキャプチャ
        (lambda (guard-k)
          (with-exception-handler
            (lambda (condition)
              ((call/cc ;; ★ re-raise用にexception-handler用の動的環境をキャプチャ
                 (lambda (handler-k)
                   (guard-k
                     (lambda ()
                       (let ((var condition))
                         (guard-aux
                           (handler-k
                             (lambda ()
                               (raise-continuable condition)))
                           clause ...))))))))
            (lambda () ;; with-exception-handlerで囲まれたbody部分
              (call-with-values
                (lambda () e1 e2 ...)
                (lambda args
                  (guard-k
                    (lambda ()
                      (apply values args)))))))))))))

この"動的環境のキャプチャ"が曲者で、基本的に他でcall/ccが使われることを想定する限りはcall/ccをここでも使わざるを得ない。例えば、Chezはcall/ccの他にone-shot continuationのためにcall/1ccを持つ( http://cisco.github.io/ChezScheme/csug9.4/control.html#./control:h3 )が、guardの実装には通常のcall/ccを使っている https://github.com/cisco/ChezScheme/blob/75107ee73f3619e1afcf043822cf5fbf675522e3/s/exceptions.ss#L254

通常のシチュエーションではguardやその各clauseの継続から複数回返ることは無いが、guardの内部でキャプチャされた継続から複数回返る場合に問題になる。

yuniでは一部の処理系で自前のexpanderを実装している関係上、移植レイヤに構文を使わないのが原則なので、guardの内部で使用されるcall/ccも何らかの形で抽象化したい。。もちろん、guardが構文である所以は単に変数のバインディングを行っているという特徴だけなので、define-record-typeのように特別扱いしても良いのかもしれない。(R7RS small(= SRFI-9)のrecordには手続きレイヤが無いため、expanderはdefine-record-typeの知識を持っていないと実装できない。つまり、letとかlambdaのようなコンパイラプリミティブと同等の扱いがdefine-record-typeには必要となってしまっている。)

逆に言うと、guardはR7RSの標準構文で唯一call/ccを内包する構文になるため、これさえ特別扱いすれば移植性要求からcall/ccを一掃できる。

ちょっと気になるのは、では、guardやwith-exception-handlerの存在を仮定した場合、内部的にはネィティブ例外で実装されているKawaの継続キャプチャを表現することは可能なのかという点。つまり、call/1ccはguardやwith-exception-handler、dynamic-wind等の自然な例外プリミティブで実装可能か?という問題が考えられる。まだあまり真面目に考えていないが、C++とかJavaScriptのようなネィティブ例外機構を持つ言語ランタイムの上にSchemeを実装する場合にcall/1ccが自然に実装できると言えるならば、call/1ccは良いプリミティブになり得る気がしている。

2018-04-01

[] キラッとプリ☆チャン はどうなるのか

Switch版のプリパラを速攻で買ったのは、1.0.0がヤバいというだけでなく、これが初のキラッとプリ☆チャンコンテンツであるという理由もある。

来週からはアニメ等の展開が始まってしまうのでゼロ付近から予測を書ける最後のチャンスということで。。答え合せは来年。

プリパラとは

"プリパラ"は、簡単には"ガチャ要素のあるアイドルゲームを女児向けアーケード筐体にしたもの"と要約できる。基本的にメディアミックス前提で設計されアニメ等も放映されている。収集対象はアイドル自体ではなく、自分の分身である"マイキャラ"に着せるコーデ(衣装)となる。通常は4スロットあるため最低でも400円(ボトムスとトップスの複合アイテムであるワンピースを使う場合は3スロットになるため300円)掛かる。ただし収集した衣装は何度でも再使用可能なので1プレイあたりの最低コストは100円になる。

ガチャなので排出されるコーデ(衣装)はランダムであり、当初は自由に貸し借りする事さえできなかった。このように厳しい仕様でも400万IDを超えるヒットになっている。仕様上複数キャラクタの同時進行が非常に容易なため単純にプレイ人口と考えることはできないが、単にアーケードゲームとして見ても相当なヒットと言える。

我々大人は無限に課金して高レアリティのコーデを集めることができるわけで、逆に低レアリティ、かつコーデの揃わない状態でどういうプレイになるかをSwitch版で試してみた。最高レアリティの衣装は4000点を超えるが、動画では全て3000点未満にしている:

D

...その辺の小学生かよ。実際には2年目以降では200円でフルコーデが揃うドリームシアターが実装されたり、3年目で自由に衣装を貸し借り出来るようになる等敷居を下げる方向に向かっては行ったが。。初回のみ自動的に適当なコーデが選ばれるが、以降のプレイでは相当額の課金をしないと公式サイトのイメージのようなプレーはできない。(ゲームではアニメキャラクタも選択できるが、その衣装は別途収集の必要がある。)

基本的にプリパラは、"ムシキング"→"オシャレ魔女ラブandベリー"(いずれもセガ)の流れを組む女児向けのアーケードゲームで、新開発のプリンタ筐体(プリチケ筐体)によるビジュアルコミュニケーション要素を最大の特徴とする。大人がtwitterなりなんなりにスクリーンショットアップロードするのを、印字されるカードで代行している。

印刷された「プリチケ」は、折ることで、自分のアイドル・マイキャラの服装の情報、コーデが記録された「マイチケ」と、交換することで自分のマイキャラがほかの子のライブに出演することのできる「トモチケ」に分かれる。

プリパラの素晴しい点は"み〜んなトモダチ、み〜んなアイドル"という一貫したコンセプトを実現しつづけた点で、プレスリリースでは

“普段は普通の私でもキラキラのアイドルに変身できる!”という女の子の永遠の憧れをかなえるコンテンツです。

のように語りつつ、男の娘アイドルに多数の男性ファンを付けるなど実績を残した。個人的にはプリパラは(日本のベンダによる)アニメ・クリエイティブの青春と言って過言でない成功を収めたと評価していて、次世代のコンテンツであるプリ☆チャンがこれを超えるのは非常に難しい問題になると感じている。

勝算がわからないプリ☆チャン

キラッとプリ☆チャンは、前作プリパラのプラットフォーム継承しつつゲームシステムを刷新したコンテンツとなっている。システム上は前作プリパラからの正当進化と言って良い内容で、

  1. ネットワーク機能の強化。前作では他人のゲームにマイキャラが出演した場合に、そのカウントが表示されるだけだった。本作では地域等を含めたより詳細なアナリティクスやレポートを含むようになりネットワーク中でのプレイ動向を知ることができるようになった。
  2. 印刷機能の強化。なんとプリンタを追加した。。
  3. 演出の強化。プレイ中にメッセージ性のあるパートは、プリパラではランダムに選択される"メイキングドラマ"が1度あるだけだったが、おそらく、他の演出(ランウェイ/メンバーアピール)を置き代える形で3度に増やされた。

... のようにゲームがかなり強化されている。

が、そもそも現実にできることのゲーム化を子ども向けに展開してヒットさせるのは非常に難しい。現実のSNSは子ども向けにパッケージはされていないものの、ゲームでやらなくても実際にやれば良いというポイントがどうしても出てきてしまう。

ポケモンが現実の虫取り遊びをパッケージしてゲームにしたように、おそらく、プリ☆チャンもポケモンにおける"対戦"のように現実では難しいものを何か提供するのではないかと思うが、それが見えてこない。スーベニアとしてのカードの魅力、定評のあるキャラクタやコーデや楽曲の品質、店舗大会のようなローカルネットワーク、..? とにかく、プリ☆チャンの企画の勝算として、何か現実では難しい内容を持っているはずで、それが気になっている。

ちなみにアニメの主人公はプリパラの小学生からプリ☆チャンでは中学生に変更している。先のインタビューで、

小さい子にとって、小学生と中学生の差は大きいですよね。その成長の部分がキャラクター性に影響を与えてしまうと各キャラクターの個性や魅力をスポイルするのではないかと思いました。まあ、もともと年を取らせる必要はあったのかなって話にもなりましたが(笑)。議論を重ねに重ねた結果、やはり小学生のらぁらが「かしこまっ!」と言いながら、年上の子を引っ張っていくのが『プリパラ』なんです。

としていて、この後のアニメではシナリオにおける小学生の比重が高まったり(神アイドル編)、そもそも主人公チーム全員を小学生にしたり(アイドルタイム編)と振っていたのを大きく揺り戻している。

現状、プリパラからは別のものを提供しようという意思は感じるものの、コアのコンセプトを "認められる満足感" という外部観察できないものに据えてしまっている。このため、そこに至るまでの過程が欠落しているように感じられ、プリパラの "アイドルになれるゲーム" というコンセプトの解りやすさから比べると大分距離がある。

これら各種の不連続な変化は原点回帰によるクリエイティブ規模の縮小を狙っているのではないかと思う。プリパラを現実に再現する事業、つまり、声優ミュージカルをやり、リアル店舗を何店舗も構え、ライブも展開するような多数のリスクを抱えた構造を一旦リセットしてコンセプトに共感する仲間を増やす時期を取っているように見える。前身のプリティーリズムがリカちゃんの文法を採用していてプリパラではそこから離れたように、プリ☆チャンでもプリパラの文法から離れる必要があったのではないだろうか。

展開の予測

プリ☆チャンのアニメ/ゲーム投入は4月で、これは準備期間を経て7月に投入されたプリパラに比べると相当な垂直立ち上げになっている。今回は筐体の刷新等も無いためプリパラのようなスタートダッシュは狙わず、定期的に話題を提供して都度新規プレイヤを獲得する方針(2年目以降に必要なモメンタムを1年掛けて獲得する)に見える。

間違いなくVTuber類はシナリオに出てくるだろうと言える。プリパラ時代もボーカロイドを模したボーカルドールが初期のシナリオで重要な役割を演じ、後のシリーズの根幹設定としても女神/精霊の形で入っている。問題は人間のYouTuberに比べてターゲット層での認知が弱いのと投入タイミングが難しいところで、文化的にいろいろ出揃った今のタイミングからデザインを詰めて半年後〜つまり年末とかになってしまう。これはモチーフに選んだ "自分発信" の構造上の問題で、プリパラのように"重い"クリエイティブで(現状の自分発信という面で大きな比重を占めている)ミームの変化に付いていくのは非常に難しい。

(プリパラの展開は楽曲やアニメ、製品のリンクなど事前に事業計画を用意する必要があるもので占められている。このため、一般的なソーシャルゲームのような短いスパンでアップデートする構造を持てない。ただ、事前のコラボ計画要求するのではなく筐体でJANコードをスキャンさせてリンクするまたは画像認識等の手法でコラボを後付けできる可能性はある。)

何か現実に"やってみた"できる項目を用意する必要がある。たぶんいくつか偉業(実績/トロフィー)を用意してそれを実際のプレイヤに課すだろう。例えば、プリパラにおけるご当地アイテムのように店舗限定要素を用意し、東京のプレイヤに"広島県で人気上昇中!"のように告知した上で実際に出向かせて特典を与える等。ターゲット層には難しくても、そのフォロワーにはかなりの割合で大人が含まれるので実際にやるのが0.1 %未満でも十分と言える。プリパラにはプレーヤーランクが存在し、アニメでもその最高ランクである"神アイドル"を目指すのが当初のストーリー根幹とされたが、実際のゲームでは累積スコアで決定される廃課金プレーヤという意味合いでしかなかった。実績システムはその点を補強できる。アニメやライブなど現実のイベントもフォローできるようにする可能性がある。実績システムよりも手間は掛かるが。。

筐体外でのタッチポイントを増やす必要が有るが...良い場所が無い。チャットボットやスマートスピーカ類。。?プリチケの優秀な点はそれ自体がタッチポイントであることで、デジタルデバイスには置き代えづらい。アニメキャストが他のプレーヤを品評する番組を制作する等ここに関してはコストを掛けるしか無いんじゃないだろうか。。プリパズや海外で展開しているようなスマホアプリ類は正直キーになるような可能性を感じない。祖父祖母世代が応援できるような仕組は有っても良いと思うがリスクに見合うかは何とも言えない。

新規に導入された会員証に加えて、ドリームシアター相当のライブでチーム証を発行するだろう。会員証は何度も使用されるのでそこにプレミアムを載せることは価値が高いし、そもそもペアライブで既に相当するもの(= ペアプリチケ)を発行している。通常のプレイで作られるephemeralなチームとの差別化は悩ましいし、チームチャンネルのようなものを作らせてもゲームデザインを難しくするだけなので何か別の形で絆を感じさせるものを用意すると思う。例えばマイルームを用意してそこに招待する等。

おもちゃ展開は。。この内容でマイク売るとも思えないので。。マスコット一切無しはシリーズの過去に例が無いのでどこかのタイミングで出すのかもしれないが。。(プリパラと違いリズムゲームシーンではマイクを持たなくなった。)

アニメの方はそれこそ日記のようなどうでも良い内容から宇宙創成まで大小さまざまなスケールの"やってみた"が登場するのではないだろうか。...シリーズ通してチアリーダー常に出てるなという気はしなくもないが、そこは作品のカラーということで残したのだろう。キャラクタは色シリーズで、紫というか前作らぁらの色が空いている。といっても1年目でいきなりクロスオーバされても困る(映画で十分)わけで新キャラクタ用に使われるだろう。

... で、制作側が勝算を見出すような要素がこの中に有るかというと。。ターゲット層にとりこのゲームで疑似体験できる体験のうちもっともコアになるのはおそらく"初めて実際の人間からネットワーク越しに自分が評価される" という体験で、それを具体的なコミュニケーションなしでどうやって実在感を伴って実現できるかに鍵が有るのではないかと思う。ゲームシステム上の数値で表現される"ファン"と実際にチケットを交換した具体的な人間の間には、マジックテープでくっついた野菜を切るおもちゃと、実際に料理ができるママレンジくらいの違いはある。

なので個人的には、"本物の人間がシステムの向うにいること"にフォーカスすると予想している。従来のプリパラは"システムでーす"というセリフを話すゲームシステムを擬人化したキャラクタまで用意して、どこか不気味なまでにシステムを強調していたが、その点を180度転換するのがプリ☆チャンなのではないか。

2018-03-25

[] SDL用の1ソース完結OpenAL実装 MojoAL

久々に新しいOpenAL実装が現われた気がする。

MojoALはSDL作者のRyan C. GordonによるステレオOpenAL実装で、1ソースでOpenALの実装に必要なチャンネル管理やステレオミキサを実装している。もともと氏はLokiでSDL関連の開発をやっていて、そのLokiはOpenALの開祖でもあるので、ある意味本家筋から追加の実装が出てきたと言える。(今普及しているOpenAL-SoftもCreativeメンテナンスしていた参照実装ベースなので本家筋に相当する。)

オープンソースなOpenAL実装には既に多機能なOpenAL-Softが有るが、これはLGPLなのでプロジェクトによっては使いづらい。またSteam RuntimeにもOpenAL-Softは含まれているが激烈に古く( https://github.com/ValveSoftware/steam-runtime/issues/18 )、あまり良いWindows向けのディストリビューションも無い。

OpenALは元々OpenGLのようにIGDを提供してハードウェアベンダがそれぞれのハードウェアに最適化したOpenALドライバを提供できるように配慮しているが、それを実際に行ったのはCreative(とnForce時代のnVidia)くらいで、結局のところ単なるオーディオAPIとして使用されることが多い。

MojoALで興味深いのはマルチスレッド安全であることを念頭に置いている点で、ソースコードのコメント https://github.com/spurious/mojoAL-mirror/blob/a0d889c447a1f4e1e4dabedf63ad1e45722c5789/mojoal.c#L51 でポイントを解説している。

SDLが提供するオーディオI/Fは"pull型"、つまり、オーディオデバイスが再生を進めるにつれ、非同期のコールバックが呼び出されてアプリケーションに新しいPCMデータを要求する。対して、OpenALは"push型"で、アプリケーションは適当なタイミングでオーディオを生成し、再生デバイスに対してpushする形になっている。

MojoALはこのギャップを埋めてSDL上でpush型のAPIを提供する。Waveファイルを再生するといったハイレベル機能は提供しない。(これは既にSDL_Mixer https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_frame.html が有る)

各種OpenAL実装

現時点で気にする必要のあるOpenAL実装は3つ:

これにMojoALも加えて良い気はする。

Creativeはここ数年ハードウェアアクセラレーションに対応したOpenALデバイスを開発していない。

VR/xRの普及はOpenALのようなAPIに良い影響を与える気がするが、実際にはOculus等のSDKは専用のAPIを提供しており、従来OpenALを統合用のAPIとしていたBlueRippleも専用のC++ API( http://www.blueripplesound.com/products/rapture3d-universal-sdk )を販売している。AeonWaveも去年発表した3.xではOpenAL実装を更新しなかった ( http://www.adalin.com/ )。

...というわけで世間の立体音響への盛り上りとは裏腹に、OpenALのようなAPIはあまり盛り上りを見せていないのが現状と言える。

2018-03-24

[][] プリパラ オールアイドルパーフェクトステージ (NS, 1.0.0)

普段ゲームはDL版を買っているけど、これの1.0.0がなかなかなキているということでパッケージ版を入手してみた。

強調しておくと、ここに書くGlitchの大部分は現状の最新版1.0.2でほぼ修正されている。特にゲーム進行に関わる問題が修正されロード速度も多少改善が見られるので、アップデート推奨。

ゲーム内容は。。個人的には歴代3DS版の方が好みかな。。Switch版は基本的にアーケードの再現に振っていて、トリコロールのステージに5人居るアニメ再現ストーリーでもシステム側の制約をそのままにした、かなり衝撃の内容となっている。特に3DS版には常に存在したマイドレスデザインを排していたり、コーデセットのようなショートカットも用意されておらず、ユーザビリティの面でもイマイチに感じる。

既存ゲームをUnityで再現する難しさ

Switch版最大の特徴は、おそらく独自エンジン(+ Bullet Physics)で実装されているであろうアーケード版をUnityで再現しているという点だろう。

残念ながら、既存タイトルのUnity再現の難しさを如実に物語る内容と言わざるを得ない。下に貼った動画にも含めているが会話シーンのテンポが妙に悪い。これは3DS版にも存在する弱点で、人物の切り替えの際に常にロードを挟んでしまう実装になっている。3DS版ではアセットが小さいためかそれほど不満には思わなかったが、今回の場合はFullHDのアセットを毎回読んでいるわけで。。

基本的にロード中はアニメーションの無い静止画を表示して影響を見えないようにしているが、最終シナリオ等ではパーティクルを表示したまま全キャラクタが揃う描写をしているため、そこではロード中に大巾にフレームレートが下がるのを観察できてしまう。

プリパラのアイドルは基本的に3人組で、この3人を纏めたSceneを定義して適宜切り替えているか、SkinnedMeshRendererのようなレンダリングコンポーネントのMesh/Materialをスクリプト側から切り替えて実装しているように見える。特に1.0.0では後者の実装に起因すると見られるビジュアル破綻がいくつか見られる。

また描画の再現性も完全とは言いがたい。例えば、背景にシャボン玉を飛しているようなシーンが、ただの板ポリゴンで実装されていてカメラ制御がない(真円にならない、動画2:00付近で顕著)。もちろんUnityにもパーティクルは存在するが、アーケードと同じような飛び方を容易に再現できるかというと難しいのではないか。

深刻なのはアニメーションの同期が正しくないところが散見される点で、特にアイドルタイム編の再現度が低い。基本的にはモデルに埋め込んだアニメーションを同期再生させているだけのように見えるが、それでなぜ正確にアニメーションが再現できないのか(動画0:07〜)は謎としか言いようがない。

細かい点としては、おそらくUnityの入力フレームワークをそのまま使用しているため連打等の入力が弱い印象を受ける(フレームスキップ時に取りこぼしを起こす)。この挙動自体は筐体でも同じだった気もするが、基本的にはリズムゲーム部分の入力と描画は非同期で実装する方が好ましい。

1.0.2最大の改良点: Flipが概ね正常になった

1.0.0をプレイしていて直ぐに気付く問題は、フレームスキップが正常に実装されていない点だろう。下の動画では2:47付近で発生しているように、描画が間に合わないケースで過去のフレームが表示される。...Unityは元々可変フレームレートのシステムであるため、普通に作ればこのようなことは発生しないはずで、この説明も根本的に何か間違っている可能性がある。

一般に、GPUは描画命令列を消費しビデオRAM上に最終的な描画を出力する。そして、生成された描画はディスプレイコントローラHDMIなりなんなりで画面に表示する。通常のシチュエーションでは、ディスプレイに対して画面を提供しているメモリは更新できないため、描画は2枚(またはそれ以上)出力され、フレーム毎に実際に表示される画面を切り替える必要が有る。この切り替えを"Flip(フリップ)"やSwapと呼ぶことが多い。

このとき、CPU処理が重くGPUの描画命令を出力できないままflipしてしまうと、直前に表示していたフレームを再度表示することになるためアニメーションがガクガクというかビクビクというか何というかになってしまう。この問題は1.0.2では修正されているように見えるが、エンディング動画が1.0.0のキャプチャと見られる動画なためそこでは依然観察できる。

Unityを採用している本作でなぜこのようなことが起るのかはちょっと解らない。Unityエンジン側の問題かもしれないし、そもそもUnityをゲーム画面で使っていないのかもしれない。プラットフォーム側の事情でフリップレートに下限が有り強制的にフリップを入れているといった事は考えられるが。。

この改善はおそらく描画側の修正というよりは、無駄なGCを減らすといったスクリプト側の修正でフレームスキップの機会そのものを減らしたのではないかと思う。ドリームシアターなど描画/スクリプト負荷の高そうなシーンでは1.0.2でも稀にこの現象が見られることがある。

その他の改良点

D

1.0.0で観察した問題を集約した動画が撮れてしまった。(上の説明からもリンクしている)

  1. アイドルタイム編のライブ導入シーケンスのアニメーションがズレている(これだけ1.0.2でも未修正)
  2. アイドルタイム編のストーリー進行中にもかかわらず神アイドルチャレンジ(前シリーズのイベント)が発動する
  3. 当然ジュエルマイクは持っていないため神アイドルチャレンジではただの棒(タクト)だけを持ってダンスすることになる
  4. リザルト表示のSEがフェード完了前に鳴る
  5. 点数の表記が999999になっている
  6. 本来シナリオ上発動するはずのイベントであるスーパーアイドルタイムは発動しない
  7. シナリオはクリアしたことになっているにも関わらず、次のシナリオが出現しないため進行不能になる(1.0.2では、このような状態のセーブデータは未クリアに戻るようになった)
  8. また、この方法でシナリオを中断するとシナリオ目標の表示も不正になる

...というわけで1.0.0ではあまりシナリオを進められなかったので、観察できない他の問題もあったのかもしれない。もちろん1.0.2はこの辺の問題の大部分を解決しており、正常にゲームがクリアできるようになった。

他の細かい改善としては:

モデル描画の改善。最も目につく改善は、キャラクタ頭部のレンダリングが改善されたことだろう。パッケージ裏や公式サイト素材では基本的に1.0.0の描画を採用しているため顎部にアウトラインが入っておらず首と繋がっているように見えるが、1.0.2ではアウトラインが描画されるように修正された。また、みらいの描画で顕著だが口元のアウトライン描画も修正されている。

家庭用のプリパラでは伝統的に動的なアウトラインが採用されている。つまり、カメラ距離に応じて押し出し量を変えたり、動的にラインを生成するアウトライン描画を採用しており、一定の太さのラインが常に描画されることになる。(筐体ではおそらくポストプロセスで描画しているのではないだろうか)

  • 1.0.2: 遠くのぴのんと近くのじゅのんで同じくらいの太さのアウトラインが描画されている

また、プリパラは伝統的に焼き込み影を採用していない(髪形等がカスタマイズ可能であるためと考えられる)。首元は遮蔽率も高く、かつ、プリパラは頭部アウトラインはカスタマイズ不可なので焼き込み影で処理してしまっても良いと思うが。。

1.0.2でも、光源(法線)の特殊処理は実装されていない。例えば、アイドルが左側に表示された場合の左目やワンポイントの"ピンクほっぺ"は、下向き時に暗く表示されてしまうのでかなり違和感が有る。

シナリオのライブシーンでチーム2人目の名前が常にマイキャラになっていた不具合が修正された。...これは単純なロジックバグだろう。メンバーアピールも同様に間違っていた。

  • 誰だよ

ちなみにアニメ本編でソロライブだった等の事情で補充メンバを入れているライブで、プリパラアイドルを使用しない場合は"ぷれいやー1"のような適当な名前が表示されていた。1.0.2ではマイキャラの2人目以降を埋めるように変更された?ように見える。ダンスは全体が存在するわけではないため、チームアピールを省略するような対応はできなかったと見られる。

...3DSのように同一人物のメンバーアピールを3回くりかえすといった対応でも別に良かったのではないだろうか。。例外はペアライブでの仲良しチェックで、これはおそらくランウェイの延長で対応している。(3DSでは、ずっトモ!なかよしチェックのモーションを残したままリズムゲームを継続 -- そもそも3DSではランウェイが再現されていない。)

  • 誰だよ

アイドルタイム編でサイリウムチェンジする等間違ったシーケンスが修正された。エフェクトは常に正確に見えるので単にSEの設定ミスが修正されただけではないだろうか。

ライブ時にコーデ決定後フェードアウトしてからロードに入るように修正された。基本的にアニメーションを半端なタイミングで止めないように徹底されている。

キャラクタやシーン変更とロードの同期が修正された。一部シーンのロード時にメッシュだけ先に切り替わりキャラクタの描画が破綻したり、暗黒空間にアイドルがT字ポーズで直立するという微妙に恐しい現象が発生していたが修正された。ただ、ライブシーンでは根本的には修正されておらず、1.0.2でも依然カメラ位置の切替と実際のゲームオブジェクトの移動/設定が同期しないケースが散見される。

  • 1.0.0: 妙な位置に表示されたぴのん(直後にそふぃに切り替わるがそこで描画が乱れる)

ドリームシアター等でテクスチャが正常にロードされないケースがあったのが修正された。例えばドリームシアター後のリザルト画面が黒背景であったり、6人ライブでのメイキングドラマの海が描画されないといった不具合が解消している。

シナリオのクリア状態が明示されるようになった。1.0.0では、そもそもシナリオリストの実装が異常で、常にシナリオグループ最初のシナリオが新規シナリオとして提示されてしまっていた。本作では基本的にシナリオは一本道であり、未クリアのシナリオは(存在する場合)シナリオグループの最後のシナリオに固定されている。このためNew表記が無くても特に困ることは無い ...が、3DSのシリーズ作では常に表示されていたので。。

シナリオのクリア条件が2つ以上ある場合は両方が表示されるようになった。1.0.0では1つのみが表示されており、かつ、そのようなシナリオは正常に攻略できないことが多かった。

ストーリーグループ毎に固有のアイドルが表示されるようになった(Nintendo Switchオリジナルストーリーではマイキャラ、他ではファルル、らぁら等)。1.0.0では直前に表示された適当なアイドルが表示されており、切り替え時に表示が乱れていた。 追記: これは1.0.2でも同様の仕様で、次に選択されたストーリーのライブのセンターが表示されるようだ。

選択肢が色を暗くするのではなく枠で示されるようになった。これは典型的なデザイン問題で、2択の頻出する本作ではどちらが選択されている選択肢なのかがわからなくなる事態が多かった。ちなみに3DS版と異なりタッチ操作には一切対応していないようだ。

  • 1.0.0: 一番上が選択された状態。1.0.2では枠が追加された。

トップメニュー等の機能しない"B もどる"表記が削除された。これに限らず、UI関連のバグは大巾に少なくなったように見える。また、誤植等も修正されている。ただ、ライブモードのアイドル選択が非常に重いのはあいかわらず。。

  • 1.0.0: 無意味な"B もどる"(ここではBボタンは機能しない)

  • 1.0.0: 達成条件の間違い(正しくはグランプリファイナルで、これには難易度等が無いので仮素材として最初の曲であるトイトイ・テイルを入れていたのではないだろうか)

おそらくメモリリークに起因すると見られる異常終了や無音でライブが進行する現象が発生しなくなった。特に家庭用のプリパラは伝統的にイベントの高速スキップが無いため異常終了すると長いイベントを再度見なければならないのでキツいものがあった。(1.0.2では一度クリアしたストーリーはライブを直接遊べるように変更された)

とにかく1.0.2はゲームプレイをかなり改善したが、それでも依然3DS版の品質には到達していないように感じられる。Unityへの移行はプリパズ等のスマホタイトルの開発リソース共有や、(筐体を含めた)今後のシリーズタイトルも同様のプラットフォームで提供することを見越したものだと考えられるが、少くとも現時点ではあまり良い印象を受けていない。この手の、筐体をビジネスの中心とするタイトルの家庭用リリースは常に微妙な立場に有るが、家庭用タイトルのリプレイ性は筐体のリテンションを維持する上でもプラスに作用するのではないかと思う。

2018-03-18

[] 言語処理系間でライブラリを共用する難しさ

comp.lang.schemeにも書いた https://groups.google.com/d/msg/comp.lang.scheme/GuHmoUNBplA/dykAeuE3CQAJ けど、処理系間でライブラリを共用するのはそれなりに難しい。

nmoshの次代ライブラリフレームワークであるyuniはこの問題に正面から取り組んでいる比較的珍しいプロジェクトで、独特のポジションに居るのではないかと思う。

Schemeと他の言語の違い

Schemeは独占的な処理系が無いというのが非常に大きいポイントだと思う。実は世間で使われている言語の殆どには何らかのリーディング処理系が有り、そのような処理系に対応できないライブラリは単に存在しないのと同じと言い切って良い:

  • PythonにおけるCPython(C言語によるオリジナル実装)
  • RubyにおけるMRI(Matz' Ruby Implementation, C言語によるオリジナル実装)
  • C++におけるgccまたはMSVC

Schemeにはこのような処理系は存在せず、そもそもライブラリの共用を第一義としてデザインされたはずの標準であるR7RSやR6RSは現時点ではライブラリの共用にはあまり使えていない。

ただ、Schemeのように独占的な処理系を持たない言語が珍しいわけではなく、Common Lispのように機能しているデファクトスタンダード(asdf, quicklisp)を持つ言語もある。このため独占的な処理系の存在はライブラリの共用のために必須の存在では無いといえる。

yuniのフォーカス

たぶん全てのSchemeを名乗る処理系をサポートすることはできない。なのでyuniはサポートする処理系を限定することにした:

  • 現時点でメンテナンスされている(例えばChez移行後のRacketはサポートから落ちるだろう)
  • R6RSかR7RS smallのいずれかをサポートしており、かつ、FFIをサポートしている(C言語で書かれている場合)
  • 非R6RS/R7RS処理系の場合は、SRFI-6(string port、R7RSサポートに必要)、SRFI-30(S式の複数行コメント、無いと不便すぎる) が必須
  • syntax-rulesをサポートしていない処理系は自前のexpander(現状はAlexpander)でサポート

これだけ条件を絞っても適合する処理系は10程度は有り( https://github.com/okuoku/yuni/blob/127a7005ab1047ef16974dda75f12fcb95726295/README.md )、さらにcyclone( https://github.com/justinethier/cyclone )のように依然候補に上がる処理系は有る。

yuniはR6RSとR7RSを混ぜて使っている。ライブラリフォーマットはR6RS(R6RS-liteと呼んでいる)、標準ライブラリはR7RSという選択は、

  • R7RSライブラリ形式は現実のアプリケーションを開発するのには不向き & R6RSのlibrary構文はdefine-libraryの厳密なサブセットになっている
  • R7RSで追加された互換性構文(cond-expand)はChezのような処理系ではサポートされておらず無意味
  • R6RSの標準ライブラリはUnicode等実装が重いものを含んでいるため他の処理系にadoptされることはあまり期待できない
  • R7RSの標準ライブラリの殆どはR6RSで実装できる

といった点に依る。

(String portやchar-ready?のようにR6RSでは実装のしようが無いものもあるが、String portはSRFI-6としてそれなりに普及しているので採用、char-ready?やu8-ready?は存在意義が無い http://d.hatena.ne.jp/mjt/20161020/p1 ため省略している)

リーディング処理系無しでライブラリエコシステムを作ることはできるのか?

仕様に無い挙動を規定してしまうような強い処理系はライブラリエコシステムの形成に良い影響を与える。というか、言語仕様だけで機能するライブラリエコシステムを作ることは非常に困難なのではないだろうか。。

yuniのモデルはリーディング処理系ではないものの、リーディングライブラリの位置を狙うことで同じことをしている -- char-ready?を切り捨てる選択をするには、どこかのタイミングで同じことをしなければならない。

個人的には、リーディング処理系にせよリーディングライブラリにせよ存在すること自体はそれほど悪いことでは無いと思っている。R6RSもR7RSもそれ単体ではライブラリエコシステムを作れないかもしれないが、その辺の経験を踏まえてR8RSなり必要なSRFIなりをデザインすれば良いわけで。。