鈎爪基礎功能

This commit is contained in:
RedisTKey 2025-12-31 13:07:31 +08:00
parent e3eeded054
commit 745342c35c
59 changed files with 1611 additions and 651 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=12 format=4 uid="uid://bj2318o3y68x2"]
[gd_scene load_steps=20 format=4 uid="uid://bj2318o3y68x2"]
[ext_resource type="PackedScene" uid="uid://1l06de041i40" path="res://_levels/l_level_1.tscn" id="1_p0ota"]
[ext_resource type="PackedScene" uid="uid://cvqehvdjpoar4" path="res://_player/player_controller.tscn" id="2_gslp7"]
@ -8,6 +8,13 @@
[ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="6_6jw57"]
[ext_resource type="Script" uid="uid://cdvgq0xqdbagk" path="res://addons/reedscene/prop/ReedPropEffect.gd" id="7_2t6pm"]
[ext_resource type="Script" uid="uid://jeybblac0kg2" path="res://addons/reedscene/prop/ReedTransition.gd" id="8_xkd7q"]
[ext_resource type="Script" uid="uid://bhexx6mj1xv3q" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="9_ady2r"]
[ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="10_m16wo"]
[ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="11_m16wo"]
[ext_resource type="Script" uid="uid://dsgl7lbyjsiif" path="res://addons/reedscene/act/ActManager.gd" id="12_xja44"]
[ext_resource type="Script" uid="uid://5e157vdk6175" path="res://addons/reedscene/scene/ReedScene.gd" id="13_2tycc"]
[ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="14_3ihdv"]
[ext_resource type="Script" uid="uid://dn0ksjoswquf5" path="res://addons/reedscene/scene/SceneManager.gd" id="15_hc6q0"]
[sub_resource type="Resource" id="Resource_2t6pm"]
script = ExtResource("7_2t6pm")
@ -32,6 +39,9 @@ value = null
func_name = &"door_open"
metadata/_custom_type_script = "uid://cdvgq0xqdbagk"
[sub_resource type="Resource" id="Resource_xja44"]
script = ExtResource("10_m16wo")
[node name="Game" type="Node2D"]
[node name="level_1" parent="." instance=ExtResource("1_p0ota")]
@ -49,6 +59,7 @@ position = Vector2(342, 176)
[node name="PropComponent" type="Node" parent="EventTriggerDoor"]
script = ExtResource("5_gslp7")
prop_id = 1
metadata/_custom_type_script = "uid://b4menkyub4ce7"
[node name="States" type="Node" parent="EventTriggerDoor/PropComponent"]
@ -70,3 +81,34 @@ script = ExtResource("8_xkd7q")
from_state_id = 0
effects = Array[ExtResource("7_2t6pm")]([SubResource("Resource_xkd7q")])
metadata/_custom_type_script = "uid://jeybblac0kg2"
[node name="PhantomCamera2D" type="Node2D" parent="."]
script = ExtResource("9_ady2r")
tween_resource = SubResource("Resource_xja44")
metadata/_custom_type_script = "uid://bhexx6mj1xv3q"
[node name="PropComponent" type="Node" parent="PhantomCamera2D"]
script = ExtResource("5_gslp7")
prop_id = 0
metadata/_custom_type_script = "uid://b4menkyub4ce7"
[node name="States" type="Node" parent="PhantomCamera2D/PropComponent"]
script = ExtResource("11_m16wo")
[node name="[ID_0] Default" type="Node" parent="PhantomCamera2D/PropComponent/States"]
script = ExtResource("6_6jw57")
state_id = 0
[node name="ReedScene" type="Node2D" parent="."]
script = ExtResource("13_2tycc")
metadata/_custom_type_script = "uid://5e157vdk6175"
[node name="SceneManager" type="Node" parent="ReedScene" node_paths=PackedStringArray("TransitionNode")]
script = ExtResource("15_hc6q0")
TransitionNode = NodePath("Transition")
[node name="ActManager" type="Node" parent="ReedScene"]
script = ExtResource("12_xja44")
[node name="Props" type="Node2D" parent="ReedScene"]
script = ExtResource("14_3ihdv")

View File

@ -124,7 +124,7 @@ unique_name_in_owner = true
script = ExtResource("16_xcbik")
grap_hook_shooting_time = 0.2
[node name="Hooking" type="LimboState" parent="PlayerHSM/Normal/GrapHook"]
[node name="Grapping" type="LimboState" parent="PlayerHSM/Normal/GrapHook"]
unique_name_in_owner = true
[node name="Dash" type="LimboState" parent="PlayerHSM/Normal"]

View File

@ -17,8 +17,7 @@ class_name Normal extends LimboHSM
@onready var grap_hook_state: LimboHSM = %GrapHook
@onready var hook_shooting_state: LimboState = %HookShooting
@onready var hooking_state: LimboState = %Hooking
@onready var grapping_state: LimboState = %Grapping
func _setup() -> void:
self._init_trans()
@ -42,9 +41,10 @@ func _init_trans() -> void:
self.add_transition(on_wall_state,ground_state,&"exit_on_air")
##用於處理鈎爪的State流轉
self.add_transition(ANYSTATE,grap_hook_state,&"want_to_grap_hook")
self.add_transition(ANYSTATE,grap_hook_state,&"want_to_grap_hook",_has_move_input)
self.add_transition(grap_hook_state,airbone_state,&"exit_on_ground")
self.add_transition(grap_hook_state,ground_state,&"exit_on_air")
func _update(delta: float) -> void:
process_direction()
@ -90,3 +90,6 @@ func _get_dash_exit_enter_state()->LimboState:
return ground_state
else:
return airbone_state
func _has_move_input() -> bool:
return agent.get_move_input() != Vector2.ZERO

View File

@ -22,7 +22,6 @@ func _enter() -> void:
func _exit() -> void:
blackboard.set_var(&"is_dashing",false) ##BB里清除Dash状态
agent.locomotion_comp.enable_movement()
agent.locomotion_comp.can_dash = false
func _update(delta: float) -> void:

View File

@ -3,8 +3,12 @@ extends LimboHSM
@onready var root: Normal = %Normal
func _setup() -> void:
self.add_transition(root.hook_shooting_state,root.hooking_state,root.hook_shooting_state.EVENT_FINISHED)
self.add_transition(root.hook_shooting_state,root.grapping_state,root.hook_shooting_state.EVENT_FINISHED)
func _enter() -> void:
get_root().blackboard.set_var(&"is_hooking",true) ##BB里锁住Dash状态
func _exit() -> void:
get_root().blackboard.set_var(&"is_hooking",false) ##BB里锁住Dash状态
agent.locomotion_comp.enable_movement()
agent.spawn_hook_comp._current_grap_hook_inst._leave_rope()

View File

@ -4,22 +4,35 @@ extends LimboState
var _timer: float
func _setup() -> void:
self.add_event_handler(&"completed_grap_hook",_handle_hook_input_completed)
agent.spawn_hook_comp.hook_reach_finished.connect(_handle_hook_stretching_end)
func _enter() -> void:
agent.locomotion_comp.suspend_movement()
agent.locomotion_comp.stop_dash(true)
agent.spawn_hook_comp.spawn_grap_hook_inst()
agent.spawn_hook_comp.grap_reach_target(grap_hook_shooting_time)
_timer = grap_hook_shooting_time
var d = agent.get_move_input() as Vector2
agent.spawn_hook_comp.spawn_grap_hook_inst(d)
func _update(delta: float) -> void:
if _timer <= 0:
self.dispatch(self.EVENT_FINISHED)
_timer -= delta
pass
func _exit() -> void:
#agent.locomotion_comp.enable_movement()
pass
func _handle_hook_input_completed() -> bool:
return true
agent.spawn_hook_comp.suspend_hook_stretching(false)
return true
func _handle_hook_stretching_end(reach_end: bool,Anchor:Node2D) -> void:
##如果鈎爪沒有找到任何錨點,則直接退出
if not Anchor:
if agent.is_on_floor():
self.dispatch(&"exit_on_ground")
else:
self.dispatch(&"exit_on_air")
return

View File

@ -237,7 +237,7 @@ script = ExtResource("6_68ewj")
[node name="RoomLeftPhantomCamera2D" type="Node2D" parent="." node_paths=PackedStringArray("follow_target")]
unique_name_in_owner = true
top_level = true
position = Vector2(141, -91.205)
position = Vector2(66, -91.205)
script = ExtResource("6_2n5r1")
priority = 5
follow_mode = 2
@ -303,12 +303,12 @@ draw_limits = true
[node name="Camera2D" type="Camera2D" parent="."]
physics_interpolation_mode = 1
position = Vector2(141, -91.205)
position = Vector2(66, -91.205)
zoom = Vector2(2, 2)
process_callback = 0
limit_left = -147
limit_left = -387
limit_top = -528
limit_right = 673
limit_right = 433
limit_bottom = 288
position_smoothing_speed = 10.0
editor_draw_limits = true

View File

@ -125,7 +125,7 @@ metadata/_edit_lock_ = true
[node name="MainCamera3D" type="Camera3D" parent="."]
unique_name_in_owner = true
physics_interpolation_mode = 1
transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 2.5, 2)
transform = Transform3D(0.999889, 0, 0, 0, 0.707092, 0.707088, 0, -0.707092, 0.707088, 0, 2.5, 3.19136)
[node name="PhantomCameraHost" type="Node" parent="MainCamera3D"]
process_priority = 300
@ -136,7 +136,7 @@ script = ExtResource("2_d1opf")
[node name="PlayerPhantomCamera3D" type="Node3D" parent="." node_paths=PackedStringArray("follow_target")]
unique_name_in_owner = true
transform = Transform3D(0.999889, 0, 0, 0, 0.707092, 0.707088, 0, -0.707092, 0.707088, 0, 2.5, 2)
transform = Transform3D(0.999889, 0, 0, 0, 0.707092, 0.707088, 0, -0.707092, 0.707088, 0, 2.5, 3.19136)
top_level = true
script = ExtResource("3_4whss")
priority = 3
@ -149,7 +149,7 @@ follow_offset = Vector3(0, 2, 2)
follow_damping = true
[node name="PlayerCharacterBody3D" parent="." instance=ExtResource("6_lr46m")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 1.19136)
script = ExtResource("7_x1jex")
[node name="-------------------" type="Node" parent="."]

View File

@ -12,9 +12,6 @@ extends Node2D
func _ready():
pcam_room_left.set_follow_offset(Vector2(0, -80))
pcam_room_right.set_follow_offset(Vector2(0, -80))
area_2d_room_left.body_entered.connect(_on_body_entered.bind(pcam_room_left))
area_2d_room_centre.body_entered.connect(_on_body_entered.bind(pcam_room_centre))
area_2d_room_right.body_entered.connect(_on_body_entered.bind(pcam_room_right))

View File

@ -144,6 +144,7 @@ func _physics_process(delta: float) -> void:
func _show_prompt(body_rid: RID, body: Node2D, body_shape_index: int, local_shape: int) -> void:
if body.is_class("TileMapLayer"): # TODO - Using string reference to support Godot 4.2
var tile_map := body
tile_map.physics_quadrant_size = 1
var tile_coords: Vector2i = tile_map.get_coords_for_body_rid(body_rid)
var cell_data: TileData = tile_map.get_cell_tile_data(tile_coords)

View File

@ -1,3 +1,3 @@
[gd_resource type="ButtonGroup" load_steps=0 format=3 uid="uid://dfu0b8jbtyr1n"]
[gd_resource type="ButtonGroup" format=3 uid="uid://dfu0b8jbtyr1n"]
[resource]

View File

@ -3,5 +3,5 @@
name="Phantom Camera"
description="Control the movement and dynamically tween 2D & 3D cameras positions. Built for Godot 4. Inspired by Cinemachine."
author="Marcus Skov"
version="0.9.3.1"
version="0.9.4"
script="plugin.gd"

View File

@ -1,7 +1,7 @@
@tool
extends EditorNode3DGizmo
#var pcam_3d: PhantomCamera3D
var pcam_3d: PhantomCamera3D
func _redraw() -> void:
clear()
@ -9,12 +9,12 @@ func _redraw() -> void:
var icon: Material = get_plugin().get_material(get_plugin().get_name(), self)
add_unscaled_billboard(icon, 0.035)
var pcam_3d: PhantomCamera3D = get_node_3d()
pcam_3d = get_node_3d()
# if pcam_3d.is_following():
# _draw_target(pcam_3d, pcam_3d.get_follow_target_position(), "follow_target")
# if pcam_3d.is_looking_at():
# _draw_target(pcam_3d, pcam_3d.get_look_at_target_position(), "look_at_target")
if pcam_3d.is_following() and pcam_3d.draw_follow_gizmo_line():
_draw_target(pcam_3d.get_follow_target_position(), "follow_target")
if pcam_3d.is_looking() and pcam_3d.draw_look_at_gizmo_line():
_draw_target(pcam_3d.get_look_at_target_position(),"look_at_target")
if pcam_3d.is_active(): return
@ -73,12 +73,13 @@ func _redraw() -> void:
add_lines(frustum_lines, frustum_material, false)
func _draw_target(pcam_3d: Node3D, target_position: Vector3, type: String) -> void:
func _draw_target(target: Vector3, type: StringName) -> void:
var target_lines: PackedVector3Array = PackedVector3Array()
var direction: Vector3 = target_position - pcam_3d.global_position
var end_position: Vector3 = pcam_3d.global_basis.z * direction
var direction: Vector3 = pcam_3d.global_position - target
var end_position: Vector3 = -direction * pcam_3d.quaternion
target_lines.push_back(Vector3.ZERO)
target_lines.push_back(end_position)
var target_material: StandardMaterial3D = get_plugin().get_material(type, self)
add_lines(target_lines, target_material, false)

View File

@ -26,7 +26,7 @@ func _has_gizmo(spatial: Node3D) -> bool:
func _init() -> void:
create_icon_material(gizmo_name, _icon_texture, false, Color.WHITE)
create_material("frustum", Color8(252, 127, 127, 255))
create_material("follow_target", Color8(185, 58, 89))
create_material("follow_target", Color8(248, 67, 47))
create_material("look_at_target", Color8(61, 207, 225))

View File

@ -29,8 +29,8 @@ public static class PhantomCameraManager
public static class MethodName
{
public const string GetPhantomCamera2Ds = "get_phantom_camera_2ds";
public const string GetPhantomCamera3Ds = "get_phantom_camera_3ds";
public const string GetPhantomCameraHosts = "get_phantom_camera_hosts";
public static readonly StringName GetPhantomCamera2Ds = new("get_phantom_camera_2ds");
public static readonly StringName GetPhantomCamera3Ds = new("get_phantom_camera_3ds");
public static readonly StringName GetPhantomCameraHosts = new("get_phantom_camera_hosts");
}
}

View File

