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

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

Godot キャラコン 05 「カメラの向いている方向に対して移動」

なんか変な疲れがあんだよなー。・・・で、グローバル方向ではなく、カメラの向いている方向に対して動くようにする。そのためには、"my_dir"に加算する値を"Vector3(1, 0, 0)"のような固定値ではなく、その時その時カメラが向いてる方向の軸(ローカル軸)にすれば良い。 カメラの軸はヘッドの軸と同じ方向なので、結局はヘッドのローカル軸を取ってくる方法を探せばよい。
f:id:ore2wakaru:20180403115314p:plain


.global_transform.basis

Spatialクラスを持ってるノードならトランスフォーム情報として、「位置・回転・拡大縮小」の情報を持っている。この情報は取り出し方によって、グローバルで取り出せたり、ローカルで取り出せたり出来る。少し前には回転に関して関数や変数を変え、グローバル軸で回したり、親の軸で回したり、自分自身の軸で回したりした。

こんな風に物体を操作するのに、いかに都合のよい関数・変数が揃っているかが、ゲームエンジンの出来と言ってもいい。

今回はローカル軸のベクトル情報が欲しいのだが、Godotの場合、なんとそんな情報が簡単に取り出せるよう変数に保持してくれている。親切設計、ありがとう。それが、global_transformbasis

  • global_transform: ノードのグローバル情報を保持している変数。ヘッドは「KinematicBody」の子になっているので、普通にアクセスすると親である「KinematicBody」規準のデータが出てくる。今回欲しいのは、ゲーム世界の原点に対しての情報なので、一旦グローバル情報として保持しているデータへのアクセスが必要。(APISpatialクラス参照)
  • basis: ノード自身が持っている回転軸の情報を保持している変数。軸のベクトルはノーマライズされてる模様。(APITransformクラス参照)

この2つを合わせヘッドノードに対し、.global_transform.basisとすることで、ヘッド自身が持つ回転軸情報をゲーム世界の原点に対して反映した時のベクトル情報(座標)にアクセス出来る。まさに一番最初の画像で示した、欲しい情報がゲット出来ることになる。


※ーーー

なんか世の中では、変数のデータをゲットする時は、global_transformってやんないで、get_global_transform()みたいにすんだな。ウェブのAPIに見当たらなかったから、どこにこんな関数があんだろうかってすげー悩んだけど、Godotエディタから見れるオンラインヘルプにあるとはねー。頼むよ・・・。
f:id:ore2wakaru:20180403134646p:plain

で、getterとかsetterってなんじゃらほい? ま、俺は今後も、普通に変数でアクセスするけどね。なんかマズイ?

ーーー※


コードの変更と追加

変更と追加部分はプレイヤを動かす所の、下線を引いた5ヶ所。以下のようにする。
f:id:ore2wakaru:20180403123303p:plain

L28

  • Wを押したときはヘッドの-z方向に動いてほしいので、こうなる。加算ではなく減算。カメラは-z方向を映しているからね。
  • また、basisVector3型なので、basis.xbasis.ybasis.zとして、それぞれの軸単体のデータを持ってこれる。
  • ただし、basis.zとして単体の軸情報を持ってきても、全体としてはゲーム世界の原点から見た(x, y, z)の3軸に対応する数値が入ってるベクトルになるから注意。

一番最初の画像を見ればいいけど点Aは、ぱっと見、(0.707, 0, 0.707)でしょ。ヘッドのx軸(1軸)しか持って来なくても、入る値はx,y,zの3軸って事な。ま、y”0”だから関係ない様に思えるけど、ヘッドがちょっとうなづき回転してたら、その分数値が動くからね。

L30、L32、L34

  • 同様にヘッド自身の軸がゲーム世界の原点に対してどーなっているかを考慮して、ベクトルの加算・減算。
  • Sならz軸方向の加算。Dならx軸方向の加算。Aならx軸方向の減算。

L35

  • これをしないと、プレイヤが斜め上とか斜め下とかに動いてしまうので、y情報はカットする。これで、平面の移動になる。

L36

  • 変更はしてないけど、ちょっと注意なのが、ここノーマライズy情報はすでにカットしているため、ヘッドが上やら下やらを向いていた場合、"my_dir"のベクトルの長さが平面上で"1"より短くなっている。これをノーマライズして長さ"1"に戻す必要がある。


テスト

カメラ方向(ヘッド方向)に動いたかな?
f:id:ore2wakaru:20180403131839g:plain


次はカメラを本来あるべき位置にもってこよう。で、適当に障害物とか置いてみるかな。あとゲーム画面からマウスが出ないようにするとかもイケるかも。細かい所に手を加えようと思う。


いらないかもしれないが、いちおう、ぜんぶん、こぴぺ

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 - 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()
    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)