‘Typing Field’のエントリ

7月が終わってしまうので日記を更新。

Typing Field、周辺の実装を進めている。まずはセキュリティ。20年前にウェザタイ作ったときはパスワードも平文で送れば良いし通信も平文でいいや、って感じだったけど、さすがにちゃんと作らないとなあ、ってことで「オンラインゲームセキュリティ」という本を読んでいる。

認証については、FIDOっぽく公開鍵を使ったパスワードレス方式にしてみた。ついでにユーザ名が一意でなくてもいいように、自動生成IDで区別するようにする。ウェザタイはすでに主要な名前は取られていて新規参戦が難しい。この方式なら好きな名前を使える。

通信はどうしようかな。単純にTLSにしてもいいけど、遅延とか問題になるんだろうか。

あとは、「気持ちいい」から考えるゲームアイデア講座」を読んでみた。ゲームシステムをいろいろ考えているけどなかなか面白くならず、試行錯誤。

今のペースだと完成まであと3年くらいかなあ。これだけに絞りたい。

気分転換にパネル表示をいじってみる。とりあえず半透明のパネルを出していたのだが、もう少し工夫したいのですりガラス処理+角丸四角形にしてみた。

中身のデザインが今ひとつだけど、とりあえずパネル自体はこんな感じかな。すりガラスも角丸もシェーダーで実装。

すりガラス

すりガラスは一般的にはガウシアンブラーを使うらしい。ただ、普通に5×5とかのカーネルで処理すると重くなるので、縦5をかけた後に横5をかける、みたいに2パスでやるらしい。検索するとそういう方針でGrabPass(レンダリングされた画像を取得するやつ)を使用した例がたくさん出てくる。

ただ、Universal Render PipelineだとGrabPassがサポートされてなくて、同様の機能を持つCameraOpaqueTextureを使うと2パスができない(と思う)。なので、カーネルを省略して十字みたいな感じに近似してみた。上の画像の通りそれっぽくはなるからいいかな。

角丸四角形

角丸四角形はなかなか難しい。公式的にはRounded Rectangleっぽいんだけどそのままコピペしてもうまくいかない。なので参考にしながら自力で実装。

float RoundedRectangle(float2 uv, float width, float height)
{
    // uvは-width/2~width/2、-height/2~height/2の座標

    // uvから一番近い矩形状の座標を計算する
    float2 pt = float2(min(abs(uv.x), width / 2 - _Radius), min(abs(uv.y), height / 2 - _Radius));

    // 計算した矩形状の位置とuvとの差分ベクトルの長さを計算する
    float d = length(pt - abs(uv));

    // 半径以内なら1、半径よりおおきければ0
    return smoothstep(d - _Radius / 10, d + _Radius / 10, _Radius);
}

fixed4 frag(v2f i) : SV_Target
{
    // 角丸四角形判定。範囲内にあるならr=1、ないならr=0
    float width = 1 / _CameraOpaqueTexture_TexelSize.x;
    float height = 1 / _CameraOpaqueTexture_TexelSize.y;
    float2 uv = float2((i.uv.x - 0.5) * width, (i.uv.y - 0.5) * height);
    float r = RoundedRectangle(uv, width, height);
 
    // 角丸四角形外(rが0に近い)は描画しない。
    clip(r - 0.1);

    ・・・
    ガウシアンフィルタ処理(他でたくさん紹介されているので省略)
    ・・・

    // 角丸のα値:r
    // オリジナルのα値:α
    // オリジナル色:o
    // ガウシアンフィルタをかけた背景色:b
    // とすると、以下で表せる
    // rα*o + (1-rα)*b

    fixed4 col = r * _Color.a * _Color
        + (1 - r * _Color.a) * b;

    return col;
}

こういうときに数学が役立つ。数学の知識がない場合、ぴったりうまくいくコードを見つけてコピペするかAssetを買うしかないので不利になる。

5月の日記がゼロになってしまうので何か書いておこう。

相変わらず副業(会社員)が休みなしでなかなかTyping Field開発が進まないのだが、今は音作りをやっている。BGM、システム音、攻撃、レベルアップなどいろいろなSEが必要。