@ -31,14 +31,6 @@ public abstract class PhantomCamera
public event IsTweeningEventHandler? IsTweening;
public event TweenCompletedEventHandler? TweenCompleted;
private readonly Callable _callableBecameActive;
private readonly Callable _callableBecameInactive;
private readonly Callable _callableFollowTargetChanged;
private readonly Callable _callableDeadZoneChanged;
private readonly Callable _callableTweenStarted;
private readonly Callable _callableIsTweening;
private readonly Callable _callableTweenCompleted;
public int Priority
{
get => (int)Node.Call(MethodName.GetPriority);
@ -135,119 +127,108 @@ public abstract class PhantomCamera
{
Node = phantomCameraNode;
_callableBecameActive = Callable.From(() => BecameActive?.Invoke());
_callableBecameInactive = Callable.From(() => BecameInactive?.Invoke());
_callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke());
_callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke());
_callableTweenStarted = Callable.From(() => TweenStarted?.Invoke());
_callableIsTweening = Callable.From(() => IsTweening?.Invoke());
_callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke());
var callableBecameActive = Callable.From(() => BecameActive?.Invoke());
var callableBecameInactive = Callable.From(() => BecameInactive?.Invoke());
var callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke());
var callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke());
var callableTweenStarted = Callable.From(() => TweenStarted?.Invoke());
var callableIsTweening = Callable.From(() => IsTweening?.Invoke());
var callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke());
Node.Connect(SignalName.BecameActive, _callableBecameActive);
Node.Connect(SignalName.BecameInactive, _callableBecameInactive);
Node.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged);
Node.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged);
Node.Connect(SignalName.TweenStarted, _callableTweenStarted);
Node.Connect(SignalName.IsTweening, _callableIsTweening);
Node.Connect(SignalName.TweenCompleted, _callableTweenCompleted);
}
~PhantomCamera()
{
Node.Disconnect(SignalName.BecameActive, _callableBecameActive);
Node.Disconnect(SignalName.BecameInactive, _callableBecameInactive);
Node.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged);
Node.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged);
Node.Disconnect(SignalName.TweenStarted, _callableTweenStarted);
Node.Disconnect(SignalName.IsTweening, _callableIsTweening);
Node.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted);
Node.Connect(SignalName.BecameActive, callableBecameActive);
Node.Connect(SignalName.BecameInactive, callableBecameInactive);
Node.Connect(SignalName.FollowTargetChanged, callableFollowTargetChanged);
Node.Connect(SignalName.DeadZoneChanged, callableDeadZoneChanged);
Node.Connect(SignalName.TweenStarted, callableTweenStarted);
Node.Connect(SignalName.IsTweening, callableIsTweening);
Node.Connect(SignalName.TweenCompleted, callableTweenCompleted);
}
public static class MethodName
{
public const string GetFollowMode = "get_follow_mode";
public const string IsActive = "is_active";
public static readonly StringName GetFollowMode = new("get_follow_mode");
public static readonly StringName IsActive = new("is_active");
public const string GetPriority = "get_priority";
public const string SetPriority = "set_priority";
public static readonly StringName GetPriority = new("get_priority");
public static readonly StringName SetPriority = new("set_priority");
public const string IsFollowing = "is_following";
public static readonly StringName IsFollowing = new("is_following");
public const string GetFollowTarget = "get_follow_target";
public const string SetFollowTarget = "set_follow_target";
public static readonly StringName GetFollowTarget = new("get_follow_target");
public static readonly StringName SetFollowTarget = new("set_follow_target");
public const string GetFollowTargets = "get_follow_targets";
public const string SetFollowTargets = "set_follow_targets";
public static readonly StringName GetFollowTargets = new("get_follow_targets");
public static readonly StringName SetFollowTargets = new("set_follow_targets");
public const string TeleportPosition = "teleport_position";
public static readonly StringName TeleportPosition = new("teleport_position");
public const string AppendFollowTargets = "append_follow_targets";
public const string AppendFollowTargetsArray = "append_follow_targets_array";
public const string EraseFollowTargets = "erase_follow_targets";
public static readonly StringName AppendFollowTargets = new("append_follow_targets");
public static readonly StringName AppendFollowTargetsArray = new("append_follow_targets_array");
public static readonly StringName EraseFollowTargets = new("erase_follow_targets");
public const string GetFollowPath = "get_follow_path";
public const string SetFollowPath = "set_follow_path";
public static readonly StringName GetFollowPath = new("get_follow_path");
public static readonly StringName SetFollowPath = new("set_follow_path");
public const string GetFollowOffset = "get_follow_offset";
public const string SetFollowOffset = "set_follow_offset";
public static readonly StringName GetFollowOffset = new("get_follow_offset");
public static readonly StringName SetFollowOffset = new("set_follow_offset");
public const string GetFollowDamping = "get_follow_damping";
public const string SetFollowDamping = "set_follow_damping";
public static readonly StringName GetFollowDamping = new("get_follow_damping");
public static readonly StringName SetFollowDamping = new("set_follow_damping");
public const string GetFollowDampingValue = "get_follow_damping_value";
public const string SetFollowDampingValue = "set_follow_damping_value";
public static readonly StringName GetFollowDampingValue = new("get_follow_damping_value");
public static readonly StringName SetFollowDampingValue = new("set_follow_damping_value");
public const string GetFollowAxisLock = "get_follow_axis_lock";
public const string SetFollowAxisLock = "set_follow_axis_lock";
public static readonly StringName GetFollowAxisLock = new("get_follow_axis_lock");
public static readonly StringName SetFollowAxisLock = new("set_follow_axis_lock");
public const string GetTweenResource = "get_tween_resource";
public const string SetTweenResource = "set_tween_resource";
public static readonly StringName GetTweenResource = new("get_tween_resource");
public static readonly StringName SetTweenResource = new("set_tween_resource");
public const string GetTweenSkip = "get_tween_skip";
public const string SetTweenSkip = "set_tween_skip";
public static readonly StringName GetTweenSkip = new("get_tween_skip");
public static readonly StringName SetTweenSkip = new("set_tween_skip");
public const string GetTweenDuration = "get_tween_duration";
public const string SetTweenDuration = "set_tween_duration";
public static readonly StringName GetTweenDuration = new("get_tween_duration");
public static readonly StringName SetTweenDuration = new("set_tween_duration");
public const string GetTweenTransition = "get_tween_transition";
public const string SetTweenTransition = "set_tween_transition";
public static readonly StringName GetTweenTransition = new("get_tween_transition");
public static readonly StringName SetTweenTransition = new("set_tween_transition");
public const string GetTweenEase = "get_tween_ease";
public const string SetTweenEase = "set_tween_ease";
public static readonly StringName GetTweenEase = new("get_tween_ease");
public static readonly StringName SetTweenEase = new("set_tween_ease");
public const string GetTweenOnLoad = "get_tween_on_load";
public const string SetTweenOnLoad = "set_tween_on_load";
public static readonly StringName GetTweenOnLoad = new("get_tween_on_load");
public static readonly StringName SetTweenOnLoad = new("set_tween_on_load");
public const string GetInactiveUpdateMode = "get_inactive_update_mode";
public const string SetInactiveUpdateMode = "set_inactive_update_mode";
public static readonly StringName GetInactiveUpdateMode = new("get_inactive_update_mode");
public static readonly StringName SetInactiveUpdateMode = new("set_inactive_update_mode");
public const string GetHostLayers = "get_host_layers";
public const string SetHostLayers = "set_host_layers";
public const string SetHostLayersValue = "set_host_layers_value";
public static readonly StringName GetHostLayers = new("get_host_layers");
public static readonly StringName SetHostLayers = new("set_host_layers");
public static readonly StringName SetHostLayersValue = new("set_host_layers_value");
public const string GetNoiseEmitterLayer = "get_noise_emitter_layer";
public const string SetNoiseEmitterLayer = "set_noise_emitter_layer";
public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer");
public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer");
public const string EmitNoise = "emit_noise";
public static readonly StringName EmitNoise = new("emit_noise");
}
public static class PropertyName
{
public const string DeadZoneWidth = "dead_zone_width";
public const string DeadZoneHeight = "dead_zone_height";
public static readonly StringName DeadZoneWidth = new("dead_zone_width");
public static readonly StringName DeadZoneHeight = new("dead_zone_height");
}
public static class SignalName
{
public const string BecameActive = "became_active";
public const string BecameInactive = "became_inactive";
public const string FollowTargetChanged = "follow_target_changed";
public const string DeadZoneChanged = "dead_zone_changed";
public const string DeadZoneReached = "dead_zone_reached";
public const string TweenStarted = "tween_started";
public const string IsTweening = "is_tweening";
public const string TweenCompleted = "tween_completed";
public const string TweenInterrupted = "tween_interrupted";
public const string NoiseEmitted = "noise_emitted";
public static readonly StringName BecameActive = new("became_active");
public static readonly StringName BecameInactive = new("became_inactive");
public static readonly StringName FollowTargetChanged = new("follow_target_changed");
public static readonly StringName DeadZoneChanged = new("dead_zone_changed");
public static readonly StringName DeadZoneReached = new("dead_zone_reached");
public static readonly StringName TweenStarted = new("tween_started");
public static readonly StringName IsTweening = new("is_tweening");
public static readonly StringName TweenCompleted = new("tween_completed");
public static readonly StringName TweenInterrupted = new("tween_interrupted");
public static readonly StringName NoiseEmitted = new("noise_emitted");
}
}

View File

@ -55,10 +55,6 @@ public class PhantomCamera2D : PhantomCamera
public event DeadZoneReachedEventHandler? DeadZoneReached;
public event NoiseEmittedEventHandler? NoiseEmitted;
private readonly Callable _callableTweenInterrupted;
private readonly Callable _callableDeadZoneReached;
private readonly Callable _callableNoiseEmitted;
public Node2D FollowTarget
{
get => (Node2D)Node2D.Call(PhantomCamera.MethodName.GetFollowTarget);
@ -211,25 +207,15 @@ public class PhantomCamera2D : PhantomCamera
set => Node2D.Call(MethodName.SetLimitTarget, value);
}
public static PhantomCamera2D FromScript(string path) => new(GD.Load<GDScript>(path).New().AsGodotObject());
public static PhantomCamera2D FromScript(GDScript script) => new(script.New().AsGodotObject());
public PhantomCamera2D(GodotObject phantomCameraNode) : base(phantomCameraNode)
{
_callableTweenInterrupted = Callable.From<Node2D>(pCam => TweenInterrupted?.Invoke(pCam));
_callableDeadZoneReached = Callable.From((Vector2 side) => DeadZoneReached?.Invoke(side));
_callableNoiseEmitted = Callable.From((Transform2D output) => NoiseEmitted?.Invoke(output));
var callableTweenInterrupted = Callable.From<Node2D>(pCam => TweenInterrupted?.Invoke(pCam));
var callableDeadZoneReached = Callable.From((Vector2 side) => DeadZoneReached?.Invoke(side));
var callableNoiseEmitted = Callable.From((Transform2D output) => NoiseEmitted?.Invoke(output));
Node2D.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted);
Node2D.Connect(SignalName.DeadZoneReached, _callableDeadZoneReached);
Node2D.Connect(SignalName.NoiseEmitted, _callableNoiseEmitted);
}
~PhantomCamera2D()
{
Node2D.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted);
Node2D.Disconnect(SignalName.DeadZoneReached, _callableDeadZoneReached);
Node2D.Disconnect(SignalName.NoiseEmitted, _callableNoiseEmitted);
Node2D.Connect(SignalName.TweenInterrupted, callableTweenInterrupted);
Node2D.Connect(SignalName.DeadZoneReached, callableDeadZoneReached);
Node2D.Connect(SignalName.NoiseEmitted, callableNoiseEmitted);
}
public void SetLimitTarget(TileMap tileMap)
@ -265,64 +251,64 @@ public class PhantomCamera2D : PhantomCamera
public new static class MethodName
{
public const string GetZoom = "get_zoom";
public const string SetZoom = "set_zoom";
public static readonly StringName GetZoom = new("get_zoom");
public static readonly StringName SetZoom = new("set_zoom");
public const string GetSnapToPixel = "get_snap_to_pixel";
public const string SetSnapToPixel = "set_snap_to_pixel";
public static readonly StringName GetSnapToPixel = new("get_snap_to_pixel");
public static readonly StringName SetSnapToPixel = new("set_snap_to_pixel");
public const string GetRotateWithTarget = "get_rotate_with_target";
public const string SetRotateWithTarget = "set_rotate_with_target";
public static readonly StringName GetRotateWithTarget = new("get_rotate_with_target");
public static readonly StringName SetRotateWithTarget = new("set_rotate_with_target");
public const string GetRotationOffset = "get_rotation_offset";
public const string SetRotationOffset = "set_rotation_offset";
public static readonly StringName GetRotationOffset = new("get_rotation_offset");
public static readonly StringName SetRotationOffset = new("set_rotation_offset");
public const string GetRotationDamping = "get_rotation_damping";
public const string SetRotationDamping = "set_rotation_damping";
public static readonly StringName GetRotationDamping = new("get_rotation_damping");
public static readonly StringName SetRotationDamping = new("set_rotation_damping");
public const string GetRotationDampingValue = "get_rotation_damping_value";
public const string SetRotationDampingValue = "set_rotation_damping_value";
public static readonly StringName GetRotationDampingValue = new("get_rotation_damping_value");
public static readonly StringName SetRotationDampingValue = new("set_rotation_damping_value");
public const string GetLimit = "get_limit";
public const string SetLimit = "set_limit";
public static readonly StringName GetLimit = new("get_limit");
public static readonly StringName SetLimit = new("set_limit");
public const string GetLimitLeft = "get_limit_left";
public const string SetLimitLeft = "set_limit_left";
public static readonly StringName GetLimitLeft = new("get_limit_left");
public static readonly StringName SetLimitLeft = new("set_limit_left");
public const string GetLimitTop = "get_limit_top";
public const string SetLimitTop = "set_limit_top";
public static readonly StringName GetLimitTop = new("get_limit_top");
public static readonly StringName SetLimitTop = new("set_limit_top");
public const string GetLimitRight = "get_limit_right";
public const string SetLimitRight = "set_limit_right";
public static readonly StringName GetLimitRight = new("get_limit_right");
public static readonly StringName SetLimitRight = new("set_limit_right");
public const string GetLimitBottom = "get_limit_bottom";
public const string SetLimitBottom = "set_limit_bottom";
public static readonly StringName GetLimitBottom = new("get_limit_bottom");
public static readonly StringName SetLimitBottom = new("set_limit_bottom");
public const string GetLimitTarget = "get_limit_target";
public const string SetLimitTarget = "set_limit_target";
public static readonly StringName GetLimitTarget = new("get_limit_target");
public static readonly StringName SetLimitTarget = new("set_limit_target");
public const string GetLimitMargin = "get_limit_margin";
public const string SetLimitMargin = "set_limit_margin";
public static readonly StringName GetLimitMargin = new("get_limit_margin");
public static readonly StringName SetLimitMargin = new("set_limit_margin");
public const string GetAutoZoom = "get_auto_zoom";
public const string SetAutoZoom = "set_auto_zoom";
public static readonly StringName GetAutoZoom = new("get_auto_zoom");
public static readonly StringName SetAutoZoom = new("set_auto_zoom");
public const string GetAutoZoomMin = "get_auto_zoom_min";
public const string SetAutoZoomMin = "set_auto_zoom_min";
public static readonly StringName GetAutoZoomMin = new("get_auto_zoom_min");
public static readonly StringName SetAutoZoomMin = new("set_auto_zoom_min");
public const string GetAutoZoomMax = "get_auto_zoom_max";
public const string SetAutoZoomMax = "set_auto_zoom_max";
public static readonly StringName GetAutoZoomMax = new("get_auto_zoom_max");
public static readonly StringName SetAutoZoomMax = new("set_auto_zoom_max");
public const string GetAutoZoomMargin = "get_auto_zoom_margin";
public const string SetAutoZoomMargin = "set_auto_zoom_margin";
public static readonly StringName GetAutoZoomMargin = new("get_auto_zoom_margin");
public static readonly StringName SetAutoZoomMargin = new("set_auto_zoom_margin");
public const string GetNoise = "get_noise";
public const string SetNoise = "set_noise";
public static readonly StringName GetNoise = new("get_noise");
public static readonly StringName SetNoise = new("set_noise");
}
public new static class PropertyName
{
public const string DrawLimits = "draw_limits";
public static readonly StringName DrawLimits = new("draw_limits");
}
}

View File

