俺に解るように説明する "Godot Engine 3.x" 入門+

ゲームエンジン Godot Engine に関すること。入門とか使い方とかチュートリアルとか、あれとかこれとか。日本語解説。

Godot キャラコン 04 「グローバル軸方向に移動」

キー入力を受けて移動させる。今回プレイヤには「KinematicBody」を使用しているので、移動用の関数move_and_slideを使えばいいだけ。簡単。とは言っても、キーを押した瞬間に時速300kmのトップスピードで動かれては困るし、キーを押した方向と言うよりはカメラ方向に対して動いてほしい・・・。だが、そういうのは後のパートで追加する予定。俺の理解が追いつくよう、先ずは超単純な移動から。
f:id:ore2wakaru:20180401141841g:plain


Input Map でキー割り当て

メニューの[Project] > [Project Settings][Input Map]タブで、以下アクションを作成しWASDキーに割り当てる。

Action Key
my_forward W
my_backward S
my_right D
my_left A

基本こうなればいいけど、もちろん、自分の好きなアクション名を好きなキーに割り当てればいいさ。
f:id:ore2wakaru:20180331094309p:plain

やり方を忘れちゃった人は、前やったココを参照。
ore2wakaru2.hatenablog.com


スクリプトに加筆

まずは、WASDで前後左右(グローバル方向)に動くようにする。そのため、2か所に加筆。なお、コメントは俺にとって瞬時に理解出来るよう、日本語にしてしまった模様。

f:id:ore2wakaru:20180401103425p:plain

L13 (←←← スクリプトエディタの13行目ってことね、以下同じ)

  • キー入力を受け、いきなり移動させるのではなく、一旦移動方向を貯めておくための変数。

L14

  • 移動速度。もちろん秒速で考える。

f:id:ore2wakaru:20180401103718p:plain

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)。ナナメ方向に出来る。すごいね加算。図にすると以下。
    f:id:ore2wakaru:20180401123638p:plain

L35

  • normalized()関数を掛けて、ノーマライズする。
  • ノーマライズとは、ベクトルの長さ(大きさ)を"1"に揃えること。
  • 例えば、Dキーを押してる時の"my_dir"は(1, 0, 0)で長さ"1"。WDキーだと(1, 0, -1)で"ルート2(1.41421356...)"。このように長さが異なってしまう。移動距離は、"my_dir"に"my_spd"を掛けたものにしてるから、このままだと、ナナメ移動時は移動距離が他より長くなってしまう。
    f:id:ore2wakaru:20180401125939p:plain
  • ナナメ方向の時に本当に欲しいデータは半径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"に関してやっといてコレかよ!」と思ってはいけない。

これ以降は、コメントを日本語にしただけで他に変更はない。 f:id:ore2wakaru:20180401103757p:plain


実行

最初の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)