前回の「ジャンプアクションゲームを作る03 – 二段ジャンプの実装」で実装ができましたが、今後のゲーム作りのためと色気を出すために味付けをしてみます。
変更する内容は下記の通りです。
- ジャンプの高さを指定できるようにする。
- きりもみジャンプにする。
1. ジャンプの高さを指定できるようにする。
例えば、ジャンプの高さを身長の1.5倍にする、ゲーム内のブロック2つ分にする、など高さが指定できるとゲームを作る上で都合が良いことは多いはずです。
結論から言うと、ジャンプの高さを h、重力加速度を g とすると、ジャンプの初速 v0 は
v0 = 平方根(2 * g * h)
で求めることができます。
実はこの式は私のテキスト『ゲームの作り方 Unityで覚える遊びのアルゴリズム』でも紹介されていますが、解説がなかったので簡単に解説します。
この式を理解するには高校レベルの微積分と物理学を思い出す必要があります。使う知識は下記の通り。
- 初速度を v0、加速度を a とすると、t 秒後の速度 vt は
vt = v0 + a*t - 速度を t で積分すると t 秒後の位置が求められる。
- ジャンプの最高到達点では速度 vt はゼロ。
上記を数式にします。
ここでは t 秒後の位置を h にしたくて、加速度は重力加速度 -g 。つまり、
vt = v0 – g*t を積分すると、t 秒後の高さ h は、
h = v0*t + 1/2 * g * t2 ……1
vt = v0 + g*t = 0 ……2
1と2の連立方程式を解くと最初の式が求まります。
また、味付けとして2回目のジャンプは高さを半分にすることにします。
それではPlayerControl.csを下記のように書き換えます。
void Update () {
// マウスをクリックするとジャンプする
if (Input.GetMouseButtonDown(0))
{
if (restJumps == 2)
{
float jumpHeight = 2.0f;
float gravity = Mathf.Abs(Physics.gravity.y);
float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
this.rigidbody.velocity = Vector3.up * velocity;
restJumps--;
}
else if (restJumps == 1)
{
float jumpHeight = 2.0f / 2;
float gravity = Mathf.Abs(Physics.gravity.y);
float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
this.rigidbody.velocity = Vector3.up * velocity;
restJumps--;
}
}
}
※わかり易さのためにUpdate毎・ジャンプ回数毎に計算する冗長なコードになっていますが、実際には計算値をあらかじめ定数にしておいたり、Start関数の中で計算しておいたりというやり方になるかと思います。
2. きりもみジャンプにする。
何だかふわふわしていてジャンプしている感がないので、きりもみジャンプ(アクセルジャンプ)にしてみます。
ここでも1回目と2回目のジャンプを差別化して、1回目は着地までにちょうど1回転、2回目はその2倍の速さで回転させます。
ちょうど1回転する回転速度(角速度)は下記のように求められます。
ジャンプの初速を v0、ジャンプしてから着地までの時間を t とすると、運動量保存の法則により、着地の瞬間の速度は -v0なので、t 秒後の速度 vt は、
vt = v0 -g*t = -v0 これを変換して、
t = 2*v0 / g …1
t 秒で360度(2*π)回転する角速度ωは、
ω = 2*π / t …2
1と2の連立方程式を解いて、
ω = π * g / v0
が求まります。
それではこの式にしたがってコードをさらに書き換えます。
// Update is called once per frame
void Update () {
// マウスをクリックするとジャンプする
if (Input.GetMouseButtonDown(0))
{
if (restJumps > 2)
{
// ジャンプの初速を計算する
float velocity = Mathf.Sqrt(2 * gravity * jumpHeight);
this.rigidbody.velocity = Vector3.up * velocity;
float angularVelocity = Mathf.PI * gravity / velocity;
this.rigidbody.angularVelocity = Vector3.up * angularVelocity;
restJumps--;
}
else if (restJumps == 1)
{
float velocity = Mathf.Sqrt(2 * gravity * jumpHeight / 2);
this.rigidbody.velocity = Vector3.up * velocity;
float angularVelocity = Mathf.PI * gravity / velocity * 2;
this.rigidbody.angularVelocity = Vector3.up * angularVelocity;
restJumps--;
}
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Floor")
{
// ジャンプ回数をリセットする
restJumps = 2;
// 回転角度をリセットする
this.rigidbody.angularVelocity = Vector3.zero;
this.rigidbody.transform.eulerAngles = Vector3.zero;
}
}
(2013.10.21 13:58追記)最後に、「二段ジャンプの実装」でY軸回転をロックしていたので、RigidbodyのConstraintsから、Freeze Rotation y のチェックを外しておきます。
※浮動小数点の計算誤差や、ジャンプ点と着地点がの高さが違うこともあるので、着地時に回転角をリセットします。
以上で二段ジャンプの実装が完成しました。