@ -69,7 +69,7 @@ public static class PhantomCamera3DExtensions
public static Vector3 GetThirdPersonRotationDegrees(this PhantomCamera3D pCam3D) =>
(Vector3)pCam3D.Node3D.Call(PhantomCamera3D.MethodName.GetThirdPersonRotationDegrees);
public static void SetThirdPersonDegrees(this PhantomCamera3D pCam3D, Vector3 rotation) =>
public static void SetThirdPersonRotationDegrees(this PhantomCamera3D pCam3D, Vector3 rotation) =>
pCam3D.Node3D.Call(PhantomCamera3D.MethodName.SetThirdPersonRotationDegrees, rotation);
public static Quaternion GetThirdPersonQuaternion(this PhantomCamera3D pCam3D) =>
@ -98,13 +98,6 @@ public class PhantomCamera3D : PhantomCamera
public event TweenInterruptedEventHandler? TweenInterrupted;
public event NoiseEmittedEventHandler? NoiseEmitted;
private readonly Callable _callableLookAtTargetChanged;
private readonly Callable _callableDeadZoneReached;
private readonly Callable _callableCamera3DResourceChanged;
private readonly Callable _callableCamera3DResourcePropertyChanged;
private readonly Callable _callableTweenInterrupted;
private readonly Callable _callableNoiseEmitted;
public Node3D FollowTarget
{
get => (Node3D)Node3D.Call(PhantomCamera.MethodName.GetFollowTarget);
@ -256,18 +249,24 @@ public class PhantomCamera3D : PhantomCamera
set => Node3D.Call(MethodName.SetLookAtDampingValue, value);
}
public Node3D Up
public Vector3 Up
{
get => (Node3D)Node3D.Call(MethodName.GetUp);
get => (Vector3)Node3D.Call(MethodName.GetUp);
set => Node3D.Call(MethodName.SetUp, value);
}
public Vector3 UpTarget
public Node3D UpTarget
{
get => (Vector3)Node3D.Call(MethodName.GetUpTarget);
get => (Node3D)Node3D.Call(MethodName.GetUpTarget);
set => Node3D.Call(MethodName.SetUpTarget, value);
}
public int KeepAspect
{
get => (int)Node3D.Call(MethodName.GetKeepAspect);
set => Node3D.Call(MethodName.SetKeepAspect, value);
}
public int CullMask
{
get => (int)Node3D.Call(MethodName.GetCullMask);
@ -342,152 +341,142 @@ public class PhantomCamera3D : PhantomCamera
public void EmitNoise(Transform3D transform) => Node3D.Call(PhantomCamera.MethodName.EmitNoise, transform);
public static PhantomCamera3D FromScript(string path) => new(GD.Load<GDScript>(path).New().AsGodotObject());
public static PhantomCamera3D FromScript(GDScript script) => new(script.New().AsGodotObject());
public PhantomCamera3D(GodotObject phantomCamera3DNode) : base(phantomCamera3DNode)
{
_callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke());
_callableDeadZoneReached = Callable.From(() => DeadZoneReached?.Invoke());
_callableCamera3DResourceChanged = Callable.From(() => Camera3DResourceChanged?.Invoke());
_callableCamera3DResourcePropertyChanged = Callable.From((StringName property, Variant value) =>
Camera3DResourcePropertyChanged?.Invoke(property, value));
_callableTweenInterrupted = Callable.From<Node3D>(pCam => TweenInterrupted?.Invoke(pCam));
_callableNoiseEmitted = Callable.From((Transform3D output) => NoiseEmitted?.Invoke(output));
var callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke());
var callableDeadZoneReached = Callable.From(() => DeadZoneReached?.Invoke());
var callableCamera3DResourceChanged = Callable.From(() => Camera3DResourceChanged?.Invoke());
var callableCamera3DResourcePropertyChanged = Callable.From((StringName property, Variant value) =>
Camera3DResourcePropertyChanged?.Invoke(property, value));
var callableTweenInterrupted = Callable.From<Node3D>(pCam => TweenInterrupted?.Invoke(pCam));
var callableNoiseEmitted = Callable.From((Transform3D output) => NoiseEmitted?.Invoke(output));
Node3D.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged);
Node3D.Connect(PhantomCamera.SignalName.DeadZoneReached, _callableDeadZoneReached);
Node3D.Connect(SignalName.Camera3DResourceChanged, _callableCamera3DResourceChanged);
Node3D.Connect(SignalName.Camera3DResourcePropertyChanged, _callableCamera3DResourcePropertyChanged);
Node3D.Connect(PhantomCamera.SignalName.TweenInterrupted, _callableTweenInterrupted);
Node3D.Connect(PhantomCamera.SignalName.NoiseEmitted, _callableNoiseEmitted);
}
~PhantomCamera3D()
{
Node3D.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged);
Node3D.Disconnect(PhantomCamera.SignalName.DeadZoneReached, _callableDeadZoneReached);
Node3D.Disconnect(SignalName.Camera3DResourceChanged, _callableCamera3DResourceChanged);
Node3D.Disconnect(SignalName.Camera3DResourcePropertyChanged, _callableCamera3DResourcePropertyChanged);
Node3D.Disconnect(PhantomCamera.SignalName.TweenInterrupted, _callableTweenInterrupted);
Node3D.Disconnect(PhantomCamera.SignalName.NoiseEmitted, _callableNoiseEmitted);
Node3D.Connect(SignalName.LookAtTargetChanged, callableLookAtTargetChanged);
Node3D.Connect(PhantomCamera.SignalName.DeadZoneReached, callableDeadZoneReached);
Node3D.Connect(SignalName.Camera3DResourceChanged, callableCamera3DResourceChanged);
Node3D.Connect(SignalName.Camera3DResourcePropertyChanged, callableCamera3DResourcePropertyChanged);
Node3D.Connect(PhantomCamera.SignalName.TweenInterrupted, callableTweenInterrupted);
Node3D.Connect(PhantomCamera.SignalName.NoiseEmitted, callableNoiseEmitted);
}
public new static class MethodName
{
public const string GetLookAtMode = "get_look_at_mode";
public static readonly StringName GetLookAtMode = new("get_look_at_mode");
public const string GetCamera3DResource = "get_camera_3d_resource";
public const string SetCamera3DResource = "set_camera_3d_resource";
public static readonly StringName GetCamera3DResource = new("get_camera_3d_resource");
public static readonly StringName SetCamera3DResource = new("set_camera_3d_resource");
public const string GetThirdPersonRotation = "get_third_person_rotation";
public const string SetThirdPersonRotation = "set_third_person_rotation";
public static readonly StringName GetThirdPersonRotation = new("get_third_person_rotation");
public static readonly StringName SetThirdPersonRotation = new("set_third_person_rotation");
public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees";
public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees";
public static readonly StringName GetThirdPersonRotationDegrees = new("get_third_person_rotation_degrees");
public static readonly StringName SetThirdPersonRotationDegrees = new("set_third_person_rotation_degrees");
public const string GetThirdPersonQuaternion = "get_third_person_quaternion";
public const string SetThirdPersonQuaternion = "set_third_person_quaternion";
public static readonly StringName GetThirdPersonQuaternion = new("get_third_person_quaternion");
public static readonly StringName SetThirdPersonQuaternion = new("set_third_person_quaternion");
public const string GetVerticalRotationOffset = "get_vertical_rotation_offset";
public const string SetVerticalRotationOffset = "set_vertical_rotation_offset";
public static readonly StringName GetVerticalRotationOffset = new("get_vertical_rotation_offset");
public static readonly StringName SetVerticalRotationOffset = new("set_vertical_rotation_offset");
public const string GetHorizontalRotationOffset = "get_horizontal_rotation_offset";
public const string SetHorizontalRotationOffset = "set_horizontal_rotation_offset";
public static readonly StringName GetHorizontalRotationOffset = new("get_horizontal_rotation_offset");
public static readonly StringName SetHorizontalRotationOffset = new("set_horizontal_rotation_offset");
public const string GetSpringLength = "get_spring_length";
public const string SetSpringLength = "set_spring_length";
public static readonly StringName GetSpringLength = new("get_spring_length");
public static readonly StringName SetSpringLength = new("set_spring_length");
public const string GetFollowDistance = "get_follow_distance";
public const string SetFollowDistance = "set_follow_distance";
public static readonly StringName GetFollowDistance = new("get_follow_distance");
public static readonly StringName SetFollowDistance = new("set_follow_distance");
public const string GetAutoFollowDistance = "get_auto_follow_distance";
public const string SetAutoFollowDistance = "set_auto_follow_distance";
public static readonly StringName GetAutoFollowDistance = new("get_auto_follow_distance");
public static readonly StringName SetAutoFollowDistance = new("set_auto_follow_distance");
public const string GetAutoFollowDistanceMin = "get_auto_follow_distance_min";
public const string SetAutoFollowDistanceMin = "set_auto_follow_distance_min";
public static readonly StringName GetAutoFollowDistanceMin = new("get_auto_follow_distance_min");
public static readonly StringName SetAutoFollowDistanceMin = new("set_auto_follow_distance_min");
public const string GetAutoFollowDistanceMax = "get_auto_follow_distance_max";
public const string SetAutoFollowDistanceMax = "set_auto_follow_distance_max";
public static readonly StringName GetAutoFollowDistanceMax = new("get_auto_follow_distance_max");
public static readonly StringName SetAutoFollowDistanceMax = new("set_auto_follow_distance_max");
public const string GetAutoFollowDistanceDivisor = "get_auto_follow_distance_divisor";
public const string SetAutoFollowDistanceDivisor = "set_auto_follow_distance_divisor";
public static readonly StringName GetAutoFollowDistanceDivisor = new("get_auto_follow_distance_divisor");
public static readonly StringName SetAutoFollowDistanceDivisor = new("set_auto_follow_distance_divisor");
public const string GetLookAtTarget = "get_look_at_target";
public const string SetLookAtTarget = "set_look_at_target";
public static readonly StringName GetLookAtTarget = new("get_look_at_target");
public static readonly StringName SetLookAtTarget = new("set_look_at_target");
public const string GetLookAtTargets = "get_look_at_targets";
public const string SetLookAtTargets = "set_look_at_targets";
public static readonly StringName GetLookAtTargets = new("get_look_at_targets");
public static readonly StringName SetLookAtTargets = new("set_look_at_targets");
public const string IsLooking = "is_looking";
public static readonly StringName IsLooking = new("is_looking");
public const string GetUp = "get_up";
public const string SetUp = "set_up";
public static readonly StringName GetUp = new("get_up");
public static readonly StringName SetUp = new("set_up");
public const string GetUpTarget = "get_up_target";
public const string SetUpTarget = "set_up_target";
public static readonly StringName GetUpTarget = new("get_up_target");
public static readonly StringName SetUpTarget = new("set_up_target");
public const string GetCollisionMask = "get_collision_mask";
public const string SetCollisionMask = "set_collision_mask";
public static readonly StringName GetCollisionMask = new("get_collision_mask");
public static readonly StringName SetCollisionMask = new("set_collision_mask");
public const string SetCollisionMaskValue = "set_collision_mask_value";
public static readonly StringName SetCollisionMaskValue = new("set_collision_mask_value");
public const string GetShape = "get_shape";
public const string SetShape = "set_shape";
public static readonly StringName GetShape = new("get_shape");
public static readonly StringName SetShape = new("set_shape");
public const string GetMargin = "get_margin";
public const string SetMargin = "set_margin";
public static readonly StringName GetMargin = new("get_margin");
public static readonly StringName SetMargin = new("set_margin");
public const string GetLookAtOffset = "get_look_at_offset";
public const string SetLookAtOffset = "set_look_at_offset";
public static readonly StringName GetLookAtOffset = new("get_look_at_offset");
public static readonly StringName SetLookAtOffset = new("set_look_at_offset");
public const string GetLookAtDamping = "get_look_at_damping";
public const string SetLookAtDamping = "set_look_at_damping";
public static readonly StringName GetLookAtDamping = new("get_look_at_damping");
public static readonly StringName SetLookAtDamping = new("set_look_at_damping");
public const string GetLookAtDampingValue = "get_look_at_damping_value";
public const string SetLookAtDampingValue = "set_look_at_damping_value";
public static readonly StringName GetLookAtDampingValue = new("get_look_at_damping_value");
public static readonly StringName SetLookAtDampingValue = new("set_look_at_damping_value");
public const string GetCullMask = "get_cull_mask";
public const string SetCullMask = "set_cull_mask";
public static readonly StringName GetKeepAspect = new("get_keep_aspect");
public static readonly StringName SetKeepAspect = new("set_keep_aspect");
public const string GetHOffset = "get_h_offset";
public const string SetHOffset = "set_h_offset";
public static readonly StringName GetCullMask = new("get_cull_mask");
public static readonly StringName SetCullMask = new("set_cull_mask");
public const string GetVOffset = "get_v_offset";
public const string SetVOffset = "set_v_offset";
public static readonly StringName GetHOffset = new("get_h_offset");
public static readonly StringName SetHOffset = new("set_h_offset");
public const string GetProjection = "get_projection";
public const string SetProjection = "set_projection";
public static readonly StringName GetVOffset = new("get_v_offset");
public static readonly StringName SetVOffset = new("set_v_offset");
public const string GetFov = "get_fov";
public const string SetFov = "set_fov";
public static readonly StringName GetProjection = new("get_projection");
public static readonly StringName SetProjection = new("set_projection");
public const string GetSize = "get_size";
public const string SetSize = "set_size";
public static readonly StringName GetFov = new("get_fov");
public static readonly StringName SetFov = new("set_fov");
public const string GetFrustumOffset = "get_frustum_offset";
public const string SetFrustumOffset = "set_frustum_offset";
public static readonly StringName GetSize = new("get_size");
public static readonly StringName SetSize = new("set_size");
public const string GetFar = "get_far";
public const string SetFar = "set_far";
public static readonly StringName GetFrustumOffset = new("get_frustum_offset");
public static readonly StringName SetFrustumOffset = new("set_frustum_offset");
public const string GetNear = "get_near";
public const string SetNear = "set_near";
public static readonly StringName GetFar = new("get_far");
public static readonly StringName SetFar = new("set_far");
public const string GetEnvironment = "get_environment";
public const string SetEnvironment = "set_environment";
public static readonly StringName GetNear = new("get_near");
public static readonly StringName SetNear = new("set_near");
public const string GetAttributes = "get_attributes";
public const string SetAttributes = "set_attributes";
public static readonly StringName GetEnvironment = new("get_environment");
public static readonly StringName SetEnvironment = new("set_environment");
public const string GetNoise = "get_noise";
public const string SetNoise = "set_noise";
public static readonly StringName GetAttributes = new("get_attributes");
public static readonly StringName SetAttributes = new("set_attributes");
public static readonly StringName GetNoise = new("get_noise");
public static readonly StringName SetNoise = new("set_noise");
}
public new static class SignalName
{
public const string LookAtTargetChanged = "look_at_target_changed";
public const string Camera3DResourceChanged = "camera_3d_resource_changed";
public const string Camera3DResourcePropertyChanged = "camera_3d_resource_property_changed";
public static readonly StringName LookAtTargetChanged = new("look_at_target_changed");
public static readonly StringName Camera3DResourceChanged = new("camera_3d_resource_changed");
public static readonly StringName Camera3DResourcePropertyChanged = new("camera_3d_resource_property_changed");
}
}

View File

