Godot キャラコン 04 「グローバル軸方向に移動」
キー入力を受けて移動させる。今回プレイヤには「KinematicBody」を使用しているので、移動用の関数move_and_slide
を使えばいいだけ。簡単。とは言っても、キーを押した瞬間に時速300kmのトップスピードで動かれては困るし、キーを押した方向と言うよりはカメラ方向に対して動いてほしい・・・。だが、そういうのは後のパートで追加する予定。俺の理解が追いつくよう、先ずは超単純な移動から。
Input Map でキー割り当て
メニューの[Project] > [Project Settings]、[Input Map]タブで、以下アクションを作成しWASDキーに割り当てる。
Action | Key |
---|---|
my_forward | W |
my_backward | S |
my_right | D |
my_left | A |
基本こうなればいいけど、もちろん、自分の好きなアクション名を好きなキーに割り当てればいいさ。
やり方を忘れちゃった人は、前やったココを参照。
ore2wakaru2.hatenablog.com
スクリプトに加筆
まずは、WASDで前後左右(グローバル方向)に動くようにする。そのため、2か所に加筆。なお、コメントは俺にとって瞬時に理解出来るよう、日本語にしてしまった模様。
L13 (←←← スクリプトエディタの13行目ってことね、以下同じ)
- キー入力を受け、いきなり移動させるのではなく、一旦移動方向を貯めておくための変数。
L14
- 移動速度。もちろん秒速で考える。
L26
- 前回分の移動方向データが残っているので、ここでクリア。クリアしないと、キー入力なしでも、そっちの方向に動いてしまう。
L27~28
- キー入力があったら、対応する移動方向に蓄える。ここでは、Wキーが押され、アクション"my_forward"が発生していたら、変数"my_dir"にVector3型で(0, 0, -1)の値を加算している。
- (0, 0, -1)はグローバル座標でz方向に"-1"ということ。
- 加算してるのは何故かと言うと、複数キーの同時入力に対応するため。
L29~34
- 同様に他のアクションと、方向をセットしていく。
- 例えば、AキーとDキーを同時に押していれば、"(-1, 0, 0) + (1, 0, 0)"で(0, 0, 0)となり、結果的に止まるよう方向がセットされる。
- "my_dir = Vector3(0, 0, 1)"みたいに単に方向をセットすると、同時にキーを押しても、後の方に作った
if
の内容でセットされてしまうから、複数キー入力に対応しない。だが加算していくと、あのキーもこのキーも押してるのは全部考慮してくれるから、これは非常に優れたやり方だと感じるよな。 - ちなみに、WキーとDキーの同時押しだと、"(0, 0, -1) + (1, 0, 0)"で(1, 0, -1)。ナナメ方向に出来る。すごいね加算。図にすると以下。
L35
normalized()
関数を掛けて、ノーマライズする。- ノーマライズとは、ベクトルの長さ(大きさ)を"1"に揃えること。
- 例えば、Dキーを押してる時の"my_dir"は(1, 0, 0)で長さ"1"。WDキーだと(1, 0, -1)で"ルート2(1.41421356...)"。このように長さが異なってしまう。移動距離は、"my_dir"に"my_spd"を掛けたものにしてるから、このままだと、ナナメ移動時は移動距離が他より長くなってしまう。
- ナナメ方向の時に本当に欲しいデータは半径1の円との交点。そのために、ノーマライズしてこれを求めるということ。これで、真横移動もナナメ移動も平等になる。
- ちなみに
normalized()
関数はclamp()
関数と違って黄色くないので、Godotの内蔵関数ではない。Vector3
クラスで定義されている関数。Vector3
型の変数に後ろから掛けて使う。Vector3
型の変数をごにょごにょしたい時は、APIの内蔵関数だけでなく、Vector3
クラスも見てみないとイケナイ。
L36
move_and_slide()
で「KinematicBody」を動かす。- ()内に動く分をセット。もちろん、1秒間に移動したい分を入れること。"my_dir"の長さはノーマライズしてあるので、どっちの方向でも"1"、それに"my_spd"を掛けてるから、結局、どっちの方向にも1秒で"my_spd"の長さだけ進むことになる。動く方向はグローバル方向。
- 注意なのは、"delta"は掛けない。掛けてはイケナイということ。Godotが勝手にやってくれている。
- 「さんざん"delta"に関してやっといてコレかよ!」と思ってはいけない。
これ以降は、コメントを日本語にしただけで他に変更はない。
実行
最初のGIFのように前後左右に動いたはず。
次回はグローバル方向ではなく、カメラ方向に対して動くように変更を加えよう。せっかくカメラがグリグリ動くんだしな。だが、まだ重力は感じないので空中遊泳。
コピペ用
extends KinematicBody # マウス var my_mou_sen = 0.01 # マウスセンシ (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_spd = 10.0 # 秒速10m (speed) 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 + Vector3(0, 0, -1) if Input.is_action_pressed("my_backward"): my_dir = my_dir + Vector3(0, 0, 1) if Input.is_action_pressed("my_right"): my_dir = my_dir + Vector3(1, 0, 0) if Input.is_action_pressed("my_left"): my_dir = my_dir + Vector3(-1, 0, 0) my_dir = my_dir.normalized() move_and_slide(my_dir * my_spd) # ヘッド動かす # 横回転 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)