BGMは一応作曲の入門本を読んではみたけど、さすがにすぐに使えるレベルにはならなさそう。昔から打ち込みとかMIDI楽譜生成ツールのPurentro作ったりはしてたのでなんとなくは分かるし、作曲少女シリーズを読むと何かしら作れそうな気分にはなるけど・・・。ちなみに同じ作者さんのトークバックは面白かった。AIで作るのも考えはしたけど、コンセプトにあったBGMという縛りがあると難しそう。てことでまずはコンセプトに合いそうなBGMをpixtaで購入。いずれ置き換えられればいいなあ。

効果音も素材から全部買ってもいいけど、結構大量に必要。簡単なものならシーケンサーで作れるってことで試行錯誤中。まあシーケンサの方が金額的には高くなるけど。ただ、どこをいじるとどんな音になるのか、ノウハウが出回ってないので難しい。サウンドエフェクトの作り方を参考にしているけど、それに載っていない音はどう作るのか想像ができない。再現したい音を観察してアタックとかエフェクトとかがこうなっているはず、みたいなことをやっていく、ってことだと思うんだけど・・・。

脈絡なくスクリーンショット。

Typing Field開発。今週はBlender+Unityのアニメーション。ずっと適当な動きのまま放っておいたのでそろそろちゃんと作り込む。

やりたいことは、プレイヤがキーボードを首からぶら下げて、問題文章が表示されたらキーボードに手を置いて、そのまま歩き回れるところまで。Action People Pack 2の部品として使えるように作っているが、モデルには関係ないはず。次回いじる際にはもう忘れているのでメモ。環境はBlender 3.1.2+Unity 2021.2.18f1。試行錯誤の結果なので不要な手順、間違ってる手順とかもあるはず。

[Blender] キーボードモデルの作成

まずBlenderでキーボードを作成。四角とベジエ曲線、キーボードのテクスチャとバンプマッピングで作った。

Advanced People Pack2で部品として使う場合はオブジェクトが一つになっている必要がるので、キーボードとロープを一つにする。ベジエ曲線を[ConvertTo]-[Mesh]、キーボードとロープを[Object]-[Join]で結合する。あとで調整できるように変換前のオブジェクトをバックアップしておく。

[Blender] Rigとの関連付け

このままモデルを出力してしまうと、Rigと関連付けられていないのでUnity側でデフォルトの位置に表示されてしまう。そのためRigに関連付ける。オブジェクト->適当なRigの順でShiftクリックで選択し、Alt-P-[With Automatic Weights]で関連付け。するとモデルの下にVertex Groupsが作られる。関連付けを解除するにはオブジェクトを選択してAlt+P-[Clear-Parent]。

この状態だと全てのRigにちょっとずつウェイトが関連付けられて変形してしまうので調整。衣服なんかだと良い具合にウェイトを調整して、ボーンアニメーションに追従させるのだが、今回の場合キーボードは曲がったりしないので特殊な設定になる。要するに一つのボーンに全体を100%ウェイトにして、他のボーンにはウェイトなしにすれば、一つのボーンの動きや角度に追従して、何も変形しないものが作れる。ってことで、今回はChestのRigに追従させる。Neckの方が妥当に見えるけど、走るモーションと組み合わせたときにキーボードと首の角度差によってめり込んでしまったのでChestにしてみた。まずChest以外のVertex Groupを削除し、Chestを選んで[Weight Paint]モードにして真っ赤(1.0)にする。

[Blender] モデルの出力

ここまでやったものをfbxで出力。Blenderを再起動する度に設定が初期化されるので面倒。オブジェクト-Rigの順番に選択して、[File]-[Export]-[FBX (.fbx)]で、以下の設定で出力。デフォルトと違うのは、Select Objects、ArmatureとMeshを選択、FBX ALL、Apply Transform、Face、Only Deform Bornsオン、Add Leaf Bonesオフ、Bake Animationオフ。その際、モデル自体は非表示にしてRigだけにしておくと余計なものがエクスポートされない。

[Unity] モデルの読み込み

次はUnityでモデルを読み込み。fbxを選択したときのプロパティから、[Rig]タブでAnimation Typeを[Humanoid]に、MaterialsタブでMaterial Creation Modeを[Import via MaterialDescription]にする。マテリアルは一度fbxからExtractしておいてUnity側で再設定。しなくてもいいかもしれないけど。