@ -55,29 +55,29 @@ public class PhantomCameraNoiseEmitter2D(GodotObject node)
public static class MethodName
{
public const string GetNoise = "get_noise";
public const string SetNoise = "set_noise";
public static readonly StringName GetNoise = new("get_noise");
public static readonly StringName SetNoise = new("set_noise");
public const string GetContinuous = "get_continuous";
public const string SetContinuous = "set_continuous";
public static readonly StringName GetContinuous = new("get_continuous");
public static readonly StringName SetContinuous = new("set_continuous");
public const string GetGrowthTime = "get_growth_time";
public const string SetGrowthTime = "set_growth_time";
public static readonly StringName GetGrowthTime = new("get_growth_time");
public static readonly StringName SetGrowthTime = new("set_growth_time");
public const string GetDuration = "get_duration";
public const string SetDuration = "set_duration";
public static readonly StringName GetDuration = new("get_duration");
public static readonly StringName SetDuration = new("set_duration");
public const string GetDecayTime = "get_decay_time";
public const string SetDecayTime = "set_decay_time";
public static readonly StringName GetDecayTime = new("get_decay_time");
public static readonly StringName SetDecayTime = new("set_decay_time");
public const string GetNoiseEmitterLayer = "get_noise_emitter_layer";
public const string SetNoiseEmitterLayer = "set_noise_emitter_layer";
public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer");
public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer");
public const string SetNoiseEmitterLayerValue = "set_noise_emitter_layer_value";
public static readonly StringName SetNoiseEmitterLayerValue = new("set_noise_emitter_layer_value");
public const string Emit = "emit";
public const string IsEmitting = "is_emitting";
public const string Stop = "stop";
public const string Toggle = "toggle";
public static readonly StringName Emit = new("emit");
public static readonly StringName IsEmitting = new("is_emitting");
public static readonly StringName Stop = new("stop");
public static readonly StringName Toggle = new("toggle");
}
}

View File

@ -55,29 +55,29 @@ public class PhantomCameraNoiseEmitter3D(GodotObject node)
public static class MethodName
{
public const string GetNoise = "get_noise";
public const string SetNoise = "set_noise";
public static readonly StringName GetNoise = new("get_noise");
public static readonly StringName SetNoise = new("set_noise");
public const string GetContinuous = "get_continuous";
public const string SetContinuous = "set_continuous";
public static readonly StringName GetContinuous = new("get_continuous");
public static readonly StringName SetContinuous = new("set_continuous");
public const string GetGrowthTime = "get_growth_time";
public const string SetGrowthTime = "set_growth_time";
public static readonly StringName GetGrowthTime = new("get_growth_time");
public static readonly StringName SetGrowthTime = new("set_growth_time");
public const string GetDuration = "get_duration";
public const string SetDuration = "set_duration";
public static readonly StringName GetDuration = new("get_duration");
public static readonly StringName SetDuration = new("set_duration");
public const string GetDecayTime = "get_decay_time";
public const string SetDecayTime = "set_decay_time";
public static readonly StringName GetDecayTime = new("get_decay_time");
public static readonly StringName SetDecayTime = new("set_decay_time");
public const string GetNoiseEmitterLayer = "get_noise_emitter_layer";
public const string SetNoiseEmitterLayer = "set_noise_emitter_layer";
public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer");
public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer");
public const string SetNoiseEmitterLayerValue = "set_noise_emitter_layer_value";
public static readonly StringName SetNoiseEmitterLayerValue = new("set_noise_emitter_layer_value");
public const string Emit = "emit";
public const string IsEmitting = "is_emitting";
public const string Stop = "stop";
public const string Toggle = "toggle";
public static readonly StringName Emit = new("emit");
public static readonly StringName IsEmitting = new("is_emitting");
public static readonly StringName Stop = new("stop");
public static readonly StringName Toggle = new("toggle");
}
}

View File

@ -553,6 +553,8 @@ func _validate_property(property: Dictionary) -> void:
follow_mode == FollowMode.GLUED:
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == "follow_damping_value" and not follow_damping:
property.usage = PROPERTY_USAGE_NO_EDITOR
###############
## Follow Group
@ -639,7 +641,6 @@ func _enter_tree() -> void:
_should_follow = false
FollowMode.GROUP:
_follow_targets_size_check()
_should_follow_checker()
_:
_should_follow_checker()
@ -649,7 +650,6 @@ func _enter_tree() -> void:
update_limit_all_sides()
func _exit_tree() -> void:
if not follow_mode == FollowMode.GROUP:
follow_targets = []
@ -659,19 +659,13 @@ func _exit_tree() -> void:
func _ready() -> void:
if is_instance_valid(follow_target):
_transform_output.origin = _get_target_position_offset()
else:
_transform_output = global_transform
_transform_output = global_transform
_phantom_camera_manager.noise_2d_emitted.connect(_noise_emitted)
if not Engine.is_editor_hint():
_preview_noise = true
if follow_mode == FollowMode.GROUP:
_follow_targets_size_check()
func _process(delta: float) -> void:
if _follow_target_physics_based or _is_active: return
@ -804,6 +798,7 @@ func _set_follow_position() -> void:
dead_zone_reached.emit(Vector2(framed_side_offset.x, framed_side_offset.y))
else:
_follow_framed_offset = _transform_output.origin - _get_target_position_offset()
_follow_target_position = global_position
return
else:
_follow_target_position = _get_target_position_offset()
@ -972,7 +967,7 @@ func _follow_targets_size_check() -> void:
_follow_targets = []
for i in follow_targets.size():
if follow_targets[i] == null: continue
if follow_targets[i].is_inside_tree():
if is_instance_valid(follow_targets[i]):
_follow_targets.append(follow_targets[i])
targets_size += 1
_follow_targets_single_target_index = i
@ -1244,8 +1239,6 @@ func get_tween_ease() -> int:
func set_is_active(node, value) -> void:
if node is PhantomCameraHost:
_is_active = value
if value:
_should_follow_checker()
queue_redraw()
else:
printerr("PCams can only be set from the PhantomCameraHost")

View File

