最近プレイしているモンスターハンターワールド。大剣でモンスターを斬る感覚が気持ちいい。その気持ちよさを表現している一つがヒットストップ。剣がモンスターに当たった瞬間などにモーションが一瞬止まったり、スローモーションになったりする表現。このヒットストップを試しに実装してみた。
tが0.0から1.0に変化するときに、ヒットストップを考慮したtを求める関数が次のとおり。
float CalcHitStop(float t, float t0, float t1, float k) { if(t<t0) { return t; } if(t0<=t && t<=t1) { return k * (t-t0) + t0; } return ( (1.0f- k * (t1-t0) - t0) / (1.0f - t1) ) * (t-t1) + (k * (t1-t0) + t0); }
tが今の経過時間、t0がヒットストップが始まる時間、t1がヒットストップが終わる時間、kがヒットストップでどのくらいの遅さになるかの変化率。0.5で半分の速度になり、0.0で一時停止となる。
ヒットストップが終わった後は遅くなった分、速くなり、全体の時間は変わらない。グラフにすると次のとおり。
例えば、0.3秒のときにヒットストップが始まり、半分の速度で動き、0.6秒のときにヒットストップが終わる場合は次のようになる。
float t; t = CalcHitStop(t, 0.3, 0.6, 0.5);
ヒットストップ後に速度が変わらない場合は次の関数。
float CalcHitStop2(float t, float t0, float t1, float k) { if(t<t0) { return t; } if(t0<=t && t<=t1) { return k * (t-t0) + t0; } return (t-t1) + (k * (t1-t0) + t0); }
こちらはヒットストップの時間分、全体の時間が長くなる。グラフにすると次のとおり。
終わりの時間は計算結果が1.0になるか判定するか、ヒットストップの時間と変化率から計算して求めれば良い。
このようにして求めたtをアニメーションの再生に使ったり、キャラクターの移動に使えばヒットストップの表現ができる。