【AGMakerを深く掘り下げる】プラグインノード - 特定のNode2Dを回転させて移動方向に向ける

著作権ライセンス

このプラグインは、Action Game Makerにおいて、無料または商用のプロジェクトを問わず、あらゆる目的で無料で使用または修正することができます。
ただし、再配布はご遠慮ください。クレジット表記は必須ではありませんが、いただければ幸いです。

プラグインの効果

このプラグインノードの名前はJatkRotateToMovementです。
このノードは、各物理フレームごとにNode2Dを回転させ、特定のNode2Dソースオブジェクトが実際に移動した方向を向かせます。

これは以前JatkRotateToMovementDemoアクションとして提示されていた考え方のノード版です。アクション版は、行動をVS(ビジュアルスクリプト)のステートに組み込み、ステートによって開始と終了を制御する場合に適しています。一方、ノード版は、通常のGodotノードと同様に、回転処理をシーンツリー内の常駐コンポーネントとして使用する場合に適しています。

このプラグインは前後のフレーム間の位置差に基づいて方向を計算するため、velocity(速度)に依存しません。global_positionが時間とともに変化すれば、GameObjectArea2DGameObject、さらには通常のNode2Dでも使用可能です。

要点

  • JatkRotateToMovement.gdをプロジェクト内の任意の場所に配置するだけで済みます
  • シーンに通常のNodeを追加し、このスクリプトをアタッチして、インスペクターで設定します
  • movement_sourceは、移動方向を観察するNode2Dです
  • node_to_rotateは、実際に回転させたいNode2Dです
  • movement_sourceを空欄にすると、デフォルトで親Node2Dが運動源として使用されます
  • node_to_rotateを空欄にすると、優先的に親Node2Dが回転対象となります
  • したがって、コンポーネントをビジュアルモデルノードの下に配置し、movement_sourceを移動中のキャラクターに向け、node_to_rotateを空欄にすることで、モデル自体を回転させることができます
  • Instant(即時)とSmooth(滑らか)の2つの回転モードを使用できます
  • rotation_speedSmoothモードでのみ有効です
  • min_displacement_squaredを使用して、非常に小さな振動による移動をフィルタリングできます

使用方法

手順

  1. 以下のJatkRotateToMovement.gdスクリプト全体をコピーし、プロジェクト内の任意の場所に保存します。
  2. AGMaker/Godotがスクリプトクラスをリフレッシュするのを待ちます。
  3. 移動中のオブジェクト、または回転させたいビジュアルNode2Dを選択します。
  4. Ctrl+Aを押して通常のNodeを追加し、JatkRotateToMovement.gdをそのノードにアタッチします。
  5. 必要に応じてmovement_sourcenode_to_rotaterotation_moderotation_speedmin_displacement_squaredを設定します。
  6. このコンポーネントが、移動と回転を担当する同じNode2Dの下にアタッチされている場合、movement_sourcenode_to_rotateの両方を空欄にできます。
  7. このコンポーネントがビジュアルモデルの下にあり、キャラクターオブジェクトの移動方向に従う場合は、movement_sourceをキャラクターに向け、node_to_rotateを空欄にします。

パラメータの説明

  • movement_source
    2フレーム間のグローバル位置の差を計算するために使用されるNode2D。空欄の場合、デフォルトで親Node2Dが使用されます。
  • node_to_rotate
    回転させる必要があるNode2D。空欄の場合、優先的に親Node2Dが使用されます。
  • rotation_mode
    Instantは現在の移動方向に即時に合わせます。Smoothは滑らかに回転します。
  • rotation_speed
    Smoothモードでのみ使用されます。数値が大きいほど、移動方向を追従する速度が速くなります。
  • min_displacement_squared
    最小移動距離の二乗しきい値。2フレーム間の移動距離の二乗がこの値より大きい場合のみ回転が発生します。二乗値を使用することで、余計な平方根計算を回避できます。

ノード版を使用すべき場合

この回転処理がシーンオブジェクト自体の一部であり、現在のVSステートに関係なく継続的に実行されるべき場合は、JatkRotateToMovementを使用します。

この回転処理が特定のVSステートによって開始および終了されるべき場合は、カスタムアクションJatkRotateToMovementDemoを使用します。

両方のバージョンのコアとなる考え方は同じです:velocityに依存せず、実際の移動に基づいて進行方向を導き出します。

スクリプト

class_name JatkRotateToMovement extends Node

enum RotationMode {
	INSTANT,
	SMOOTH,
}

@export var node_to_rotate: Node2D
@export var movement_source: Node2D
@export var rotation_mode: RotationMode = RotationMode.SMOOTH
@export var rotation_speed: float = 5.0
@export var min_displacement_squared: float = 0.0001

var _resolved_node_to_rotate: Node2D
var _resolved_movement_source: Node2D
var _previous_global_position: Vector2 = Vector2.ZERO
var _has_previous_global_position: bool = false


func _ready() -> void:
	process_physics_priority = 100
	_resolve_runtime_nodes()


func _physics_process(delta: float) -> void:
	if _resolved_movement_source == null or not is_instance_valid(_resolved_movement_source):
		_resolve_runtime_nodes()
		return

	if _resolved_node_to_rotate == null or not is_instance_valid(_resolved_node_to_rotate):
		_resolved_node_to_rotate = _resolve_node_to_rotate()
		if _resolved_node_to_rotate == null:
			return

	var current_global_position: Vector2 = _resolved_movement_source.global_position
	if not _has_previous_global_position:
		_previous_global_position = current_global_position
		_has_previous_global_position = true
		return

	var displacement: Vector2 = current_global_position - _previous_global_position
	_previous_global_position = current_global_position

	if displacement.length_squared() < min_displacement_squared:
		return

	_apply_rotation_from_displacement(displacement, delta)


func _resolve_runtime_nodes() -> void:
	_resolved_movement_source = _resolve_movement_source()
	_resolved_node_to_rotate = _resolve_node_to_rotate()
	_has_previous_global_position = false

	if _resolved_movement_source == null:
		printerr("[JatkRotateToMovement] movement_source is not set and parent is not a Node2D")
		return

	if _resolved_node_to_rotate == null:
		printerr("[JatkRotateToMovement] node_to_rotate is not set and no fallback Node2D is available")
		return

	_previous_global_position = _resolved_movement_source.global_position
	_has_previous_global_position = true


func _resolve_movement_source() -> Node2D:
	if movement_source != null:
		return movement_source

	return get_parent() as Node2D


func _resolve_node_to_rotate() -> Node2D:
	if node_to_rotate != null:
		return node_to_rotate

	var parent_node_2d: Node2D = get_parent() as Node2D
	if parent_node_2d != null:
		return parent_node_2d

	if _resolved_movement_source != null:
		return _resolved_movement_source

	return _resolve_movement_source()


func _apply_rotation_from_displacement(displacement: Vector2, delta: float) -> void:
	var target_angle: float = displacement.angle()

	match rotation_mode:
		RotationMode.INSTANT:
			_resolved_node_to_rotate.global_rotation = target_angle
		RotationMode.SMOOTH:
			var current_angle: float = _resolved_node_to_rotate.global_rotation
			var angle_diff: float = wrapf(target_angle - current_angle, -PI, PI)
			var rotation_weight: float = minf(1.0, maxf(rotation_speed, 0.0) * maxf(delta, 0.0))
			_resolved_node_to_rotate.global_rotation = current_angle + angle_diff * rotation_weight

JatkRotateToMovement.gd (3.0 KB)