Action People Pack2で部品として扱う場合、Item 1か何かのプリセットを増やして、キーボードモデルとマテリアルを設定する。マテリアル側でBump Mappingを設定し直しているのでちゃんとキーが立体的に見える。ここで設定を変更した場合、キャラクター側で設定をロードし直すのだが、Animation Controllerがデフォルト値に戻って自作のアニメーションが再生されなくなってしまうので、手動で設定し直す。

[Blender] アニメーションの作成

Blender側でアニメーションを作成する。モデルと同じblenderファイルでできればいいんだけど、何故かボーンがうまくUnityに反映されなくなってしまう(手首の角度が変になった)ので別々のファイルにしてある。

Rigを選択して、Posing Modeで好きなようにポージング。[A]キーでRigを全て選択して[Pose]-[Animation]-[Insert Keyframe]。とりあえず単純にキーフレーム1個だけ。

1つのfbxファイルに複数のアニメーションを入れることもできて、その場合、Animationタブの「Dope Sheet」になっている箇所を「Action Editor」にして、アニメーションを追加する。

設定が終わったら、[A]キーでRigを全て選択してfbxエクスポート。さっきの設定に加えて、[Bake Animation]をオンにしておく。その際、モデル自体は非表示にしてRigだけにしておくと余計なものがエクスポートされない。

[Unity] アニメーションの設定

Unityでアニメーションを読み込み。fbxを選択したときのプロパティから、[Rig]タブでAnimation Typeを[Humanoid]に、MaterialsタブでMaterial Creation Modeを[None]。Animationタブで、Clipsにアニメーションが全て表示されていることを確認。

あとは普通にAnimation ControllerでMotionを設定する。複数のアニメーションを共存させる場合はLayer分けして、AvatorMaskを設定。

[Unity] 実行

ついに実行。以下のスクショは分かりやすいようにあえて前カメラで見ている。

久々にお休みにしたのでTyping Fieldの開発を進める、はずだったんだけど割と機能は揃ってきたので、アイテム作りを進める。まずはデフォルトで持っている「宝箱を開けるアイテム」のアイコン。

ってところで今日は終わってしまった。1個アイテム作る度にアイコン作りに1日かかるのか。。。アセットも探してみたけど、全てのアイコンで雰囲気とか向きとか光の方向を合わせないといけないので、探すより自分で作った方がいいだろうと。割とちゃんと作れたと思うけどどうかな。

ずっと表向きの職業が忙しいのだが、Typing Online(仮)をなんとか進めている。早いところゲーム製作一本に絞りたい。

タイトルはひとまずTyping Field Onlineにしてみた。タイピングが全ての空間みたいな。

そしてワードは自動生成。Weather Typingのときもディープラーニングによる自動生成を入れてみたけど、今回はそれをメインにするのでいろいろ改善してみた。最終的に、日本語と英語で、ウェザタイの文章っぽいフレーズができるようになったのでこういう感じに。

あとはCPUのAIをちゃんと作り込んで、攻撃アイテムを持ってなければアイテム探し、持っていればプレイヤーを探して攻撃、みたいにしたので一人プレーがちゃんとできるようになった。

てことでプレイしてみたけど、これは結構面白いと思う。アイテム運があったり、相手の隙を突いて攻撃、相殺みたいなウェザタイにはない戦い方もできるし。複数の対人対戦やってみたい。

ただ3D酔いしやすいかな。眼が疲れているのか判断が難しいけど。そのあたりのガイドラインを調べるか。

Twitterに載せたのでTyping Online(仮)の進捗をこちらにも。細々と開発は進めていて、キャラクターカスタマイズとかBGMを調達したり足音を作ったりしてみた。

ゲームシステム的には、入力の相殺を入れることで、相手の入力をキャンセルすることで接戦状態を作り出したり、Comboやワードの難易度で攻撃力を変えたりルールに工夫をしてみたところ、割と面白くなった気がする。下の動画では最初から無限ワードを持ってるけど、最初は何も持っていない状態にすればもう少し駆け引きが作れるかな。

あとは、ウェザタイとは違って途中参加をOKにしてみた。こう言うゲームだと一斉参加で誰が最後まで生き残るか、みたいなルールが普通っぽい気もするが、なかなか人数が集まらないはずなので、一人でCPU戦をしつつ誰かが来るのを待てる方がいいかな、と。部屋を作れるようにすれば大会モードも可能だろうし。

