判定差のお話
2019.04.28
前回の続きっぽい別バナです。
って感じに前後関係が追いにくいからblogって嫌いなのよね。このあたりはそのうちまとめて別ページにします。
まず判定差(ここではプレイの度に挙動が違う場合のある動作すべての意味)について、ガチ勢な方から提供頂いた貴重な情報をまとめてみます。
Ringedge時代はさらに他にも要因があったようですがとりあえずそこは省略します。
・筐体差
筐体(Nu/IOボード)に依存する固有の挙動。基本的に固定。アップデートで変化することがある。
・日付差/起動差
divaアプリ自体の再起動(筐体再起動/一旦Nuのサービスモードに戻る等)をする度に判定が変化することがある。
・ステージ差
特定のステージのみで判定が変化することがある。
・PV分岐差
分岐する/しないにより、その後のノーツの判定が変化することがある。
・時間差
上記の要因によらず、判定が変化することがある。
ということらしいですが、もちろん体感さっぱりわからん。
判別方法というのもあるらしく、
とのことだけど
「○が広ければxxx台」
とか言われてもどうやって○が広いかどうかを判別しろと。いや、原理は解るけどできません。
ところで、ここの記事はあくまで判定差の原因を突き止めようというだけで、ハイスコアを出してランキングインを目指そう、とかそういうページではないですよ。
腕があればお立ち台とか載ってみたいがな!
というわけなので、情報ソースはランカーの人達が気にするノーツ、つまりHOLDの入出ノーツになるけど、そんなに偶然キーとなるノーツばかりが変化する訳はないので、スコアタに関係ないノーツでもこのような事象は発生しているはず。
ただ、何をもって、「変化した」と定義するかは難しい。
たとえば「C-C MAX」が限られた条件だけで入るという判定差で、もしその筐体のすべての動作が正確に2倍速で動いたとして、超人が居ればやはり「C-C MAX」は入る。明らかに実時間で言えばCOOL期間は半分だけど、それは今回の検証として「変化した」とは言えない。
やはり、指標としては、ホールドノーツのちょうど5秒後にノーツがあって、MAXを取った後に判定がどうなるか、かな。
前回の解析結果によると、SAD/SAFEが一番期間が短いので、SAFEでホールド入って、5秒後のノーツでMAX取ったあとにSAFEを取れるか、とか。
でもHOLDを絡めないで検証したいんだよね。
※HOLDを絡めると、PVをそこまで進めないと検証できない、という、めんどくさいだけの話です。
まあそれはそれとして、課題だった奴。HOLD点の増加基準。結果解れば単純ですが、処理は結構やっかいでした。
はい、またいきなりソースです。HOLD関連処理関数です。
・引数
rcx=HOLD処理構造体アドレス
r8=押されているボタン(0x40)、○(0x80)、×(0x100)、□(0x200)のor
xmm1=前回この関数が呼ばれてからの経過時間
ちなみにこの関数自体は、どうやらVSYNCに同期して呼び出されるようです。
わりと冗長な処理とかもあるので関係ないところはがっつり削ったり順序変えたりしてます
14012C9F3: mov rdi,rcx
14012C9F6: mov ecx,dword ptr [rcx+0Ch] ;ホールド状態
14012CA15: dec ecx
14012CA17: je 000000014012CA97 ;ホールド中はjmp
14012CA1C: dec ecx
14012CA1E: je 000000014012CA73 ;MAX到達で飛ぶ
14012CA97: mov ecx,dword ptr [rdi+10h] ;ホールド中のボタン。
14012CA9A: test ecx,ecx
14012CA9C: je 000000014012CA44
14012CA9E: mov eax,ecx
14012CAA0: and eax,r8d
14012CAA3: cmp eax,ecx
14012CAA5: jne 000000014012CA84 ;押されているボタンが減った処理へ
14012CAAC: xorps xmm0,xmm0
14012CAA7: addss xmm6,dword ptr [rdi+14h] ;ホールド開始からの時間
14012CAB8: movss dword ptr [rdi+14h],xmm6 ;ホールド時間を更新
14012CAB4: mov r15d,dword ptr [rdi+18h] ;ホールド点(ゲーム中のHOLDボーナスバー表示)
14012CABD: subss xmm6,dword ptr [rdi+2Ch] ;最後にボタンが追加されてからの時間
14012CACC: comiss xmm6,dword ptr [140999A70h] ;5.000
14012CAD3: jb 000000014012CADD ;最後にボタン追加されてから5秒経ってないとjmp
14012CAD5: movss xmm6,dword ptr [140999A70h] ;5.000を超過したら5.000に丸め込み
14012CADD: comiss xmm6,dword ptr [rdi+1Ch] ;0.167。ただしボタン追加されたら0になる。
14012CAE1: jbe 000000014012CB2D ;ホールド開始から0.167s経過していないとjmp
14012CAE3: mov r8d,dword ptr [rdi+24h] ;1フレームあたりのホールド点。ボタン数で変化
14012CAE7: mov eax,r8d
14012CAEA: cdq
14012CAEB: sub eax,edx
14012CAED: sar eax,1
14012CAEF: movd xmm0,r8d
14012CAF4: cvtdq2ps xmm0,xmm0
14012CAF7: mulss xmm0,xmm6
14012CAFB: divss xmm0,dword ptr [rdi+20h] ;[rdi+20h]=常に0.017 xmm0=xmm0/0.017
14012CB00: cvttss2si ecx,xmm0
14012CB04: add eax,ecx
14012CB06: cdq
14012CB07: idiv eax,r8d
14012CB0A: mov ebx,eax
14012CB0C: imul ebx,r8d ;ebx=round([rdi+24h] * xmm6 / [rdi+20h])
14012CB10: cmp dword ptr [rdi],esi
14012CB12: jne 000000014012CB23
14012CB2D: mov ecx,dword ptr [rdi+30h] ;ホールド追加した時点の点数
14012CB30: add ecx,ebx
14012CB36: mov ebp,ecx
14012CB38: mov dword ptr [rdi+18h],ecx
;ebp=現時点の総ホールド点
;r15d=加算済みホールド点
14012CB41: sub ebp,r15d ;今回の加算点
14012CB52: comiss xmm6,dword ptr [140999A70h] ;5.000
14012CB59: mov dword ptr [rax+74h],3
14012CB60: mov dword ptr [rax+78h],ebx
14012CB63: mov dword ptr [rax+7Ch],r12d
14012CB67: jb 000000014012CA4C ;5.000未満
;MAX到達処理
14012CB6D: mov eax,dword ptr [rdi+34h] ;同時押しボタン数
14012CB7B: imul eax,eax,5DCh ;eax*1500=MAXボーナス
14012CB81: mov dword ptr [rdi+3Ch],eax ;MAX点を保持、この時点では加算されない
14012CB74: mov dword ptr [rdi+0Ch],2
14012CB84: jmp 000000014012CA4C
;ホールドMAXでここにくる
14012CA79: mov ebp,dword ptr [rdi+3Ch]
14012CA95: jmp 000000014012CA4C
14012CA4C:
14012CA60: mov eax,ebp ;戻り値代入
14012CA72: ret
つまりどういうことかというと、
・ホールドは開始から0.167秒は加算されない(知らなかった・・・)
・得点(MAX BONUS除く)はround(基準点(10~40) × ホールド秒 ÷ 0.017)
・ホールドMAX BONUSは、ホールドMAXの次のフレームで加算
ですね。
ところで、単精度浮動小数点の丸め込み誤差っていうのに気づいて、上記0.017とかは正確ではありません。
0.167=(float)0x3E2AAAAB
=(bin)0.00101010101010101010101011
=1/8+・・・+1/67108864
=11453246464/68719476736
=10/60+1/(67108864*3)≒10/60
0.017 =(float)0x3c888889
=(bin)0.00000100010001000100010001001
=1/64+・・・+1/536870912
=8947849/536870912
=1/60+7/(536870912*15)≒1/60
まあ実質1フレ単位ですがフレーム数とは非同期で計算はされていますね。
後日に続く
って感じに前後関係が追いにくいからblogって嫌いなのよね。このあたりはそのうちまとめて別ページにします。
まず判定差(ここではプレイの度に挙動が違う場合のある動作すべての意味)について、ガチ勢な方から提供頂いた貴重な情報をまとめてみます。
Ringedge時代はさらに他にも要因があったようですがとりあえずそこは省略します。
・筐体差
筐体(Nu/IOボード)に依存する固有の挙動。基本的に固定。アップデートで変化することがある。
・日付差/起動差
divaアプリ自体の再起動(筐体再起動/一旦Nuのサービスモードに戻る等)をする度に判定が変化することがある。
・ステージ差
特定のステージのみで判定が変化することがある。
・PV分岐差
分岐する/しないにより、その後のノーツの判定が変化することがある。
・時間差
上記の要因によらず、判定が変化することがある。
ということらしいですが、もちろん体感さっぱりわからん。
判別方法というのもあるらしく、
とのことだけど
「○が広ければxxx台」
とか言われてもどうやって○が広いかどうかを判別しろと。いや、原理は解るけどできません。
ところで、ここの記事はあくまで判定差の原因を突き止めようというだけで、ハイスコアを出してランキングインを目指そう、とかそういうページではないですよ。
腕があればお立ち台とか載ってみたいがな!
というわけなので、情報ソースはランカーの人達が気にするノーツ、つまりHOLDの入出ノーツになるけど、そんなに偶然キーとなるノーツばかりが変化する訳はないので、スコアタに関係ないノーツでもこのような事象は発生しているはず。
ただ、何をもって、「変化した」と定義するかは難しい。
たとえば「C-C MAX」が限られた条件だけで入るという判定差で、もしその筐体のすべての動作が正確に2倍速で動いたとして、超人が居ればやはり「C-C MAX」は入る。明らかに実時間で言えばCOOL期間は半分だけど、それは今回の検証として「変化した」とは言えない。
やはり、指標としては、ホールドノーツのちょうど5秒後にノーツがあって、MAXを取った後に判定がどうなるか、かな。
前回の解析結果によると、SAD/SAFEが一番期間が短いので、SAFEでホールド入って、5秒後のノーツでMAX取ったあとにSAFEを取れるか、とか。
でもHOLDを絡めないで検証したいんだよね。
※HOLDを絡めると、PVをそこまで進めないと検証できない、という、めんどくさいだけの話です。
まあそれはそれとして、課題だった奴。HOLD点の増加基準。結果解れば単純ですが、処理は結構やっかいでした。
はい、またいきなりソースです。HOLD関連処理関数です。
・引数
rcx=HOLD処理構造体アドレス
r8=押されているボタン(0x40)、○(0x80)、×(0x100)、□(0x200)のor
xmm1=前回この関数が呼ばれてからの経過時間
ちなみにこの関数自体は、どうやらVSYNCに同期して呼び出されるようです。
わりと冗長な処理とかもあるので関係ないところはがっつり削ったり順序変えたりしてます
14012C9F3: mov rdi,rcx
14012C9F6: mov ecx,dword ptr [rcx+0Ch] ;ホールド状態
14012CA15: dec ecx
14012CA17: je 000000014012CA97 ;ホールド中はjmp
14012CA1C: dec ecx
14012CA1E: je 000000014012CA73 ;MAX到達で飛ぶ
14012CA97: mov ecx,dword ptr [rdi+10h] ;ホールド中のボタン。
14012CA9A: test ecx,ecx
14012CA9C: je 000000014012CA44
14012CA9E: mov eax,ecx
14012CAA0: and eax,r8d
14012CAA3: cmp eax,ecx
14012CAA5: jne 000000014012CA84 ;押されているボタンが減った処理へ
14012CAAC: xorps xmm0,xmm0
14012CAA7: addss xmm6,dword ptr [rdi+14h] ;ホールド開始からの時間
14012CAB8: movss dword ptr [rdi+14h],xmm6 ;ホールド時間を更新
14012CAB4: mov r15d,dword ptr [rdi+18h] ;ホールド点(ゲーム中のHOLDボーナスバー表示)
14012CABD: subss xmm6,dword ptr [rdi+2Ch] ;最後にボタンが追加されてからの時間
14012CACC: comiss xmm6,dword ptr [140999A70h] ;5.000
14012CAD3: jb 000000014012CADD ;最後にボタン追加されてから5秒経ってないとjmp
14012CAD5: movss xmm6,dword ptr [140999A70h] ;5.000を超過したら5.000に丸め込み
14012CADD: comiss xmm6,dword ptr [rdi+1Ch] ;0.167。ただしボタン追加されたら0になる。
14012CAE1: jbe 000000014012CB2D ;ホールド開始から0.167s経過していないとjmp
14012CAE3: mov r8d,dword ptr [rdi+24h] ;1フレームあたりのホールド点。ボタン数で変化
14012CAE7: mov eax,r8d
14012CAEA: cdq
14012CAEB: sub eax,edx
14012CAED: sar eax,1
14012CAEF: movd xmm0,r8d
14012CAF4: cvtdq2ps xmm0,xmm0
14012CAF7: mulss xmm0,xmm6
14012CAFB: divss xmm0,dword ptr [rdi+20h] ;[rdi+20h]=常に0.017 xmm0=xmm0/0.017
14012CB00: cvttss2si ecx,xmm0
14012CB04: add eax,ecx
14012CB06: cdq
14012CB07: idiv eax,r8d
14012CB0A: mov ebx,eax
14012CB0C: imul ebx,r8d ;ebx=round([rdi+24h] * xmm6 / [rdi+20h])
14012CB10: cmp dword ptr [rdi],esi
14012CB12: jne 000000014012CB23
14012CB2D: mov ecx,dword ptr [rdi+30h] ;ホールド追加した時点の点数
14012CB30: add ecx,ebx
14012CB36: mov ebp,ecx
14012CB38: mov dword ptr [rdi+18h],ecx
;ebp=現時点の総ホールド点
;r15d=加算済みホールド点
14012CB41: sub ebp,r15d ;今回の加算点
14012CB52: comiss xmm6,dword ptr [140999A70h] ;5.000
14012CB59: mov dword ptr [rax+74h],3
14012CB60: mov dword ptr [rax+78h],ebx
14012CB63: mov dword ptr [rax+7Ch],r12d
14012CB67: jb 000000014012CA4C ;5.000未満
;MAX到達処理
14012CB6D: mov eax,dword ptr [rdi+34h] ;同時押しボタン数
14012CB7B: imul eax,eax,5DCh ;eax*1500=MAXボーナス
14012CB81: mov dword ptr [rdi+3Ch],eax ;MAX点を保持、この時点では加算されない
14012CB74: mov dword ptr [rdi+0Ch],2
14012CB84: jmp 000000014012CA4C
;ホールドMAXでここにくる
14012CA79: mov ebp,dword ptr [rdi+3Ch]
14012CA95: jmp 000000014012CA4C
14012CA4C:
14012CA60: mov eax,ebp ;戻り値代入
14012CA72: ret
つまりどういうことかというと、
・ホールドは開始から0.167秒は加算されない(知らなかった・・・)
・得点(MAX BONUS除く)はround(基準点(10~40) × ホールド秒 ÷ 0.017)
・ホールドMAX BONUSは、ホールドMAXの次のフレームで加算
ですね。
ところで、単精度浮動小数点の丸め込み誤差っていうのに気づいて、上記0.017とかは正確ではありません。
0.167=(float)0x3E2AAAAB
=(bin)0.00101010101010101010101011
=1/8+・・・+1/67108864
=11453246464/68719476736
=10/60+1/(67108864*3)≒10/60
0.017 =(float)0x3c888889
=(bin)0.00000100010001000100010001001
=1/64+・・・+1/536870912
=8947849/536870912
=1/60+7/(536870912*15)≒1/60
まあ実質1フレ単位ですがフレーム数とは非同期で計算はされていますね。
後日に続く