ITGギミック作成記9: ITG環境でのハイスピ取得

一通りStepManiaもしくはITGの譜面が作れるようになって、ちょっとギミックを使って演出を強化したりプレイヤーの邪魔をしたりしたいな、という方向けにいくつか記事を書いていこうと思います。第9回です。

しばらくStepMania5向きのluaの書き方を紹介してきましたが、その中で「XMod/CMod/MModのいずれでもハイスピを読み込む」方法を紹介しました。これと同等の機能をITGでも実装してみましょう。

 具体的にはこの記事で書いた方法をITGにも適用するだけです。

そのまま使えればいいんですけど、ITG luaではstring.matchが使えないとかいう致命的な問題がありますので、そこだけ使える関数で代用してやる必要があります。

それと、こういう汎用的な機能は前回紹介したmods-taro.luaみたいな感じで一行で呼び出せるようにしたいので、xmlを分ける方法についても簡単に紹介します。

 

さて、前回の記事で述べました通り、従来このシリーズでは「このオプション使ってる?」って総当たりしてハイスピを取得していました。これを前回の記事では「オプション全部よこせ!」からハイスピ情報を抜き出す形に変更しました。

更に該当記事では最後のほうでこっそりと「ITGではScreenGamePlayにならないとオプションが取得できない」みたいなことを書いていました。これ、StepMania5ではプレイヤー側にオプション情報を保存しているのに対してITGではスクリーン側にオプション情報を保存しているっぽいからなんですよね。

 

というわけでまずはScreenGamePlayを取得するレイヤーを、前回のSM5記事と同様に書いてみます。

また、上で述べた通り今回はこの手の機能を別のxmlに分割して保存しますので、仮にparaph_HS.xmlという名前でこれを書いていきます。

ちなみに今回の記事(実は前回の記事もですが)、一切テストプレイしてないのでもしかしたら動かないかもしれません。

 

f:id:paraphrohn:20200309211612p:plain

paraph_HS.xmlを新しく作って、その中にdefault.xmlと同じ書式でScreenを取得するレイヤーを書き込みました。やっていること自体は前回と同じなので省略します。

SCREENMAN:GetTopScreen()関係は、変数名が曲をまたいでダブりやすい上にダブると間違いなく落ちます。InitCommand内で必ずnil;指定するようにしておきましょう。

 

ともあれ、これでScreenが取得できたはずなので、これを使ってオプションを読むことをします。

とりあえず、xmlそのものへの組み込みは考えずに、オプションの取得方法について考えてみます。

 

プレイヤーの使用しているオプション一覧は、ITGでは

local po = TS:GetChild('PlayerOptionsP'..tostring(pn)):GetText(); 

 で取得できます。ここでpnは1または2を指定します(tostringをconcatenateするとPlayerOptionsP1またはPlayerOptionsP2になります。わかりやすい)。

あと頭にlocalをつけていますが、簡単に言えば「次のendまでの間だけ使える変数」としての定義です。なぜlocal定義するかはまた後ほど。

 

さて、ここからSM5と同様に、string.matchだけ使わないようにしながらハイスピを取得しましょう。

f:id:paraphrohn:20200309215221p:plain

完成です。

解説します。

 

まず、string.matchの代用として、string.findを使用します。これは説明が少し難しいものなのですが……

string = 'In The Groove';

としたときに、例えば

string.find(string, 'In') = 1, 2

string.find(string, 'The') = 4, 6

string.find(string, 'Groove') = 8, 13

string.find(string, 'StepMania') = nil

という風に、2番目の入力が、1番目の入力の何文字目から何文字目にあるかを返すものです。また、

string.find(string, '(In)') = 1, 2, In

string.find(string, '(In) The') = 1, 6, In

のように、2番目の入力のうち()で囲まれている範囲内があれば、それも一緒に返してくれます。

なので、例えばa,b,cという三つの変数に対してstring.findを使うと

a, b, c = string.find(string, '(The)')

a =  4

b = 6

c = The

という風にそれぞれ最初の文字数、最後の文字数、一致した文字、を取得できます。

これを利用して、前回の正規表現と同じようにハイスピを拾いに行っているのが

 a, b, hispeed = a, b, hispeed = string.find(po, '(%d+)x');

のような行であるわけです。 

後は基本的に前回と同じなので省略します。

 

で、これでハイスピが無事取得できるわけで、この機能を外部から呼び出せるようにしてみます。それが関数の宣言です。

f:id:paraphrohn:20200309220229p:plain

上の中身を取り込んだ関数を、getHSとして定義します。

pnおよびBPMはgetHS関数を用いるときに手入力する変数で、pnは1または2を、BPMはまあBPMを入力します。

上のgetHS関数は、最終的に取得したハイスピであるhispeedを、最後のほうでreturnする = 値として返すようにしています。

なので、hispeedP1 = getHS(1, 120)とすればBPM120としたときの1P側のハイスピが取得できるということになります。

 

はい関数の説明をします。

例えば下の関数は割とわかりやすいと思います。

f:id:paraphrohn:20200309220439p:plain

c = tashizan(1, 2)とすれば、c = 3が代入されます。

ところで、上の関数は書き方として

f:id:paraphrohn:20200309220518p:plain

としても同じ結果が返ってきます。tashizanのなかで使われているcはlocal変数なので外部とは干渉せず、この場合でもc = tashizan(1, 2)とすればc=3になります。

 

とまあ、function (関数名)(入力)という形で関数を定義してやって、その関数が返す値はreturnの後に書いてやればそれでいいというわけです。筆者もよく理解していません。

 

ではこうやって作ったgetHS(pn, BPM)関数を、先ほど作成したScreenを取得するだけのparaph_HS.xmlに導入してみましょう。

f:id:paraphrohn:20200309220803p:plain

えっそこ?????いやでもここに入れるのが何かと都合いいんですよ。ほんまか?

まあActorFrameのなかに命令書き込むこともできるっていうのを示したかっただけです。ドユコト?次回説明します。

 

ともあれこれでparaph_HS.xmlの準備はできましたので、これをdefault.xmlから使えるようにします。前回のmods-taro.luaをdefault.luaから読んだ感じです。

f:id:paraphrohn:20200309221101p:plain

まあそういうことです。

 

後は、default.xml内のgotTSMessageCommand内で、例えば

if p1e then hispeedP1 = getHS(1, 120); end

if p2e then hispeedP2 = getHS(2, 120); end

みたいなことをしてやればX,C,M問わずハイスピが取得できます。これは便利ですね。

たったの2行で取得できるのでdefault.xmlがかなり読みやすくなります。少なくとも80行くらいあるparaph_HS.xmlの中身をそのままdefault.xmlに書き込むよりははるかに読みやすいです。

つまり僕は早くこのparaph_HS.xmlを使って昔のluaを全部XCM対応にしろって話なんですが

 

2020/3/9追記

paraph_HS.xmlのダウンロードリンクを追加しました。使ってみたい方はぜひどうぞ。

paraph_HS.xml - Google ドライブ

 

というわけで今回はITG環境でのハイスピの取得と、xmlの分け方について軽く説明しました。

 

次回は今までさんざん「レイヤー」と呼んでたものは何なのか? について説明します。これが理解できると、例えばドキドキ☆フレン・シーフォluaが半分くらいに圧縮できたりします。あと、U SAiDの矢印モデルの処理が理解できたり、Dual-Egoの矢印を動かす原理が理解できたりします。

更にはMODS譜面で矢印レーンを動かしまくったりとかの処理ができます。つまりめちゃくちゃいろいろできるようになるというわけです。