変換元のlua(OpenITG向け)
<ActorFrame><children>
<Layer
Type="Quad"
InitCommand="%function(self)
if GAMESTATE:IsPlayerEnabled(PLAYER_1) then
difficulty1P = GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty();
else
difficulty1P = -1;
end
if GAMESTATE:IsPlayerEnabled(PLAYER_2) then
difficulty2P = GAMESTATE:GetCurrentSteps(PLAYER_2):GetDifficulty();
else
difficulty2P = -1;
end
if (difficulty1P > difficulty2P) then
difficulty = difficulty1P
else
difficulty = difficulty2P
end
end"
/>
<Layer
File="bg_nm.png"
Condition=" difficulty == 2 "
OnCommand="%function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end"
/>
<Layer
File="bg_hd.png"
Condition=" difficulty == 3 "
OnCommand="%function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end"
/>
<Layer
File="bg_shd.png"
Condition=" difficulty == 4 "
OnCommand="%function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end"
/>
<Layer
File="Someday_bga.avi"
OnCommand="%function(self)
self:animate(0)
self:diffusealpha(0)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:sleep(2.206)
self:diffusealpha(1)
self:animate(1)
end"
/>
</children></ActorFrame>
自動変換したlua
local xtl_cond_a_result= difficulty == 2
local xtl_cond_b_result= difficulty == 3
local xtl_cond_c_result= difficulty == 4
local function optional_actor(cond, actor)
if cond then return actor end
return Def.Actor{}
end
return Def.ActorFrame{
Name= "xtl_actor_d",
Def.Quad{
Name= "xtl_actor_e",
InitCommand= function(self)
if GAMESTATE:IsPlayerEnabled(PLAYER_1) then
difficulty1P = GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty();
else
difficulty1P = -1;
end
if GAMESTATE:IsPlayerEnabled(PLAYER_2) then
difficulty2P = GAMESTATE:GetCurrentSteps(PLAYER_2):GetDifficulty();
else
difficulty2P = -1;
end
if (difficulty1P > difficulty2P) then
difficulty = difficulty1P
else
difficulty = difficulty2P
end
end,
},
optional_actor(xtl_cond_a_result,
Def.Sprite{
Name= "xtl_actor_f",
OnCommand= function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end,
Texture= "bg_nm.png",
}),
optional_actor(xtl_cond_b_result,
Def.Sprite{
Name= "xtl_actor_g",
OnCommand= function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end,
Texture= "bg_hd.png",
}),
optional_actor(xtl_cond_c_result,
Def.Sprite{
Name= "xtl_actor_h",
OnCommand= function(self)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:diffusealpha(1)
self:sleep(2.206)
self:diffusealpha(0)
end,
Texture= "bg_shd.png",
}),
Def.Sprite{
Name= "xtl_actor_i",
OnCommand= function(self)
self:animate(0)
self:diffusealpha(0)
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM)
self:sleep(2.206)
self:diffusealpha(1)
self:animate(1)
end,
Texture= "Someday_bga.avi",
},
}
修正したlua(SM5向け)
return Def.ActorFrame{
Def.Quad{
InitCommand= function(self)
d = Enum.Reverse( Difficulty );
if GAMESTATE:IsPlayerEnabled(PLAYER_1) then
difficulty1P = d[GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty()];
else
difficulty1P = -1;
end
if GAMESTATE:IsPlayerEnabled(PLAYER_2) then
difficulty2P = d[GAMESTATE:GetCurrentSteps(PLAYER_2):GetDifficulty()];
else
difficulty2P = -1;
end
if (difficulty1P > difficulty2P) then
difficulty = difficulty1P
else
difficulty = difficulty2P
end
end,
},
Def.Sprite{
OnCommand= function(self)
if (difficulty == 2) then
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM);
self:diffusealpha(1);
self:sleep(2.206);
self:diffusealpha(0);
else
self:diffusealpha(0);
end
end,
Texture= "bg_nm.png",
},
Def.Sprite{
OnCommand= function(self)
if (difficulty == 3) then
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM);
self:diffusealpha(1);
self:sleep(2.206);
self:diffusealpha(0);
else
self:diffusealpha(0);
end
end,
Texture= "bg_hd.png",
},
Def.Sprite{
OnCommand= function(self)
if (difficulty == 4) then
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM);
self:diffusealpha(1);
self:sleep(2.206);
self:diffusealpha(0);
else
self:diffusealpha(0);
end
end,
Texture= "bg_shd.png",
},
Def.Sprite{
OnCommand= function(self)
self:animate(0);
self:diffusealpha(0);
self:stretchto(SCREEN_LEFT,SCREEN_TOP,SCREEN_RIGHT,SCREEN_BOTTOM);
self:sleep(2.206);
self:diffusealpha(1);
self:animate(1);
end,
Texture= "Someday_bga.avi",
},
}
解説
デバッグの結果わかったのは以下の3点でした。
①GetDifficulty()で数値ではなくDifficulty_Hardのような文字列が返る
GetDifficulty()で難易度を取得した際に、OpenITGでは0~5の数値が返るのですが、StepMania5系では”Difficulty_Hard”のような文字列が返ることがデバッグの結果わかりました。どうして
解決方法はいくつか考えられるのですが、ここでは元の実装を流用するため、Difficulty_Hardのような文字列を数値に変換する方法を考えます。
結論としては以下です。
(OpenITG系の実装)
difficulty1P = GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty()
↓
(StepMania5系の実装)
d = Enum.Reverse( Difficulty );
difficulty1P = d[GAMESTATE:GetCurrentSteps(PLAYER_1):GetDifficulty()];
StepMania5.0系ではEnumsの中でDifficulty_Hard等のインデックスと難易度数値の紐付きが定義されています。
Enum.Reverse()はそのテーブル(文字列→数値)を返す関数です。
SM5 for lua(URLは記事の末尾に記載)では” Returns a reverse lookup table”と解説されており、関数名もreverseが付いていますので「逆引き」となりますが、DNSの逆引きとは意味が異なるので注意が必要かもしれません。
1行目でdにテーブルが格納されるので、あとは変数に代入しているだけです。
d = Enum.Reverse( Difficulty );については逆引きテーブルの配列を代入しているだけなので1回宣言すればOKです。
また、変数名(上記ではd)は何でもよいです。
②Conditionが関数に変換される
自動変換するとConditionの部分がoptional_actor(cond, actor)という関数に変換されます。
条件に合致すればactorを返し、合致しなければ空のactorを返す(何もしない)という関数のようです。
自動変換されるということは、てっきりStepMania5系ではCondition命令が無いのかと思っていました。
実際はSM5系でもCondition命令は存在しますし、動作も下記③で述べる判定タイミングの問題以外はOpenITG系と同じように動きます。どうして
そのため、Condition命令を使うように書き直しました。(下記③の理由で最終的には採用せず)
ちなみに、怒首領Papasitoのluaで、難易度に応じて演出を変える(MOD無し/大往生/デスレーベル)部分についてはConditionを用いた実装でSM5対応をしていますので、興味があればluaの中身を見て確認してもらえればと思います。
参考:[ITG] フメンタイン2020投稿譜面について - kaiの日記
③Conditionの判定タイミングが早い
以前の記事([ITG] Lua講座(第1回) ランダムでMVを分岐させる – SPACE OF SOUL - kaiの日記)でConditionの判定タイミングがInitCommandより早いことは書きましたが、SM5系では本当に早くて、簡単な計算でも間に合っていないことがデバッグの結果わかりました。
今回は判定条件がやや複雑で、かつ3レイヤで同じ判定条件を使うことから、difficultyを計算する部分をまるごとConditionに突っ込むのはやめました。
Conditionを使わず、OnCommand内で条件に合致した場合のみ表示(self:diffusealpha(1))する実装に変えました。
以上①②③を踏まえて修正したのが上記のluaです。
SM5系はOpenITGと違って、luaが間違っていても本体ごと落ちるということがなく、エラーも吐かずただただ動かないだけなので切り分けには苦労しました。
デバッグには下記のメッセージ表示コマンドが役立ちました。
SCREENMAN:SystemMessage(文字列)
画面の左上の方に表示されます。
例えば、difficultyの数値を表示する場合は
SCREENMAN:SystemMessage(tostring(difficulty))
です。