@ -167,6 +167,8 @@ enum FollowLockAxis {
if follow_mode == FollowMode.NONE:
_should_follow = false
top_level = false
_draw_follow_gizmo_line = false
_check_draw_gizmo()
_is_parents_physics()
notify_property_list_changed()
return
@ -182,6 +184,14 @@ enum FollowLockAxis {
_:
_should_follow_checker()
## Disables Follow Gizmo Line for Follow Glued
if follow_mode == FollowMode.GLUED:
_draw_follow_gizmo_line = false
_check_draw_gizmo()
else:
if draw_follow_line: _draw_follow_gizmo_line = true
_check_draw_gizmo()
if follow_mode == FollowMode.FRAMED:
if _follow_framed_initial_set and follow_target:
_follow_framed_initial_set = false
@ -237,14 +247,24 @@ enum FollowLockAxis {
if look_at_mode == LookAtMode.NONE:
_should_look_at = false
_draw_look_at_gizmo_line = false
_check_draw_gizmo()
notify_property_list_changed()
return
if look_at_mode == LookAtMode.MIMIC:
_draw_look_at_gizmo_line = false
_check_draw_gizmo()
else:
if draw_look_at_line: _draw_look_at_gizmo_line = true
_check_draw_gizmo()
if not look_at_mode == LookAtMode.GROUP:
if look_at_target is Node3D:
_should_look_at = true
else: # If Look At Group
_look_at_targets_size_check()
notify_property_list_changed()
## NOTE - Warning that Look At + Follow Mode hasn't been fully tested together yet
@ -324,6 +344,12 @@ enum FollowLockAxis {
get = get_environment
## Overrides the [member Camera3D.compositor] resource property.
@export var compositor: Compositor = null:
set = set_compositor,
get = get_compositor
@export_group("Follow Parameters")
## Offsets the [member follow_target] position.
@export var follow_offset: Vector3 = Vector3.ZERO:
@ -467,7 +493,6 @@ var horizontal_rotation_offset: float = 0:
set = set_margin,
get = get_margin
@export_group("Look At Parameters")
## Offsets the target's [param Vector3] position that the
## [param PhantomCamera3D] is looking at.
@ -491,7 +516,6 @@ var horizontal_rotation_offset: float = 0:
get = get_look_at_damping_value
@export_subgroup("Up Direction")
## Defines the upward direction of the [param PhantomCamera3D] when [member look_at_mode] is set. [br]
## This value will be overriden if [member up_target] is defined.
@export var up: Vector3 = Vector3.UP:
@ -528,6 +552,67 @@ var horizontal_rotation_offset: float = 0:
set = set_noise_emitter_layer,
get = get_noise_emitter_layer
@export_group("Editor")
@export_subgroup("Align with View")
## Adds an editor button that positions and rotates the [param PhantomCamera3D] to match the 3D viewport's transform.[br][br]
## This editor button is not visible if the [param PhantomCamera3D] is following [i]or[/i] looking at a target.[br][br]
## [b]Note[/b]: This is only functional in the editor.
@export_tool_button("Align Transform with View", "CenterView")
var align_transform_with_view: Callable = func():
var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo()
var property: StringName = &"global_transform"
undo_redo.create_action("Aligned " + name + "'s transform with view")
undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_transform)
undo_redo.add_undo_property(self, property, global_transform)
undo_redo.commit_action()
## Adds an editor button that positions the [param PhantomCamera3D] to match the 3D viewport's position.[br][br]
## This editor button is not visible if the [param PhantomCamera3D] is following a target.[br][br]
## [b]Note[/b]: This is only functional in the editor.
@export_tool_button("Align Position with View", "ToolMove")
var align_position_with_view: Callable = func():
var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo()
var property: StringName = &"global_position"
undo_redo.create_action("Aligned " + name + "'s position with view")
undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_position)
undo_redo.add_undo_property(self, property, global_position)
undo_redo.commit_action()
## Adds an editor button that rotates the [param PhantomCamera3D] to match the 3D viewport's rotation.[br][br]
## This editor button is not visible if the [param PhantomCamera3D] is looking at a target.[br][br]
## [b]Note[/b]: This is only functional in the editor.
@export_tool_button("Align Rotation with View", "ToolRotate")
var align_rotation_with_view: Callable = func():
var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo()
var property: StringName = &"global_rotation"
undo_redo.create_action("Aligned " + name + "'s rotation with view")
undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_rotation)
undo_redo.add_undo_property(self, property, global_rotation)
undo_redo.commit_action()
## Change which viewport the alignment buttons should be based on.[br]
## [b]Note:[/b] If you are only using 1 viewport, keep the default value to 0.
@export_range(0, 3) var viewport_index: int = 0:
set(value):
viewport_index = value
get:
return viewport_index
@export_subgroup("Gizmo Line")
## Draws a line between the [param PhantomCamera3D] and its follow target position.[br]
## This is only drawn when a valid [member follow_target] or [member follow_targets] is set.
@export var draw_follow_line: bool = false:
set = set_draw_follow_line,
get = get_draw_follow_line
## Draws a line between the [param PhantomCamera3D] and its look at target position.[br]
## This is only drawn when a valid [member follow_target] or [member look_at_targets] is set.
@export var draw_look_at_line: bool = false:
set = set_draw_look_at_line,
get = get_draw_look_at_line
#endregion
#region Private Variables
@ -556,8 +641,8 @@ var _current_rotation: Vector3 = Vector3.ZERO
var _up: Vector3 = Vector3.UP
var _has_up_target: bool = false
var _follow_target_position: Vector3 = Vector3.ZERO
var _look_at_target_position: Vector3 = Vector3.ZERO
var _follow_target_output_position: Vector3 = Vector3.ZERO
var _look_at_target_output_position: Vector3 = Vector3.ZERO
var _transform_output: Transform3D = Transform3D()
var _transform_noise: Transform3D = Transform3D()
@ -574,6 +659,10 @@ var _has_follow_spring_arm: bool = false
var _has_noise_resource: bool = false
var _draw_gizmo: bool = false
var _draw_follow_gizmo_line: bool = false
var _follow_target_position: Vector3 = Vector3.ZERO
var _draw_look_at_gizmo_line: bool = false
# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box.
var _phantom_camera_manager: Node = null
@ -634,11 +723,6 @@ func _validate_property(property: Dictionary) -> void:
################
## Follow Target
################
if property.name == "follow_target":
if follow_mode == FollowMode.NONE or \
follow_mode == FollowMode.GROUP:
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == "follow_path" and \
follow_mode != FollowMode.PATH:
property.usage = PROPERTY_USAGE_NO_EDITOR
@ -648,10 +732,17 @@ func _validate_property(property: Dictionary) -> void:
####################
if follow_mode == FollowMode.NONE:
match property.name:
"follow_target", \
"follow_offset", \
"follow_damping", \
"follow_damping_value", \
"follow_axis_lock":
"follow_axis_lock", \
"draw_follow_line":
property.usage = PROPERTY_USAGE_NO_EDITOR
if follow_mode == FollowMode.GROUP:
match property.name:
"follow_target":
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == "follow_offset":
@ -716,19 +807,27 @@ func _validate_property(property: Dictionary) -> void:
##########
## Look At
##########
if look_at_mode == LookAtMode.NONE:
match property.name:
"look_at_target", \
"look_at_offset" , \
"look_at_damping", \
"look_at_damping_value", \
"up", \
"up_target":
property.usage = PROPERTY_USAGE_NO_EDITOR
elif look_at_mode == LookAtMode.GROUP:
match property.name:
"look_at_target":
property.usage = PROPERTY_USAGE_NO_EDITOR
match look_at_mode:
LookAtMode.NONE:
match property.name:
"look_at_target", \
"look_at_offset" , \
"look_at_damping", \
"look_at_damping_value", \
"up", \
"up_target", \
"draw_look_at_line":
property.usage = PROPERTY_USAGE_NO_EDITOR
LookAtMode.MIMIC:
match property.name:
"draw_look_at_line":
property.usage = PROPERTY_USAGE_NO_EDITOR
LookAtMode.GROUP:
match property.name:
"look_at_target", \
"draw_look_at_line":
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == "look_at_target":
if look_at_mode == LookAtMode.NONE or \
@ -746,6 +845,27 @@ func _validate_property(property: Dictionary) -> void:
if property.name == "up" and _has_up_target:
property.usage = PROPERTY_USAGE_NO_EDITOR
##########################
## Align With Viewport
##########################
if property.name == 'align_transform_with_view' and \
(_should_look_at == true or _should_follow == true):
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == 'align_position_with_view' and \
(
_should_follow == true or \
_should_follow == true and follow_mode == FollowMode.THIRD_PERSON
):
property.usage = PROPERTY_USAGE_NO_EDITOR
if property.name == 'align_rotation_with_view' and \
(
_should_look_at == true or \
_should_follow == true and follow_mode == FollowMode.THIRD_PERSON
):
property.usage = PROPERTY_USAGE_NO_EDITOR
func _enter_tree() -> void:
_phantom_camera_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME)
@ -824,18 +944,8 @@ func _ready():
_follow_spring_arm.global_rotation = initial_rotation
_has_follow_spring_arm = true
top_level = false
FollowMode.FRAMED:
if not Engine.is_editor_hint():
if is_instance_valid(follow_target):
_follow_framed_offset = global_position - _get_target_position_offset()
_current_rotation = global_rotation
FollowMode.GROUP:
_follow_targets_size_check()
_:
if is_instance_valid(follow_target):
_transform_output.origin = _get_target_position_offset()
else:
_transform_output.origin = global_position
_transform_output.origin = global_position
if not Engine.is_editor_hint():
_preview_noise = true
@ -848,6 +958,9 @@ func _ready():
func _process(delta: float) -> void:
if Engine.is_editor_hint() and _draw_gizmo:
update_gizmos()
if _follow_target_physics_based or _is_active: return
process_logic(delta)
@ -912,10 +1025,11 @@ func _look_at(delta: float) -> void:
func _set_follow_position() -> void:
match follow_mode:
FollowMode.GLUED:
_follow_target_position = follow_target.global_position
_follow_target_output_position = follow_target.global_position
FollowMode.SIMPLE:
_follow_target_position = _get_target_position_offset()
_follow_target_output_position = _get_target_position_offset()
_set_follow_gizmo_line_position(follow_target.global_position)
FollowMode.GROUP:
if _has_multiple_follow_targets:
@ -929,13 +1043,15 @@ func _set_follow_position() -> void:
else:
distance = follow_distance
_follow_target_position = \
_follow_target_output_position = \
bounds.get_center() + \
follow_offset + \
global_basis.z * \
distance
_set_follow_gizmo_line_position(bounds.get_center())
else:
_follow_target_position = \
_follow_target_output_position = \
follow_targets[_follow_targets_single_target_index].global_position + \
follow_offset + \
global_basis.z * \
@ -943,15 +1059,16 @@ func _set_follow_position() -> void:
FollowMode.PATH:
var path_position: Vector3 = follow_path.global_position
_follow_target_position = \
_follow_target_output_position = \
follow_path.curve.get_closest_point(
follow_target.global_position - path_position
) + path_position
_set_follow_gizmo_line_position(follow_target.global_position)
FollowMode.FRAMED:
if not Engine.is_editor_hint():
if not _is_active:
_follow_target_position = _get_target_position_offset_distance()
_follow_target_output_position = _get_target_position_offset_distance()
else:
viewport_position = get_viewport().get_camera_3d().unproject_position(_get_target_position_offset())
var visible_rect_size: Vector2 = get_viewport().get_visible_rect().size
@ -959,9 +1076,10 @@ func _set_follow_position() -> void:
_current_rotation = global_rotation
if _current_rotation != global_rotation:
_follow_target_position = _get_target_position_offset_distance()
_follow_target_output_position = _get_target_position_offset_distance()
if _get_framed_side_offset() != Vector2.ZERO:
var framed_offset: Vector2 = _get_framed_side_offset()
var target_position: Vector3 = _get_target_position_offset() + _follow_framed_offset
var glo_pos: Vector3
@ -969,13 +1087,13 @@ func _set_follow_position() -> void:
if dead_zone_width == 0 && dead_zone_height != 0:
glo_pos = _get_target_position_offset_distance()
glo_pos.z = target_position.z
_follow_target_position = glo_pos
_follow_target_output_position = glo_pos
elif dead_zone_width != 0 && dead_zone_height == 0:
glo_pos = _get_target_position_offset_distance()
glo_pos.x = target_position.x
_follow_target_position = glo_pos
_follow_target_output_position = glo_pos
else:
_follow_target_position = _get_target_position_offset_distance()
_follow_target_output_position = _get_target_position_offset_distance()
else:
if _current_rotation != global_rotation:
var opposite: float = sin(-global_rotation.x) * follow_distance + _get_target_position_offset().y
@ -983,17 +1101,35 @@ func _set_follow_position() -> void:
glo_pos.z = sqrt(pow(follow_distance, 2) - pow(opposite, 2)) + _get_target_position_offset().z
glo_pos.x = global_position.x
_follow_target_position = glo_pos
_follow_target_output_position = glo_pos
_current_rotation = global_rotation
else:
dead_zone_reached.emit()
_follow_target_position = target_position
# FIX: Only move camera in the axis where dead zone is breached
var current_global_position: Vector3 = global_position
var current_offset: Vector3 = global_position - _get_target_position_offset()
# Update stored offset for non-breached axes
if framed_offset.x == 0:
_follow_framed_offset.x = current_offset.x
if framed_offset.y == 0:
_follow_framed_offset.z = current_offset.z
# Lock camera position on non-breached axes
if framed_offset.x == 0:
target_position.x = current_global_position.x
if framed_offset.y == 0:
target_position.z = current_global_position.z
_follow_target_output_position = target_position
else:
_follow_framed_offset = global_position - _get_target_position_offset()
_follow_target_position = global_position
_current_rotation = global_rotation
return
else:
_follow_target_position = _get_target_position_offset_distance()
_follow_target_output_position = _get_target_position_offset_distance()
var unprojected_position: Vector2 = _get_raw_unprojected_position()
var viewport_width: float = get_viewport().size.x
var viewport_height: float = get_viewport().size.y
@ -1013,32 +1149,36 @@ func _set_follow_position() -> void:
unprojected_position.y = (unprojected_position.y / aspect_ratio_scale + 1) / 2
viewport_position = unprojected_position
_set_follow_gizmo_line_position(follow_target.global_position)
FollowMode.THIRD_PERSON:
if not Engine.is_editor_hint():
if not _has_follow_spring_arm: return
_follow_target_position = _get_target_position_offset()
_follow_target_output_position = _get_target_position_offset()
else:
_follow_target_position = _get_target_position_offset_distance_direction()
_follow_target_output_position = _get_target_position_offset_distance_direction()
# _follow_target_position = _get_target_position_offset_distance_direction()
_set_follow_gizmo_line_position(follow_target.global_position)
func _set_look_at_position() -> void:
match look_at_mode:
LookAtMode.MIMIC:
_look_at_target_position = global_position - look_at_target.global_basis.z
_look_at_target_output_position = global_position - look_at_target.global_basis.z
LookAtMode.SIMPLE:
_look_at_target_position =look_at_target.global_position
_look_at_target_output_position = look_at_target.global_position
LookAtMode.GROUP:
if not _has_multiple_look_at_targets:
_look_at_target_position =look_at_targets[_look_at_targets_single_target_index].global_position
_look_at_target_output_position = look_at_targets[_look_at_targets_single_target_index].global_position
else:
var bounds: AABB = AABB(look_at_targets[0].global_position, Vector3.ZERO)
for node in look_at_targets:
bounds = bounds.expand(node.global_position)
_look_at_target_position =bounds.get_center()
_look_at_target_output_position = bounds.get_center()
_look_at_target_output_position += look_at_offset
func _get_target_position_offset() -> Vector3:
return follow_target.global_position + follow_offset
@ -1063,7 +1203,7 @@ func _set_follow_velocity(index: int, value: float) -> void:
func _interpolate_position(delta: float) -> void:
if follow_damping and not Engine.is_editor_hint():
if not _is_third_person_follow:
global_position = _follow_target_position
global_position = _follow_target_output_position
for i in 3:
_transform_output.origin[i] = _smooth_damp(
global_position[i],
@ -1077,7 +1217,7 @@ func _interpolate_position(delta: float) -> void:
else:
for i in 3:
_camera_target.global_position[i] = _smooth_damp(
_follow_target_position[i],
_follow_target_output_position[i],
_camera_target.global_position[i],
i,
_follow_velocity_ref[i],
@ -1087,7 +1227,7 @@ func _interpolate_position(delta: float) -> void:
)
_transform_output.origin = global_position
else:
_camera_target.global_position = _follow_target_position
_camera_target.global_position = _follow_target_output_position
_transform_output.origin = global_position
if _is_third_person_follow:
@ -1098,7 +1238,7 @@ func _interpolate_position(delta: float) -> void:
func _look_at_target_quat(target_position: Vector3, up_direction: Vector3 = Vector3.UP) -> Quaternion:
var direction: Vector3 = -(target_position - global_position + look_at_offset).normalized()
var direction: Vector3 = -(target_position - global_position).normalized()
var basis_z: Vector3 = direction.normalized()
var basis_x: Vector3 = up_direction.cross(basis_z)
@ -1121,7 +1261,7 @@ func _interpolate_rotation(delta: float) -> void:
if _has_up_target:
_up = up_target.global_basis.y
var target_quat: Quaternion = _look_at_target_quat(_look_at_target_position, _up)
var target_quat: Quaternion = _look_at_target_quat(_look_at_target_output_position, _up)
if look_at_damping:
var current_quat: Quaternion = quaternion.normalized()
@ -1251,7 +1391,7 @@ func _follow_targets_size_check() -> void:
_follow_targets = []
for i in follow_targets.size():
if follow_targets[i] == null: continue
if follow_targets[i].is_inside_tree():
if is_instance_valid(follow_targets[i]):
_follow_targets.append(follow_targets[i])
targets_size += 1
_follow_targets_single_target_index = i
@ -1357,6 +1497,18 @@ func _is_parents_physics(target: Node = self) -> void:
func _camera_resource_changed() -> void:
camera_3d_resource_changed.emit()
func _set_follow_gizmo_line_position(target_position: Vector3) -> void:
if Engine.is_editor_hint():
_follow_target_position = target_position
func _check_draw_gizmo() -> void:
if _draw_follow_gizmo_line or _draw_look_at_gizmo_line:
_draw_gizmo = true
else:
_draw_gizmo = false
update_gizmos()
#endregion
#region Public Functions
@ -1405,7 +1557,7 @@ func emit_noise(value: Transform3D) -> void:
func teleport_position() -> void:
_follow_velocity_ref = Vector3.ZERO
_set_follow_position()
_transform_output.origin = _follow_target_position
_transform_output.origin = _follow_target_output_position
_phantom_camera_manager.pcam_teleport.emit(self)
@ -1421,6 +1573,16 @@ func is_following() -> bool:
func is_looking() -> bool:
return _should_look_at
## Returns the world space coodinate of where the target the camera is following.
## In most cases, this is the
func get_follow_target_position() -> Vector3:
return _follow_target_position
## Returns the world space coordinate of where the camera is looking at.
func get_look_at_target_position() -> Vector3:
return _look_at_target_output_position
#endregion
@ -1489,8 +1651,6 @@ func get_tween_ease() -> int:
func set_is_active(node: Node, value: bool) -> void:
if node is PhantomCameraHost:
_is_active = value
if value:
_should_follow_checker()
else:
printerr("PCams can only be set from the PhantomCameraHost")
## Gets current active state of the [param PhantomCamera3D].
@ -1553,6 +1713,7 @@ func set_follow_target(value: Node3D) -> void:
if not follow_mode == FollowMode.GROUP:
_should_follow = false
follow_target_changed.emit()
update_gizmos()
notify_property_list_changed()
## Removes the current [Node3D] [member follow_target].
func erase_follow_target() -> void:
@ -1846,6 +2007,24 @@ func get_margin() -> float:
return margin
func set_draw_follow_line(value: bool) -> void:
draw_follow_line = value
_draw_follow_gizmo_line = value
_check_draw_gizmo()
func get_draw_follow_line() -> bool:
return draw_follow_line
func set_draw_look_at_line(value: bool) -> void:
draw_look_at_line = value
_draw_look_at_gizmo_line = value
_check_draw_gizmo()
func get_draw_look_at_line() -> bool:
return draw_look_at_line
## Gets the current [member look_at_mode]. Value is based on [enum LookAtMode]
## enum.[br]
## Note: To set a new [member look_at_mode], a separate [param PhantomCamera3D] should be used.
@ -2136,6 +2315,15 @@ func set_attributes(value: CameraAttributes) -> void:
func get_attributes() -> CameraAttributes:
return attributes
## Assigns a new [Compositor] resource to the [Camera3DResource].
func set_compositor(value: Compositor) -> void:
compositor = value
camera_3d_resource_property_changed.emit("compositor", value)
## Gets the [Camera3D.compositor] value assigned to the [Camera3DResource].
func get_compositor() -> Compositor:
return compositor
## Assigns a new [member Camera3D.h_offset] value.[br]
## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to
@ -2269,6 +2457,15 @@ func get_follow_target_physics_based() -> bool:
return _follow_target_physics_based
## Used internally in phantom_camera_3d_gizmo.gd to check if the Follow line should be shown.
func draw_follow_gizmo_line() -> bool:
return _draw_follow_gizmo_line
## Used internally in phantom_camera_3d_gizmo.gd to check if the Look At line should be shown.
func draw_look_at_gizmo_line() -> bool:
return _draw_look_at_gizmo_line
func get_class() -> String:
return "PhantomCamera3D"

View File

@ -23,21 +23,15 @@ public class PhantomCameraHost()
{
public Node Node { get; } = null!;
public PhantomCameraHost(Node node) : this()
public PhantomCameraHost(GodotObject node) : this()
{
Node = node;
Node = node as Node;
_callablePCamBecameActive = Callable.From<Node>(pCam => PCamBecameActive?.Invoke(pCam));
_callablePCamBecameInactive = Callable.From<Node>(pCam => PCamBecameInactive?.Invoke(pCam));
var callablePCamBecameActive = Callable.From<Node>(pCam => PCamBecameActive?.Invoke(pCam));
var callablePCamBecameInactive = Callable.From<Node>(pCam => PCamBecameInactive?.Invoke(pCam));
Node.Connect(SignalName.PCamBecameActive, _callablePCamBecameActive);
Node.Connect(SignalName.PCamBecameInactive, _callablePCamBecameInactive);
}
~PhantomCameraHost()
{
Node.Disconnect(SignalName.PCamBecameActive, _callablePCamBecameActive);
Node.Disconnect(SignalName.PCamBecameInactive, _callablePCamBecameInactive);
Node.Connect(SignalName.PCamBecameActive, callablePCamBecameActive);
Node.Connect(SignalName.PCamBecameInactive, callablePCamBecameInactive);
}
public delegate void PCamBecameActiveEventHandler(Node pCam);
@ -49,6 +43,7 @@ public class PhantomCameraHost()
private readonly Callable _callablePCamBecameActive;
private readonly Callable _callablePCamBecameInactive;
// For when Godot becomes the minimum version
// public InterpolationMode InterpolationMode
// {
@ -76,53 +71,43 @@ public class PhantomCameraHost()
public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween);
public ActivePhantomCameraQueryResult? GetActivePhantomCamera()
public PhantomCamera? GetActivePhantomCamera()
{
var result = Node.Call(MethodName.GetActivePhantomCamera);
return result.VariantType == Variant.Type.Nil ? null : new ActivePhantomCameraQueryResult(result.AsGodotObject());
if (result.Obj is Node2D node2D)
{
return new PhantomCamera2D(node2D);
}
if (result.Obj is Node3D node3D)
{
return new PhantomCamera3D(node3D);
}
return null;
}
public static class PropertyName
{
public const string Camera2D = "camera_2d";
public const string Camera3D = "camera_3d";
public static readonly StringName Camera2D = new("camera_2d");
public static readonly StringName Camera3D = new("camera_3d");
}
public static class MethodName
{
public const string GetActivePhantomCamera = "get_active_pcam";
public const string GetTriggerPhantomCameraTween = "get_trigger_pcam_tween";
public static readonly StringName GetActivePhantomCamera = new("get_active_pcam");
public static readonly StringName GetTriggerPhantomCameraTween = new("get_trigger_pcam_tween");
public const string GetInterpolationMode = "get_interpolation_mode";
public const string SetInterpolationMode = "set_interpolation_mode";
public static readonly StringName GetInterpolationMode = new("get_interpolation_mode");
public static readonly StringName SetInterpolationMode = new("set_interpolation_mode");
public const string SetHostLayersValue = "set_host_layers_value";
public static readonly StringName SetHostLayersValue = new("set_host_layers_value");
}
public static class SignalName
{
public const string PCamBecameActive = "pcam_became_active";
public const string PCamBecameInactive = "pcam_became_inactive";
}
}
public class ActivePhantomCameraQueryResult(GodotObject godotObject)
{
public bool Is2D => godotObject.IsClass("Node2D") || ((Node)godotObject).Name.ToString().Contains("PhantomCamera2D")
|| ((Node)godotObject).Name.ToString().Contains("PCam2D")
|| ((Node)godotObject).Name.ToString().Contains("2D");
public bool Is3D => godotObject.IsClass("Node3D") || ((Node)godotObject).Name.ToString().Contains("PhantomCamera3D")
|| ((Node)godotObject).Name.ToString().Contains("PCam3D")
|| ((Node)godotObject).Name.ToString().Contains("3D");
public PhantomCamera2D? AsPhantomCamera2D()
{
return Is2D ? new PhantomCamera2D(godotObject) : null;
}
public PhantomCamera3D? AsPhantomCamera3D()
{
return Is3D ? new PhantomCamera3D(godotObject) : null;
public static readonly StringName PCamBecameActive = new("pcam_became_active");
public static readonly StringName PCamBecameInactive = new("pcam_became_inactive");
}
}

