1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Godot4.0でジャイロセンサーでボールを転がしてみた

Last updated at Posted at 2023-04-28

 ゲームエンジンGodot4.0で3Dスマホゲームを作りたいと思いますが、その前にお勉強しています。
 2023/4/4リリースのGodot_v4.0.2-stable_win64を使用しています。

目的

 スマホの傾きをジャイロセンサーで読み取ることができたので、床の上にボールを置いてスマホを傾けると傾けたほうにボールが転がるようなアプリを作りたいと思います。
目的.png

プロジェクト設定でスマホを縦で使うように設定する

 プロジェクトメニューからプロジェクト設定を実行します。
 表示>ウインドウを選択して、ビューボートの幅、高さを変更します。また画面解像度が変わった場合に自動的に画面の大きさとControlなどの各パーツのサイズ比率を保つように、ストレッチ/モードをcanvas_itemsに変更します。
 ポータブル/方向をPortraitにすると、スマホを縦に持った画面で実行できるようになります。
スクリーンショット (722).png
また後でタッチ操作を実装するときにWindowsPC上でデバッグできるようにしたいので、入力デバイス>ポインティングの「マウスでタッチ操作をエミュレート」にチェックをいれます。
スクリーンショット (724).png
 ついでに、Androidアプリ(apkファイル)エクスポートのための設定もしておきます。
 右上のAdvanced SettingsをONにして、レンダリング>テクスチャのVRAM圧縮/「Import ETC2 ASTC」をオンにします。設定後は保存して再起動ボタンを押下します。
スクリーンショット (725).png

新規シーンを作成する

 カメラとライト、床とボールをひとつ追加します。ライトは真上から床を照らします。
 StaticBody3Dの床の上にRigidBody3Dのボールを置きます。RigidBody3Dにすることでボールの動きはGodotの物理エンジンにお任せです。

ノード名:ノードクラス名 インスペクタ変更値
World : Node3D
 ┝━Camera3D Position : (0m, 12m, 12m) Rotation : (-60°, 0°, 0°)
 ┝━DirectionalLight3D Position : (-10m, 0m, 0m) Rotation : (-90°, 0°, 0°) shadow:オン
 ┝━Floor : StaticBody3D Position : (0m, -0.5m, 0m)
 ┃  ┝━MeshInstance3D  新規BoxMesh3D Size x:10m, y:1m, z:20m
 ┃  ┗━CollisionShape3D 新規BoxShape3D Size x:10m, y:1m, z:20m
 ┗━MyBall : RigidBody3D Position : (0m, 4m, 0m)
    ┝━MeshInstance3D  新規SphereMesh3D
    ┗━CollisionShape3D 新規SphereShape3D

シーンはこのようになります。
スクリーンショット (727).png
実行します。ボールが上から落ちてきて床の上で止まります。
スクリーンショット (726).png

床を傾けるスクリプトを実装

 スクリプトを実装して床を傾けるようにします。
 シーンのルートノートWorldを右クリックして、スクリプトをアタッチします。
 world.gdという名称になるのでそのまま保存します。

extends Node3D

func _physics_process(delta):
	var v3_gyro_rad = Input.get_gyroscope()
	$Floor.rotate_y(  v3_gyro_rad.z * delta)
	$Floor.rotate_z( -v3_gyro_rad.y * delta)
	$Floor.rotate_x(  v3_gyro_rad.x * delta)

 実行してもwindowsアプリでは何もおきません。
 Android用のapkファイルをエクスポートして、スマホで実行しましょう。
 スマホを傾けるとジャイロセンサー値によって床が傾きますが、おもったよりも自由な方向を向いてしまいます。

 下図はスマホを上から見たときの画面と回転軸です。
 やりたいこととしては、下図の左のようにスマホを左右に傾けたときは床も左右に傾いてほしい。
 中央の図のように奥手前に傾けたときも床は奥手前に傾いてほしい。
 しかし右の図のスマホを自動車のハンドルのように回した時には、回ってほしくないのです。
スクリーンショット (730).png

 ではスクリプトを下記のようにして、rotate_yを実行しないようにすればよいと思うのですが、スマホを前後左右に傾けて戻してを繰り替えすと、床がY軸周りを回転しているように見えます。

extends Node3D

func _physics_process(delta):
	var v3_gyro_rad = Input.get_gyroscope()
	$Floor.rotate_z( -v3_gyro_rad.y * delta)
	$Floor.rotate_x(  v3_gyro_rad.x * delta)

 たぶん下記のオイラー角の問題点なのでしょうか?

オイラー角の問題点
https://docs.godotengine.org/ja/stable/tutorials/3d/using_transforms.html#axis-order

