ITGギミック作成記3: Twist of Fate D20の低速を作る (オプション固定)

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

前回まででとうとうRed Swan D21のギミックが完成しましたので、今回は見た目のみならずゲームプレイにも深刻な影響を及ぼすあの譜面のギミックを作っていこうと思います。

 Twist of Fate feat. ruriling D20

namu.wikiで「中盤の部分に強制変速で超低速で変わってパターン自体が読み取りヒジルないナンセンス低速区間が登場する。レポート打つことはほとんど不可能であり、覚えてプレイするのが唯一の答えだとする状況」だとか「それなのにあんな不謹慎な採譜を」だとかすごい言われようをしている中盤の低速を作ります。

この低速の一番の特徴は何といってもオプションに関係なく強制的にハイスピをx0.5に固定される点でしょう。どれだけハイスピを上げようと無意味なので世の中の高速厨多くのプレイヤーが多いに苦しめられたわけです。

同時に譜面も光ってますが、これは前回作りましたので省略します。

 

さてこういったオプションを強制的に変更するギミックはどうやって作るかと言えば、前回使ったFGCHANGES中のxmlを使って作ります。は?って思われるかもしれませんが作れるので作れるんです。

では早速前回作ったQuadを用いてフラッシュを作ったRed Swanもどき.xmlを使ってTwist of Fateを作っていきましょう。

f:id:paraphrohn:20190620233227p:plain

前回作った部分です*1。とりあえず譜面は光りますのでこれはこのまま置いておきましょう。これとは別に本体に命令を出してオプションを変更させます。

 

とりあえずレイヤーを追加します。

f:id:paraphrohn:20190620233605p:plain

は?って思われるかもしれませんが、基本的にfg中のコマンドはレイヤー中で動かします。たとえそれが画像であろうと長方形であろうとオプション変更だろうと関数だろうと変数だろうとです。

別に一つの操作に対して一つのレイヤーを使う必要は無く、大体の命令はレイヤーをまとめてしまえるのですが、今回はせっかく作った譜面フラッシュと干渉して上手くいかなかったりするのを防ぐために新しいレイヤーを作ります。

TypeがQuadになってるのは、Quadが最も動作の軽いレイヤータイプであるためです。今回のように1,2枚しか出さない場合は特に影響ありませんが、複雑なxmlになって100枚も200枚も出す場合には割と効いてくる……はずです。

 

さて、前回説明した通りQuadは長方形を画面に表示します。この追加したQuadが何か間違えて画面に表示されないよう、隠してしまいましょう。

f:id:paraphrohn:20190620234212p:plain

隠しました。あるいは diffuselapha, 0.0;でもいいと思います。最初に参考にしたxmlがこの書き方だったので、筆者はhidden,1を使うようにしています。公式でもhidden,1が使われています。

さて前回はOnCommandを使いましたが今回はInitCommandになっています。この違いが何なのか筆者にはよくわかってませんが、イメージとしては

OnCommand: Layerが生成されたら起動する命令

InitCommand: Layerを生成するときに起動する命令

だと思っています。なので処理される順番はInitCommand -> OnCommandだと思います。誰か教えてください。 2019/6/21 記事末尾に追記しました

 

次にオプションを強制的に変更する命令を出す命令を書きます。理由は分かりませんが、OnCommandにオプション変更の命令を書いても動きませんでした……

f:id:paraphrohn:20190621000913p:plain
見慣れない書式が出てきました。一つずつ説明します。

 

・"%function(self) end"

実は、〇〇Command="(命令),();"という書き方は、〇〇Command="%function(self) self:命令,(); end"の省略形です。今回のように、""の中が長くなる場合は省略せずに書いた方が後々見やすいはずです。

selfというのは現在のActorを対象に命令を下すことを示しています。筆者はActorが何のことなのかわかってないので何も考えずに書いてます。おそらく現在のレイヤーのことだと思います。 2019/6/21追記 現在のレイヤーのことでした。

・queuecommand()

()内のCommandを実行する命令です。簡単に言えば命令を出す命令というわけです。今回は、'TwistofFate'というコマンドを実行します。なので、TwistofFateというコマンド内で、改めてオプションを強制変更することを考えます。いずれにせよ、こういう形にしておけば、TwistofFateコマンドを書き換えるだけでいろんな命令が出せるようになるので便利です。

 

というわけで、TwistofFateコマンドを書いていきます。同じレイヤーにTwistofFateコマンドの定義を追加します。

f:id:paraphrohn:20190621001130p:plain

見ればわかる通り、OnCommandと同じ書式で書いています。重要なのは(指定したコマンド名)Command="%function(self) end"です。そして、新たな書式がまたあります。

 

・GAMESTATE:ApplyGameCommand()

これがオプションを強制変更するコマンドです。書式は下の通りになっています。

f:id:paraphrohn:20190620235651p:plain

オプションを変更する早さ オプションがどれくらいの時間をかけて変化するかを指定します。*10000とかなら一瞬で変わりますし、*1ならばゆっくり時間をかけて変わります。ハイスピをゆっくり変えるとどうなるかというと、

Log-In D20みたいなことができるわけです。今回のTwist of Fateは急に低速になるので*10000にしています。

オプションの変更先 どういったオプションを変更するかです。ハイスピの変更は 〇〇xになります。ほかにどういったオプションが指定できるのかは、また後ほど説明します。