この後は、CPUのAIを作り込んで、ワードやアイテムを一通り用意する予定。分かっているバグが30個くらいあるのでテストプレイするにはまだしばらくかかるけど。

表向きの仕事が忙しすぎて7月日記更新できなかった。一応細々実装を進めてはいて、夏休みを全て使ってバグ取りをしていた。

クオリティはおいておいて、システムはだいたいこんな感じかな。フィールド上のワードとかアイテムを取得して、使うときはタイピングで呪文詠唱。

あとは、いろいろワードとアイテムを揃えて、キャラカスタマイズとか総打鍵数カウントとか、エフェクトをまともに実装して、SEとか音楽とか作って、カスタムキーボードスキンとか追加すれば完成かな。・・・完成まで何年かかるんだろう。

タイピングオンライン(仮)。なんとなくキーボードを使った呪文詠唱戦みたいなコンセプトにすることが決まったので、少しづつ世界観を作り込んでいかないと。

てことで手始めに「Unityゲームエフェクト入門」を読みつつエフェクトの練習を始める。本に載っていたほぼそのままのやつで以下のような感じになった。

The Typing of the DeadはThe House of the Deadの派生なのでキーボードから弾を撃つ感じだったけど、魔法戦だとこんな感じかなあ。これだけでも割と変わりますね。効果音とか舞台設定がコンセプトと合ってないのでいまいちいいのかどうだか分からない感はあるけど、いい感じのバカっぽさはある。

ウェザタイ4.1ではディープラーニングを使ったワード自動生成を実装した。で、Unityで作っているタイピングソフトでは基本的にワードは自動生成にしたいな、ということで調査。

PyTorch+BarracudaでLSTM

UnityではBarracudaというエンジンを使ってディープラーニングを動かせる。多分例によって他で学習したものを使えるだけ。つまりPythonなどで作ったモデルをONNXに変換するとUnityでも使えるようになるのだが、前回の日記のようにkerasではTensorFlow2が使えなかったりして変換ができなかった。PyTorchも変換したモデルをUnity上に持ってくるとエラーになっていたのだが、Barracudaのバージョンを1.3.3(実験版)にすると読み込んでくれた。

で、Embedded層、LSTM層を入れたモデルを動かしてみたのだが、どうも一箇所おかしな部分がある。BarracudaReferenceCPU.csのLSTM関数で、入力テンソルの形が(ワードベクトル, バッチ)になっているぽくて、LTSMの各ゲートの重みテンソル(ワードベクトル, 隠れ状態)との内積を取ろうとして行列の型エラーになってしまう。具体的にはここ。

var i_mad_w = _(Add(new[] { _(MatMul(X_sequence, false, W[g_i], false)), Wb[g_i] }));
var j_mad_w = _(Add(new[] { _(MatMul(X_sequence, false, W[g_j], false)), Wb[g_j] }));
var f_mad_w = _(Add(new[] { _(MatMul(X_sequence, false, W[g_f], false)), Wb[g_f] }));
var o_mad_w = _(Add(new[] { _(MatMul(X_sequence, false, W[g_o], false)), Wb[g_o] }));

MatMul(行列積)の第2引数が転置フラグになっていて、ここをtrueにするとうまくいく。使い方が悪いんだとは思うけど、外側からなんとかできるようなものではなさそうなのでPyTorchとの相性なのかなあ。

他にも、元々はモデルの最後に出力ワードの確率を取るためにsoftmaxを入れていたのだが、そっちもreshapeでなぜか列が増えてしまって型エラーになったり。こっちはC#側で計算すればいいので問題はないけど。

結果

こんな感じ。まだデバッグ版でCPU使用だけど、生成の速度は1文で1秒未満くらいかな。日本語の出来としては・・・学習が15分くらいなので、もう少し学習すればよくなるかも。

まとめ

Barracudaを実験版にしてさらにオープンソースに上記の修正をすると動くのだが、Unityライセンスを調べないと公開はできないかなあ。公開までにリリース版が出てバグ?が修正されているといいなあ。