View File

@ -100,6 +100,7 @@ var _cam_attribute_changed: bool = false
var _cam_attribute_assigned: bool = false
#region CameraAttributes
var _prev_cam_auto_exposure_scale: float = 0.4
var _cam_auto_exposure_scale_changed: bool = false
@ -137,9 +138,11 @@ var _cam_dof_blur_near_distance_changed: bool = false
var _cam_dof_blur_near_transition_default: float = 1
var _prev_cam_dof_blur_near_transition: float = _cam_dof_blur_near_transition_default
var _cam_dof_blur_near_transition_changed: bool = false
#endregion
#region CameraAttributesPhysical
var _prev_cam_exposure_min_exposure_value: float = 10.0
var _cam_exposure_min_exposure_value_changed: bool = false
@ -270,6 +273,8 @@ func _enter_tree() -> void:
_is_2d = false
camera_3d = parent
if not is_node_ready(): return
if _is_2d:
if not _phantom_camera_manager.get_phantom_camera_2ds().is_empty():
for pcam in _phantom_camera_manager.get_phantom_camera_2ds():
@ -373,11 +378,10 @@ func _find_pcam_with_highest_priority() -> void:
func _check_pcam_priority(pcam: Node) -> void:
if not _pcam_is_in_host_layer(pcam): return
if not pcam.visible: return # Prevents hidden PCams from becoming active
if pcam.get_priority() > _active_pcam_priority:
if pcam.get_priority() >= _active_pcam_priority:
_assign_new_active_pcam(pcam)
_active_pcam_missing = false
else:
pcam.set_tween_skip(self, false)
pcam.set_tween_skip(self, false)
func _assign_new_active_pcam(pcam: Node) -> void:

View File

@ -21,97 +21,96 @@ public class Camera3DResource(Resource resource)
public KeepAspect KeepAspect
{
get => (KeepAspect)(int)Resource.Call(MethodName.GetKeepAspect);
set => Resource.Call(MethodName.SetKeepAspect, (int)value);
get => (KeepAspect)(int)Resource.Get(PropertyName.KeepAspect);
set => Resource.Set(PropertyName.KeepAspect, (int)value);
}
public int CullMask
{
get => (int)Resource.Call(MethodName.GetCullMask);
set => Resource.Call(MethodName.SetCullMask, value);
get => (int)Resource.Get(PropertyName.CullMask);
set => Resource.Set(PropertyName.CullMask, value);
}
public void SetCullMaskValue(int layer, bool value) => Resource.Call(MethodName.SetCullMaskValue, layer, value);
public float HOffset
{
get => (float)Resource.Call(MethodName.GetHOffset);
set => Resource.Call(MethodName.SetHOffset, value);
get => (float)Resource.Get(PropertyName.HOffset);
set => Resource.Set(PropertyName.HOffset, value);
}
public float VOffset
{
get => (float)Resource.Call(MethodName.GetVOffset);
set => Resource.Call(MethodName.SetVOffset, value);
get => (float)Resource.Get(PropertyName.VOffset);
set => Resource.Set(PropertyName.VOffset, value);
}
public ProjectionType Projection
{
get => (ProjectionType)(int)Resource.Call(MethodName.GetProjection);
set => Resource.Call(MethodName.SetProjection, (int)value);
get => (ProjectionType)(int)Resource.Get(PropertyName.Projection);
set => Resource.Set(PropertyName.Projection, (int)value);
}
public float Fov
{
get => (float)Resource.Call(MethodName.GetFov);
set => Resource.Call(MethodName.SetFov, Mathf.Clamp(value, 1, 179));
get => (float)Resource.Get(PropertyName.Fov);
set => Resource.Set(PropertyName.Fov, Mathf.Clamp(value, 1, 179));
}
public float Size
{
get => (float)Resource.Call(MethodName.GetSize);
set => Resource.Call(MethodName.SetSize, Mathf.Clamp(value, 0.001f, float.PositiveInfinity));
get => (float)Resource.Get(PropertyName.Size);
set => Resource.Set(PropertyName.Size, Mathf.Clamp(value, 0.001f, float.PositiveInfinity));
}
public Vector2 FrustumOffset
{
get => (Vector2)Resource.Call(MethodName.GetFrustumOffset);
set => Resource.Call(MethodName.SetFrustumOffset, value);
get => (Vector2)Resource.Get(PropertyName.FrustumOffset);
set => Resource.Set(PropertyName.FrustumOffset, value);
}
public float Near
{
get => (float)Resource.Call(MethodName.GetNear);
set => Resource.Call(MethodName.SetNear, Mathf.Clamp(value, 0.001f, float.PositiveInfinity));
get => (float)Resource.Get(PropertyName.Near);
set => Resource.Set(PropertyName.Near, Mathf.Clamp(value, 0.001f, float.PositiveInfinity));
}
public float Far
{
get => (float)Resource.Call(MethodName.GetFar);
set => Resource.Call(MethodName.SetFar, Mathf.Clamp(value, 0.01f, float.PositiveInfinity));
get => (float)Resource.Get(PropertyName.Far);
set => Resource.Set(PropertyName.Far, Mathf.Clamp(value, 0.01f, float.PositiveInfinity));
}
public static Camera3DResource New()
{
Resource resource = new();
#if GODOT4_4_OR_GREATER
resource.SetScript(GD.Load<GDScript>("uid://b8hhnqsugykly"));
#else
resource.SetScript(GD.Load<GDScript>("res://addons/phantom_camera/scripts/resources/camera_3d_resource.gd"));
#endif
return new Camera3DResource(resource);
}
public static class MethodName
public static class PropertyName
{
public const string GetKeepAspect = "get_keep_aspect";
public const string SetKeepAspect = "set_keep_aspect";
public static readonly StringName KeepAspect = new("keep_aspect");
public const string GetCullMask = "get_cull_mask";
public const string SetCullMask = "set_cull_mask";
public const string SetCullMaskValue = "set_cull_mask_value";
public static readonly StringName CullMask = new("cull_mask");
public const string GetHOffset = "get_h_offset";
public const string SetHOffset = "set_h_offset";
public static readonly StringName HOffset = new("h_offset");
public const string GetVOffset = "get_v_offset";
public const string SetVOffset = "set_v_offset";
public static readonly StringName VOffset = new("v_offset");
public const string GetProjection = "get_projection";
public const string SetProjection = "set_projection";
public static readonly StringName Projection = new("projection");
public const string GetFov = "get_fov";
public const string SetFov = "set_fov";
public static readonly StringName Fov = new("fov");
public const string GetSize = "get_size";
public const string SetSize = "set_size";
public static readonly StringName Size = new("size");
public const string GetFrustumOffset = "get_frustum_offset";
public const string SetFrustumOffset = "set_frustum_offset";
public static readonly StringName FrustumOffset = new("frustum_offset");
public const string GetNear = "get_near";
public const string SetNear = "set_near";
public static readonly StringName Near = new("near");
public const string GetFar = "get_far";
public const string SetFar = "set_far";
public static readonly StringName Far = new("far");
}
}

View File

@ -59,34 +59,45 @@ public class PhantomCameraNoise2D(Resource resource)
get => (float)Resource.Call(MethodName.GetPositionalMultiplierY);
set => Resource.Call(MethodName.SetPositionalMultiplierY, value);
}
public static PhantomCameraNoise2D New()
{
Resource resource = new();
#if GODOT4_4_OR_GREATER
resource.SetScript(GD.Load<GDScript>("uid://dimvdouy8g0sv"));
#else
resource.SetScript(GD.Load<GDScript>("res://addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd"));
#endif
return new PhantomCameraNoise2D(resource);
}
public static class MethodName
{
public const string GetAmplitude = "get_amplitude";
public const string SetAmplitude = "set_amplitude";
public static readonly StringName GetAmplitude = new("get_amplitude");
public static readonly StringName SetAmplitude = new("set_amplitude");
public const string GetFrequency = "get_frequency";
public const string SetFrequency = "set_frequency";
public static readonly StringName GetFrequency = new("get_frequency");
public static readonly StringName SetFrequency = new("set_frequency");
public const string GetRandomizeNoiseSeed = "get_randomize_noise_seed";
public const string SetRandomizeNoiseSeed = "set_randomize_noise_seed";
public static readonly StringName GetRandomizeNoiseSeed = new("get_randomize_noise_seed");
public static readonly StringName SetRandomizeNoiseSeed = new("set_randomize_noise_seed");
public const string GetNoiseSeed = "get_noise_seed";
public const string SetNoiseSeed = "set_noise_seed";
public static readonly StringName GetNoiseSeed = new("get_noise_seed");
public static readonly StringName SetNoiseSeed = new("set_noise_seed");
public const string GetRotationalNoise = "get_rotational_noise";
public const string SetRotationalNoise = "set_rotational_noise";
public static readonly StringName GetRotationalNoise = new("get_rotational_noise");
public static readonly StringName SetRotationalNoise = new("set_rotational_noise");
public const string GetPositionalNoise = "get_positional_noise";
public const string SetPositionalNoise = "set_positional_noise";
public static readonly StringName GetPositionalNoise = new("get_positional_noise");
public static readonly StringName SetPositionalNoise = new("set_positional_noise");
public const string GetRotationalMultiplier = "get_rotational_multiplier";
public const string SetRotationalMultiplier = "set_rotational_multiplier";
public static readonly StringName GetRotationalMultiplier = new("get_rotational_multiplier");
public static readonly StringName SetRotationalMultiplier = new("set_rotational_multiplier");
public const string GetPositionalMultiplierX = "get_positional_multiplier_x";
public const string SetPositionalMultiplierX = "set_positional_multiplier_x";
public static readonly StringName GetPositionalMultiplierX = new("get_positional_multiplier_x");
public static readonly StringName SetPositionalMultiplierX = new("set_positional_multiplier_x");
public const string GetPositionalMultiplierY = "get_positional_multiplier_y";
public const string SetPositionalMultiplierY = "set_positional_multiplier_y";
public static readonly StringName GetPositionalMultiplierY = new("get_positional_multiplier_y");
public static readonly StringName SetPositionalMultiplierY = new("set_positional_multiplier_y");
}
}

View File

@ -77,43 +77,54 @@ public class PhantomCameraNoise3D(Resource resource)
get => (float)Resource.Call(MethodName.GetPositionalMultiplierZ);
set => Resource.Call(MethodName.SetPositionalMultiplierZ, value);
}
public static PhantomCameraNoise3D New()
{
Resource resource = new();
#if GODOT4_4_OR_GREATER
resource.SetScript(GD.Load<GDScript>("uid://cuffvge5ad4aa"));
#else
resource.SetScript(GD.Load<GDScript>("res://addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd"));
#endif
return new PhantomCameraNoise3D(resource);
}
public static class MethodName
{
public const string GetAmplitude = "get_amplitude";
public const string SetAmplitude = "set_amplitude";
public static readonly StringName GetAmplitude = new("get_amplitude");
public static readonly StringName SetAmplitude = new("set_amplitude");
public const string GetFrequency = "get_frequency";
public const string SetFrequency = "set_frequency";
public static readonly StringName GetFrequency = new("get_frequency");
public static readonly StringName SetFrequency = new("set_frequency");
public const string GetRandomizeNoiseSeed = "get_randomize_noise_seed";
public const string SetRandomizeNoiseSeed = "set_randomize_noise_seed";
public static readonly StringName GetRandomizeNoiseSeed = new("get_randomize_noise_seed");
public static readonly StringName SetRandomizeNoiseSeed = new("set_randomize_noise_seed");
public const string GetNoiseSeed = "get_noise_seed";
public const string SetNoiseSeed = "set_noise_seed";
public static readonly StringName GetNoiseSeed = new("get_noise_seed");
public static readonly StringName SetNoiseSeed = new("set_noise_seed");
public const string GetRotationalNoise = "get_rotational_noise";
public const string SetRotationalNoise = "set_rotational_noise";
public static readonly StringName GetRotationalNoise = new("get_rotational_noise");
public static readonly StringName SetRotationalNoise = new("set_rotational_noise");
public const string GetPositionalNoise = "get_positional_noise";
public const string SetPositionalNoise = "set_positional_noise";
public static readonly StringName GetPositionalNoise = new("get_positional_noise");
public static readonly StringName SetPositionalNoise = new("set_positional_noise");
public const string GetRotationalMultiplierX = "get_rotational_multiplier_x";
public const string SetRotationalMultiplierX = "set_rotational_multiplier_x";
public static readonly StringName GetRotationalMultiplierX = new("get_rotational_multiplier_x");
public static readonly StringName SetRotationalMultiplierX = new("set_rotational_multiplier_x");
public const string GetRotationalMultiplierY = "get_rotational_multiplier_y";
public const string SetRotationalMultiplierY = "set_rotational_multiplier_y";
public static readonly StringName GetRotationalMultiplierY = new("get_rotational_multiplier_y");
public static readonly StringName SetRotationalMultiplierY = new("set_rotational_multiplier_y");
public const string GetRotationalMultiplierZ = "get_rotational_multiplier_z";
public const string SetRotationalMultiplierZ = "set_rotational_multiplier_z";
public static readonly StringName GetRotationalMultiplierZ = new("get_rotational_multiplier_z");
public static readonly StringName SetRotationalMultiplierZ = new("set_rotational_multiplier_z");
public const string GetPositionalMultiplierX = "get_positional_multiplier_x";
public const string SetPositionalMultiplierX = "set_positional_multiplier_x";
public static readonly StringName GetPositionalMultiplierX = new("get_positional_multiplier_x");
public static readonly StringName SetPositionalMultiplierX = new("set_positional_multiplier_x");
public const string GetPositionalMultiplierY = "get_positional_multiplier_y";
public const string SetPositionalMultiplierY = "set_positional_multiplier_y";
public static readonly StringName GetPositionalMultiplierY = new("get_positional_multiplier_y");
public static readonly StringName SetPositionalMultiplierY = new("set_positional_multiplier_y");
public const string GetPositionalMultiplierZ = "get_positional_multiplier_z";
public const string SetPositionalMultiplierZ = "set_positional_multiplier_z";
public static readonly StringName GetPositionalMultiplierZ = new("get_positional_multiplier_z");
public static readonly StringName SetPositionalMultiplierZ = new("set_positional_multiplier_z");
}
}

View File

@ -54,11 +54,22 @@ public class PhantomCameraTween(Resource tweenResource)
get => (EaseType)(int)Resource.Get(PropertyName.Ease);
set => Resource.Set(PropertyName.Ease, (int)value);
}
public static PhantomCameraTween New()
{
Resource resource = new();
#if GODOT4_4_OR_GREATER
resource.SetScript(GD.Load<GDScript>("uid://8umksf8e80fw"));
#else
resource.SetScript(GD.Load<GDScript>("res://addons/phantom_camera/scripts/resources/tween_resource.gd"));
#endif
return new PhantomCameraTween(resource);
}
public static class PropertyName
{
public const string Duration = "duration";
public const string Transition = "transition";
public const string Ease = "ease";
public static readonly StringName Duration = new("duration");
public static readonly StringName Transition = new("transition");
public static readonly StringName Ease = new("ease");
}
}

View File

@ -17,7 +17,7 @@ func _ready() -> void:
component_owner = owner
#组件在初始化时都会等待character的加载
if not component_owner.is_node_ready() : await component_owner.ready
if component_owner and not component_owner.is_node_ready() : await component_owner.ready
_init_component()
@abstract

View File

@ -0,0 +1,3 @@
[gd_scene format=3 uid="uid://dfm5wy1rmci68"]
[node name="Anchor" type="Node2D"]

View File