適用するプレイヤー 1Pならば1、2Pならば2を指定します。1人プレイなのに2P側でプレイする人がいる? ならばこうすればいいです。

f:id:paraphrohn:20190621001522p:plain

1Pも2Pも容赦なくHSを0.5倍にします。

 

どうでもいいですが、GAMESTATE:ApplyGameCommandで何故オプションが変更されているかというと、openITG/notITGでそういう風に決まっているからです。

f:id:paraphrohn:20190620235256p:plain

openITGのgithubから引っ張ってきました。確かになんとなくそうなってるように見えますし、よく見たらプレイヤー番号を入力しない場合は全プレイヤーに適用されることが分かります。なので、GAMESTATE:ApplyGameCommand('mod, *10000 0.5x');だけで問題無いですね。

 

ともあれこれでハイスピを強制変更しているはずです。早速notITGで再生してみます。

できてしまいました。Twist of Fate D20の低速、完成です。

答えを知っていれば、つまり完成品からコピペしてくればこんなにも簡単に作れてしまうわけです。もしエラーを吐くなら、何かしら記号が抜けてたりタイプミスしてたりしてるはずなのでよく探してみてください。

 

あまりにもあっけないので、では今回用いたGAMESTATE:ApplyGameCommandをつかって他にどんなオプションが変更できるか見てみます。

例1: ('mod, *10000 sudden')

Gargoyl full song S20の最初の低速みたいな感じのができます。

 

例2: ('mod, *10000 50.0% stealth')

ノートが真っ白になります。上のsuddenから出てくる瞬間の挙動です。このように、オプションを中途半端に適用したいときは (小数点付き数字)% を手前に付けると実現できます。50.0% suddenなら画面の下半分でノートが真っ白になるという誰得な演出ができます。ちなみに100.0% stealth、もしくはstealthにするとノートが見えないのでクソゲーになります。

 

例3: ('mod, *10000 reverse')

リバースがかかります。0.0% reverseならばリバースを強制解除します。ちなみに、0.0% reverseはno reverseでも代用できます。これは他のstealthなどでも同じです。

50.0% reverseにするとクソゲーになります。試してみてください。

 

例4: ('mod, *10000 Bumpy, *10000 Tornado, *10000 Drunk')

MODS譜面みたいになります。このように、複数のオプションを同時に適用したいときはカンマ区切りで並べてやれば大丈夫です。

具体的にどんなオプションが使えるのかは、下のtxtにまとまっています。

https://sm.heysora.net/itg.txt

興味がある方はぜひ色々試してみてください。できれば0.0%と50.0%と100.0%で。

 

 

 

ところで気付いた方もいるかもしれませんが。今回の方法だと、一回変更したオプションを元に戻すことができません。なのでTwist of Fateは中盤以降ずっと0.5x強制になりますしGargoyle full songもずっとヒドサド状態になってしまいます。

もちろん、戻したい拍に別のxmlを置いて戻すことが考えられます。ですがいったいどうやってプレイヤーが特定のハイスピを使っていたと調べればよいのでしょう

少し厄介な話になりますが、

・ハイスピを戻すxmlが起動した瞬間には、ハイスピは0.5xになっている

・ハイスピを0.5xにするxml内でプレイヤーのハイスピを取得したとしても、戻すluaには共有されない

という特徴から、二つのxmlを使ってハイスピを元に戻すことはできません。つまり一つのxml内で完結させる必要があります。

それと、前回使用したsleepを用いて特定の秒数だけ待ってから戻す手法が考えられます。例えばこんな感じです。

f:id:paraphrohn:20190621002851p:plain

元のハイスピはあらかじめ何らかの方法で取得しているものとします。こうすれば、確かにTwistofFateCommandが発動してから2秒後にReturntoNormalコマンドが発動してハイスピが元に戻ります。これくらいなら大丈夫です。

ですがもし、もっと複雑な挙動をする譜面を書きたいときは? 例えば拙作One Way Streetのような。

 はっきり言って、これを全部sleepで何秒待って~ってやってるのは作業量が多すぎます。しかも、ちょっとズレが見つかった時に、ほぼ全部のsleepの値を変えなければなりませんし、「やっぱりこのタイミングで戻そう!」ってなった時にもsleepを全部変えなければなりません。やってられません。というわけで、できれば#BGCHANGESのように、拍数を指定して、このタイミングで命令A、このタイミングで命令B、といった形式にしたいのです。

そういった欲求を満たすのが次回紹介するUpdate形式です。次はこのUpdate形式を使ってChase Me D23の、最初のハイスピが半分から元の速度になるシーンを再現します。これができれば、公式MODSっぽい譜面はいくらでも作れるといっても過言ではありません(過言かもしれません)。それではまた次回。

 

 

追記

InitCommandとOnCommandの違いについてですが、

InitCommand: とにかく早く起動する命令

OnCommand: 大体の準備ができたら起動する命令

という違いのようです。InitCommandで関数やレイヤーなどを読みに行くと、それらが準備される前に読み込もうとしてエラーを吐きますので、

・InitCommandではサイズの変更やhiddenのみ

・他のコマンドは全部OnCommand

とするのが一番無難のようです。InitCommand無しでも大丈夫です。

*1:今回からエディタがSublime Textに変わりました。使いやすかったです。