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

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

Godot キャラコン 09 「障害物とカメラ 簡単2本RayCast」

1本より2本! どーせ角度的に床は透けないし、透けるのは壁。だったら、ヘッドの左右からレイを飛ばして2本で判定したらいいんじゃね? ということで2本RayCastしてみる。結論としては、こっちの方が断然キレイ。・・・ただ天井があった場合は、天井が透けるかもな。その時は3本で判定すればいいじゃん。
f:id:ore2wakaru:20180413141828p:plain


「RayCast」ノード と プロパティ

[1]

ヘッドから出すレイを2本にするので、「RayCast」ノードを2個にする。

f:id:ore2wakaru:20180413142747p:plain

[2]

1本はヘッドの左の方から、もう1本はヘッドの右の方から出るようにする。ただし、完全に真横に付けると、レイの出発点が壁の中になる事があるので、ちょっと内側に置く。これは、プレイヤのカプセルコリジョンとの兼ね合いで調節する。

f:id:ore2wakaru:20180413144512p:plain

  1. 左用。
  2. 8mだとちょっと負荷が高いかもと思って6mにした。2本飛ばすしな。ま、適当に。
  3. ヘッドも、プレイヤのカプセルコリジョンも半径25cm。先にも書いたが、25cmずらすとレイの出発点が壁の中、というかピッタンコ壁の面からになってその壁を判定しなくなる事があるので、余裕をもって-22cmとした。

右用のも同様に作るが、ずらすのは+22cmに。もちろんEnabled"On"に。


スクリプトへの追加と変更

[1]

変数部分の追加・変更。

f:id:ore2wakaru:20180413145554p:plain

L13~14

  • ノード操作用の変数は当然2個に。

L15~16

  • ヒットポイントまでの距離をしまっておく変数を作る。
  • レイがヒットした場合は、左右「RayCast」ノードの中心から、距離が短い方(レイの長さが短い方)のどちらかに合わせてカメラを前に出すため。

[2]

レディー関数のボディー。

f:id:ore2wakaru:20180413150217p:plain

L31~32

  • いつものように代入してるだけ。

[3]

障害物に当たってたらカメラを前に、当たってなければ標準距離に、の部分。バッサリ変更。

f:id:ore2wakaru:20180413150701p:plain

L76~77

  • 左のRayCastがヒットしてたら、ヒットポイントまでの距離を"my_ray_l_dis"に入れる。
  • "my_ray_l.global_transform.origin": 左「RayCast」ノードのグローバル位置。(Vector3型)
  • distance_to(): は"A.distance_to(B)"と使ってAとBの距離を求める関数。(AもBもVector3型)
  • "my_ray_l.get_collision_point()": は前回やったように、ヒットポイント。グローバル位置。(Vector3型)
    図にするとこう。
    f:id:ore2wakaru:20180413153005p:plain
    Aの部分が"my_ray_l.global_transform.origin"で、
    Bの部分が"my_ray_l.get_collision_point()"
    長ったらしくなっているだけ。

L78~79

  • ヒットしなかったら、今回も標準距離を入れる。

L81~84

  • 同上、右用。

L85

  • カメラは今でもヘッドの子なので、ヘッド規準のローカル位置で位置を指定する。
  • min()は"min(A, B)"と使ってABの内、小さい数値の方を採用する内蔵関数


結果

f:id:ore2wakaru:20180413160559p:plain

前より良くなったような感じがすればok。

もっとイイ方法ないかな~。


ぽぴぺ

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_ray_l                    # RayCast_Lノード入れる用
var my_ray_r                    # RayCast_Rノード入れる用
var my_ray_l_dis = 6.0          # RayCast_Lがヒットした時その場所までの距離 (distance)
var my_ray_r_dis = 6.0          # RayCast_Rがヒットした時その場所までの距離 (distance)
var my_cam                      # カメラノード入れる用
var my_cam_dis = 6.0            # ヘッドとカメラまでの標準距離 (camera distance)

# プレイヤ
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")
    my_ray_l = get_node("Spatial_Head/RayCast_L")
    my_ray_r = get_node("Spatial_Head/RayCast_R")
    my_cam = get_node("Spatial_Head/Camera_Main")

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
    # 移動とy保持
    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)
    
    # 障害物に当たってたらカメラを前に、当たってなければ標準距離に
    # 左レイ判定
    if my_ray_l.is_colliding():
        my_ray_l_dis = my_ray_l.global_transform.origin.distance_to(my_ray_l.get_collision_point())
    else:
        my_ray_l_dis = my_cam_dis
    # 右レイ判定
    if my_ray_r.is_colliding():
        my_ray_r_dis = my_ray_r.global_transform.origin.distance_to(my_ray_r.get_collision_point())
    else:
        my_ray_r_dis = my_cam_dis
    my_cam.translation = Vector3(0, 0, min(my_ray_l_dis, my_ray_r_dis))