@ -0,0 +1,23 @@
[gd_scene load_steps=3 format=3 uid="uid://ddwoxlqluxiq5"]
[ext_resource type="Script" uid="uid://bvxgviq7l64ck" path="res://addons/reedcomponent/grap_hook/garpping_hook_v_2.gd" id="1_jrg4x"]
[sub_resource type="CircleShape2D" id="CircleShape2D_jrg4x"]
[node name="GarppingHookV2" type="Node2D"]
script = ExtResource("1_jrg4x")
[node name="Line2D" type="Line2D" parent="."]
unique_name_in_owner = true
points = PackedVector2Array(0, 0, 80, 0)
width = 8.0
[node name="Area2D" type="Area2D" parent="."]
unique_name_in_owner = true
position = Vector2(80, 0)
collision_layer = 0
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
shape = SubResource("CircleShape2D_jrg4x")
debug_color = Color(0, 0.6399631, 0.3615963, 0.41960785)

View File

@ -0,0 +1,144 @@
class_name Hook extends Node2D
## ================
## Export Field
## ================
@export var min_length := 140
@export var max_length := 200
@export var streching_speed:float = 1400
## 线段
@onready var line_2d: Line2D = %Line2D
## 检测碰撞的区域
@onready var area_2d: Area2D = %Area2D
const GRAPABLE_GROUP = &"GRAPABLE"
const ANCHOR_NODE = preload("uid://dfm5wy1rmci68")
signal stretching_finished(reach_limit:bool, anchor_node: Node2D)
## ================
## Private Field
## ================
var _binded_hook_comp
var _is_stretching: bool = false
var _stretching_dir: Vector2 = Vector2.ZERO
var _cached_cancel: bool = false
var _anchor: Node2D
func _ready() -> void:
area_2d.position = Vector2.ZERO
area_2d.area_entered.connect(grap_detected)
area_2d.body_entered.connect(grap_detected)
##初始化
func init(hook_comp:SpawnHookComponet,reset_to_target: bool):
_binded_hook_comp = hook_comp
if reset_to_target:
var p : Vector2= hook_comp.owner.global_position
self.global_position = p
##开始stretching
func start_stretching(direction: Vector2) -> void:
_is_stretching = true
_cached_cancel = false
_stretching_dir = direction
##结束stretching
func end_stretching(force_end: bool = false) -> bool:
##如果还没有达到最短的stretching length则继续
if not force_end and _is_stretching:
var d = self.global_position.distance_to(area_2d.global_position)
if d < min_length:
_cached_cancel = true
return false
_is_stretching = false
_stretching_dir = Vector2.ZERO
return true
##是否正在stretching
func is_stretching() -> bool:
return _is_stretching
##物理更新
func _physics_process(delta: float) -> void:
if _is_stretching:
_update_stretching(delta)
##更新绳索的动画表现
func _process(delta: float) -> void:
update_line_target_pos_with_index(0,self.global_position)
update_line_target_pos_with_index(1,area_2d.global_position)
##更新钩爪爪手的位置
func _update_stretching(delta: float) -> void:
var d = self.global_position.distance_to(area_2d.global_position)
#如果已经存在一个缓存的取消,且当前的长度小于最小长度,则直接取消
if _cached_cancel:
if d >= min_length:
stretching_finished.emit(true,null)
end_stretching(true)
return
#如果达到的最大的长度,直接取消
if d > max_length:
stretching_finished.emit(true,null)
end_stretching(true)
return
area_2d.global_position += delta * streching_speed * _stretching_dir
## ================
## Tool Func
## ================
##更新特定点的位置
func update_line_target_pos_with_index(point_index: int, target_pos: Vector2) -> void:
line_2d.set_point_position(
point_index,
target_pos - global_position
)
##当触碰到可以被抓握的Area后自动在当前位置生成一个钩爪锚点后续的移动交给其他系统
func grap_detected(node: Node2D) -> void:
if node.is_in_group(GRAPABLE_GROUP):
end_stretching(true)
var d : float= self.global_position.distance_to(area_2d.global_position)
var b = d == max_length
var anchor := _create_anchor_on_node(node)
if anchor:
stretching_finished.emit(b,anchor)
##创建钩爪锚点
func _create_anchor_on_node(area: Node2D) -> Node2D:
# 如果之前有锚点,先清掉
if _anchor and is_instance_valid(_anchor):
_anchor.queue_free()
# 1. 创建 Anchor
_anchor = Node2D.new()
_anchor.name = "Anchor"
add_child(_anchor)
# 2. Anchor 初始位置 = Area 当前世界位置
_anchor.global_position = area.global_position
# 3. 在 Area 上挂 RemoteTransform2D
var remote := RemoteTransform2D.new()
remote.name = "AnchorRemote"
area.add_child(remote)
remote.remote_path = _anchor.get_path()
# 4. 只同步位置(锚点一般不需要跟旋转/缩放)
remote.update_rotation = false
remote.update_scale = false
return _anchor

View File

@ -0,0 +1 @@
uid://bvxgviq7l64ck

View File

@ -6,96 +6,89 @@ class_name SpawnHookComponet extends ComponentBase
@export var binded_hook_move_input_property_name: StringName
##用於Grapping Hook的基本Instance
const GRAPPING_HOOK = preload("uid://wlbuqjf2rg03")
const GRAPPING_HOOK = preload("uid://ddwoxlqluxiq5")
var _ray_direction: Vector2
var _ray_reference: RayCast2D
var _ray_length: float = 100
##當前的Graphook的實例
var _current_grap_hook_inst: GrappingHook
var _current_grap_hook_inst: Hook
##钩爪伸长达到动画位置
signal hook_reach_finished
func _physics_process(delta: float) -> void:
#更新當前的grap_hook的移動
update_grap_physics(get_grap_move_input(),delta)
##用来设置绑定的Ray的方向。
func set_ray_direction(in_direction: Vector2) -> Vector2:
if _ray_direction == in_direction:
return _ray_direction
_ray_direction = in_direction
if _ray_reference:
_ray_reference.target_position = Vector2(
_ray_direction.x,
_ray_direction.y
) * _ray_length
return _ray_direction
signal hook_reach_finished(reach_limit: float, anchor: Node2D)
##生成一个Grap Hook
func spawn_grap_hook_inst() -> GrappingHook:
##如果沒有正確的和任何可以抓取的物體發生碰撞,會直接返回
if not _ray_reference.is_colliding(): return null
func spawn_grap_hook_inst(dir: Vector2) -> Hook:
var i = GRAPPING_HOOK.instantiate() as GrappingHook
i.init(self)
var i = GRAPPING_HOOK.instantiate() as Hook
i.init(self,true)
get_tree().current_scene.add_child(i)
i.start_hook_with_instigator(_ray_reference.get_collision_point(),component_owner)
i.start_stretching(dir)
_current_grap_hook_inst = i
i.stretching_finished.connect(_on_hook_stretching_end)
return _current_grap_hook_inst
func grap_reach_target(duration: float) -> bool:
if not _ray_reference or not _current_grap_hook_inst:return false
##重置所有Points的坐标点位置。
_current_grap_hook_inst.reset_line_points_to_target(component_owner.global_position)
var start_pos: Vector2 = component_owner.global_position
var target_pos: Vector2 = _ray_reference.get_collision_point()
var t := get_tree().create_tween()
t.set_trans(Tween.TRANS_SINE)
t.set_ease(Tween.EASE_OUT)
t.tween_method(
_current_grap_hook_inst.update_line_anchor_pos,
start_pos,
target_pos,
duration
)
##结束时会发送广播
t.finished.connect(_on_hook_reach_finished)
return true
##尝试关闭hook的延长
func suspend_hook_stretching(force_suspend: bool = false) -> bool:
if not _current_grap_hook_inst: return false
return _current_grap_hook_inst.end_stretching(force_suspend)
##更新GrapHook的物理
func update_grap_physics(input: Vector2,delta: float) -> void:
if input.is_zero_approx(): return
if not _current_grap_hook_inst: return
var i = input.normalized()
_current_grap_hook_inst.move_hook(input,delta)
##当Hook stretching 关闭的时候,触发。
func _on_hook_stretching_end(reach_limit,anchor:Node2D) -> void:
hook_reach_finished.emit(reach_limit,anchor)
##此函數默認為Vector.Zeor,子類可以重寫以得到一個全新的結果,也可以通過綁定的方式獲得一個結果。
func get_grap_move_input() -> Vector2:
#如果我們在組件裏綁定了Component上的一個字段也可以直接獲取。
if binded_hook_move_input_property_name in component_owner:
return component_owner.get(binded_hook_move_input_property_name)
return Vector2.ZERO
##从当前的hook获取到hook的anchor
func get_current_hook_anchor() -> Node2D:
return _current_grap_hook_inst._anchor
func _on_hook_reach_finished() -> void:
_current_grap_hook_inst.m_update_rope_with_nodes = true
hook_reach_finished.emit()
#func grap_reach_target(duration: float) -> bool:
#if not _ray_reference or not _current_grap_hook_inst:return false
#
###重置所有Points的坐标点位置。
#_current_grap_hook_inst.reset_line_points_to_target(component_owner.global_position)
#
#var start_pos: Vector2 = component_owner.global_position
#var target_pos: Vector2 = _ray_reference.get_collision_point()
#
#var t := get_tree().create_tween()
#t.set_trans(Tween.TRANS_SINE)
#t.set_ease(Tween.EASE_OUT)
#
#t.tween_method(
#_current_grap_hook_inst.update_line_anchor_pos,
#start_pos,
#target_pos,
#duration
#)
#
###结束时会发送广播
#t.finished.connect(_on_hook_reach_finished)
#
#return true
#
###更新GrapHook的物理
#func update_grap_physics(input: Vector2,delta: float) -> void:
#if input.is_zero_approx(): return
#if not _current_grap_hook_inst: return
#
#var i = input.normalized()
#_current_grap_hook_inst.move_hook(input,delta)
#
###此函數默認為Vector.Zeor,子類可以重寫以得到一個全新的結果,也可以通過綁定的方式獲得一個結果。
#func get_grap_move_input() -> Vector2:
#
##如果我們在組件裏綁定了Component上的一個字段也可以直接獲取。
#if binded_hook_move_input_property_name in component_owner:
#return component_owner.get(binded_hook_move_input_property_name)
#
#return Vector2.ZERO
#
#func _on_hook_reach_finished() -> void:
#_current_grap_hook_inst.m_update_rope_with_nodes = true
#hook_reach_finished.emit()
#
func _init_component() -> void:
_ray_reference = binded_hook_ray_cast_2d
pass

View File

@ -0,0 +1,6 @@
@icon("res://addons/reedscene/act/icon/ActIcon.svg")
class_name Act
extends Resource
##prop_state_map通过输入Dictionary和状态来设定State
@export var prop_state_map: Dictionary[int, SingleAct] = {}

View File

@ -0,0 +1 @@
uid://fxpk2ot6otfh

View File

@ -0,0 +1,117 @@
#@icon("")
@tool
class_name ActManager extends Node
##ActManager里记录了一系列的ActID和对应的Act主要用来同步场景的Act比如我们现在使用ActID = 0的Act那么就会让所有的Act的状态重置回到ActID = 0的Act的状态
@export var prop_state_map: Dictionary[int, Act] = {}
##用于生成一个基础的Act的工具按钮
@export_tool_button("Generate Default Act")
var _gen_default_act: Callable = Callable(self, "_editor_generate_default_act")
##是否要输出报错信息
@export var debug_log: bool = false
##用来扫描prop
func _scan_scene_for_props(scene_root: Node) -> Dictionary:
var result := {}
if scene_root == null:
return result
_scan_node_recursive(scene_root, result)
return result
##递归扫描
func _scan_node_recursive(node: Node, out: Dictionary) -> void:
if node is PropComponent:
var prop := node as PropComponent
if out.has(prop.prop_id):
push_warning(
"[ActManagerInspector] Duplicate prop_id: %d (%s)"
% [prop.prop_id, node.get_path()]
)
else:
out[prop.prop_id] = {
"node": prop,
"path": node.get_path()
}
for child in node.get_children():
_scan_node_recursive(child, out)
func _editor_generate_default_act() -> void:
if not Engine.is_editor_hint():
return
var scene := _get_owner_scene()
if scene == null:
_editor_popup(
"ActManager must be a child of ReedScene.",
"Generate Default Act - Failed"
)
return
var props_root := scene.get_node_or_null(scene.props_root_path)
if props_root == null:
_editor_popup(
"Props root not found:\n%s" % scene.props_root_path,
"Generate Default Act - Failed"
)
return
var act := Act.new()
act.prop_state_map.clear()
var count := 0
for prop_node in props_root.get_children():
if not prop_node is Node:
continue
var prop_comp := _find_prop_component_top(prop_node)
if prop_comp == null:
continue
var pid := prop_comp.prop_id
var init_state := prop_comp.initial_state_id
act.prop_state_map[pid] = init_state
count += 1
prop_state_map[0] = act
notify_property_list_changed()
_editor_popup(
"Default Act generated successfully.\n\nProp count: %d\nAct ID: 0" % count,
"Generate Default Act - Success"
)
if debug_log:
print("[ActManager][Editor] Generated Default Act with", count, "props.")
## ==============================
## 工具函数
## ==============================
func _editor_popup(message: String, title: String = "ActManager") -> void:
if not Engine.is_editor_hint():
return
var dialog := AcceptDialog.new()
dialog.title = title
dialog.dialog_text = message
get_tree().root.add_child(dialog)
dialog.popup_centered()
func _get_owner_scene() -> ReedScene:
var p := get_parent()
if p is ReedScene:
return p as ReedScene
return null
func _find_prop_component_top(prop: Node) -> PropComponent:
for child in prop.get_children():
if child is PropComponent:
return child as PropComponent
return null

View File

@ -0,0 +1,96 @@
@tool
extends EditorInspectorPlugin
# ✅ 由 EditorPlugin 注入
var editor_interface: EditorInterface
func _can_handle(object) -> bool:
return object is ActManager
func _parse_begin(object) -> void:
var act_manager := object as ActManager
var reed_scene := _get_owner_scene(act_manager)
var text := ""
text += "Act Table (Debug)\n"
text += "Act Count: %d\n" % act_manager.prop_state_map.size()
text += "------------------\n"
if reed_scene == null:
text += "ERROR: ActManager must be a direct child of ReedScene.\n"
text += "Current parent: %s\n" % (act_manager.get_parent().name if act_manager.get_parent() else "<null>")
var label := Label.new()
label.text = text
label.autowrap_mode = TextServer.AUTOWRAP_WORD
add_custom_control(label)
return
var props_root := reed_scene.get_node_or_null(reed_scene.props_root_path)
if props_root == null:
text += "ERROR: ReedScene Props root not found: %s\n" % str(reed_scene.props_root_path)
var label2 := Label.new()
label2.text = text
label2.autowrap_mode = TextServer.AUTOWRAP_WORD
add_custom_control(label2)
return
# ✅ 只扫描 Props root 下的 Prop
var prop_index := _scan_props_root_for_props(props_root)
text += "Scene: %s\n" % reed_scene.name
text += "Props Root: %s\n" % str(props_root.get_path())
text += "Prop Count: %d\n" % prop_index.size()
text += "------------------\n"
for prop_id in prop_index.keys():
var info: Dictionary = prop_index[prop_id]
text += "PropID: %d | %s\n" % [int(prop_id), str(info["path"])]
var label := Label.new()
label.text = text
label.autowrap_mode = TextServer.AUTOWRAP_WORD
add_custom_control(label)
##扫描Prop root
func _scan_props_root_for_props(props_root: Node) -> Dictionary:
var result := {}
for prop in props_root.get_children():
if not prop is Node:
continue
# PropComponent 必须在 prop 的最上层子节点集中
var prop_comp := _find_prop_component_in_top_children(prop)
if prop_comp == null:
continue
var pid := prop_comp.prop_id
if result.has(pid):
var old_info: Dictionary = result[pid]
push_warning(
"[ActManagerInspector] Duplicate prop_id under Props root: %d\n A: %s\n B: %s"
% [pid, str(old_info["path"]), str(prop_comp.get_path())]
)
else:
result[pid] = {
"node": prop_comp,
"path": prop_comp.get_path()
}
return result
##获取到Prop下的所有的PropComponent
func _find_prop_component_in_top_children(prop: Node) -> PropComponent:
for child in prop.get_children():
if child is PropComponent:
return child as PropComponent
return null
##获取到ActManager上方的Scene应当自查为ReedScene
func _get_owner_scene(act_manager: ActManager) -> ReedScene:
var p := act_manager.get_parent()
if p is ReedScene:
return p as ReedScene
return null