床を左右と手前奥方向にだけ傾くようにする

 Worldシーンの子にNode3DのX軸回転用の子とZ軸回転用の孫を作ってその下にFloorをひ孫としてぶら下げるようにしましょう。
 シーン内のWorldを右クリックして、子ノードを追加からNode3Dを追加します。「x」に名称を変更します。
 xを右クリックして、子ノードを追加からNode3Dを追加します。「z」に名称を変更します。
 そして、Floorをzにドラッグアンドドロップしましょう。
スクリーンショット (731).png

 res://world.gdを下記のように修正します。

extends Node3D

func _physics_process(delta):
	var v3_gyro_rad = Input.get_gyroscope()
	$x/z.rotate_z( -v3_gyro_rad.y * delta)
	$x.rotate_x(  v3_gyro_rad.x * delta)

 これでなぜうまくいくのかは正直理解していないのですが、結果を見ると期待通り床が左右と奥行方向のみ傾くようになります。

Windowsアプリでデバッグできるようにする

 デバッグ用にマウスのクリック&ドラッグ操作で床の傾きを変更できるようにしましょう。 
 スマホで実行した場合は、タッチ操作/スマホ傾けのどちらの操作でも床が傾きます。

2023/5/5 現在の床の傾きをtransform.basis.get_euler()で取得するように変更しました

extends Node3D

@export var m_d_speed = 0.1		# 大きくすると床が傾きやすくなる

var m_touch_pos = Vector2.ZERO		# タッチした時の位置
var m_drag_pos = Vector2.ZERO		# 現在のタッチ位置・ドラッグ位置
var m_f_is_screen_touch = false		# スクリーンにタッチ(ドラッグ含む)している場合true

func _input(event):
	if event is InputEventScreenTouch:
		if event.is_pressed():
			# タッチしたところを初期位置として記憶する
			m_touch_pos  = event.position
			m_drag_pos = event.position
			m_f_is_screen_touch = true
		else:
			m_f_is_screen_touch = false
	elif event is InputEventScreenDrag:
		m_drag_pos = event.position
		m_f_is_screen_touch = true

func _physics_process(delta):
	# ジャイロセンサーによる床傾け
	var v3_gyro_rad = Input.get_gyroscope()
	$x/z.rotate_z( -v3_gyro_rad.y * delta)
	$x.rotate_x(  v3_gyro_rad.x * delta)

	# タッチ操作による床傾け
	if m_f_is_screen_touch:
		# タッチのX軸方向の操作で左右に傾けたいので、Z軸を回転する
		var diff_x_px = m_drag_pos.x - m_touch_pos.x	# スクリーン上のタッチ位置とドラッグ中の位置の差をピクセル量で計算
		var target_z_deg = -(diff_x_px * m_d_speed)		# 符号反転する
		var v3_z_rot : Vector3 = $x/z.transform.basis.get_euler()
		$x/z.rotate_z( deg_to_rad(target_z_deg) - v3_z_rot.z)
		
		# タッチのY軸方向の操作で手前・奥に傾けたいので、x軸を回転する
		var diff_y_px = m_drag_pos.y - m_touch_pos.y
		var target_x_deg = diff_y_px * m_d_speed
		var v3_x_rot : Vector3 =  $x.transform.basis.get_euler()
		$x.rotate_x( deg_to_rad(target_x_deg) - v3_x_rot.x )

ボールの動きを調整する

 ボールの動きが止まりにくくて操作しにくいので調整します。

 シーン内の「MyBall」を選択した状態で、インスペクタのRigidBody3D/LinearのDampを「2」に変更します。
「damps the body's movement」とあるので、動きを鈍らせる、減衰させる、ような効果があるようです。

float linear_damp
https://docs.godotengine.org/ja/latest/classes/class_rigidbody.html#class-rigidbody-property-linear-damp

 また加速が弱いので、インスペクタのRigidBody/GravityScaleを「2」にしました。
スクリーンショット (732).png

 私の感覚で適当に調整をしています。
 またプロジェクト全体に適用する場合はプロジェクト設定の物理/3Dの方を修正します。

スクリーンショット (733).png

ボールが弾むようにする

 実行直後4mの高さから落ちますが、床についたときにピタッと止まるのが不自然なので、少し跳ね返るようにします。

 シーン内の「MyBall」を選択した状態で、インスペクタのRigidBody3DのPhysics Material Overrideの右の<空>をクリックして、新規PhysicsMaterialを選択します。
スクリーンショット (734).png
 設定されたPhysicsMaterialをクリックして詳細設定を表示して、Bounceを0.5にすると、ボールが跳ねるようになります。
スクリーンショット (735).png

 DampやGravityScaleの設定値とも相互に影響していると思います。

github

以上

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?