Godot キャラコン 07 「加速・減速 と 重力」...根本的に間違ってるかも
移動に関しては、「キーを押すといきなりトップスピード、離すと瞬時にストップ」という仕様になっている。これを、やや滑らかな加速・減速の仕組みを持ったものに修正する。あと、重力も追加する。で、結論的には一応ソレっぽくは動くんだけど、イマイチあってるか間違ってるか微妙なところ・・・。何かモヤっとした部分が・・・。ま、俺の理解できた範囲で書いておく。
加速・減速
キーを押した瞬間にいきなりトップスピードで動き出すのではなく、徐々に加速して行きトップスピードに近づくようにする。また、キーを離した時もピタッとすぐに止まるのではなく、徐々にスピードが遅くなり停止するようにする。
L14
- 加速・減速機能を追加するには、"my_dir"の他にもう一つ
Vector3
型の変数が必要なので、追加する。この"my_vel"で移動位置を指定することになる。 - "my_dir"は完全にキー入力の方向を入れるだけのものにし、
move_and_slide()
関数では"my_vel"を使用する。
L16~17
- 加速率と減速率を指定する変数を追加する。
- 例えば加速の"0.03"とは、現状スピードとトップスピードまでの速度差を3%ずつ縮めて速度を増していくことを表す。
- 減速は停止に向かって速度を減じていく割合。値を加速のものより大きく設定("0.08")したことで、止まる時の方がシュッと早く止まる。
- だが、いろいろ勝手に変えてイイ感じの数値にすればいい。
- ただし加速・減速処理は
_physics_process(delta)
内で行うので、デフォでは1秒間に60回行うものとして考えておく事。もし[Project Settings]で_physics_process(delta)
のアクセスタイミングを変えたら、ここの値も変える必要がある。
L18~19
- 重力用。後で。
L44
- "my_dir"は移動キーの入力があれば何らかの数値が入るが、押されてなければ初めにリセットしてあるので(L32)、そのまま"Vector3(0, 0, 0)"が入る。
- これを利用し、移動キーの入力が無ければ減速処理(L45)に、入力があれば加速処理(L47)に行くようにしている。
多分、本当はキーが押されているかいないかで判断するのではなく、"my_dir"と"my_vel"の内積で分けるんだと思うんだけど、俺はこうしちゃった。これやりたい人は、Vector3
クラスのdot()
関数を参照。多分、
if my_dir.dot(my_vel) > 0:
とかやって、加速処理と減速処理を分けるんだと思う、多分。(これだと俺のと処理の順番がひっくり返る感じかな?) 違うかも。。。
L45
- 減速処理。
linear_interpolate()
はVector3
クラスで定義されている関数。Vector3
型の変数に、後ろから掛けて使う。働きは、AとBの間でtで決めた割合だけ進んだCの値を返すということをする。AからBへ値をちょっとづつ近づけたい時に使う。下図。
- ちなみに
float
型なら、lerp()
関数(内蔵関数)でいけるが、Vector3
型の時は、linear_interpolate()
を使う必要がある。 - 減速では、現状速度(1秒後の目標移動位置)の"my_vel"を"(0, 0, 0)"へ向けて、8%ずつ近づけて行き、その結果を"my_vel"に代入し、これを新しい現状速度としている。
L47
- 加速処理。
- 同様に、"my_vel"はトップスピードである"my_dir * my_spd"(トップスピードで行くと1秒後に到達する位置)に向けて、3%ずつ差を詰めていく。
- 理論的には、いつまで経ってもトップスピードにはならないが、ごく近い数値までは到達する。減速の場合も本当は"0"にはならないハズなんだが、四捨五入かなんかで"0"になるようだ。
L49~51
- 重力用。後で。
以下変化なし。
重力
キネマティックボディーは外部からの力を感じない、そんなボディー。重力も外部の力のひとつであるよって、重力を感じて落下することはない。
だから、キャラクタコントローラにキネマティックボディーを選択した場合は、面倒くさいが自力でスクリプトする必要がある。
L18
- 重力加速度を設定。別に9.8とかにこだわらなくてイイ。15とか20でも全然イイ。ちょっと大きめの方がゲームではソレっぽいと思う。
L19
- "my_vel"では、y情報をクリアしてしまっているので、重力用に保持する為の変数が必要。
L49
- 移動時に保持してあったy情報"my_yyy"に重力加速度を加え、"my_vel.y"に戻す。deltaを掛け忘れない事。
- これで、重力を感じた雰囲気を
move_and_slide()
で出せるようになる。
L51
- ビックリしたけど、
move_and_slide()
はリターンタイプがVector3
型! ただ「KinematicBody」を動かすだけのvoid
型ではないんだなー! ビックリ。
- で、動かした分の情報が返される。それから、これ、代入してるだけに見えるけど、値を返すだけではないから注意。移動もするからね。
- 今回保持しておきたい情報は、yの動きなので、".y"を掛けて、これだけ"my_yyy"に保存している。
- こんなめんどくさい事をしないと、無限に重力加速度がガシガシ足されることになってしまう。平行移動してる時はy方向の動きは"0"になるから一旦クリアできるということ。
確認
では、適当に床やスロープを作って確認だ。ソレっぽく動いたかな?
※ーーー
なんか床のコリジョンを[New PlaneShape]にすると動かないんだけど、俺だけ? 厚みがないとダメなんかな床。
結局、床や坂道は玉転がしの床みたいにキューブで作ったけど。
ーーー※
ではまた。
コピペ用:
extends KinematicBody # マウス var my_mou_sen = 0.003 # マウスセンシ (mouse sensitivity) var my_mou_rel = Vector2(0, 0) # マウスが動いた差分 (mouse relative) # ヘッド (カメラピボット) var my_head # ヘッドノード入れる用 var my_head_ang = 0.0 # ヘッドのうなづき角度(ラジアン) (head angle) var my_head_ang_max = PI / 3.0 # ヘッドのうなづき限界角度(60°) (head angle MAX) # プレイヤ var my_dir = Vector3(0, 0, 0) # キー入力での移動方向 (direction) var my_vel = Vector3(0, 0, 0) # 目標移動地点 (velocity) var my_spd = 10.0 # 移動速度 秒速10m (run speed) var my_acl = 0.03 # 加速率 (acceleration) var my_dcl = 0.08 # 減速率 (deceleration) var my_gra = -10.0 # 重力 (gravity) var my_yyy = 0.0 # 現在のy方向の大きさ保管用 func _ready(): my_head = get_node("Spatial_Head") func _input(event): if event is InputEventMouseMotion: my_mou_rel += event.relative func _physics_process(delta): # プレイヤ動かす # キー入力により移動方向をセットする my_dir = Vector3(0, 0, 0) if Input.is_action_pressed("my_forward"): my_dir = my_dir - my_head.global_transform.basis.z if Input.is_action_pressed("my_backward"): my_dir = my_dir + my_head.global_transform.basis.z if Input.is_action_pressed("my_right"): my_dir = my_dir + my_head.global_transform.basis.x if Input.is_action_pressed("my_left"): my_dir = my_dir - my_head.global_transform.basis.x my_dir.y = 0.0 my_dir = my_dir.normalized() # 加速・減速 if my_dir == Vector3(0, 0, 0): my_vel = my_vel.linear_interpolate(Vector3(0, 0, 0), my_dcl) else: my_vel = my_vel.linear_interpolate(my_dir * my_spd, my_acl) # 重力 my_vel.y = my_yyy + my_gra * delta my_yyy = move_and_slide(my_vel).y # ヘッド動かす # 横回転 my_head.global_rotate(Vector3(0, 1, 0), -my_mou_rel.x * my_mou_sen) # 縦回転 my_head_ang = my_head_ang - my_mou_rel.y * my_mou_sen my_head_ang = clamp(my_head_ang, -my_head_ang_max, my_head_ang_max) my_head.rotation.x = my_head_ang # マウス移動がない時に勝手に動かない様、0に my_mou_rel = Vector2(0, 0)