From 745342c35c6fdb06906694a3900456848321e1c6 Mon Sep 17 00:00:00 2001 From: RedisTKey Date: Wed, 31 Dec 2025 13:07:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=88=8E=E7=88=AA=E5=9F=BA=E7=A4=8E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _game/Game.tscn | 4 +- _game/LevelDemonstration.tscn | 44 ++- _player/Avatar.tscn | 2 +- _player/states/character_state_machine.gd | 9 +- _player/states/ghost.gd | 1 - _player/states/grap_hook.gd | 8 +- _player/states/hook_shooting.gd | 31 +- .../2D-4.3/2d_limit_example_scene.tscn | 8 +- .../3D-4.4/3d_tweening_example_scene.tscn | 6 +- .../scripts/2D/2d_room_limit_tween_4.3.gd | 3 - .../2D/player_character_body_2d_4.3.gd | 1 + .../host_list/host_list_item_group.tres | 2 +- addons/phantom_camera/plugin.cfg | 2 +- .../scripts/gizmos/phantom_camera_3d_gizmo.gd | 19 +- .../gizmos/phantom_camera_3d_gizmo_plugin.gd | 2 +- .../scripts/managers/PhantomCameraManager.cs | 6 +- .../scripts/phantom_camera/PhantomCamera.cs | 157 ++++----- .../scripts/phantom_camera/PhantomCamera2D.cs | 100 +++--- .../scripts/phantom_camera/PhantomCamera3D.cs | 213 ++++++------ .../PhantomCameraNoiseEmitter2D.cs | 34 +- .../PhantomCameraNoiseEmitter3D.cs | 34 +- .../phantom_camera/phantom_camera_2d.gd | 17 +- .../phantom_camera/phantom_camera_3d.gd | 321 ++++++++++++++---- .../phantom_camera_host/PhantomCameraHost.cs | 73 ++-- .../phantom_camera_host.gd | 10 +- .../scripts/resources/Camera3DResource.cs | 85 +++-- .../scripts/resources/PhantomCameraNoise2D.cs | 47 ++- .../scripts/resources/PhantomCameraNoise3D.cs | 59 ++-- .../scripts/resources/PhantomCameraTween.cs | 17 +- addons/reedcomponent/component_base.gd | 2 +- addons/reedcomponent/grap_hook/Anchor.tscn | 3 + .../grap_hook/garpping_hook_v2.tscn | 23 ++ .../grap_hook/garpping_hook_v_2.gd | 144 ++++++++ .../grap_hook/garpping_hook_v_2.gd.uid | 1 + .../grap_hook/spawn_hook_component.gd | 139 ++++---- addons/reedscene/act/Act.gd | 6 + addons/reedscene/act/Act.gd.uid | 1 + addons/reedscene/act/ActManager.gd | 117 +++++++ .../{act_manager.gd.uid => ActManager.gd.uid} | 0 addons/reedscene/act/ActManagerInspector.gd | 96 ++++++ .../reedscene/act/ActManagerInspector.gd.uid | 1 + addons/reedscene/act/SingleAct.gd | 4 + addons/reedscene/act/SingleAct.gd.uid | 1 + addons/reedscene/act/act_manager.gd | 1 - addons/reedscene/act/icon/ActIcon.svg | 1 + addons/reedscene/act/icon/ActIcon.svg.import | 43 +++ addons/reedscene/plugin.cfg | 4 +- addons/reedscene/prop/PropComponent.gd | 24 +- addons/reedscene/prop/PropManager.gd | 109 ++++++ addons/reedscene/prop/PropManager.gd.uid | 1 + addons/reedscene/prop/PropState.gd | 4 +- addons/reedscene/prop/StateManager.gd | 88 +++++ addons/reedscene/prop/StateManager.gd.uid | 1 + .../prop/icon/prop_states_manager.svg | 1 + .../prop/icon/prop_states_manager.svg.import | 43 +++ addons/reedscene/reedscene.gd | 12 + addons/reedscene/scene/ReedScene.gd | 61 ++-- addons/reedscene/update.md | 15 + project.godot | 1 + 59 files changed, 1611 insertions(+), 651 deletions(-) create mode 100644 addons/reedcomponent/grap_hook/Anchor.tscn create mode 100644 addons/reedcomponent/grap_hook/garpping_hook_v2.tscn create mode 100644 addons/reedcomponent/grap_hook/garpping_hook_v_2.gd create mode 100644 addons/reedcomponent/grap_hook/garpping_hook_v_2.gd.uid create mode 100644 addons/reedscene/act/Act.gd create mode 100644 addons/reedscene/act/Act.gd.uid create mode 100644 addons/reedscene/act/ActManager.gd rename addons/reedscene/act/{act_manager.gd.uid => ActManager.gd.uid} (100%) create mode 100644 addons/reedscene/act/ActManagerInspector.gd create mode 100644 addons/reedscene/act/ActManagerInspector.gd.uid create mode 100644 addons/reedscene/act/SingleAct.gd create mode 100644 addons/reedscene/act/SingleAct.gd.uid delete mode 100644 addons/reedscene/act/act_manager.gd create mode 100644 addons/reedscene/act/icon/ActIcon.svg create mode 100644 addons/reedscene/act/icon/ActIcon.svg.import create mode 100644 addons/reedscene/prop/PropManager.gd create mode 100644 addons/reedscene/prop/PropManager.gd.uid create mode 100644 addons/reedscene/prop/StateManager.gd create mode 100644 addons/reedscene/prop/StateManager.gd.uid create mode 100644 addons/reedscene/prop/icon/prop_states_manager.svg create mode 100644 addons/reedscene/prop/icon/prop_states_manager.svg.import create mode 100644 addons/reedscene/update.md diff --git a/_game/Game.tscn b/_game/Game.tscn index 1c936db..a5fe956 100644 --- a/_game/Game.tscn +++ b/_game/Game.tscn @@ -7,14 +7,14 @@ [node name="Game" type="Node2D"] -[node name="level_1" parent="." instance=ExtResource("1_pvnxo")] +[node name="level_1" parent="." groups=["GRAPABLE"] instance=ExtResource("1_pvnxo")] tile_map_data = PackedByteArray("AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAkAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAA0AAAAAAAAAAAAAAA4AAAAAAAAAAAAAAA8AAAAAAAAAAAAAABAAAAAAAAAAAAAAABEAAAAAAAAAAAAAABIAAAAAAAAAAAAAABMAAAAAAAAAAAAAABQAAAAAAAAAAAAAABUAAAAAAAAAAAAAABYAAAAAAAAAAAABAAAAAAAAAAAAAAABAAEAAAAAAAAAAAABAAIAAAAAAAAAAAABAAMAAAAAAAAAAAABAAQAAAAAAAAAAAABAAUAAAAAAAAAAAABAAYAAAAAAAAAAAABAAcAAAAAAAAAAAABAAgAAAAAAAAAAAABAAkAAAAAAAAAAAABAAoAAAAAAAAAAAABAAsAAAAAAAAAAAABAAwAAAAAAAAAAAABAA0AAAAAAAAAAAABAA4AAAAAAAAAAAABAA8AAAAAAAAAAAABABAAAAAAAAAAAAABABEAAAAAAAAAAAABABIAAAAAAAAAAAABABMAAAAAAAAAAAABABQAAAAAAAAAAAABABUAAAAAAAAAAAABABYAAAAAAAAAAAACAAAAAAAAAAAAAAACAAEAAAAAAAAAAAACAAIAAAAAAAAAAAACAAMAAAAAAAAAAAACAAQAAAAAAAAAAAACAAUAAAAAAAAAAAACAAYAAAAAAAAAAAACAAcAAAAAAAAAAAACAAgAAAAAAAAAAAACAAkAAAAAAAAAAAACAAoAAAAAAAAAAAACAAsAAAAAAAAAAAACAAwAAAAAAAAAAAACAA0AAAAAAAAAAAACAA4AAAAAAAAAAAACAA8AAAAAAAAAAAACABAAAAAAAAAAAAACABEAAAAAAAAAAAACABIAAAAAAAAAAAACABMAAAAAAAAAAAACABQAAAAAAAAAAAACABUAAAAAAAAAAAACABYAAAAAAAAAAAADAAAAAAAAAAAAAAADAAEAAAAAAAAAAAADAAIAAAAAAAAAAAADAAMAAAAAAAAAAAADAAQAAAAAAAAAAAADAAUAAAAAAAAAAAADAAYAAAAAAAAAAAADAAcAAAAAAAAAAAADAAgAAAAAAAAAAAADAAkAAAAAAAAAAAADAAoAAAAAAAAAAAADAAsAAAAAAAAAAAADAAwAAAAAAAAAAAADAA0AAAAAAAAAAAADAA4AAAAAAAAAAAADAA8AAAAAAAAAAAADABAAAAAAAAAAAAADABEAAAAAAAAAAAADABIAAAAAAAAAAAADABMAAAAAAAAAAAADABQAAAAAAAAAAAADABUAAAAAAAAAAAADABYAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAEAAIAAAAAAAAAAAAEAAMAAAAAAAAAAAAEAAQAAAAAAAAAAAAEAAUAAAAAAAAAAAAEAAYAAAAAAAAAAAAEAAcAAAAAAAAAAAAEAAgAAAAAAAAAAAAEAAkAAAAAAAAAAAAEAAoAAAAAAAAAAAAEAAsAAAAAAAAAAAAEAAwAAAAAAAAAAAAEAA0AAAAAAAAAAAAEAA4AAAAAAAAAAAAEAA8AAAAAAAAAAAAEABAAAAAAAAAAAAAEABEAAAAAAAAAAAAEABIAAAAAAAAAAAAEABMAAAAAAAAAAAAEABQAAAAAAAAAAAAEABUAAAAAAAAAAAAEABYAAAAAAAAAAAAFAAAAAAAAAAAAAAAFAAEAAAAAAAAAAAAFAAIAAAAAAAAAAAAFAAMAAAAAAAAAAAAFAAQAAAAAAAAAAAAFAAUAAAAAAAAAAAAFAAYAAAAAAAAAAAAFAAcAAAAAAAAAAAAFAAgAAAAAAAAAAAAFAAkAAAAAAAAAAAAFAAoAAAAAAAAAAAAFAAsAAAAAAAAAAAAFAAwAAAAAAAAAAAAFAA0AAAAAAAAAAAAFAA4AAAAAAAAAAAAFAA8AAAAAAAAAAAAFABAAAAAAAAAAAAAFABEAAAAAAAAAAAAFABIAAAAAAAAAAAAFABMAAAAAAAAAAAAFABQAAAAAAAAAAAAFABUAAAAAAAAAAAAFABYAAAAAAAAAAAAGABIAAAAAAAAAAAAGABMAAAAAAAAAAAAGABQAAAAAAAAAAAAGABUAAAAAAAAAAAAGABYAAAAAAAAAAAAHABIAAAAAAAAAAAAHABMAAAAAAAAAAAAHABQAAAAAAAAAAAAHABUAAAAAAAAAAAAHABYAAAAAAAAAAAAIABIAAAAAAAAAAAAIABMAAAAAAAAAAAAIABQAAAAAAAAAAAAIABUAAAAAAAAAAAAIABYAAAAAAAAAAAAJABIAAAAAAAAAAAAJABMAAAAAAAAAAAAJABQAAAAAAAAAAAAJABUAAAAAAAAAAAAJABYAAAAAAAAAAAAKABIAAAAAAAAAAAAKABMAAAAAAAAAAAAKABQAAAAAAAAAAAAKABUAAAAAAAAAAAAKABYAAAAAAAAAAAALABIAAAAAAAAAAAALABMAAAAAAAAAAAALABQAAAAAAAAAAAALABUAAAAAAAAAAAALABYAAAAAAAAAAAAMABIAAAAAAAAAAAAMABMAAAAAAAAAAAAMABQAAAAAAAAAAAAMABUAAAAAAAAAAAAMABYAAAAAAAAAAAANABIAAAAAAAAAAAANABMAAAAAAAAAAAANABQAAAAAAAAAAAANABUAAAAAAAAAAAANABYAAAAAAAAAAAAOABIAAAAAAAAAAAAOABMAAAAAAAAAAAAOABQAAAAAAAAAAAAOABUAAAAAAAAAAAAOABYAAAAAAAAAAAAPABIAAAAAAAAAAAAPABMAAAAAAAAAAAAPABQAAAAAAAAAAAAPABUAAAAAAAAAAAAPABYAAAAAAAAAAAAQABIAAAAAAAAAAAAQABMAAAAAAAAAAAAQABQAAAAAAAAAAAAQABUAAAAAAAAAAAAQABYAAAAAAAAAAAARABIAAAAAAAAAAAARABMAAAAAAAAAAAARABQAAAAAAAAAAAARABUAAAAAAAAAAAARABYAAAAAAAAAAAASABIAAAAAAAAAAAASABMAAAAAAAAAAAASABQAAAAAAAAAAAASABUAAAAAAAAAAAASABYAAAAAAAAAAAATABIAAAAAAAAAAAATABMAAAAAAAAAAAATABQAAAAAAAAAAAATABUAAAAAAAAAAAATABYAAAAAAAAAAAAUABIAAAAAAAAAAAAUABMAAAAAAAAAAAAUABQAAAAAAAAAAAAUABUAAAAAAAAAAAAUABYAAAAAAAAAAAAVABIAAAAAAAAAAAAVABMAAAAAAAAAAAAVABQAAAAAAAAAAAAVABUAAAAAAAAAAAAVABYAAAAAAAAAAAARAA8AAAAAAAAAAAARABAAAAAAAAAAAAARABEAAAAAAAAAAAASAA8AAAAAAAAAAAASABAAAAAAAAAAAAASABEAAAAAAAAAAAATAA8AAAAAAAAAAAATABAAAAAAAAAAAAATABEAAAAAAAAAAAAUAA8AAAAAAAAAAAAUABAAAAAAAAAAAAAUABEAAAAAAAAAAAAVAA8AAAAAAAAAAAAVABAAAAAAAAAAAAAVABEAAAAAAAAAAAAGAAAAAAAAAAAAAAAGAAEAAAAAAAAAAAAGAAIAAAAAAAAAAAAGAAMAAAAAAAAAAAAGAAQAAAAAAAAAAAAGAAUAAAAAAAAAAAAGAAYAAAAAAAAAAAAGAAcAAAAAAAAAAAAGAAgAAAAAAAAAAAAHAAAAAAAAAAAAAAAHAAEAAAAAAAAAAAAHAAIAAAAAAAAAAAAHAAMAAAAAAAAAAAAHAAQAAAAAAAAAAAAHAAUAAAAAAAAAAAAHAAYAAAAAAAAAAAAHAAcAAAAAAAAAAAAHAAgAAAAAAAAAAAAIAAAAAAAAAAAAAAAIAAEAAAAAAAAAAAAIAAIAAAAAAAAAAAAIAAMAAAAAAAAAAAAIAAQAAAAAAAAAAAAIAAUAAAAAAAAAAAAIAAYAAAAAAAAAAAAIAAcAAAAAAAAAAAAIAAgAAAAAAAAAAAAJAAAAAAAAAAAAAAAJAAEAAAAAAAAAAAAJAAIAAAAAAAAAAAAJAAMAAAAAAAAAAAAJAAQAAAAAAAAAAAAJAAUAAAAAAAAAAAAJAAYAAAAAAAAAAAAJAAcAAAAAAAAAAAAJAAgAAAAAAAAAAAAKAAAAAAAAAAAAAAAKAAEAAAAAAAAAAAAKAAIAAAAAAAAAAAAKAAMAAAAAAAAAAAAKAAQAAAAAAAAAAAAKAAUAAAAAAAAAAAAKAAYAAAAAAAAAAAAKAAcAAAAAAAAAAAAKAAgAAAAAAAAAAAALAAAAAAAAAAAAAAALAAEAAAAAAAAAAAALAAIAAAAAAAAAAAALAAMAAAAAAAAAAAALAAQAAAAAAAAAAAALAAUAAAAAAAAAAAALAAYAAAAAAAAAAAALAAcAAAAAAAAAAAALAAgAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAAEAAAAAAAAAAAAMAAIAAAAAAAAAAAAMAAMAAAAAAAAAAAAMAAQAAAAAAAAAAAAMAAUAAAAAAAAAAAAMAAYAAAAAAAAAAAAMAAcAAAAAAAAAAAAMAAgAAAAAAAAAAAANAAAAAAAAAAAAAAANAAEAAAAAAAAAAAANAAIAAAAAAAAAAAANAAMAAAAAAAAAAAANAAQAAAAAAAAAAAANAAUAAAAAAAAAAAANAAYAAAAAAAAAAAANAAcAAAAAAAAAAAANAAgAAAAAAAAAAAAOAAAAAAAAAAAAAAAOAAEAAAAAAAAAAAAOAAIAAAAAAAAAAAAOAAMAAAAAAAAAAAAOAAQAAAAAAAAAAAAOAAUAAAAAAAAAAAAOAAYAAAAAAAAAAAAOAAcAAAAAAAAAAAAOAAgAAAAAAAAAAAAcAAkAAAAAAAAAAAAcAAoAAAAAAAAAAAAcAAsAAAAAAAAAAAAcAAwAAAAAAAAAAAAcAA0AAAAAAAAAAAAcAA4AAAAAAAAAAAAcAA8AAAAAAAAAAAAcABAAAAAAAAAAAAAcABEAAAAAAAAAAAAcABIAAAAAAAAAAAAcABMAAAAAAAAAAAAcABQAAAAAAAAAAAAcABUAAAAAAAAAAAAcABYAAAAAAAAAAAAdAAkAAAAAAAAAAAAdAAoAAAAAAAAAAAAdAAsAAAAAAAAAAAAdAAwAAAAAAAAAAAAdAA0AAAAAAAAAAAAdAA4AAAAAAAAAAAAdAA8AAAAAAAAAAAAdABAAAAAAAAAAAAAdABEAAAAAAAAAAAAdABIAAAAAAAAAAAAdABMAAAAAAAAAAAAdABQAAAAAAAAAAAAdABUAAAAAAAAAAAAdABYAAAAAAAAAAAAeAAkAAAAAAAAAAAAeAAoAAAAAAAAAAAAeAAsAAAAAAAAAAAAeAAwAAAAAAAAAAAAeAA0AAAAAAAAAAAAeAA4AAAAAAAAAAAAeAA8AAAAAAAAAAAAeABAAAAAAAAAAAAAeABEAAAAAAAAAAAAeABIAAAAAAAAAAAAeABMAAAAAAAAAAAAeABQAAAAAAAAAAAAeABUAAAAAAAAAAAAeABYAAAAAAAAAAAAfAAkAAAAAAAAAAAAfAAoAAAAAAAAAAAAfAAsAAAAAAAAAAAAfAAwAAAAAAAAAAAAfAA0AAAAAAAAAAAAfAA4AAAAAAAAAAAAfAA8AAAAAAAAAAAAfABAAAAAAAAAAAAAfABEAAAAAAAAAAAAfABIAAAAAAAAAAAAfABMAAAAAAAAAAAAfABQAAAAAAAAAAAAfABUAAAAAAAAAAAAfABYAAAAAAAAAAAAgAAkAAAAAAAAAAAAgAAoAAAAAAAAAAAAgAAsAAAAAAAAAAAAgAAwAAAAAAAAAAAAgAA0AAAAAAAAAAAAgAA4AAAAAAAAAAAAgAA8AAAAAAAAAAAAgABAAAAAAAAAAAAAgABEAAAAAAAAAAAAgABIAAAAAAAAAAAAgABMAAAAAAAAAAAAgABQAAAAAAAAAAAAgABUAAAAAAAAAAAAgABYAAAAAAAAAAAAhAAkAAAAAAAAAAAAhAAoAAAAAAAAAAAAhAAsAAAAAAAAAAAAhAAwAAAAAAAAAAAAhAA0AAAAAAAAAAAAhAA4AAAAAAAAAAAAhAA8AAAAAAAAAAAAhABAAAAAAAAAAAAAhABEAAAAAAAAAAAAhABIAAAAAAAAAAAAhABMAAAAAAAAAAAAhABQAAAAAAAAAAAAhABUAAAAAAAAAAAAhABYAAAAAAAAAAAAiAAkAAAAAAAAAAAAiAAoAAAAAAAAAAAAiAAsAAAAAAAAAAAAiAAwAAAAAAAAAAAAiAA0AAAAAAAAAAAAiAA4AAAAAAAAAAAAiAA8AAAAAAAAAAAAiABAAAAAAAAAAAAAiABEAAAAAAAAAAAAiABIAAAAAAAAAAAAiABMAAAAAAAAAAAAiABQAAAAAAAAAAAAiABUAAAAAAAAAAAAiABYAAAAAAAAAAAAjAAkAAAAAAAAAAAAjAAoAAAAAAAAAAAAjAAsAAAAAAAAAAAAjAAwAAAAAAAAAAAAjAA0AAAAAAAAAAAAjAA4AAAAAAAAAAAAjAA8AAAAAAAAAAAAjABAAAAAAAAAAAAAjABEAAAAAAAAAAAAjABIAAAAAAAAAAAAjABMAAAAAAAAAAAAjABQAAAAAAAAAAAAjABUAAAAAAAAAAAAjABYAAAAAAAAAAAAkAAkAAAAAAAAAAAAkAAoAAAAAAAAAAAAkAAsAAAAAAAAAAAAkAAwAAAAAAAAAAAAkAA0AAAAAAAAAAAAkAA4AAAAAAAAAAAAkAA8AAAAAAAAAAAAkABAAAAAAAAAAAAAkABEAAAAAAAAAAAAkABIAAAAAAAAAAAAkABMAAAAAAAAAAAAkABQAAAAAAAAAAAAkABUAAAAAAAAAAAAkABYAAAAAAAAAAAAlAAwAAAAAAAAAAAAlAA0AAAAAAAAAAAAlAA4AAAAAAAAAAAAlAA8AAAAAAAAAAAAlABAAAAAAAAAAAAAlABEAAAAAAAAAAAAlABIAAAAAAAAAAAAlABMAAAAAAAAAAAAlABQAAAAAAAAAAAAlABUAAAAAAAAAAAAlABYAAAAAAAAAAAAmAAwAAAAAAAAAAAAmAA0AAAAAAAAAAAAmAA4AAAAAAAAAAAAmAA8AAAAAAAAAAAAmABAAAAAAAAAAAAAmABEAAAAAAAAAAAAmABIAAAAAAAAAAAAmABMAAAAAAAAAAAAmABQAAAAAAAAAAAAmABUAAAAAAAAAAAAmABYAAAAAAAAAAAAnAA0AAAAAAAAAAAAnAA4AAAAAAAAAAAAnAA8AAAAAAAAAAAAnABAAAAAAAAAAAAAnABEAAAAAAAAAAAAnABIAAAAAAAAAAAAnABMAAAAAAAAAAAAnABQAAAAAAAAAAAAnABUAAAAAAAAAAAAnABYAAAAAAAAAAAAoAA0AAAAAAAAAAAAoAA4AAAAAAAAAAAAoAA8AAAAAAAAAAAAoABAAAAAAAAAAAAAoABEAAAAAAAAAAAAoABIAAAAAAAAAAAAoABMAAAAAAAAAAAAoABQAAAAAAAAAAAAoABUAAAAAAAAAAAAoABYAAAAAAAAAAAApAA0AAAAAAAAAAAApAA4AAAAAAAAAAAApAA8AAAAAAAAAAAApABAAAAAAAAAAAAApABEAAAAAAAAAAAApABIAAAAAAAAAAAApABMAAAAAAAAAAAApABQAAAAAAAAAAAApABUAAAAAAAAAAAApABYAAAAAAAAAAAAqAA0AAAAAAAAAAAAqAA4AAAAAAAAAAAAqAA8AAAAAAAAAAAAqABAAAAAAAAAAAAAqABEAAAAAAAAAAAAqABIAAAAAAAAAAAAqABMAAAAAAAAAAAAqABQAAAAAAAAAAAAqABUAAAAAAAAAAAAqABYAAAAAAAAAAAArAAwAAAAAAAAAAAArAA0AAAAAAAAAAAArAA4AAAAAAAAAAAArAA8AAAAAAAAAAAArABAAAAAAAAAAAAArABEAAAAAAAAAAAArABIAAAAAAAAAAAArABMAAAAAAAAAAAArABQAAAAAAAAAAAArABUAAAAAAAAAAAArABYAAAAAAAAAAAAsAAkAAAAAAAAAAAAsAAoAAAAAAAAAAAAsAAsAAAAAAAAAAAAsAAwAAAAAAAAAAAAsAA0AAAAAAAAAAAAsAA4AAAAAAAAAAAAsAA8AAAAAAAAAAAAsABAAAAAAAAAAAAAsABEAAAAAAAAAAAAsABIAAAAAAAAAAAAsABMAAAAAAAAAAAAsABQAAAAAAAAAAAAsABUAAAAAAAAAAAAsABYAAAAAAAAAAAAtAAkAAAAAAAAAAAAtAAoAAAAAAAAAAAAtAAsAAAAAAAAAAAAtAAwAAAAAAAAAAAAtAA0AAAAAAAAAAAAtAA4AAAAAAAAAAAAtAA8AAAAAAAAAAAAtABAAAAAAAAAAAAAtABEAAAAAAAAAAAAtABIAAAAAAAAAAAAtABMAAAAAAAAAAAAtABQAAAAAAAAAAAAtABUAAAAAAAAAAAAtABYAAAAAAAAAAAAuAAkAAAAAAAAAAAAuAAoAAAAAAAAAAAAuAAsAAAAAAAAAAAAuAAwAAAAAAAAAAAAuAA0AAAAAAAAAAAAuAA4AAAAAAAAAAAAuAA8AAAAAAAAAAAAuABAAAAAAAAAAAAAuABEAAAAAAAAAAAAuABIAAAAAAAAAAAAuABMAAAAAAAAAAAAuABQAAAAAAAAAAAAuABUAAAAAAAAAAAAuABYAAAAAAAAAAAAvAAkAAAAAAAAAAAAvAAoAAAAAAAAAAAAvAAsAAAAAAAAAAAAvAAwAAAAAAAAAAAAvAA0AAAAAAAAAAAAvAA4AAAAAAAAAAAAvAA8AAAAAAAAAAAAvABAAAAAAAAAAAAAvABEAAAAAAAAAAAAvABIAAAAAAAAAAAAvABMAAAAAAAAAAAAvABQAAAAAAAAAAAAvABUAAAAAAAAAAAAvABYAAAAAAAAAAAAwAAkAAAAAAAAAAAAwAAoAAAAAAAAAAAAwAAsAAAAAAAAAAAAwAAwAAAAAAAAAAAAwAA0AAAAAAAAAAAAwAA4AAAAAAAAAAAAwAA8AAAAAAAAAAAAwABAAAAAAAAAAAAAwABEAAAAAAAAAAAAwABIAAAAAAAAAAAAwABMAAAAAAAAAAAAwABQAAAAAAAAAAAAwABUAAAAAAAAAAAAwABYAAAAAAAAAAAAxAAkAAAAAAAAAAAAxAAoAAAAAAAAAAAAxAAsAAAAAAAAAAAAxAAwAAAAAAAAAAAAxAA0AAAAAAAAAAAAxAA4AAAAAAAAAAAAxAA8AAAAAAAAAAAAxABAAAAAAAAAAAAAxABEAAAAAAAAAAAAxABIAAAAAAAAAAAAxABMAAAAAAAAAAAAxABQAAAAAAAAAAAAxABUAAAAAAAAAAAAxABYAAAAAAAAAAAAyAAkAAAAAAAAAAAAyAAoAAAAAAAAAAAAyAAsAAAAAAAAAAAAyAAwAAAAAAAAAAAAyAA0AAAAAAAAAAAAyAA4AAAAAAAAAAAAyAA8AAAAAAAAAAAAyABAAAAAAAAAAAAAyABEAAAAAAAAAAAAyABIAAAAAAAAAAAAyABMAAAAAAAAAAAAyABQAAAAAAAAAAAAyABUAAAAAAAAAAAAyABYAAAAAAAAAAAAzAAkAAAAAAAAAAAAzAAoAAAAAAAAAAAAzAAsAAAAAAAAAAAAzAAwAAAAAAAAAAAAzAA0AAAAAAAAAAAAzAA4AAAAAAAAAAAAzAA8AAAAAAAAAAAAzABAAAAAAAAAAAAAzABEAAAAAAAAAAAAzABIAAAAAAAAAAAAzABMAAAAAAAAAAAAzABQAAAAAAAAAAAAzABUAAAAAAAAAAAAzABYAAAAAAAAAAAAlAAsAAAAAAAAAAAArAAsAAAAAAAAAAAAqAAwAAAAAAAAAAAAAAPv/AAAAAAAAAAAAAPz/AAAAAAAAAAAAAP3/AAAAAAAAAAAAAP7/AAAAAAAAAAAAAP//AAAAAAAAAAABAPv/AAAAAAAAAAABAPz/AAAAAAAAAAABAP3/AAAAAAAAAAABAP7/AAAAAAAAAAABAP//AAAAAAAAAAACAPv/AAAAAAAAAAACAPz/AAAAAAAAAAACAP3/AAAAAAAAAAACAP7/AAAAAAAAAAACAP//AAAAAAAAAAADAPv/AAAAAAAAAAADAPz/AAAAAAAAAAADAP3/AAAAAAAAAAADAP7/AAAAAAAAAAADAP//AAAAAAAAAAAEAPv/AAAAAAAAAAAEAPz/AAAAAAAAAAAEAP3/AAAAAAAAAAAEAP7/AAAAAAAAAAAEAP//AAAAAAAAAAAFAPv/AAAAAAAAAAAFAPz/AAAAAAAAAAAFAP3/AAAAAAAAAAAFAP7/AAAAAAAAAAAFAP//AAAAAAAAAAAGAPv/AAAAAAAAAAAGAPz/AAAAAAAAAAAGAP3/AAAAAAAAAAAGAP7/AAAAAAAAAAAGAP//AAAAAAAAAAAHAPv/AAAAAAAAAAAHAPz/AAAAAAAAAAAHAP3/AAAAAAAAAAAHAP7/AAAAAAAAAAAHAP//AAAAAAAAAAAIAPv/AAAAAAAAAAAIAPz/AAAAAAAAAAAIAP3/AAAAAAAAAAAIAP7/AAAAAAAAAAAIAP//AAAAAAAAAAAJAPv/AAAAAAAAAAAJAPz/AAAAAAAAAAAJAP3/AAAAAAAAAAAJAP7/AAAAAAAAAAAJAP//AAAAAAAAAAAKAPv/AAAAAAAAAAAKAPz/AAAAAAAAAAAKAP3/AAAAAAAAAAAKAP7/AAAAAAAAAAAKAP//AAAAAAAAAAALAPv/AAAAAAAAAAALAPz/AAAAAAAAAAALAP3/AAAAAAAAAAALAP7/AAAAAAAAAAALAP//AAAAAAAAAAAMAPv/AAAAAAAAAAAMAPz/AAAAAAAAAAAMAP3/AAAAAAAAAAAMAP7/AAAAAAAAAAAMAP//AAAAAAAAAAANAPv/AAAAAAAAAAANAPz/AAAAAAAAAAANAP3/AAAAAAAAAAANAP7/AAAAAAAAAAANAP//AAAAAAAAAAAOAPv/AAAAAAAAAAAOAPz/AAAAAAAAAAAOAP3/AAAAAAAAAAAOAP7/AAAAAAAAAAAOAP//AAAAAAAAAAAAAPb/AAAAAAAAAAAAAPf/AAAAAAAAAAAAAPj/AAAAAAAAAAAAAPn/AAAAAAAAAAAAAPr/AAAAAAAAAAABAPb/AAAAAAAAAAABAPf/AAAAAAAAAAABAPj/AAAAAAAAAAABAPn/AAAAAAAAAAABAPr/AAAAAAAAAAACAPb/AAAAAAAAAAACAPf/AAAAAAAAAAACAPj/AAAAAAAAAAACAPn/AAAAAAAAAAACAPr/AAAAAAAAAAADAPb/AAAAAAAAAAADAPf/AAAAAAAAAAADAPj/AAAAAAAAAAADAPn/AAAAAAAAAAADAPr/AAAAAAAAAAAEAPb/AAAAAAAAAAAEAPf/AAAAAAAAAAAEAPj/AAAAAAAAAAAEAPn/AAAAAAAAAAAEAPr/AAAAAAAAAAAFAPb/AAAAAAAAAAAFAPf/AAAAAAAAAAAFAPj/AAAAAAAAAAAFAPn/AAAAAAAAAAAFAPr/AAAAAAAAAAAGAPb/AAAAAAAAAAAGAPf/AAAAAAAAAAAGAPj/AAAAAAAAAAAGAPn/AAAAAAAAAAAGAPr/AAAAAAAAAAAHAPb/AAAAAAAAAAAHAPf/AAAAAAAAAAAHAPj/AAAAAAAAAAAHAPn/AAAAAAAAAAAHAPr/AAAAAAAAAAAIAPb/AAAAAAAAAAAIAPf/AAAAAAAAAAAIAPj/AAAAAAAAAAAIAPn/AAAAAAAAAAAIAPr/AAAAAAAAAAAJAPb/AAAAAAAAAAAJAPf/AAAAAAAAAAAJAPj/AAAAAAAAAAAJAPn/AAAAAAAAAAAJAPr/AAAAAAAAAAAKAPb/AAAAAAAAAAAKAPf/AAAAAAAAAAAKAPj/AAAAAAAAAAAKAPn/AAAAAAAAAAAKAPr/AAAAAAAAAAALAPb/AAAAAAAAAAALAPf/AAAAAAAAAAALAPj/AAAAAAAAAAALAPn/AAAAAAAAAAALAPr/AAAAAAAAAAAMAPb/AAAAAAAAAAAMAPf/AAAAAAAAAAAMAPj/AAAAAAAAAAAMAPn/AAAAAAAAAAAMAPr/AAAAAAAAAAANAPb/AAAAAAAAAAANAPf/AAAAAAAAAAANAPj/AAAAAAAAAAANAPn/AAAAAAAAAAANAPr/AAAAAAAAAAAOAPb/AAAAAAAAAAAOAPf/AAAAAAAAAAAOAPj/AAAAAAAAAAAOAPn/AAAAAAAAAAAOAPr/AAAAAAAAAAAPAPb/AAAAAAAAAAAPAPf/AAAAAAAAAAAPAPj/AAAAAAAAAAAPAPn/AAAAAAAAAAAPAPr/AAAAAAAAAAAQAPb/AAAAAAAAAAAQAPf/AAAAAAAAAAAQAPj/AAAAAAAAAAAQAPn/AAAAAAAAAAAQAPr/AAAAAAAAAAARAPb/AAAAAAAAAAARAPf/AAAAAAAAAAARAPj/AAAAAAAAAAARAPn/AAAAAAAAAAARAPr/AAAAAAAAAAASAPb/AAAAAAAAAAASAPf/AAAAAAAAAAASAPj/AAAAAAAAAAASAPn/AAAAAAAAAAASAPr/AAAAAAAAAAATAPb/AAAAAAAAAAATAPf/AAAAAAAAAAATAPj/AAAAAAAAAAATAPn/AAAAAAAAAAATAPr/AAAAAAAAAAAUAPb/AAAAAAAAAAAUAPf/AAAAAAAAAAAUAPj/AAAAAAAAAAAUAPn/AAAAAAAAAAAUAPr/AAAAAAAAAAAVAPb/AAAAAAAAAAAVAPf/AAAAAAAAAAAVAPj/AAAAAAAAAAAVAPn/AAAAAAAAAAAVAPr/AAAAAAAAAAAWAPb/AAAAAAAAAAAWAPf/AAAAAAAAAAAWAPj/AAAAAAAAAAAWAPn/AAAAAAAAAAAWAPr/AAAAAAAAAAAXAPb/AAAAAAAAAAAXAPf/AAAAAAAAAAAXAPj/AAAAAAAAAAAXAPn/AAAAAAAAAAAXAPr/AAAAAAAAAAAYAPb/AAAAAAAAAAAYAPf/AAAAAAAAAAAYAPj/AAAAAAAAAAAYAPn/AAAAAAAAAAAYAPr/AAAAAAAAAAAZAPb/AAAAAAAAAAAZAPf/AAAAAAAAAAAZAPj/AAAAAAAAAAAZAPn/AAAAAAAAAAAZAPr/AAAAAAAAAAAaAPb/AAAAAAAAAAAaAPf/AAAAAAAAAAAaAPj/AAAAAAAAAAAaAPn/AAAAAAAAAAAaAPr/AAAAAAAAAAAbAPb/AAAAAAAAAAAbAPf/AAAAAAAAAAAbAPj/AAAAAAAAAAAbAPn/AAAAAAAAAAAbAPr/AAAAAAAAAAAcAPb/AAAAAAAAAAAcAPf/AAAAAAAAAAAcAPj/AAAAAAAAAAAcAPn/AAAAAAAAAAAcAPr/AAAAAAAAAAAdAPb/AAAAAAAAAAAdAPf/AAAAAAAAAAAdAPj/AAAAAAAAAAAdAPn/AAAAAAAAAAAdAPr/AAAAAAAAAAAeAPb/AAAAAAAAAAAeAPf/AAAAAAAAAAAeAPj/AAAAAAAAAAAeAPn/AAAAAAAAAAAeAPr/AAAAAAAAAAAfAPb/AAAAAAAAAAAfAPf/AAAAAAAAAAAfAPj/AAAAAAAAAAAfAPn/AAAAAAAAAAAfAPr/AAAAAAAAAAAgAPb/AAAAAAAAAAAgAPf/AAAAAAAAAAAgAPj/AAAAAAAAAAAgAPn/AAAAAAAAAAAgAPr/AAAAAAAAAAAhAPb/AAAAAAAAAAAhAPf/AAAAAAAAAAAhAPj/AAAAAAAAAAAhAPn/AAAAAAAAAAAhAPr/AAAAAAAAAAAiAPb/AAAAAAAAAAAiAPf/AAAAAAAAAAAiAPj/AAAAAAAAAAAiAPn/AAAAAAAAAAAiAPr/AAAAAAAAAAAjAPb/AAAAAAAAAAAjAPf/AAAAAAAAAAAjAPj/AAAAAAAAAAAjAPn/AAAAAAAAAAAjAPr/AAAAAAAAAAAkAPb/AAAAAAAAAAAkAPf/AAAAAAAAAAAkAPj/AAAAAAAAAAAkAPn/AAAAAAAAAAAkAPr/AAAAAAAAAAAlAPb/AAAAAAAAAAAlAPf/AAAAAAAAAAAlAPj/AAAAAAAAAAAlAPn/AAAAAAAAAAAlAPr/AAAAAAAAAAAmAPb/AAAAAAAAAAAmAPf/AAAAAAAAAAAmAPj/AAAAAAAAAAAmAPn/AAAAAAAAAAAmAPr/AAAAAAAAAAAnAPb/AAAAAAAAAAAnAPf/AAAAAAAAAAAnAPj/AAAAAAAAAAAnAPn/AAAAAAAAAAAnAPr/AAAAAAAAAAAoAPb/AAAAAAAAAAAoAPf/AAAAAAAAAAAoAPj/AAAAAAAAAAAoAPn/AAAAAAAAAAAoAPr/AAAAAAAAAAApAPb/AAAAAAAAAAApAPf/AAAAAAAAAAApAPj/AAAAAAAAAAApAPn/AAAAAAAAAAApAPr/AAAAAAAAAAAqAPb/AAAAAAAAAAAqAPf/AAAAAAAAAAAqAPj/AAAAAAAAAAAqAPn/AAAAAAAAAAAqAPr/AAAAAAAAAAArAPb/AAAAAAAAAAArAPf/AAAAAAAAAAArAPj/AAAAAAAAAAArAPn/AAAAAAAAAAArAPr/AAAAAAAAAAAsAPb/AAAAAAAAAAAsAPf/AAAAAAAAAAAsAPj/AAAAAAAAAAAsAPn/AAAAAAAAAAAsAPr/AAAAAAAAAAAtAPb/AAAAAAAAAAAtAPf/AAAAAAAAAAAtAPj/AAAAAAAAAAAtAPn/AAAAAAAAAAAtAPr/AAAAAAAAAAAuAPb/AAAAAAAAAAAuAPf/AAAAAAAAAAAuAPj/AAAAAAAAAAAuAPn/AAAAAAAAAAAuAPr/AAAAAAAAAAAvAPb/AAAAAAAAAAAvAPf/AAAAAAAAAAAvAPj/AAAAAAAAAAAvAPn/AAAAAAAAAAAvAPr/AAAAAAAAAAAwAPb/AAAAAAAAAAAwAPf/AAAAAAAAAAAwAPj/AAAAAAAAAAAwAPn/AAAAAAAAAAAwAPr/AAAAAAAAAAAxAPb/AAAAAAAAAAAxAPf/AAAAAAAAAAAxAPj/AAAAAAAAAAAxAPn/AAAAAAAAAAAxAPr/AAAAAAAAAAAyAPb/AAAAAAAAAAAyAPf/AAAAAAAAAAAyAPj/AAAAAAAAAAAyAPn/AAAAAAAAAAAyAPr/AAAAAAAAAAAzAPb/AAAAAAAAAAAzAPf/AAAAAAAAAAAzAPj/AAAAAAAAAAAzAPn/AAAAAAAAAAAzAPr/AAAAAAAAAAA0APb/AAAAAAAAAAA0APf/AAAAAAAAAAA0APj/AAAAAAAAAAA0APn/AAAAAAAAAAA0APr/AAAAAAAAAAA1APb/AAAAAAAAAAA1APf/AAAAAAAAAAA1APj/AAAAAAAAAAA1APn/AAAAAAAAAAA1APr/AAAAAAAAAAA2APb/AAAAAAAAAAA2APf/AAAAAAAAAAA2APj/AAAAAAAAAAA2APn/AAAAAAAAAAA2APr/AAAAAAAAAAA3APb/AAAAAAAAAAA3APf/AAAAAAAAAAA3APj/AAAAAAAAAAA3APn/AAAAAAAAAAA3APr/AAAAAAAAAAA4APb/AAAAAAAAAAA4APf/AAAAAAAAAAA4APj/AAAAAAAAAAA4APn/AAAAAAAAAAA4APr/AAAAAAAAAAA5APb/AAAAAAAAAAA5APf/AAAAAAAAAAA5APj/AAAAAAAAAAA5APn/AAAAAAAAAAA5APr/AAAAAAAAAAA6APb/AAAAAAAAAAA6APf/AAAAAAAAAAA6APj/AAAAAAAAAAA6APn/AAAAAAAAAAA6APr/AAAAAAAAAAAWAA8AAAABAAAAAAAWABAAAAABAAAAAAAWABEAAAABAAAAAAAWABIAAAABAAAAAAAWABMAAAABAAAAAAAWABQAAAABAAAAAAAWABUAAAABAAAAAAAWABYAAAABAAAAAAAbAA8AAAABAAAAAAAbABAAAAABAAAAAAAbABEAAAABAAAAAAAbABIAAAABAAAAAAAbABMAAAABAAAAAAAbABQAAAABAAAAAAAbABUAAAABAAAAAAAbABYAAAABAAAAAAAhAAgAAAABAAAAAAAiAAgAAAABAAAAAAAjAAgAAAABAAAAAABKAAkAAAAAAAAAAABKAAoAAAAAAAAAAABKAAsAAAAAAAAAAABKAAwAAAAAAAAAAABLAAkAAAAAAAAAAABLAAoAAAAAAAAAAABLAAsAAAAAAAAAAABLAAwAAAAAAAAAAABKAAUAAAAAAAAAAABKAAYAAAAAAAAAAABKAAcAAAAAAAAAAABKAAgAAAAAAAAAAABKAA0AAAAAAAAAAABLAAUAAAAAAAAAAABLAAYAAAAAAAAAAABLAAcAAAAAAAAAAABLAAgAAAAAAAAAAABLAA0AAAAAAAAAAABQABMAAAAAAAAAAABQABQAAAAAAAAAAABQABUAAAAAAAAAAABQABYAAAAAAAAAAABQABcAAAAAAAAAAABRABMAAAAAAAAAAABRABQAAAAAAAAAAABRABUAAAAAAAAAAABRABYAAAAAAAAAAABRABcAAAAAAAAAAABSABMAAAAAAAAAAABSABQAAAAAAAAAAABSABUAAAAAAAAAAABSABYAAAAAAAAAAABSABcAAAAAAAAAAABTABMAAAAAAAAAAABTABQAAAAAAAAAAABTABUAAAAAAAAAAABTABYAAAAAAAAAAABTABcAAAAAAAAAAABUABMAAAAAAAAAAABUABQAAAAAAAAAAABUABUAAAAAAAAAAABUABYAAAAAAAAAAABUABcAAAAAAAAAAABVABMAAAAAAAAAAABVABQAAAAAAAAAAABVABUAAAAAAAAAAABVABYAAAAAAAAAAABVABcAAAAAAAAAAABWABMAAAAAAAAAAABWABQAAAAAAAAAAABWABUAAAAAAAAAAABWABYAAAAAAAAAAABWABcAAAAAAAAAAABXAAsAAAAAAAAAAABXAAwAAAAAAAAAAABXAA0AAAAAAAAAAABXAA4AAAAAAAAAAABXABMAAAAAAAAAAABXABQAAAAAAAAAAABXABUAAAAAAAAAAABXABYAAAAAAAAAAABXABcAAAAAAAAAAABYAAsAAAAAAAAAAABYAAwAAAAAAAAAAABYAA0AAAAAAAAAAABYAA4AAAAAAAAAAABYABMAAAAAAAAAAABYABQAAAAAAAAAAABYABUAAAAAAAAAAABYABYAAAAAAAAAAABYABcAAAAAAAAAAABZAAsAAAAAAAAAAABZAAwAAAAAAAAAAABZAA0AAAAAAAAAAABZAA4AAAAAAAAAAABZABMAAAAAAAAAAABZABQAAAAAAAAAAABZABUAAAAAAAAAAABZABYAAAAAAAAAAABZABcAAAAAAAAAAABaAAsAAAAAAAAAAABaAAwAAAAAAAAAAABaAA0AAAAAAAAAAABaAA4AAAAAAAAAAABaABMAAAAAAAAAAABaABQAAAAAAAAAAABaABUAAAAAAAAAAABaABYAAAAAAAAAAABaABcAAAAAAAAAAABbAAsAAAAAAAAAAABbAAwAAAAAAAAAAABbAA0AAAAAAAAAAABbAA4AAAAAAAAAAABbABMAAAAAAAAAAABbABQAAAAAAAAAAABbABUAAAAAAAAAAABbABYAAAAAAAAAAABbABcAAAAAAAAAAABcAAsAAAAAAAAAAABcAAwAAAAAAAAAAABcAA0AAAAAAAAAAABcAA4AAAAAAAAAAABcABMAAAAAAAAAAABcABQAAAAAAAAAAABcABUAAAAAAAAAAABcABYAAAAAAAAAAABcABcAAAAAAAAAAABdAAsAAAAAAAAAAABdAAwAAAAAAAAAAABdAA0AAAAAAAAAAABdAA4AAAAAAAAAAABdABMAAAAAAAAAAABdABQAAAAAAAAAAABdABUAAAAAAAAAAABdABYAAAAAAAAAAABdABcAAAAAAAAAAABeAAsAAAAAAAAAAABeAAwAAAAAAAAAAABeAA0AAAAAAAAAAABeAA4AAAAAAAAAAABeABMAAAAAAAAAAABeABQAAAAAAAAAAABeABUAAAAAAAAAAABeABYAAAAAAAAAAABeABcAAAAAAAAAAAA0AA8AAAAAAAAAAAA0ABAAAAAAAAAAAAA0ABEAAAAAAAAAAAA0ABIAAAAAAAAAAAA0ABMAAAAAAAAAAAA0ABQAAAAAAAAAAAA0ABUAAAAAAAAAAAA0ABYAAAAAAAAAAAA1AA8AAAAAAAAAAAA1ABAAAAAAAAAAAAA1ABEAAAAAAAAAAAA1ABIAAAAAAAAAAAA1ABMAAAAAAAAAAAA1ABQAAAAAAAAAAAA1ABUAAAAAAAAAAAA1ABYAAAAAAAAAAAA2AA8AAAAAAAAAAAA2ABAAAAAAAAAAAAA2ABEAAAAAAAAAAAA2ABIAAAAAAAAAAAA2ABMAAAAAAAAAAAA2ABQAAAAAAAAAAAA2ABUAAAAAAAAAAAA2ABYAAAAAAAAAAAA3AA8AAAAAAAAAAAA3ABAAAAAAAAAAAAA3ABEAAAAAAAAAAAA3ABIAAAAAAAAAAAA3ABMAAAAAAAAAAAA3ABQAAAAAAAAAAAA3ABUAAAAAAAAAAAA3ABYAAAAAAAAAAAA4AA8AAAAAAAAAAAA4ABAAAAAAAAAAAAA4ABEAAAAAAAAAAAA4ABIAAAAAAAAAAAA4ABMAAAAAAAAAAAA4ABQAAAAAAAAAAAA4ABUAAAAAAAAAAAA4ABYAAAAAAAAAAAA5AA8AAAAAAAAAAAA5ABAAAAAAAAAAAAA5ABEAAAAAAAAAAAA5ABIAAAAAAAAAAAA5ABMAAAAAAAAAAAA5ABQAAAAAAAAAAAA5ABUAAAAAAAAAAAA5ABYAAAAAAAAAAAA6AA8AAAAAAAAAAAA6ABAAAAAAAAAAAAA6ABEAAAAAAAAAAAA6ABIAAAAAAAAAAAA6ABMAAAAAAAAAAAA6ABQAAAAAAAAAAAA6ABUAAAAAAAAAAAA6ABYAAAAAAAAAAAA7AA8AAAAAAAAAAAA7ABAAAAAAAAAAAAA7ABEAAAAAAAAAAAA7ABIAAAAAAAAAAAA7ABMAAAAAAAAAAAA7ABQAAAAAAAAAAAA7ABUAAAAAAAAAAAA7ABYAAAAAAAAAAAA8AA8AAAAAAAAAAAA8ABAAAAAAAAAAAAA8ABEAAAAAAAAAAAA8ABIAAAAAAAAAAAA8ABMAAAAAAAAAAAA8ABQAAAAAAAAAAAA8ABUAAAAAAAAAAAA8ABYAAAAAAAAAAAA9AA8AAAAAAAAAAAA9ABAAAAAAAAAAAAA9ABEAAAAAAAAAAAA9ABIAAAAAAAAAAAA9ABMAAAAAAAAAAAA9ABQAAAAAAAAAAAA9ABUAAAAAAAAAAAA9ABYAAAAAAAAAAAA+AA8AAAAAAAAAAAA+ABAAAAAAAAAAAAA+ABEAAAAAAAAAAAA+ABIAAAAAAAAAAAA+ABMAAAAAAAAAAAA+ABQAAAAAAAAAAAA+ABUAAAAAAAAAAAA+ABYAAAAAAAAAAAA/AA8AAAAAAAAAAAA/ABAAAAAAAAAAAAA/ABEAAAAAAAAAAAA/ABIAAAAAAAAAAAA/ABMAAAAAAAAAAAA/ABQAAAAAAAAAAAA/ABUAAAAAAAAAAAA/ABYAAAAAAAAAAABAAA8AAAAAAAAAAABAABAAAAAAAAAAAABAABEAAAAAAAAAAABAABIAAAAAAAAAAABAABMAAAAAAAAAAABAABQAAAAAAAAAAABAABUAAAAAAAAAAABAABYAAAAAAAAAAABBAA8AAAAAAAAAAABBABAAAAAAAAAAAABBABEAAAAAAAAAAABBABIAAAAAAAAAAABBABMAAAAAAAAAAABBABQAAAAAAAAAAABBABUAAAAAAAAAAABBABYAAAAAAAAAAAA0AAkAAAABAAAAAAA0AAoAAAABAAAAAAA0AAsAAAABAAAAAAA0AAwAAAABAAAAAAA0AA0AAAABAAAAAAA0AA4AAAABAAAAAAA1AA4AAAABAAAAAAA2AA4AAAABAAAAAAA3AA4AAAABAAAAAAA4AA4AAAABAAAAAABJAAUAAAABAAAAAABJAAYAAAABAAAAAABJAAcAAAABAAAAAABJAAgAAAABAAAAAABJAAkAAAABAAAAAABJAAoAAAABAAAAAABJAAsAAAABAAAAAABJAAwAAAABAAAAAABJAA0AAAABAAAAAABMAAkAAAABAAAAAAA=") [node name="PlayerController" parent="." node_paths=PackedStringArray("auto_controlled_avatar") instance=ExtResource("2_j2xwq")] auto_controlled_avatar = NodePath("../Avatar") [node name="Avatar" parent="." instance=ExtResource("1_fdx6o")] -position = Vector2(218, 251) +position = Vector2(217, -276) collision_mask = 4 [node name="TriggerFallRock" parent="." instance=ExtResource("3_lvu1v")] diff --git a/_game/LevelDemonstration.tscn b/_game/LevelDemonstration.tscn index 524c5ad..ede40d8 100644 --- a/_game/LevelDemonstration.tscn +++ b/_game/LevelDemonstration.tscn @@ -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") diff --git a/_player/Avatar.tscn b/_player/Avatar.tscn index f141afb..693a0f4 100644 --- a/_player/Avatar.tscn +++ b/_player/Avatar.tscn @@ -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"] diff --git a/_player/states/character_state_machine.gd b/_player/states/character_state_machine.gd index 0d216af..98cacc8 100644 --- a/_player/states/character_state_machine.gd +++ b/_player/states/character_state_machine.gd @@ -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 diff --git a/_player/states/ghost.gd b/_player/states/ghost.gd index 67a2724..436d5d6 100644 --- a/_player/states/ghost.gd +++ b/_player/states/ghost.gd @@ -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: diff --git a/_player/states/grap_hook.gd b/_player/states/grap_hook.gd index d667ba2..c696d98 100644 --- a/_player/states/grap_hook.gd +++ b/_player/states/grap_hook.gd @@ -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() diff --git a/_player/states/hook_shooting.gd b/_player/states/hook_shooting.gd index a9c2e65..6ef9259 100644 --- a/_player/states/hook_shooting.gd +++ b/_player/states/hook_shooting.gd @@ -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 diff --git a/addons/phantom_camera/examples/example_scenes/2D-4.3/2d_limit_example_scene.tscn b/addons/phantom_camera/examples/example_scenes/2D-4.3/2d_limit_example_scene.tscn index 35538eb..798b549 100644 --- a/addons/phantom_camera/examples/example_scenes/2D-4.3/2d_limit_example_scene.tscn +++ b/addons/phantom_camera/examples/example_scenes/2D-4.3/2d_limit_example_scene.tscn @@ -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 diff --git a/addons/phantom_camera/examples/example_scenes/3D-4.4/3d_tweening_example_scene.tscn b/addons/phantom_camera/examples/example_scenes/3D-4.4/3d_tweening_example_scene.tscn index d596960..2470a7e 100644 --- a/addons/phantom_camera/examples/example_scenes/3D-4.4/3d_tweening_example_scene.tscn +++ b/addons/phantom_camera/examples/example_scenes/3D-4.4/3d_tweening_example_scene.tscn @@ -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="."] diff --git a/addons/phantom_camera/examples/scripts/2D/2d_room_limit_tween_4.3.gd b/addons/phantom_camera/examples/scripts/2D/2d_room_limit_tween_4.3.gd index 970c52e..8be7e44 100644 --- a/addons/phantom_camera/examples/scripts/2D/2d_room_limit_tween_4.3.gd +++ b/addons/phantom_camera/examples/scripts/2D/2d_room_limit_tween_4.3.gd @@ -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)) diff --git a/addons/phantom_camera/examples/scripts/2D/player_character_body_2d_4.3.gd b/addons/phantom_camera/examples/scripts/2D/player_character_body_2d_4.3.gd index 41ab5e2..796795a 100644 --- a/addons/phantom_camera/examples/scripts/2D/player_character_body_2d_4.3.gd +++ b/addons/phantom_camera/examples/scripts/2D/player_character_body_2d_4.3.gd @@ -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) diff --git a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres b/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres index c6439b5..64c4600 100644 --- a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres +++ b/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres @@ -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] diff --git a/addons/phantom_camera/plugin.cfg b/addons/phantom_camera/plugin.cfg index 6325827..a76a06f 100644 --- a/addons/phantom_camera/plugin.cfg +++ b/addons/phantom_camera/plugin.cfg @@ -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" diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd index 27e7eed..42e71a5 100644 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd +++ b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd @@ -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) diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd index 2caf713..622e7bf 100644 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd +++ b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd @@ -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)) diff --git a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs index 5005230..f0f6b96 100644 --- a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs +++ b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs @@ -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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs index 267adf0..7ee47a7 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs @@ -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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs index 20b3ba5..0173c2c 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs @@ -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(path).New().AsGodotObject()); - public static PhantomCamera2D FromScript(GDScript script) => new(script.New().AsGodotObject()); - public PhantomCamera2D(GodotObject phantomCameraNode) : base(phantomCameraNode) { - _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); - _callableDeadZoneReached = Callable.From((Vector2 side) => DeadZoneReached?.Invoke(side)); - _callableNoiseEmitted = Callable.From((Transform2D output) => NoiseEmitted?.Invoke(output)); + var callableTweenInterrupted = Callable.From(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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs index a3081d9..1e4cd07 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs @@ -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(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(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(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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs index 1e73b57..4444dad 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs @@ -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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs index 18620b2..2821f1f 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs @@ -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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd index 966c580..f38c531 100644 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd +++ b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd @@ -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") diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd index 2b4480c..6ffd061 100644 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd +++ b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd @@ -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" diff --git a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs index be211bd..7bd2a01 100644 --- a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs +++ b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs @@ -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(pCam => PCamBecameActive?.Invoke(pCam)); - _callablePCamBecameInactive = Callable.From(pCam => PCamBecameInactive?.Invoke(pCam)); + var callablePCamBecameActive = Callable.From(pCam => PCamBecameActive?.Invoke(pCam)); + var callablePCamBecameInactive = Callable.From(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"); } } diff --git a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd b/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd index 8c6308c..71b99cc 100644 --- a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd +++ b/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd @@ -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: diff --git a/addons/phantom_camera/scripts/resources/Camera3DResource.cs b/addons/phantom_camera/scripts/resources/Camera3DResource.cs index 7bd00b5..54ad098 100644 --- a/addons/phantom_camera/scripts/resources/Camera3DResource.cs +++ b/addons/phantom_camera/scripts/resources/Camera3DResource.cs @@ -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("uid://b8hhnqsugykly")); +#else + resource.SetScript(GD.Load("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"); } } diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs b/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs index 16b7273..f19d445 100644 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs +++ b/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs @@ -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("uid://dimvdouy8g0sv")); +#else + resource.SetScript(GD.Load("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"); } } diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs b/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs index 175a427..0c1a3ce 100644 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs +++ b/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs @@ -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("uid://cuffvge5ad4aa")); +#else + resource.SetScript(GD.Load("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"); } } diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs index 1c332b7..500e165 100644 --- a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs +++ b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs @@ -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("uid://8umksf8e80fw")); +#else + resource.SetScript(GD.Load("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"); } } diff --git a/addons/reedcomponent/component_base.gd b/addons/reedcomponent/component_base.gd index 8893dc5..4ce9991 100644 --- a/addons/reedcomponent/component_base.gd +++ b/addons/reedcomponent/component_base.gd @@ -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 diff --git a/addons/reedcomponent/grap_hook/Anchor.tscn b/addons/reedcomponent/grap_hook/Anchor.tscn new file mode 100644 index 0000000..8399312 --- /dev/null +++ b/addons/reedcomponent/grap_hook/Anchor.tscn @@ -0,0 +1,3 @@ +[gd_scene format=3 uid="uid://dfm5wy1rmci68"] + +[node name="Anchor" type="Node2D"] diff --git a/addons/reedcomponent/grap_hook/garpping_hook_v2.tscn b/addons/reedcomponent/grap_hook/garpping_hook_v2.tscn new file mode 100644 index 0000000..9a232d8 --- /dev/null +++ b/addons/reedcomponent/grap_hook/garpping_hook_v2.tscn @@ -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) diff --git a/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd b/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd new file mode 100644 index 0000000..ea968a3 --- /dev/null +++ b/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd @@ -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 diff --git a/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd.uid b/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd.uid new file mode 100644 index 0000000..bc55c44 --- /dev/null +++ b/addons/reedcomponent/grap_hook/garpping_hook_v_2.gd.uid @@ -0,0 +1 @@ +uid://bvxgviq7l64ck diff --git a/addons/reedcomponent/grap_hook/spawn_hook_component.gd b/addons/reedcomponent/grap_hook/spawn_hook_component.gd index ce5ce6f..29b43d8 100644 --- a/addons/reedcomponent/grap_hook/spawn_hook_component.gd +++ b/addons/reedcomponent/grap_hook/spawn_hook_component.gd @@ -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 diff --git a/addons/reedscene/act/Act.gd b/addons/reedscene/act/Act.gd new file mode 100644 index 0000000..1b04043 --- /dev/null +++ b/addons/reedscene/act/Act.gd @@ -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] = {} diff --git a/addons/reedscene/act/Act.gd.uid b/addons/reedscene/act/Act.gd.uid new file mode 100644 index 0000000..dab70ce --- /dev/null +++ b/addons/reedscene/act/Act.gd.uid @@ -0,0 +1 @@ +uid://fxpk2ot6otfh diff --git a/addons/reedscene/act/ActManager.gd b/addons/reedscene/act/ActManager.gd new file mode 100644 index 0000000..fd9b8ab --- /dev/null +++ b/addons/reedscene/act/ActManager.gd @@ -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 diff --git a/addons/reedscene/act/act_manager.gd.uid b/addons/reedscene/act/ActManager.gd.uid similarity index 100% rename from addons/reedscene/act/act_manager.gd.uid rename to addons/reedscene/act/ActManager.gd.uid diff --git a/addons/reedscene/act/ActManagerInspector.gd b/addons/reedscene/act/ActManagerInspector.gd new file mode 100644 index 0000000..5ee3235 --- /dev/null +++ b/addons/reedscene/act/ActManagerInspector.gd @@ -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 "") + + 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 diff --git a/addons/reedscene/act/ActManagerInspector.gd.uid b/addons/reedscene/act/ActManagerInspector.gd.uid new file mode 100644 index 0000000..e86c840 --- /dev/null +++ b/addons/reedscene/act/ActManagerInspector.gd.uid @@ -0,0 +1 @@ +uid://082tx8ee707t diff --git a/addons/reedscene/act/SingleAct.gd b/addons/reedscene/act/SingleAct.gd new file mode 100644 index 0000000..c9c760b --- /dev/null +++ b/addons/reedscene/act/SingleAct.gd @@ -0,0 +1,4 @@ +class_name SingleAct extends Resource + +@export var state_id: int = 0 +@export var use_trans: bool = false diff --git a/addons/reedscene/act/SingleAct.gd.uid b/addons/reedscene/act/SingleAct.gd.uid new file mode 100644 index 0000000..2ad1ada --- /dev/null +++ b/addons/reedscene/act/SingleAct.gd.uid @@ -0,0 +1 @@ +uid://baqgorvlumyju diff --git a/addons/reedscene/act/act_manager.gd b/addons/reedscene/act/act_manager.gd deleted file mode 100644 index e2da7e1..0000000 --- a/addons/reedscene/act/act_manager.gd +++ /dev/null @@ -1 +0,0 @@ -class_name ActManager extends Node diff --git a/addons/reedscene/act/icon/ActIcon.svg b/addons/reedscene/act/icon/ActIcon.svg new file mode 100644 index 0000000..e93047e --- /dev/null +++ b/addons/reedscene/act/icon/ActIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/reedscene/act/icon/ActIcon.svg.import b/addons/reedscene/act/icon/ActIcon.svg.import new file mode 100644 index 0000000..65a2131 --- /dev/null +++ b/addons/reedscene/act/icon/ActIcon.svg.import @@ -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 diff --git a/addons/reedscene/plugin.cfg b/addons/reedscene/plugin.cfg index 6ca7ff7..ff8d40d 100644 --- a/addons/reedscene/plugin.cfg +++ b/addons/reedscene/plugin.cfg @@ -1,7 +1,7 @@ [plugin] name="ReedScene" -description="" +description="这是一个用于2D游戏场景管理的插件,通过组件化的方式,将一个场景分成了场景中的各个组件和组件所对应的状态,用户可以通过切换状态,来实现一些动画的效果,也可以通过切换状态,来快速的事件存档,不同事件触发不同的结果的任务系统等,总之比较灵活" author="ReedZhu" -version="" +version=".1" script="reedscene.gd" diff --git a/addons/reedscene/prop/PropComponent.gd b/addons/reedscene/prop/PropComponent.gd index d818177..bc812dd 100644 --- a/addons/reedscene/prop/PropComponent.gd +++ b/addons/reedscene/prop/PropComponent.gd @@ -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") diff --git a/addons/reedscene/prop/PropManager.gd b/addons/reedscene/prop/PropManager.gd new file mode 100644 index 0000000..fe6db07 --- /dev/null +++ b/addons/reedscene/prop/PropManager.gd @@ -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 Warnings(Inspector 黄色警告) +# ===================================================== +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 diff --git a/addons/reedscene/prop/PropManager.gd.uid b/addons/reedscene/prop/PropManager.gd.uid new file mode 100644 index 0000000..e69de37 --- /dev/null +++ b/addons/reedscene/prop/PropManager.gd.uid @@ -0,0 +1 @@ +uid://pxjf5vst08eo diff --git a/addons/reedscene/prop/PropState.gd b/addons/reedscene/prop/PropState.gd index 0b813ab..c073be1 100644 --- a/addons/reedscene/prop/PropState.gd +++ b/addons/reedscene/prop/PropState.gd @@ -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 diff --git a/addons/reedscene/prop/StateManager.gd b/addons/reedscene/prop/StateManager.gd new file mode 100644 index 0000000..941b9ca --- /dev/null +++ b/addons/reedscene/prop/StateManager.gd @@ -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 diff --git a/addons/reedscene/prop/StateManager.gd.uid b/addons/reedscene/prop/StateManager.gd.uid new file mode 100644 index 0000000..7fd7039 --- /dev/null +++ b/addons/reedscene/prop/StateManager.gd.uid @@ -0,0 +1 @@ +uid://di41kt2tj34c2 diff --git a/addons/reedscene/prop/icon/prop_states_manager.svg b/addons/reedscene/prop/icon/prop_states_manager.svg new file mode 100644 index 0000000..a2efbf7 --- /dev/null +++ b/addons/reedscene/prop/icon/prop_states_manager.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/reedscene/prop/icon/prop_states_manager.svg.import b/addons/reedscene/prop/icon/prop_states_manager.svg.import new file mode 100644 index 0000000..cd11af5 --- /dev/null +++ b/addons/reedscene/prop/icon/prop_states_manager.svg.import @@ -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 diff --git a/addons/reedscene/reedscene.gd b/addons/reedscene/reedscene.gd index 5af87f6..ee49086 100644 --- a/addons/reedscene/reedscene.gd +++ b/addons/reedscene/reedscene.gd @@ -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) diff --git a/addons/reedscene/scene/ReedScene.gd b/addons/reedscene/scene/ReedScene.gd index 2ec6487..0a456d1 100644 --- a/addons/reedscene/scene/ReedScene.gd +++ b/addons/reedscene/scene/ReedScene.gd @@ -1,6 +1,5 @@ @tool @icon("uid://p0oxphym6oqg") - ''' 此类为一个level的最小单元,其可以承载任意多的prop和act 你可以这样理解,一个level(大关卡),带有n个小关卡,n = scene的数量 @@ -24,10 +23,29 @@ 对于Scene,Scene通过PropComponent上的ID,通过ID获取到PropComponent所挂载的组件,这也是为什么我强制要求 PropComponent必须是处于Prop的最上层子节点集。 + + Scene自身也有SceneID,SceneID是实现跨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() diff --git a/addons/reedscene/update.md b/addons/reedscene/update.md new file mode 100644 index 0000000..3ecb77a --- /dev/null +++ b/addons/reedscene/update.md @@ -0,0 +1,15 @@ +# ReedScene 插件更新列表 + +## 更新日志 + +V0.1 + +- 添加了基础的Act,Prop,Scene的概念。 + +## 更新计划 + +V0.1 + +主要的工作是搭建一个插件的基本架构 + +- [x] 角色墙滑 diff --git a/project.godot b/project.godot index 30899db..aa9da25 100644 --- a/project.godot +++ b/project.godot @@ -39,6 +39,7 @@ enabled=PackedStringArray("res://addons/phantom_camera/plugin.cfg", "res://addon ROOM="房间分组,其下存在所有的Room" PLAYER="玩家分组,其下只存在玩家控制器" +GRAPABLE="" [input]