[AGMaker 入門] プラグインノード:特定の Node2D を移動方向に向ける回転

ライセンス契約

このプラグインは、Action Game Maker 上で、無料プロジェクトか商用プロジェクトかを問わず、あらゆる目的で自由に使用または改変できます。ただし、再配布はしないでください。
クレジットの表示は必須ではありませんが、歓迎します。

プラグインの機能

このプラグインノードの名前は JatkRotateToMovement です。
その機能は、各物理フレームごとに Node2D を回転させ、選択された Node2D ソースの実際の移動変位方向を指すようにすることです。

これは、以前の JatkRotateToMovementDemo アクション案のノード版です。アクション版は、その挙動を VS ステート内部に保持させたい場合に便利です。一方、このノード版は、回転挙動を通常の Godot ノードと同様に、再利用可能なコンポーネントとしてシーンツリー内に保持させたい場合に便利です。

フレーム間の位置差分から方向を計算するため、velocity に依存しません。GameObjectArea2DGameObject、または時間とともに global_position が変化する任意の通常の Node2D とともに動作可能です。

重要なポイント

  • JatkRotateToMovement.gd をプロジェクト内の任意の場所に配置するだけで十分です。
  • シーンに通常の Node を追加し、このスクリプトをアタッチして、インスペクターで設定します。
  • movement_source は、移動の観測対象となる Node2D です。
  • node_to_rotate は、回転させる対象となる Node2D です。
  • movement_source を空のままにすると、ノードは親の Node2D を移動ソースとして使用します。
  • node_to_rotate を空のままにすると、ノードはまず親の Node2D を回転させようとします。
  • つまり、このコンポーネントをビジュアルモデルの下に配置し、movement_source を移動するキャラクターに設定して、node_to_rotate を空にすれば、モデル自体を回転させることができます。
  • InstantSmooth の 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
    フレームごとの global_position の差分を移動方向として使用する Node2D です。空のままにすると、親の Node2D が使用されます。
  • node_to_rotate
    回転させる対象となる Node2D です。空のままにすると、まず親の Node2D が使用されます。
  • rotation_mode
    Instant は現在の移動変位方向に即座に揃えます。Smooth は徐々にその方向へ回転させます。
  • rotation_speed
    Smooth モードでのみ使用されます。値が大きいほど回転が速く追従します。
  • min_displacement_squared
    最小の変位二乗閾値です。フレーム間の変位二乗がこの値より大きい場合のみ回転が発生します。二乗値を使用することで、不要な平方根計算を回避できます。

ノード版を使用するタイミング

回転挙動がシーンオブジェクト自体に属し、どの 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 が設定されておらず、親が Node2D ではありません")
		return

	if _resolved_node_to_rotate == null:
		printerr("[JatkRotateToMovement] node_to_rotate が設定されておらず、代替となる Node2D が利用できません")
		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)

「いいね!」 1