なお、今回Barracudaをデバッグしつつ、LSTMを自分で実装したものと実行内容を比較してようやく問題が判明した(なんとなく転置がいるのはすぐ分かったけど、デバッグしてようやく理由が分かった)のだが、やっぱり一回自分で実装しているから分かる部分もある。ライブラリの使い方を知っているだけじゃなくて中身も知ってる方がいい。

ができないっていう話だけど。

ウェザタイのワード自動生成をUnityでも動かせるようにしたいので検討しているのだが、うまい方法がない。

UnityにBarracudaという機能があって、ニューラルネットのパラメータの標準的なフォーマットONNXが使えるらしい。ウェザタイのワード自動生成では、embed層、LSTM層、全結合層のスタンダードな構成なのでPythonの深層学習ライブラリで作るなら割と簡単。

で、まずはkerasのLSTMを使って日本語生成してみた。それ自体はうまくいったのだが、ONNXへの変換でつまる。keras2onnxではそもそもTensorflow2の機能を使っているとダメのようで、disable_v2_behaviorをやってみたりしたが、うまくいかない。

次にPyTorchを試す。こちらも日本語生成自体はうまくいった。ONNX変換もダミーインプットにちょっと試行錯誤がいるが、torch.onnx.exportで変換はできた。が、Unityにもっていくとインポートができない。embedレイヤーが使っているインデックスをサポートしていないっぽい。

追記:Barracudaのプレビュー版にしたらインポートまではできた。

最後、C#で一から作ったライブラリも試してみる。ウェザタイで作ったやつは高速化のため、行列の積にAVXとかSSEとかのSIMD命令を使っているのだが、Unityはマルチプラットフォームなのでそのままは使えない。そういう場合はBurstコンパイラを使うことになるらしいのだが、いろいろ制限があるようで、InvalidOperationExceptionになってしまう。C#で行列の積を実装すればいいはいいのだが、現実的じゃない計算コストがかかってしまう。

てところでいずれも無理。UnityでLSTM動かしている人はまだいないのかな。Pythonで大量に文章を自動生成できるので、100万ワードくらい生成して配布すればまあいいのかもしれないけど。。。

オンラインタイピングソフトの進捗。少し細かい実装をして最低限ゲームとして動くようになった。CPUをいくつか配置してタイピングでライフを奪っていく感じ。

実装としては、まだカスタマイズはできないけど、Advanced People Pack 2でカスタマイズしたキャラクターを使えるようになったり、Blenderで作ったキーボードと腕のモーションをUnityに取り入れてアニメーションさせてみたり、FL Studioで作った効果音を入れてみたり。いろいろベース部分を作ったので作り方には慣れてきた感じ。今は何故かCubaseで作った曲を流せるようにしている途中。

対戦は試してないけど一応動くようには作っているはず。ここからどうしようかな。

この話を書くなら今かな、ということで新タイピングソフトの構想を。

経緯

Realforce Typing Championshipから、タイピングが競技として話題になることも少し増えてきた。RTCは頂点を決めるものなので今のところWeather Typingを使って頂いているが、やっぱりもう少し派手な演出とか複雑なゲーム性とか、今時なタイピングソフトが欲しいところ。THE TYPING OF THE DEADは対戦向けではないし、そもそも公式リンクが何にもなくAmazonにリンクするくらい入手困難だし。

その辺りタイピングサミットくらいから考えていて、5年くらい前にウェザタイに戦略性を入れようと考えたりもしたけど、ウェザタイはガチなタイピング勝負がコンセプトなので運まかせとかアイテムとかとは相性が悪いし、ワードや入力方式などウェザタイの狭い枠組みでやっても地味なので検討を中断。

で、これとは別に3年前からウェザタイオフラインを考えていた。オフラインで集まってキーボードとモニターを肩から提げてみんなで赤外線で打ち合うというリアルTOD。赤外線通信部分は作ってもらったのでもうあるんだけど、ウェザタイの大改造が必要なのとキーボードを吊り下げる板とかモニタコストのハードルが高くて中断中。

で、1年くらい前にN高 ネット運動会で最近のオンラインゲーム(フォートナイトとか)を見て、みんなが欲しい、観たいタイピングソフトはこういうのなんだろうな、というので去年から試しに作り始めた。ウェザタイオフラインのオンライン版(?)の意味もあって、ソフトウェアはソフトウェアで別に作った方がデバッグが楽かな、というものある。