View File

@ -0,0 +1 @@
uid://082tx8ee707t

View File

@ -0,0 +1,4 @@
class_name SingleAct extends Resource
@export var state_id: int = 0
@export var use_trans: bool = false

View File

@ -0,0 +1 @@
uid://baqgorvlumyju

View File

@ -1 +0,0 @@
class_name ActManager extends Node

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767102882495" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17013" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M513.194667 85.290667c-17.664 0-35.541333 9.856-41.344 30.677333l-14.634667 54.656H299.861333a128 128 0 0 0-128 128v298.666667a128 128 0 0 0 128 128h2.688L258.56 883.968c-6.314667 22.698667 6.613333 47.018667 29.312 53.333333 22.698667 6.314667 47.018667-6.656 53.333333-29.354666l50.688-182.656h78.634667v128a42.666667 42.666667 0 0 0 85.333333 0v-128h78.677334l50.688 182.656c6.272 22.698667 30.592 35.669333 53.333333 29.354666 22.698667-6.314667 35.626667-30.634667 29.312-53.333333l-43.989333-158.677333h2.645333a128 128 0 0 0 128-128v-298.666667a128 128 0 0 0-128-128h-157.312l-14.677333-54.656c-5.802667-20.821333-23.68-30.677333-41.344-30.677333z" fill="#be8dbd" p-id="17014"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dkkvlam0m6cno"
path="res://.godot/imported/ActIcon.svg-bc2f37aa408ca4bb121c48c1713e51e9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/reedscene/act/icon/ActIcon.svg"
dest_files=["res://.godot/imported/ActIcon.svg-bc2f37aa408ca4bb121c48c1713e51e9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -1,7 +1,7 @@
[plugin]
name="ReedScene"
description=""
description="这是一个用于2D游戏场景管理的插件通过组件化的方式将一个场景分成了场景中的各个组件和组件所对应的状态用户可以通过切换状态来实现一些动画的效果也可以通过切换状态来快速的事件存档不同事件触发不同的结果的任务系统等总之比较灵活"
author="ReedZhu"
version=""
version=".1"
script="reedscene.gd"

View File

@ -11,8 +11,8 @@ class_name PropComponent extends Node
## Export
## ==============================
##此物件的描述ID
@export var prop_id: int
##此物件的描述ID无法主动修改由PropManager发信
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY) var prop_id: int = -1
##初始的默认state_id
@export var initial_state_id: int = 0
@ -61,11 +61,16 @@ func _build_state_cache() -> void:
push_warning("[PropComponent:%s] States root not found" % prop_id)
return
if not _states_root is ReedPropStateManager:
push_error("[PropComponent:%s] States root is not ReedPropStateManager" % prop_id)
return
for c in _states_root.get_children():
if c is ReedPropState:
var s := c as ReedPropState
if _state_map.has(s.state_id):
push_error("[PropComponent:%s] Duplicate state_id: %d" % [prop_id, s.state_id])
push_error("[PropComponent:%s] Duplicate state_id: %d"
% [prop_id, s.state_id])
continue
_state_map[s.state_id] = s
@ -167,12 +172,19 @@ func _editor_ensure_states_root() -> void:
if states != null:
return
states = Node.new()
# 创建 StateManager 而不是普通 Node
states = ReedPropStateManager.new()
states.name = states_root_name
add_child(states)
# ⚠️ 关键:让它成为 Scene 的一部分
# 让其成为 Scene 的一部分(非常重要)
states.owner = get_tree().edited_scene_root
# 自动添加一个默认 State
var default_state := ReedPropState.new()
default_state.name = "Default"
states.add_child(default_state)
default_state.owner = get_tree().edited_scene_root
if debug_log:
print("[PropComponent] Created States root")
print("[PropComponent] Created States(StateManager) + Default State")

View File

@ -0,0 +1,109 @@
@tool
class_name PropManager
extends Node2D
##是否要自动的向其子集中添加一个PropComp
@export var auto_attach_prop_component := true
##是否要输出Debug Message
@export var debug_log := true
##输出一个ID的前缀方便策划调整
const PREFIX := "[Prop_%04d]"
func _ready() -> void:
if not Engine.is_editor_hint():
return
_ensure_existing_props()
child_entered_tree.connect(_on_child_entered_tree)
# 强制让 Editor 重新计算 configuration warnings
update_configuration_warnings()
func _on_child_entered_tree(node: Node) -> void:
if not Engine.is_editor_hint():
return
_try_attach_and_assign(node)
# 子节点变化后,刷新 Inspector 警告
update_configuration_warnings()
func _ensure_existing_props() -> void:
for child in get_children():
_try_attach_and_assign(child)
func _try_attach_and_assign(prop: Node) -> void:
if not auto_attach_prop_component:
return
if prop == null:
return
if prop is PropComponent:
return
var comp := _find_prop_component_top(prop)
if comp == null:
comp = PropComponent.new()
comp.name = "PropComponent" # 临时名
prop.add_child(comp)
comp.owner = get_tree().edited_scene_root
# 确保 prop_id 唯一且有效
if int(comp.prop_id) < 0 or _is_prop_id_used_elsewhere(int(comp.prop_id), comp):
comp.prop_id = _alloc_prop_id()
# 用 prop_id 重命名 PropComponent
comp.name = PREFIX % int(comp.prop_id)
if debug_log:
print("[PropManager][Editor] Ensure PropComponent:", prop.name, "->", comp.name, "id=", comp.prop_id)
func _find_prop_component_top(prop: Node) -> PropComponent:
for child in prop.get_children():
if child is PropComponent:
return child as PropComponent
return null
func _alloc_prop_id() -> int:
var max_id := -1
for prop in get_children():
var comp := _find_prop_component_top(prop)
if comp == null:
continue
max_id = maxi(max_id, int(comp.prop_id))
return max_id + 1
func _is_prop_id_used_elsewhere(prop_id: int, self_comp: PropComponent) -> bool:
for prop in get_children():
var comp := _find_prop_component_top(prop)
if comp == null:
continue
if comp == self_comp:
continue
if int(comp.prop_id) == prop_id:
return true
return false
# =====================================================
# Editor Configuration WarningsInspector 黄色警告)
# =====================================================
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
if not Engine.is_editor_hint():
return warnings
for prop in get_children():
if prop is PropComponent:
continue
var comp := _find_prop_component_top(prop)
if comp == null:
warnings.append(
"Prop '%s' has no PropComponent as a top-level child." % prop.name
)
return warnings

View File

@ -0,0 +1 @@
uid://pxjf5vst08eo

View File

@ -2,8 +2,8 @@
@icon("res://addons/reedscene/prop/icon/prop_state_icon.svg")
class_name ReedPropState extends Node
##状态编号,默认为-1-1是不可用编号请修改
@export var state_id: int = -1
##状态编号,默认为-1-1是不可用编号状态编号由StateManager发布不可以自己修改。
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY) var state_id: int = -1
##该State的信息是否需要被存入Save中
@export var save_game: bool = false

View File

@ -0,0 +1,88 @@
@tool
@icon("res://addons/reedscene/prop/icon/prop_states_manager.svg")
class_name ReedPropStateManager
extends Node
@export var auto_refresh: bool = true
func _ready() -> void:
if not Engine.is_editor_hint():
return
child_entered_tree.connect(_on_child_entered_tree)
child_exiting_tree.connect(_on_child_exiting_tree)
_refresh_states()
func _notification(what: int) -> void:
if not Engine.is_editor_hint():
return
if what == NOTIFICATION_CHILD_ORDER_CHANGED:
if auto_refresh:
call_deferred("_refresh_states")
func _on_child_entered_tree(node: Node) -> void:
if not Engine.is_editor_hint():
return
if node is ReedPropState and auto_refresh:
call_deferred("_refresh_states")
func _on_child_exiting_tree(node: Node) -> void:
if not Engine.is_editor_hint():
return
if node is ReedPropState and auto_refresh:
call_deferred("_refresh_states")
func _refresh_states() -> void:
var index := 0
for child in get_children():
if not child is ReedPropState:
continue
_assign_state_id(child, index)
_rename_state_node(child, index)
index += 1
func _assign_state_id(state: ReedPropState, id: int) -> void:
if state.state_id == id:
return
state.set("state_id", id)
func _rename_state_node(state: ReedPropState, id: int) -> void:
var base_name := _strip_id_prefix(state.name)
var expected := "[ID_%d] %s" % [id, base_name]
if state.name == expected:
return
state.name = expected
func _strip_id_prefix(name: String) -> String:
var result := name
# 连续剥离所有 [ID_x] 前缀,防止叠加
while result.begins_with("[ID_"):
var end := result.find("]")
if end == -1:
break
# 跳过 "] " 或 "]"
var cut := end + 1
if cut < result.length() and result[cut] == " ":
cut += 1
result = result.substr(cut)
return result.strip_edges()
func _extract_raw_name(name: String) -> String:
if name.begins_with("[ID:"):
var end := name.find("]")
if end != -1 and end + 2 < name.length():
return name.substr(end + 2)
return name

View File

@ -0,0 +1 @@
uid://di41kt2tj34c2

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767100309546" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6972" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.390625" height="200"><path d="M1005.065 566.407L778.527 438.471a38.248 38.248 0 0 0 3.614-15.299V170.734a38.55 38.55 0 0 0-20.359-34.092L533.317 5.695a38.067 38.067 0 0 0-40.055 0L264.736 136.642a38.971 38.971 0 0 0-20.419 34.092c0.06 1.686 0.542 3.192 0.723 4.819v247.619a38.67 38.67 0 0 0 3.855 15.6L21.514 566.407a39.754 39.754 0 0 0-20.359 34.092V856.31c1.084 13.07 9.035 24.636 21.082 30.72l227.02 130.947a42.404 42.404 0 0 0 20.418 5.42 42.585 42.585 0 0 0 20.42-5.42L515.97 891.185l220.574 126.792a38.067 38.067 0 0 0 20.36 5.42 41.44 41.44 0 0 0 17.527-5.42l230.634-128.237a38.61 38.61 0 0 0 20.419-34.092V600.499a40.236 40.236 0 0 0-20.42-34.092zM704.139 399.982l-151.126 83.182V319.21l151.126-82.76v163.533z m52.041 282.374l-143.536-79.99-3.313-1.867 147.572-82.52 146.97 81.195-147.693 83.182zM513.62 84.781l146.91 83.242-148.295 82.58-146.246-82.58L513.62 84.781zM270.399 517.979l146.85 82.52-6.265 3.494-142.03 78.363L122.706 600.5l147.692-82.52zM309.731 915.7V751.444l147.692-82.64v163.654L309.73 915.7z m634.137-83.242L796.236 915.7V750.781l93.903-52.282 53.729-29.755v163.714z" p-id="6973" fill="#d4237a"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ebci83e8e4c5"
path="res://.godot/imported/prop_states_manager.svg-00bb8dafdf44685d5ebeae84cfe13a05.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/reedscene/prop/icon/prop_states_manager.svg"
dest_files=["res://.godot/imported/prop_states_manager.svg-00bb8dafdf44685d5ebeae84cfe13a05.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -1,2 +1,14 @@
@tool
extends EditorPlugin
var inspector_plugin
func _enter_tree() -> void:
inspector_plugin = preload("res://addons/reedscene/act/ActManagerInspector.gd").new()
# ✅ 注入 EditorInterface
inspector_plugin.editor_interface = get_editor_interface()
add_inspector_plugin(inspector_plugin)
func _exit_tree() -> void:
if inspector_plugin:
remove_inspector_plugin(inspector_plugin)

View File

@ -1,6 +1,5 @@
@tool
@icon("uid://p0oxphym6oqg")
'''
level的最小单元prop和act
leveln个小关卡n = scene的数量
@ -24,10 +23,29 @@
SceneScene通过PropComponent上的IDID获取到PropComponent所挂载的组件
PropComponent必须是处于Prop的最上层子节点集
Scene自身也有SceneIDSceneID是实现跨Scene修改的核心SceneID是插件内置的系统主动生成的
Scene来说SceneID是唯一且绝对的2D游戏可能没有这些问题
Scene1的时候Scene9的一个ActChange西Scene的玩家所
'''
class_name ReedScene extends Node2D
## ==============================
## Const Config
## ==============================
##Act管理器的命名
const ACT_MANAGER_NAME := "ActManager"
##Prop的根节点命名
const PROPS_ROOT_NAME := "Props"
##场景管理器的根节点命名
const SCENE_MANAGER_NAME := "SceneManager"
## ==============================
## Export Config
## ==============================
@ -44,11 +62,11 @@ var _props_root: Node
var _prop_map: Dictionary = {} # prop_id -> PropComponent
## ==============================
## Lifecycle
##
## ==============================
func _enter_tree() -> void:
if Engine.is_editor_hint():
_editor_auto_attach_prop_components()
_editor_ensure_scene_nodes() ##进入场景树自动添加ActManager,PropsRoot,和SceneManager
## ==============================
## Resolve References
@ -119,30 +137,29 @@ func _on_act_changed(from_act: StringName, to_act: StringName) -> void:
for prop_comp in _prop_map.values():
prop_comp.on_act_changed(from_act, to_act)
## ==============================
## Editor Helpers
## ==============================
func _editor_auto_attach_prop_components() -> void:
if not auto_attach_prop_component:
return
##进入场景树自动添加ActManager,PropsRoot,和SceneManager
func _editor_ensure_scene_nodes() -> void:
_editor_ensure_node(SCENE_MANAGER_NAME, SceneManager)
_editor_ensure_node(ACT_MANAGER_NAME, ActManager)
_editor_ensure_node(PROPS_ROOT_NAME, PropManager)
var props_root := get_node_or_null(props_root_path)
if props_root == null:
return
##添加命名和节点
func _editor_ensure_node(name: String, type: Variant) -> Node:
var node := get_node_or_null(name)
if node != null:
return node
for prop in props_root.get_children():
if not prop is Node:
continue
node = type.new()
node.name = name
add_child(node)
if _find_prop_component(prop) != null:
continue
# ⚠️ 关键:让节点成为 Scene 的一部分
node.owner = get_tree().edited_scene_root
var comp := PropComponent.new()
prop.add_child(comp)
comp.owner = get_tree().edited_scene_root
if debug_log:
print("[ReedScene][Editor] Created node:", name)
if debug_log:
print("[ReedScene][Editor] Auto attached PropComponent to:", prop.name)
return node
func _ready() -> void:
_resolve_act_manager()

View File

@ -0,0 +1,15 @@
# ReedScene 插件更新列表
## 更新日志
V0.1
- 添加了基础的ActPropScene的概念。
## 更新计划
V0.1
主要的工作是搭建一个插件的基本架构
- [x] 角色墙滑

View File

@ -39,6 +39,7 @@ enabled=PackedStringArray("res://addons/phantom_camera/plugin.cfg", "res://addon
ROOM="房间分组其下存在所有的Room"
PLAYER="玩家分组,其下只存在玩家控制器"
GRAPABLE=""
[input]