许可协议
您可以自由地在 Action Game Maker 中使用或修改此插件,用于任何目的,无论是创建免费项目还是商业项目。但是,请勿重新分发该插件。
无需注明出处,但我们会很感激。
插件效果
此插件节点的名称为 JatkRotateToMovement。
其功能是在每个物理帧旋转一个 Node2D,使其指向所选 Node2D 源的实际移动位移方向。
这是早期 JatkRotateToMovementDemo 动作概念的节点版本。当您希望该行为存在于 VS 状态内部时,动作版本非常方便。当您希望旋转行为像普通 Godot 节点一样作为可重用组件保留在场景树中时,此节点版本非常方便。
因为它根据帧之间的位置差计算方向,所以不依赖于 velocity。它可以与 GameObject、Area2DGameObject 或任何随时间改变 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以旋转模型本身。 - 它支持两种旋转模式:
Instant(即时)和Smooth(平滑)。 rotation_speed仅在Smooth模式下使用。min_displacement_squared可用于忽略非常微小的位移抖动。
核心
操作步骤
- 复制下方的
JatkRotateToMovement.gd脚本,将其放置在项目中的任何位置,然后保存。 - 等待 AGMaker/Godot 刷新脚本类。
- 选择要旋转的移动对象或视觉
Node2D。 - 按
Ctrl+A添加一个普通Node,然后将JatkRotateToMovement.gd附加到它上面。 - 根据需要配置
movement_source、node_to_rotate、rotation_mode、rotation_speed和min_displacement_squared。 - 如果该组件是同一个
Node2D的子节点,而该Node2D既要提供移动又要被旋转,您可以将movement_source和node_to_rotate都留空。 - 如果该组件位于视觉模型下方,但应跟随角色对象的移动,请将
movement_source设置为角色,并留空node_to_rotate。
参数说明
movement_source
用于作为移动方向的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)