仕様

世界観はまだ定まってないけど、タイピングしないと生きていけない的なものなのかなあ。大規模サーバは作れないので、ウェザタイと同様のP2Pで10人くらいのバトルロイヤル。

地形やキャラクターをランダムに作ったりワードはAI自動生成にしたり、ランダム性を積極的に取り入れて飽きないように。ワードはウェザタイのAI自動生成をベースに。音楽も自動生成したいところではあるけど、さすがにフリー音源を使うかな。SEはウェザタイ以上のものを作れなかったので流用かな。

Advanced People Pack 2はキャラクターメイキングもできてすごく便利。

あとはウェザタイの枠組みを離れるならどんな工夫ができるか。例えば、ある文字アイテムを取ったらその文字を打ったときの攻撃力が高くなるとか、今から5分間、特定の文字の出現頻度が上がるとか、英語しか出ないとか。あとは普通にコンボボーナスとか。1対複数で囲むなんてのもやれば、ある程度強い人とも戦えるかな。

進捗

進捗。オンラインゲームの作りはウェザタイの仕組みが割とそのまま使えて、ローカルではリアルタイムに処理してリモート側もそれなりに補間して動きつつ完全同期させるのはできてきた。タイピングソフトとして必要な部品とか対戦サーバは当然あるし。

ここから独自処理を入れていくわけだけど、例によって一人で作っているので、いつ完成するかは・・・。数年レベルかも。こういうソフトが欲しいっていうことなので、ウェザタイとTODのいいとこ取りのいいソフトができるのなら誰かが先に作ってくれてもいい。Twitterを見てると、割とみんなこういうソフトが欲しい、作りたいと思っている人は多そうな感じ?

3Dオンラインゲーム版のタイピングソフトの進捗。2台のPCで起動して、互いにキャラを動かしてタイピングしあうところまではできた。まだカクカクするのはおいておいて、プレイしても特に面白くはない。このあとゲーム性を考えていく必要があるわけだが、むずい。アイデアはいくつかあるけど面白くなるのかはやってみないと分からない。

ゲームエンジンとしてはUnityを使ってるけど、オンラインゲームの開発むずくないですかね。ウェザタイは1台のPCで2つソフト起動してたけど、Unityだと2台PC起動して、ソースコード編集する度にGit経由でコード共有してるのですごくめんどい。

進捗メモ。CPU戦が動くようになった。ウェザタイの通信プロトコルをほぼそのまま流用しているので、対戦モードにすればそのまま対戦もできると思う。けど、課題が山積み。

まず文字が読めない。自分のキャラクターは非表示にして、自分のワードは画面上のどこか別の場所に表示しないと。あとワードは頭の上がいいかも?

移動モードとタイピングモードの切り替えをTabでやってるんだけど、やっぱりめんどくさい。やっぱりマウスで視点移動の方がいいかな。

まだ演出が何も入ってない状態ではあるけど、面白くするにはいろいろ必要。ウェザタイの名前にはしないつもりなので、ガチタイピングではなく、例えば、弱い人でもあらかじめたくさん打ち溜めてシールドをはっておけば強い人に勝てるとか、複数人で一人を囲むとか、後ろから打つとか、どこかを打つとアイテムが出るとか、考えられるのはそんなところ?

システム的には、ウェザタイはサーバはあるけど単なる中継なので基本はP2Pの形。これだとプレイヤーの途中参加が困難。そうなると何人か揃ったら開始、誰かが生き残ったら終了みたいな形になる。人がいなくてもある程度遊べるようにするには、サーバクライアント型にして、サーバにゲーム状態を保存して、CPUをたくさん配置する、とかになるけど、それだとサーバの負荷が高いかなあ。

ウェザタイオフラインのオンライン版のためにUnityを勉強中。思ったのは、これってWorld Testerでやりたかったことじゃん、という。

World Testerは物理シミュレーションソフトだけど、定義ファイルでフィールドや物体を自由に作って配置することができる。あとは定義ファイルじゃなく3D空間で自由に配置できるといいんだけどな、と思いつつ放置していた。これができて、スクリプトを追加できればUnityになる(ならない)。

とりあえず地形を生成して、歩き回るところまではできた。