Godot キャラコン 21 「おばさん、やっとこアイドル~ウォークする」
長かった。こんなしょぼい事でも1ヶ月半掛かるのかよ。YouTube動画なら30分だぜ、まったく、どうなってんだ。ただ、ゲームエンジンが無かったら、一生無理だったろうな。
んー、アニメがしょぼい。
「おばさん」ボディーの入れ替え
「おばさん」ボディを BodyF01 から BodyF02 に入れ替える。アニメの入ってるやつにする。
[1]
そんで、ファイルシステムパネルの Body フォルダの中にインポートした dae とマテリアルも全部消しとく。
[2]
- 消したら BodyF02 をインポートして。[New Inherited] で新シーンを作成して、追加。
今後もアニメを追加したものに入れ替える時の手間を省くため、名前は変えずに "Scene Root" のまま。 - 「AnimationTreePlayer」ノード追加。
ブレンドアニメをさせるには、インスペクタの設定と、tree の作成を行う必要がある。
[3]
まずはインスペクタ。
[Master Player]、[Base Path]、[Active]、この辺の設定がおかしいとアニメしないから注意。
[4]
次に tree。
スクリプト
変更したとこだけ。
[1]
変数追加。
[2]
_ready()
関数内で変数に代入。onready var
しないで、こっちで揃えた。
[3]
ブレンド値入れる。場所は方向転換の下。
length()
関数はベクトルの長さ(大きさ)をとってくるもの。- "my_vel" は加速したり、減速したりさせてるので、"my_spd" で割って最大スピード比を出してる。
- これで、"0~1" のブレンド値を得ようって事。(YouTudeの人、なかなかやるな。)
- ついでに、カッコ内の "blend2" はノード名。ノードタイプではないから注意。
ただ、落下してる時はすごいスピードになるので、どうなるんだろかな? "1" 越えるんじゃね?
"my_vel.y"の情報は抜いて、
my_blend2 = sqrt(my_vel.x * my_vel.x + my_vel.z * my_vel.z) / my_spd
sqrt()
関数は平方根を出すの内蔵関数。
で、ブレンド値をとった方がいいかもな。坂道とかにも。
※====
床の縁から落として見たら、アニメが破綻するな。"1" を越えた値を入れるとアカンのやな。じゃ、min()
関数を使って、
my_dae_anime_tree.blend2_node_set_amount("blend2", min(my_blend2, 1))
か?
====※
これで歩く「おばさん」出来ちゃった
だけど、やっぱり、歩き始めは常に右足からいってもらいたいんだよな~。その方が自然だろ。今回のこれは、常にループしてるアニメをブレンドしてるから、テキトウなところから開始しちゃうんだよな。どうにかなんねーかな。
それから、あれだ、方向転換の時が変。もうちょいイイ見栄えを望むと、ここにも専用のアニメが必要だな。
※====
人間の歩くスピードは、1分で80m~90mらしいので、"my_spd" は変更した。あと、カメラの距離も。このへんは適当に。あと、どっか変えたかもしれないけど、そのへんも適当に。
====※
コード
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 = [0, 1, 2, 3] # RayCast_L, _R, _U, _Dノード入れる用 var my_ray_dis = [5.0, 5.0, 5.0, 5.0] # RayCast_@がヒットした時その場所までの距離 (distance) var my_ray_dis_min = 3.0 # 距離の最小値を入れる用 var my_cam # カメラノード入れる用 var my_cam_dis = 3.0 # ヘッドとカメラまでの標準距離 (camera distance) # プレイヤ var my_dir = Vector3(0, 0, 0) # キー入力での移動方向 (direction) var my_vel = Vector3(0, 0, 0) # 目標移動地点 (velocity) var my_spd = 1.5 # 移動速度 秒速5m (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方向の大きさ保管用 # ボディ var my_body # インポートdaeボディーのノード用 # C = A.slerp(B, t) var my_body_quat_s # A (start値) でありslerp()の結果のC var my_body_quat_g # B (goal値) var my_body_quat_t = 0.1 # t (acceleration値) # アニメ var my_dae_anime # AnimationPlayerノード用 var my_dae_anime_tree # AnimationTreePlayerノード用 var my_blend2 = 0.0 # Blend値 func _ready(): # ヘッド レイ カメラ my_head = get_node("Spatial_Head") my_ray[0] = get_node("Spatial_Head/RayCast_L") my_ray[1] = get_node("Spatial_Head/RayCast_R") my_ray[2] = get_node("Spatial_Head/RayCast_U") my_ray[3] = get_node("Spatial_Head/RayCast_D") my_cam = get_node("Spatial_Head/Camera_Main") # daeボディ my_body = get_node("Scene Root") my_body_quat_s = Quat(my_body.global_transform.basis) # daeアニメ my_dae_anime = get_node("Scene Root/AnimationPlayer") my_dae_anime_tree = get_node("AnimationTreePlayer") my_dae_anime.get_animation("idle01").loop = true my_dae_anime.get_animation("walk01").loop = true 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_body_quat_g = Quat(Vector3(0, 1, 0), atan2(my_vel.x, my_vel.z)) my_body_quat_s = my_body_quat_s.slerp(my_body_quat_g, my_body_quat_t) my_body.transform = Transform(my_body_quat_s) # アニメ「アイドル~ウォーク 」 my_blend2 = my_vel.length() / my_spd my_dae_anime_tree.blend2_node_set_amount("blend2", my_blend2) # ヘッド動かす # 横回転 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) # 障害物に当たってたらカメラを前に、当たってなければ標準距離に # 当たり判定と距離取得 my_ray_hit(0) # 左レイ判定 my_ray_hit(1) # 右レイ判定 my_ray_hit(2) # 上レイ判定 my_ray_hit(3) # 下レイ判定 # 最小値取得 my_ray_dis_min = my_ray_dis[0] for i in [1, 2, 3]: my_ray_dis_min = min(my_ray_dis_min, my_ray_dis[i]) # カメラ位置セット my_cam.translation = Vector3(0, 0, my_ray_dis_min) func my_ray_hit(i): if my_ray[i].is_colliding(): my_ray_dis[i] = my_ray[i].global_transform.origin.distance_to(my_ray[i].get_collision_point()) else: my_ray_dis[i] = my_cam_dis