From 436cc03ea329f9ed191468d7e06c3218d1b883ed Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Wed, 17 Jul 2024 00:00:43 -0600 Subject: [PATCH 1/8] WIP: Adding CSharp wrapper - Begin effort on PCam3D and some basic Godot extensions and PCam types --- .gitignore | 6 +- addons/phantom_camera/csharp/PCam3D.cs | 188 ++++++++++++++++++ addons/phantom_camera/csharp/PhantomCamera.cs | 35 ++++ 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 addons/phantom_camera/csharp/PCam3D.cs create mode 100644 addons/phantom_camera/csharp/PhantomCamera.cs diff --git a/.gitignore b/.gitignore index 95a3b9f6..644f7758 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,11 @@ export_presets.cfg .godot/ -# Docs specic ignores +# CSharp Dev +*.csproj +*.sln + +# Docs specific ignores #/docs/ node_modules cache diff --git a/addons/phantom_camera/csharp/PCam3D.cs b/addons/phantom_camera/csharp/PCam3D.cs new file mode 100644 index 00000000..d387cb47 --- /dev/null +++ b/addons/phantom_camera/csharp/PCam3D.cs @@ -0,0 +1,188 @@ +using Godot; + +namespace PhantomCamera; + +public class PCam3D +{ + public delegate void PCam3DEventHandler(); + public delegate void PCam3DTweenInterruptedEventHandler(Variant pCam3D); + + public event PCam3DEventHandler BecameActive; + public event PCam3DEventHandler BecameInactive; + public event PCam3DEventHandler FollowTargetChanged; + public event PCam3DEventHandler LookAtTargetChanged; + public event PCam3DEventHandler DeadZoneChanged; + public event PCam3DEventHandler TweenStarted; + public event PCam3DEventHandler IsTweening; + public event PCam3DEventHandler TweenCompleted; + public event PCam3DTweenInterruptedEventHandler TweenInterrupted; + + private readonly GodotObject _godotObject; + + private readonly Callable _callableBecameActive; + private readonly Callable _callableBecameInactive; + private readonly Callable _callableFollowTargetChanged; + private readonly Callable _callableLookAtTargetChanged; + private readonly Callable _callableDeadZoneChanged; + private readonly Callable _callableTweenStarted; + private readonly Callable _callableIsTweening; + private readonly Callable _callableTweenCompleted; + private readonly Callable _callableTweenInterrupted; + + public FollowMode FollowMode => _godotObject.Call(MethodName.GetFollowMode).As(); + + public LookAtMode LookAtMode => _godotObject.Call(MethodName.GetLookAtMode).As(); + + public bool IsActive => _godotObject.Call(MethodName.IsActive).As(); + + public int Priority + { + get => _godotObject.Call(MethodName.GetPriority).As(); + set => _godotObject.Call(MethodName.SetPriority, value); + } + + public Vector3 ThirdPersonRotation + { + get => _godotObject.Call(MethodName.GetThirdPersonRotation).As(); + set => _godotObject.Call(MethodName.SetThirdPersonRotation, value); + } + + public Vector3 ThirdPersonRotationDegrees + { + get => _godotObject.Call(MethodName.GetThirdPersonRotationDegrees).As(); + set => _godotObject.Call(MethodName.SetThirdPersonRotationDegrees, value); + } + + public Quaternion ThirdPersonQuaternion + { + get => _godotObject.Call(MethodName.GetThirdPersonQuaternion).As(); + set => _godotObject.Call(MethodName.SetThirdPersonQuaternion, value); + } + + public float SpringLength + { + get => _godotObject.Call(MethodName.GetSpringLength).As(); + set => _godotObject.Call(MethodName.SetSpringLength, value); + } + + public static PCam3D FromScript(string path) => GD.Load(path).AsPCam3D(); + + public PCam3D(GodotObject godotObject) + { + _godotObject = godotObject; + + _callableBecameActive = Callable.From(OnBecameActive); + _callableBecameInactive = Callable.From(OnBecameInactive); + _callableFollowTargetChanged = Callable.From(OnFollowTargetChanged); + _callableLookAtTargetChanged = Callable.From(OnLookAtTargetChanged); + _callableDeadZoneChanged = Callable.From(OnDeadZoneChanged); + _callableTweenStarted = Callable.From(OnTweenStarted); + _callableIsTweening = Callable.From(OnIsTweening); + _callableTweenCompleted = Callable.From(OnTweenCompleted); + _callableTweenInterrupted = Callable.From(OnTweenInterrupted); + + _godotObject.Connect(SignalName.BecameActive, _callableBecameActive); + _godotObject.Connect(SignalName.BecameInactive, _callableBecameInactive); + _godotObject.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + _godotObject.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + _godotObject.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + _godotObject.Connect(SignalName.TweenStarted, _callableTweenStarted); + _godotObject.Connect(SignalName.IsTweening, _callableIsTweening); + _godotObject.Connect(SignalName.TweenCompleted, _callableTweenCompleted); + _godotObject.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PCam3D() + { + _godotObject.Disconnect(SignalName.BecameActive, _callableBecameActive); + _godotObject.Disconnect(SignalName.BecameInactive, _callableBecameInactive); + _godotObject.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + _godotObject.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + _godotObject.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + _godotObject.Disconnect(SignalName.TweenStarted, _callableTweenStarted); + _godotObject.Disconnect(SignalName.IsTweening, _callableIsTweening); + _godotObject.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); + _godotObject.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + protected virtual void OnBecameActive() + { + BecameActive?.Invoke(); + } + + protected virtual void OnBecameInactive() + { + BecameInactive?.Invoke(); + } + + protected virtual void OnFollowTargetChanged() + { + FollowTargetChanged?.Invoke(); + } + + protected virtual void OnLookAtTargetChanged() + { + LookAtTargetChanged?.Invoke(); + } + + protected virtual void OnDeadZoneChanged() + { + DeadZoneChanged?.Invoke(); + } + + protected virtual void OnTweenStarted() + { + TweenStarted?.Invoke(); + } + + protected virtual void OnIsTweening() + { + IsTweening?.Invoke(); + } + + protected virtual void OnTweenInterrupted(Variant pCam3D) + { + TweenInterrupted?.Invoke(pCam3D); + } + + protected virtual void OnTweenCompleted() + { + TweenCompleted?.Invoke(); + } + + + public static class MethodName + { + public const string GetFollowMode = "get_follow_mode"; + public const string GetLookAtMode = "get_look_at_mode"; + public const string IsActive = "is_active"; + + public const string GetPriority = "get_priority"; + public const string SetPriority = "set_priority"; + + public const string GetThirdPersonRotation = "get_third_person_rotation"; + public const string SetThirdPersonRotation = "set_third_person_rotation"; + + public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; + public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; + + public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; + public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; + + public const string GetSpringLength = "get_spring_length"; + public const string SetSpringLength = "set_spring_length"; + } + + 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 LookAtTargetChanged = "look_at_target_changed"; + public const string DeadZoneChanged = "dead_zone_changed"; + public const string TweenStarted = "tween_started"; + public const string IsTweening = "is_tweening"; + public const string TweenInterrupted = "tween_interrupted"; + public const string TweenCompleted = "tween_completed"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/csharp/PhantomCamera.cs b/addons/phantom_camera/csharp/PhantomCamera.cs new file mode 100644 index 00000000..3ea3a960 --- /dev/null +++ b/addons/phantom_camera/csharp/PhantomCamera.cs @@ -0,0 +1,35 @@ +using Godot; + +namespace PhantomCamera; + +public static class GodotExtension +{ + public static PCam3D AsPCam3D(this GodotObject godotObject) + { + return new PCam3D(godotObject); + } + + public static PCam3D AsPCam3D(this GDScript godotScript) + { + return new PCam3D(godotScript.New().AsGodotObject()); + } +} + +public enum FollowMode +{ + None, + Glued, + Simple, + Group, + Path, + Framed, + ThirdPerson +} + +public enum LookAtMode +{ + None, + Mimic, + Simple, + Group +} \ No newline at end of file From b6493f0f2a83b015b40dc68637b735f08fe9b891 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Wed, 17 Jul 2024 23:55:42 -0600 Subject: [PATCH 2/8] WIP: rough first pass at PhantomCamera with 2D and 3D classes --- .gitignore | 1 + addons/phantom_camera/PhantomCamera.cs | 341 ++++++++++++++++++ addons/phantom_camera/csharp/PCam3D.cs | 188 ---------- addons/phantom_camera/csharp/PhantomCamera.cs | 35 -- dev_scenes/3d/dev_scene_csharp_3d.tscn | 83 +++++ dev_scenes/3d/scripts/DevPlayer.cs | 42 +++ dev_scenes/3d/scripts/DevSceneCSharp3D.cs | 13 + 7 files changed, 480 insertions(+), 223 deletions(-) create mode 100644 addons/phantom_camera/PhantomCamera.cs delete mode 100644 addons/phantom_camera/csharp/PCam3D.cs delete mode 100644 addons/phantom_camera/csharp/PhantomCamera.cs create mode 100644 dev_scenes/3d/dev_scene_csharp_3d.tscn create mode 100644 dev_scenes/3d/scripts/DevPlayer.cs create mode 100644 dev_scenes/3d/scripts/DevSceneCSharp3D.cs diff --git a/.gitignore b/.gitignore index 644f7758..e3f3cb15 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ export_presets.cfg # CSharp Dev *.csproj *.sln +*.DotSettings # Docs specific ignores #/docs/ diff --git a/addons/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/PhantomCamera.cs new file mode 100644 index 00000000..3dfe823c --- /dev/null +++ b/addons/phantom_camera/PhantomCamera.cs @@ -0,0 +1,341 @@ +using Godot; + +#nullable enable + +namespace PhantomCamera; + +public static class GodotExtension +{ + public static PhantomCamera3D AsPhantomCamera3D(this Node3D node3D) + { + return new PhantomCamera3D(node3D); + } + + public static PhantomCamera2D AsPhantomCamera2D(this Node2D node2D) + { + return new PhantomCamera2D(node2D); + } +} + +public enum FollowMode +{ + None, + Glued, + Simple, + Group, + Path, + Framed, + ThirdPerson +} + +public enum LookAtMode +{ + None, + Mimic, + Simple, + Group +} + +public enum InactiveUpdateMode +{ + Always, + Never +} + +public abstract class PhantomCamera +{ + protected GodotObject Pcam; + + public delegate void BecameActiveEventHandler(); + public delegate void BecameInactiveEventHandler(); + public delegate void FollowTargetChangedEventHandler(); + public delegate void LookAtTargetChangedEventHandler(); + public delegate void DeadZoneChangedEventHandler(); + public delegate void TweenStartedEventHandler(); + public delegate void IsTweeningEventHandler(); + public delegate void TweenCompletedEventHandler(); + + public event BecameActiveEventHandler? BecameActive; + public event BecameInactiveEventHandler? BecameInactive; + public event FollowTargetChangedEventHandler? FollowTargetChanged; + public event LookAtTargetChangedEventHandler? LookAtTargetChanged; + public event DeadZoneChangedEventHandler? DeadZoneChanged; + public event TweenStartedEventHandler? TweenStarted; + public event IsTweeningEventHandler? IsTweening; + public event TweenCompletedEventHandler? TweenCompleted; + + private readonly Callable _callableBecameActive; + private readonly Callable _callableBecameInactive; + private readonly Callable _callableFollowTargetChanged; + private readonly Callable _callableLookAtTargetChanged; + private readonly Callable _callableDeadZoneChanged; + private readonly Callable _callableTweenStarted; + private readonly Callable _callableIsTweening; + private readonly Callable _callableTweenCompleted; + + public int Priority + { + get => (int)Pcam.Call(MethodName.GetPriority); + set => Pcam.Call(MethodName.SetPriority, value); + } + + public FollowMode FollowMode => Pcam.Call(MethodName.GetFollowMode).As(); + + // TODO: Tween resource property (PhantomCameraTween type) + + public bool IsActive => (bool)Pcam.Call(MethodName.IsActive); + + public bool TweenOnLoad + { + get => (bool)Pcam.Call(MethodName.GetTweenOnLoad); + set => Pcam.Call(MethodName.SetTweenOnLoad, value); + } + + public InactiveUpdateMode InactiveUpdateMode + { + get => Pcam.Call(MethodName.GetInactiveUpdateMode).As(); + set => Pcam.Call(MethodName.SetInactiveUpdateMode, (int)value); + } + + protected PhantomCamera(GodotObject phantomCameraNode) + { + Pcam = phantomCameraNode; + + _callableBecameActive = Callable.From(() => BecameActive?.Invoke()); + _callableBecameInactive = Callable.From(() => BecameInactive?.Invoke()); + _callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke()); + _callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); + _callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke()); + _callableTweenStarted = Callable.From(() => TweenStarted?.Invoke()); + _callableIsTweening = Callable.From(() => IsTweening?.Invoke()); + _callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke()); + + Pcam.Connect(SignalName.BecameActive, _callableBecameActive); + Pcam.Connect(SignalName.BecameInactive, _callableBecameInactive); + Pcam.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + Pcam.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + Pcam.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + Pcam.Connect(SignalName.TweenStarted, _callableTweenStarted); + Pcam.Connect(SignalName.IsTweening, _callableIsTweening); + Pcam.Connect(SignalName.TweenCompleted, _callableTweenCompleted); + } + + ~PhantomCamera() + { + Pcam.Disconnect(SignalName.BecameActive, _callableBecameActive); + Pcam.Disconnect(SignalName.BecameInactive, _callableBecameInactive); + Pcam.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + Pcam.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + Pcam.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + Pcam.Disconnect(SignalName.TweenStarted, _callableTweenStarted); + Pcam.Disconnect(SignalName.IsTweening, _callableIsTweening); + Pcam.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); + } + + public static class MethodName + { + public const string GetFollowMode = "get_follow_mode"; + public const string IsActive = "is_active"; + + public const string GetPriority = "get_priority"; + public const string SetPriority = "set_priority"; + + public const string GetTweenOnLoad = "get_tween_on_load"; + public const string SetTweenOnLoad = "set_tween_on_load"; + + public const string GetInactiveUpdateMode = "get_inactive_update_mode"; + public const string SetInactiveUpdateMode = "set_inactive_update_mode"; + } + + 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 LookAtTargetChanged = "look_at_target_changed"; + public const string DeadZoneChanged = "dead_zone_changed"; + 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 class PhantomCamera2D : PhantomCamera +{ + public delegate void TweenInterruptedEventHandler(Node2D pCam); + + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableTweenInterrupted; + + public Vector2 Zoom + { + get => (Vector2)Pcam.Call(MethodName.GetZoom); + set => Pcam.Call(MethodName.SetZoom, value); + } + + public bool SnapToPixel + { + get => (bool)Pcam.Call(MethodName.GetSnapToPixel); + set => Pcam.Call(MethodName.SetSnapToPixel, value); + } + + public int LimitLeft + { + get => (int)Pcam.Call(MethodName.GetLimitLeft); + set => Pcam.Call(MethodName.SetLimitLeft, value); + } + + public int LimitTop + { + get => (int)Pcam.Call(MethodName.GetLimitTop); + set => Pcam.Call(MethodName.SetLimitTop, value); + } + + public int LimitRight + { + get => (int)Pcam.Call(MethodName.GetLimitRight); + set => Pcam.Call(MethodName.SetLimitRight, value); + } + + public int LimitBottom + { + get => (int)Pcam.Call(MethodName.GetLimitBottom); + set => Pcam.Call(MethodName.SetLimitBottom, value); + } + + public NodePath LimitTarget + { + get => (NodePath)Pcam.Call(MethodName.GetLimitTarget); + set => Pcam.Call(MethodName.SetLimitTarget, value); + } + + public Vector4I LimitMargin + { + get => (Vector4I)Pcam.Call(MethodName.GetLimitMargin); + set => Pcam.Call(MethodName.SetLimitMargin, 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)); + Pcam.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera2D() + { + Pcam.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public void SetLimit(Side side, int value) + { + Pcam.Call(MethodName.SetLimit, (int)side, value); + } + + public int GetLimit(Side side) + { + return (int)Pcam.Call(MethodName.GetLimit, (int)side); + } + + public new static class MethodName + { + public const string GetZoom = "get_zoom"; + public const string SetZoom = "set_zoom"; + + public const string GetSnapToPixel = "get_snap_to_pixel"; + public const string SetSnapToPixel = "set_snap_to_pixel"; + + public const string GetLimit = "get_limit"; + public const string SetLimit = "set_limit"; + + public const string GetLimitLeft = "get_limit_left"; + public const string SetLimitLeft = "set_limit_left"; + + public const string GetLimitTop = "get_limit_top"; + public const string SetLimitTop = "set_limit_top"; + + public const string GetLimitRight = "get_limit_right"; + public const string SetLimitRight = "set_limit_right"; + + public const string GetLimitBottom = "get_limit_bottom"; + public const string SetLimitBottom = "set_limit_bottom"; + + public const string GetLimitTarget = "get_limit_target"; + public const string SetLimitTarget = "set_limit_target"; + + public const string GetLimitMargin = "get_limit_margin"; + public const string SetLimitMargin = "set_limit_margin"; + } +} + +public class PhantomCamera3D : PhantomCamera +{ + public delegate void TweenInterruptedEventHandler(Node3D pCam); + + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableTweenInterrupted; + + public new LookAtMode LookAtMode => Pcam.Call(MethodName.GetLookAtMode).As(); + + // TODO: Camera3DResource + + public Vector3 ThirdPersonRotation + { + get => Pcam.Call(MethodName.GetThirdPersonRotation).As(); + set => Pcam.Call(MethodName.SetThirdPersonRotation, value); + } + + public Vector3 ThirdPersonRotationDegrees + { + get => Pcam.Call(MethodName.GetThirdPersonRotationDegrees).As(); + set => Pcam.Call(MethodName.SetThirdPersonRotationDegrees, value); + } + + public Quaternion ThirdPersonQuaternion + { + get => Pcam.Call(MethodName.GetThirdPersonQuaternion).As(); + set => Pcam.Call(MethodName.SetThirdPersonQuaternion, value); + } + + public float SpringLength + { + get => Pcam.Call(MethodName.GetSpringLength).As(); + set => Pcam.Call(MethodName.SetSpringLength, value); + } + + 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) + { + _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); + Pcam.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera3D() + { + Pcam.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public new static class MethodName + { + public const string GetLookAtMode = "get_look_at_mode"; + + public const string GetThirdPersonRotation = "get_third_person_rotation"; + public const string SetThirdPersonRotation = "set_third_person_rotation"; + + public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; + public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; + + public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; + public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; + + public const string GetSpringLength = "get_spring_length"; + public const string SetSpringLength = "set_spring_length"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/csharp/PCam3D.cs b/addons/phantom_camera/csharp/PCam3D.cs deleted file mode 100644 index d387cb47..00000000 --- a/addons/phantom_camera/csharp/PCam3D.cs +++ /dev/null @@ -1,188 +0,0 @@ -using Godot; - -namespace PhantomCamera; - -public class PCam3D -{ - public delegate void PCam3DEventHandler(); - public delegate void PCam3DTweenInterruptedEventHandler(Variant pCam3D); - - public event PCam3DEventHandler BecameActive; - public event PCam3DEventHandler BecameInactive; - public event PCam3DEventHandler FollowTargetChanged; - public event PCam3DEventHandler LookAtTargetChanged; - public event PCam3DEventHandler DeadZoneChanged; - public event PCam3DEventHandler TweenStarted; - public event PCam3DEventHandler IsTweening; - public event PCam3DEventHandler TweenCompleted; - public event PCam3DTweenInterruptedEventHandler TweenInterrupted; - - private readonly GodotObject _godotObject; - - private readonly Callable _callableBecameActive; - private readonly Callable _callableBecameInactive; - private readonly Callable _callableFollowTargetChanged; - private readonly Callable _callableLookAtTargetChanged; - private readonly Callable _callableDeadZoneChanged; - private readonly Callable _callableTweenStarted; - private readonly Callable _callableIsTweening; - private readonly Callable _callableTweenCompleted; - private readonly Callable _callableTweenInterrupted; - - public FollowMode FollowMode => _godotObject.Call(MethodName.GetFollowMode).As(); - - public LookAtMode LookAtMode => _godotObject.Call(MethodName.GetLookAtMode).As(); - - public bool IsActive => _godotObject.Call(MethodName.IsActive).As(); - - public int Priority - { - get => _godotObject.Call(MethodName.GetPriority).As(); - set => _godotObject.Call(MethodName.SetPriority, value); - } - - public Vector3 ThirdPersonRotation - { - get => _godotObject.Call(MethodName.GetThirdPersonRotation).As(); - set => _godotObject.Call(MethodName.SetThirdPersonRotation, value); - } - - public Vector3 ThirdPersonRotationDegrees - { - get => _godotObject.Call(MethodName.GetThirdPersonRotationDegrees).As(); - set => _godotObject.Call(MethodName.SetThirdPersonRotationDegrees, value); - } - - public Quaternion ThirdPersonQuaternion - { - get => _godotObject.Call(MethodName.GetThirdPersonQuaternion).As(); - set => _godotObject.Call(MethodName.SetThirdPersonQuaternion, value); - } - - public float SpringLength - { - get => _godotObject.Call(MethodName.GetSpringLength).As(); - set => _godotObject.Call(MethodName.SetSpringLength, value); - } - - public static PCam3D FromScript(string path) => GD.Load(path).AsPCam3D(); - - public PCam3D(GodotObject godotObject) - { - _godotObject = godotObject; - - _callableBecameActive = Callable.From(OnBecameActive); - _callableBecameInactive = Callable.From(OnBecameInactive); - _callableFollowTargetChanged = Callable.From(OnFollowTargetChanged); - _callableLookAtTargetChanged = Callable.From(OnLookAtTargetChanged); - _callableDeadZoneChanged = Callable.From(OnDeadZoneChanged); - _callableTweenStarted = Callable.From(OnTweenStarted); - _callableIsTweening = Callable.From(OnIsTweening); - _callableTweenCompleted = Callable.From(OnTweenCompleted); - _callableTweenInterrupted = Callable.From(OnTweenInterrupted); - - _godotObject.Connect(SignalName.BecameActive, _callableBecameActive); - _godotObject.Connect(SignalName.BecameInactive, _callableBecameInactive); - _godotObject.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - _godotObject.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - _godotObject.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); - _godotObject.Connect(SignalName.TweenStarted, _callableTweenStarted); - _godotObject.Connect(SignalName.IsTweening, _callableIsTweening); - _godotObject.Connect(SignalName.TweenCompleted, _callableTweenCompleted); - _godotObject.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - ~PCam3D() - { - _godotObject.Disconnect(SignalName.BecameActive, _callableBecameActive); - _godotObject.Disconnect(SignalName.BecameInactive, _callableBecameInactive); - _godotObject.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - _godotObject.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - _godotObject.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); - _godotObject.Disconnect(SignalName.TweenStarted, _callableTweenStarted); - _godotObject.Disconnect(SignalName.IsTweening, _callableIsTweening); - _godotObject.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); - _godotObject.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - protected virtual void OnBecameActive() - { - BecameActive?.Invoke(); - } - - protected virtual void OnBecameInactive() - { - BecameInactive?.Invoke(); - } - - protected virtual void OnFollowTargetChanged() - { - FollowTargetChanged?.Invoke(); - } - - protected virtual void OnLookAtTargetChanged() - { - LookAtTargetChanged?.Invoke(); - } - - protected virtual void OnDeadZoneChanged() - { - DeadZoneChanged?.Invoke(); - } - - protected virtual void OnTweenStarted() - { - TweenStarted?.Invoke(); - } - - protected virtual void OnIsTweening() - { - IsTweening?.Invoke(); - } - - protected virtual void OnTweenInterrupted(Variant pCam3D) - { - TweenInterrupted?.Invoke(pCam3D); - } - - protected virtual void OnTweenCompleted() - { - TweenCompleted?.Invoke(); - } - - - public static class MethodName - { - public const string GetFollowMode = "get_follow_mode"; - public const string GetLookAtMode = "get_look_at_mode"; - public const string IsActive = "is_active"; - - public const string GetPriority = "get_priority"; - public const string SetPriority = "set_priority"; - - public const string GetThirdPersonRotation = "get_third_person_rotation"; - public const string SetThirdPersonRotation = "set_third_person_rotation"; - - public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; - public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; - - public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; - public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; - - public const string GetSpringLength = "get_spring_length"; - public const string SetSpringLength = "set_spring_length"; - } - - 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 LookAtTargetChanged = "look_at_target_changed"; - public const string DeadZoneChanged = "dead_zone_changed"; - public const string TweenStarted = "tween_started"; - public const string IsTweening = "is_tweening"; - public const string TweenInterrupted = "tween_interrupted"; - public const string TweenCompleted = "tween_completed"; - } -} \ No newline at end of file diff --git a/addons/phantom_camera/csharp/PhantomCamera.cs b/addons/phantom_camera/csharp/PhantomCamera.cs deleted file mode 100644 index 3ea3a960..00000000 --- a/addons/phantom_camera/csharp/PhantomCamera.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Godot; - -namespace PhantomCamera; - -public static class GodotExtension -{ - public static PCam3D AsPCam3D(this GodotObject godotObject) - { - return new PCam3D(godotObject); - } - - public static PCam3D AsPCam3D(this GDScript godotScript) - { - return new PCam3D(godotScript.New().AsGodotObject()); - } -} - -public enum FollowMode -{ - None, - Glued, - Simple, - Group, - Path, - Framed, - ThirdPerson -} - -public enum LookAtMode -{ - None, - Mimic, - Simple, - Group -} \ No newline at end of file diff --git a/dev_scenes/3d/dev_scene_csharp_3d.tscn b/dev_scenes/3d/dev_scene_csharp_3d.tscn new file mode 100644 index 00000000..dab817ba --- /dev/null +++ b/dev_scenes/3d/dev_scene_csharp_3d.tscn @@ -0,0 +1,83 @@ +[gd_scene load_steps=14 format=3 uid="uid://brb8fl27ofqhv"] + +[ext_resource type="Script" path="res://dev_scenes/3d/scripts/DevSceneCSharp3D.cs" id="1_fd2f4"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="2_m8s1w"] +[ext_resource type="Script" path="res://dev_scenes/3d/scripts/DevPlayer.cs" id="3_45c2y"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="3_xni2u"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="4_c7bav"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/camera_3d_resource.gd" id="5_jea0a"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_vuocs"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_evu1o"] +sky_material = SubResource("ProceduralSkyMaterial_vuocs") + +[sub_resource type="Environment" id="Environment_5sile"] +background_mode = 2 +sky = SubResource("Sky_evu1o") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_vcumb"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_04sci"] + +[sub_resource type="Resource" id="Resource_nsh7j"] +script = ExtResource("4_c7bav") +duration = 1.0 +transition = 0 +ease = 2 + +[sub_resource type="Resource" id="Resource_ujhhy"] +script = ExtResource("5_jea0a") +cull_mask = 1048575 +h_offset = 0.0 +v_offset = 0.0 +projection = 0 +fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 + +[node name="Node3D" type="Node3D"] +script = ExtResource("1_fd2f4") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_5sile") + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.25252, 4) + +[node name="PhantomCameraHost" type="Node" parent="Camera3D"] +script = ExtResource("2_m8s1w") + +[node name="CSGBox3D" type="CSGBox3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.514567, 0) +use_collision = true +size = Vector3(10, 1, 10) + +[node name="Player" type="CharacterBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25252, 0) +script = ExtResource("3_45c2y") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Player"] +mesh = SubResource("CapsuleMesh_vcumb") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"] +shape = SubResource("CapsuleShape3D_04sci") + +[node name="PlayerCam" type="Node3D" parent="Player" node_paths=PackedStringArray("follow_target")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 4) +script = ExtResource("3_xni2u") +follow_mode = 6 +follow_target = NodePath("..") +tween_resource = SubResource("Resource_nsh7j") +camera_3d_resource = SubResource("Resource_ujhhy") +follow_offset = Vector3(0, 1, 3) diff --git a/dev_scenes/3d/scripts/DevPlayer.cs b/dev_scenes/3d/scripts/DevPlayer.cs new file mode 100644 index 00000000..db6246bd --- /dev/null +++ b/dev_scenes/3d/scripts/DevPlayer.cs @@ -0,0 +1,42 @@ +using Godot; +using System; + +public partial class DevPlayer : CharacterBody3D +{ + public const float Speed = 5.0f; + public const float JumpVelocity = 4.5f; + + // Get the gravity from the project settings to be synced with RigidBody nodes. + public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle(); + + public override void _PhysicsProcess(double delta) + { + Vector3 velocity = Velocity; + + // Add the gravity. + if (!IsOnFloor()) + velocity.Y -= gravity * (float)delta; + + // Handle Jump. + if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) + velocity.Y = JumpVelocity; + + // Get the input direction and handle the movement/deceleration. + // As good practice, you should replace UI actions with custom gameplay actions. + Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); + Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized(); + if (direction != Vector3.Zero) + { + velocity.X = direction.X * Speed; + velocity.Z = direction.Z * Speed; + } + else + { + velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed); + velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed); + } + + Velocity = velocity; + MoveAndSlide(); + } +} diff --git a/dev_scenes/3d/scripts/DevSceneCSharp3D.cs b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs new file mode 100644 index 00000000..b9228398 --- /dev/null +++ b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs @@ -0,0 +1,13 @@ +using Godot; +using PhantomCamera; + +public partial class DevSceneCSharp3D : Node3D +{ + public override void _Ready() + { + var pCamNode = GetNode("Player/PlayerCam"); + var pCam = pCamNode.AsPhantomCamera3D(); + + GD.Print(pCam); + } +} From 70f5b66c9d5e2f6a6c33fe15e5d708e62a5bbeb5 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Fri, 19 Jul 2024 18:35:19 -0600 Subject: [PATCH 3/8] (#286) Finished the C# API --- addons/phantom_camera/PhantomCamera.cs | 341 ---------- .../example_scenes/3D/3d_example_scene.tscn | 25 + .../3D/checker_pattern_dark.png.import | 5 +- .../icons/phantom_camera_gizmo.svg.import | 5 +- .../phantom_camera/scripts/PhantomCamera.cs | 633 ++++++++++++++++++ dev_scenes/3d/dev_scene_3d.tscn | 10 + dev_scenes/3d/dev_scene_csharp_3d.tscn | 4 +- dev_scenes/3d/scripts/DevPlayer.cs | 42 -- dev_scenes/3d/scripts/DevSceneCSharp3D.cs | 5 +- 9 files changed, 675 insertions(+), 395 deletions(-) delete mode 100644 addons/phantom_camera/PhantomCamera.cs create mode 100644 addons/phantom_camera/scripts/PhantomCamera.cs delete mode 100644 dev_scenes/3d/scripts/DevPlayer.cs diff --git a/addons/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/PhantomCamera.cs deleted file mode 100644 index 3dfe823c..00000000 --- a/addons/phantom_camera/PhantomCamera.cs +++ /dev/null @@ -1,341 +0,0 @@ -using Godot; - -#nullable enable - -namespace PhantomCamera; - -public static class GodotExtension -{ - public static PhantomCamera3D AsPhantomCamera3D(this Node3D node3D) - { - return new PhantomCamera3D(node3D); - } - - public static PhantomCamera2D AsPhantomCamera2D(this Node2D node2D) - { - return new PhantomCamera2D(node2D); - } -} - -public enum FollowMode -{ - None, - Glued, - Simple, - Group, - Path, - Framed, - ThirdPerson -} - -public enum LookAtMode -{ - None, - Mimic, - Simple, - Group -} - -public enum InactiveUpdateMode -{ - Always, - Never -} - -public abstract class PhantomCamera -{ - protected GodotObject Pcam; - - public delegate void BecameActiveEventHandler(); - public delegate void BecameInactiveEventHandler(); - public delegate void FollowTargetChangedEventHandler(); - public delegate void LookAtTargetChangedEventHandler(); - public delegate void DeadZoneChangedEventHandler(); - public delegate void TweenStartedEventHandler(); - public delegate void IsTweeningEventHandler(); - public delegate void TweenCompletedEventHandler(); - - public event BecameActiveEventHandler? BecameActive; - public event BecameInactiveEventHandler? BecameInactive; - public event FollowTargetChangedEventHandler? FollowTargetChanged; - public event LookAtTargetChangedEventHandler? LookAtTargetChanged; - public event DeadZoneChangedEventHandler? DeadZoneChanged; - public event TweenStartedEventHandler? TweenStarted; - public event IsTweeningEventHandler? IsTweening; - public event TweenCompletedEventHandler? TweenCompleted; - - private readonly Callable _callableBecameActive; - private readonly Callable _callableBecameInactive; - private readonly Callable _callableFollowTargetChanged; - private readonly Callable _callableLookAtTargetChanged; - private readonly Callable _callableDeadZoneChanged; - private readonly Callable _callableTweenStarted; - private readonly Callable _callableIsTweening; - private readonly Callable _callableTweenCompleted; - - public int Priority - { - get => (int)Pcam.Call(MethodName.GetPriority); - set => Pcam.Call(MethodName.SetPriority, value); - } - - public FollowMode FollowMode => Pcam.Call(MethodName.GetFollowMode).As(); - - // TODO: Tween resource property (PhantomCameraTween type) - - public bool IsActive => (bool)Pcam.Call(MethodName.IsActive); - - public bool TweenOnLoad - { - get => (bool)Pcam.Call(MethodName.GetTweenOnLoad); - set => Pcam.Call(MethodName.SetTweenOnLoad, value); - } - - public InactiveUpdateMode InactiveUpdateMode - { - get => Pcam.Call(MethodName.GetInactiveUpdateMode).As(); - set => Pcam.Call(MethodName.SetInactiveUpdateMode, (int)value); - } - - protected PhantomCamera(GodotObject phantomCameraNode) - { - Pcam = phantomCameraNode; - - _callableBecameActive = Callable.From(() => BecameActive?.Invoke()); - _callableBecameInactive = Callable.From(() => BecameInactive?.Invoke()); - _callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke()); - _callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); - _callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke()); - _callableTweenStarted = Callable.From(() => TweenStarted?.Invoke()); - _callableIsTweening = Callable.From(() => IsTweening?.Invoke()); - _callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke()); - - Pcam.Connect(SignalName.BecameActive, _callableBecameActive); - Pcam.Connect(SignalName.BecameInactive, _callableBecameInactive); - Pcam.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - Pcam.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - Pcam.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); - Pcam.Connect(SignalName.TweenStarted, _callableTweenStarted); - Pcam.Connect(SignalName.IsTweening, _callableIsTweening); - Pcam.Connect(SignalName.TweenCompleted, _callableTweenCompleted); - } - - ~PhantomCamera() - { - Pcam.Disconnect(SignalName.BecameActive, _callableBecameActive); - Pcam.Disconnect(SignalName.BecameInactive, _callableBecameInactive); - Pcam.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - Pcam.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - Pcam.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); - Pcam.Disconnect(SignalName.TweenStarted, _callableTweenStarted); - Pcam.Disconnect(SignalName.IsTweening, _callableIsTweening); - Pcam.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); - } - - public static class MethodName - { - public const string GetFollowMode = "get_follow_mode"; - public const string IsActive = "is_active"; - - public const string GetPriority = "get_priority"; - public const string SetPriority = "set_priority"; - - public const string GetTweenOnLoad = "get_tween_on_load"; - public const string SetTweenOnLoad = "set_tween_on_load"; - - public const string GetInactiveUpdateMode = "get_inactive_update_mode"; - public const string SetInactiveUpdateMode = "set_inactive_update_mode"; - } - - 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 LookAtTargetChanged = "look_at_target_changed"; - public const string DeadZoneChanged = "dead_zone_changed"; - 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 class PhantomCamera2D : PhantomCamera -{ - public delegate void TweenInterruptedEventHandler(Node2D pCam); - - public event TweenInterruptedEventHandler? TweenInterrupted; - - private readonly Callable _callableTweenInterrupted; - - public Vector2 Zoom - { - get => (Vector2)Pcam.Call(MethodName.GetZoom); - set => Pcam.Call(MethodName.SetZoom, value); - } - - public bool SnapToPixel - { - get => (bool)Pcam.Call(MethodName.GetSnapToPixel); - set => Pcam.Call(MethodName.SetSnapToPixel, value); - } - - public int LimitLeft - { - get => (int)Pcam.Call(MethodName.GetLimitLeft); - set => Pcam.Call(MethodName.SetLimitLeft, value); - } - - public int LimitTop - { - get => (int)Pcam.Call(MethodName.GetLimitTop); - set => Pcam.Call(MethodName.SetLimitTop, value); - } - - public int LimitRight - { - get => (int)Pcam.Call(MethodName.GetLimitRight); - set => Pcam.Call(MethodName.SetLimitRight, value); - } - - public int LimitBottom - { - get => (int)Pcam.Call(MethodName.GetLimitBottom); - set => Pcam.Call(MethodName.SetLimitBottom, value); - } - - public NodePath LimitTarget - { - get => (NodePath)Pcam.Call(MethodName.GetLimitTarget); - set => Pcam.Call(MethodName.SetLimitTarget, value); - } - - public Vector4I LimitMargin - { - get => (Vector4I)Pcam.Call(MethodName.GetLimitMargin); - set => Pcam.Call(MethodName.SetLimitMargin, 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)); - Pcam.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - ~PhantomCamera2D() - { - Pcam.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - public void SetLimit(Side side, int value) - { - Pcam.Call(MethodName.SetLimit, (int)side, value); - } - - public int GetLimit(Side side) - { - return (int)Pcam.Call(MethodName.GetLimit, (int)side); - } - - public new static class MethodName - { - public const string GetZoom = "get_zoom"; - public const string SetZoom = "set_zoom"; - - public const string GetSnapToPixel = "get_snap_to_pixel"; - public const string SetSnapToPixel = "set_snap_to_pixel"; - - public const string GetLimit = "get_limit"; - public const string SetLimit = "set_limit"; - - public const string GetLimitLeft = "get_limit_left"; - public const string SetLimitLeft = "set_limit_left"; - - public const string GetLimitTop = "get_limit_top"; - public const string SetLimitTop = "set_limit_top"; - - public const string GetLimitRight = "get_limit_right"; - public const string SetLimitRight = "set_limit_right"; - - public const string GetLimitBottom = "get_limit_bottom"; - public const string SetLimitBottom = "set_limit_bottom"; - - public const string GetLimitTarget = "get_limit_target"; - public const string SetLimitTarget = "set_limit_target"; - - public const string GetLimitMargin = "get_limit_margin"; - public const string SetLimitMargin = "set_limit_margin"; - } -} - -public class PhantomCamera3D : PhantomCamera -{ - public delegate void TweenInterruptedEventHandler(Node3D pCam); - - public event TweenInterruptedEventHandler? TweenInterrupted; - - private readonly Callable _callableTweenInterrupted; - - public new LookAtMode LookAtMode => Pcam.Call(MethodName.GetLookAtMode).As(); - - // TODO: Camera3DResource - - public Vector3 ThirdPersonRotation - { - get => Pcam.Call(MethodName.GetThirdPersonRotation).As(); - set => Pcam.Call(MethodName.SetThirdPersonRotation, value); - } - - public Vector3 ThirdPersonRotationDegrees - { - get => Pcam.Call(MethodName.GetThirdPersonRotationDegrees).As(); - set => Pcam.Call(MethodName.SetThirdPersonRotationDegrees, value); - } - - public Quaternion ThirdPersonQuaternion - { - get => Pcam.Call(MethodName.GetThirdPersonQuaternion).As(); - set => Pcam.Call(MethodName.SetThirdPersonQuaternion, value); - } - - public float SpringLength - { - get => Pcam.Call(MethodName.GetSpringLength).As(); - set => Pcam.Call(MethodName.SetSpringLength, value); - } - - 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) - { - _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); - Pcam.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - ~PhantomCamera3D() - { - Pcam.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - public new static class MethodName - { - public const string GetLookAtMode = "get_look_at_mode"; - - public const string GetThirdPersonRotation = "get_third_person_rotation"; - public const string SetThirdPersonRotation = "set_third_person_rotation"; - - public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; - public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; - - public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; - public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; - - public const string GetSpringLength = "get_spring_length"; - public const string SetSpringLength = "set_spring_length"; - } -} \ No newline at end of file diff --git a/addons/phantom_camera/examples/example_scenes/3D/3d_example_scene.tscn b/addons/phantom_camera/examples/example_scenes/3D/3d_example_scene.tscn index 553ddebd..33fcb3ad 100644 --- a/addons/phantom_camera/examples/example_scenes/3D/3d_example_scene.tscn +++ b/addons/phantom_camera/examples/example_scenes/3D/3d_example_scene.tscn @@ -16,14 +16,24 @@ script = ExtResource("4_m2vbn") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="Resource" id="Resource_o161n"] script = ExtResource("4_m2vbn") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="BoxMesh" id="BoxMesh_7tjw4"] size = Vector3(2, 0.5, 4) @@ -53,7 +63,12 @@ script = ExtResource("4_m2vbn") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="BoxShape3D" id="BoxShape3D_wcrbb"] size = Vector3(6.8, 0.1, 5.4) @@ -69,7 +84,12 @@ script = ExtResource("4_m2vbn") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="BoxShape3D" id="BoxShape3D_ctyr8"] size = Vector3(7.4, 0.1, 3.6) @@ -85,7 +105,12 @@ script = ExtResource("4_m2vbn") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="BoxShape3D" id="BoxShape3D_ua072"] size = Vector3(6.8, 0.1, 3.6) diff --git a/addons/phantom_camera/examples/textures/3D/checker_pattern_dark.png.import b/addons/phantom_camera/examples/textures/3D/checker_pattern_dark.png.import index 98915e8d..cfcc34e5 100644 --- a/addons/phantom_camera/examples/textures/3D/checker_pattern_dark.png.import +++ b/addons/phantom_camera/examples/textures/3D/checker_pattern_dark.png.import @@ -4,16 +4,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://c7ja4woxol8yc" path.bptc="res://.godot/imported/checker_pattern_dark.png-70cedad2d3abf4ad6166d6614eefa7fb.bptc.ctex" -path.astc="res://.godot/imported/checker_pattern_dark.png-70cedad2d3abf4ad6166d6614eefa7fb.astc.ctex" metadata={ -"imported_formats": ["s3tc_bptc", "etc2_astc"], +"imported_formats": ["s3tc_bptc"], "vram_texture": true } [deps] source_file="res://addons/phantom_camera/examples/textures/3D/checker_pattern_dark.png" -dest_files=["res://.godot/imported/checker_pattern_dark.png-70cedad2d3abf4ad6166d6614eefa7fb.bptc.ctex", "res://.godot/imported/checker_pattern_dark.png-70cedad2d3abf4ad6166d6614eefa7fb.astc.ctex"] +dest_files=["res://.godot/imported/checker_pattern_dark.png-70cedad2d3abf4ad6166d6614eefa7fb.bptc.ctex"] [params] diff --git a/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import b/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import index e89d99c5..7b49608d 100644 --- a/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import +++ b/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import @@ -4,16 +4,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://e36npe2rbxyg" path.s3tc="res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.s3tc.ctex" -path.etc2="res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.etc2.ctex" metadata={ -"imported_formats": ["s3tc_bptc", "etc2_astc"], +"imported_formats": ["s3tc_bptc"], "vram_texture": true } [deps] source_file="res://addons/phantom_camera/icons/phantom_camera_gizmo.svg" -dest_files=["res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.s3tc.ctex", "res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.etc2.ctex"] +dest_files=["res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.s3tc.ctex"] [params] diff --git a/addons/phantom_camera/scripts/PhantomCamera.cs b/addons/phantom_camera/scripts/PhantomCamera.cs new file mode 100644 index 00000000..e2401469 --- /dev/null +++ b/addons/phantom_camera/scripts/PhantomCamera.cs @@ -0,0 +1,633 @@ +using System.Linq; +using Godot; + +#nullable enable + +namespace PhantomCamera; + +public enum FollowMode +{ + None, + Glued, + Simple, + Group, + Path, + Framed, + ThirdPerson +} + +public enum LookAtMode +{ + None, + Mimic, + Simple, + Group +} + +public enum InactiveUpdateMode +{ + Always, + Never +} + +public enum TransitionType +{ + Linear, + Sine, + Quintic, + Quartic, + Quadratic, + Exponential, + Elastic, + Cubic, + Circ, + Bounce, + Back +} + +public enum EaseType +{ + In, + Out, + InOut, + OutIn +} + +public enum ProjectionType +{ + Perspective, + Orthogonal, + Frustum +} + +// TODO: For Godot 4.3 +// public enum InterpolationMode +// { +// Auto, +// Idle, +// Physics +// } + +public static class PhantomCameraExtension +{ + public static PhantomCamera3D AsPhantomCamera3D(this Node3D node3D) + { + return new PhantomCamera3D(node3D); + } + + public static PhantomCamera2D AsPhantomCamera2D(this Node2D node2D) + { + return new PhantomCamera2D(node2D); + } +} + +public class PhantomCameraTween +{ + public Resource Resource { get; } + + public float Duration + { + get => (float)Resource.Call(MethodName.GetTweenDuration); + set => Resource.Call(MethodName.SetTweenDuration, value); + } + + public TransitionType Transition + { + get => (TransitionType)(int)Resource.Call(MethodName.GetTweenTransition); + set => Resource.Call(MethodName.SetTweenTransition, (int)value); + } + + public EaseType Ease + { + get => (EaseType)(int)Resource.Call(MethodName.GetTweenEase); + set => Resource.Call(MethodName.SetTweenEase, (int)value); + } + + public PhantomCameraTween(Resource tweenResource) => Resource = tweenResource; + + public static class MethodName + { + public const string GetTweenDuration = "get_tween_durartion"; + public const string SetTweenDuration = "set_tween_durartion"; + + public const string GetTweenTransition = "get_tween_transition"; + public const string SetTweenTransition = "set_tween_transition"; + + public const string GetTweenEase = "get_tween_ease"; + public const string SetTweenEase = "set_tween_ease"; + } +} + +public class Camera3DResource +{ + public readonly Resource Resource; + + public const float MinOffset = 0; + public const float MaxOffset = 1; + + public const float MinFov = 1; + public const float MaxFov = 179; + + public const float MinSize = 0.001f; + public const float MaxSize = 100; + + public const float MinNear = 0.001f; + public const float MaxNear = 10; + + public const float MinFar = 0.01f; + public const float MaxFar = 4000; + + public int CullMask + { + get => (int)Resource.Call(MethodName.GetCullMask); + set => Resource.Call(MethodName.SetCullMask, value); + } + + public float HOffset + { + get => (float)Resource.Call(MethodName.GetHOffset); + set => Resource.Call(MethodName.SetHOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); + } + + public float VOffset + { + get => (float)Resource.Call(MethodName.GetVOffset); + set => Resource.Call(MethodName.SetVOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); + } + + public ProjectionType Projection + { + get => (ProjectionType)(int)Resource.Call(MethodName.GetProjection); + set => Resource.Call(MethodName.SetProjection, (int)value); + } + + public float Fov + { + get => (float)Resource.Call(MethodName.GetFov); + set => Resource.Call(MethodName.SetFov, Mathf.Clamp(value, MinFov, MaxFov)); + } + + public float Size + { + get => (float)Resource.Call(MethodName.GetSize); + set => Resource.Call(MethodName.SetSize, Mathf.Clamp(value, MinSize, MaxSize)); + } + + public Vector2 FrustumOffset + { + get => (Vector2)Resource.Call(MethodName.GetFrustumOffset); + set => Resource.Call(MethodName.SetFrustumOffset, value); + } + + public float Near + { + get => (float)Resource.Call(MethodName.GetNear); + set => Resource.Call(MethodName.SetNear, Mathf.Clamp(value, MinNear, MaxNear)); + } + + public float Far + { + get => (float)Resource.Call(MethodName.GetFar); + set => Resource.Call(MethodName.SetFar, Mathf.Clamp(value, MinFar, MaxFar)); + } + + public Camera3DResource(Resource resource) => Resource = resource; + + public void SetCullMaskValue(int layerNumber, bool value) + { + Resource.Call(MethodName.SetCullMaskValue, layerNumber, value); + } + + public static class MethodName + { + public const string GetCullMask = "get_cull_mask"; + public const string SetCullMask = "set_cull_mask"; + public const string SetCullMaskValue = "set_cull_mask_value"; + + public const string GetHOffset = "get_h_offset"; + public const string SetHOffset = "set_h_offset"; + + public const string GetVOffset = "get_v_offset"; + public const string SetVOffset = "set_v_offset"; + + public const string GetProjection = "get_projection"; + public const string SetProjection = "set_projection"; + + public const string GetFov = "get_fov"; + public const string SetFov = "set_fov"; + + public const string GetSize = "get_size"; + public const string SetSize = "set_size"; + + public const string GetFrustumOffset = "get_frustum_offset"; + public const string SetFrustumOffset = "set_frustum_offset"; + + public const string GetNear = "get_near"; + public const string SetNear = "set_near"; + + public const string GetFar = "get_far"; + public const string SetFar = "set_far"; + } +} + +public abstract class PhantomCamera +{ + protected readonly GodotObject Node; + + public delegate void BecameActiveEventHandler(); + public delegate void BecameInactiveEventHandler(); + public delegate void FollowTargetChangedEventHandler(); + public delegate void LookAtTargetChangedEventHandler(); + public delegate void DeadZoneChangedEventHandler(); + public delegate void TweenStartedEventHandler(); + public delegate void IsTweeningEventHandler(); + public delegate void TweenCompletedEventHandler(); + + public event BecameActiveEventHandler? BecameActive; + public event BecameInactiveEventHandler? BecameInactive; + public event FollowTargetChangedEventHandler? FollowTargetChanged; + public event LookAtTargetChangedEventHandler? LookAtTargetChanged; + public event DeadZoneChangedEventHandler? DeadZoneChanged; + public event TweenStartedEventHandler? TweenStarted; + public event IsTweeningEventHandler? IsTweening; + public event TweenCompletedEventHandler? TweenCompleted; + + private readonly Callable _callableBecameActive; + private readonly Callable _callableBecameInactive; + private readonly Callable _callableFollowTargetChanged; + private readonly Callable _callableLookAtTargetChanged; + 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); + set => Node.Call(MethodName.SetPriority, value); + } + + public FollowMode FollowMode => (FollowMode)(int)Node.Call(MethodName.GetFollowMode); + + public bool IsActive => (bool)Node.Call(MethodName.IsActive); + + public PhantomCameraTween TweenResource + { + get => new((Resource)Node.Call(MethodName.GetTweenResource)); + set => Node.Call(MethodName.SetTweenResource, value.Resource); + } + + public bool TweenOnLoad + { + get => (bool)Node.Call(MethodName.GetTweenOnLoad); + set => Node.Call(MethodName.SetTweenOnLoad, value); + } + + public InactiveUpdateMode InactiveUpdateMode + { + get => (InactiveUpdateMode)(int)Node.Call(MethodName.GetInactiveUpdateMode); + set => Node.Call(MethodName.SetInactiveUpdateMode, (int)value); + } + + protected PhantomCamera(GodotObject phantomCameraNode) + { + Node = phantomCameraNode; + + _callableBecameActive = Callable.From(() => BecameActive?.Invoke()); + _callableBecameInactive = Callable.From(() => BecameInactive?.Invoke()); + _callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke()); + _callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); + _callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke()); + _callableTweenStarted = Callable.From(() => TweenStarted?.Invoke()); + _callableIsTweening = Callable.From(() => IsTweening?.Invoke()); + _callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke()); + + Node.Connect(SignalName.BecameActive, _callableBecameActive); + Node.Connect(SignalName.BecameInactive, _callableBecameInactive); + Node.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + Node.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + 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.LookAtTargetChanged, _callableLookAtTargetChanged); + Node.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + Node.Disconnect(SignalName.TweenStarted, _callableTweenStarted); + Node.Disconnect(SignalName.IsTweening, _callableIsTweening); + Node.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); + } + + public static class MethodName + { + public const string GetFollowMode = "get_follow_mode"; + public const string IsActive = "is_active"; + + public const string GetPriority = "get_priority"; + public const string SetPriority = "set_priority"; + + public const string GetTweenResource = "get_tween_resource"; + public const string SetTweenResource = "set_tween_resource"; + + public const string GetTweenOnLoad = "get_tween_on_load"; + public const string SetTweenOnLoad = "set_tween_on_load"; + + public const string GetInactiveUpdateMode = "get_inactive_update_mode"; + public const string SetInactiveUpdateMode = "set_inactive_update_mode"; + } + + 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 LookAtTargetChanged = "look_at_target_changed"; + public const string DeadZoneChanged = "dead_zone_changed"; + 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 class PhantomCamera2D : PhantomCamera +{ + public Node2D Node2D => (Node2D)Node; + + public delegate void TweenInterruptedEventHandler(Node2D pCam); + + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableTweenInterrupted; + + public Vector2 Zoom + { + get => (Vector2)Node.Call(MethodName.GetZoom); + set => Node.Call(MethodName.SetZoom, value); + } + + public bool SnapToPixel + { + get => (bool)Node.Call(MethodName.GetSnapToPixel); + set => Node.Call(MethodName.SetSnapToPixel, value); + } + + public int LimitLeft + { + get => (int)Node.Call(MethodName.GetLimitLeft); + set => Node.Call(MethodName.SetLimitLeft, value); + } + + public int LimitTop + { + get => (int)Node.Call(MethodName.GetLimitTop); + set => Node.Call(MethodName.SetLimitTop, value); + } + + public int LimitRight + { + get => (int)Node.Call(MethodName.GetLimitRight); + set => Node.Call(MethodName.SetLimitRight, value); + } + + public int LimitBottom + { + get => (int)Node.Call(MethodName.GetLimitBottom); + set => Node.Call(MethodName.SetLimitBottom, value); + } + + public NodePath LimitTarget + { + get => (NodePath)Node.Call(MethodName.GetLimitTarget); + set => Node.Call(MethodName.SetLimitTarget, value); + } + + public Vector4I LimitMargin + { + get => (Vector4I)Node.Call(MethodName.GetLimitMargin); + set => Node.Call(MethodName.SetLimitMargin, 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)); + Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera2D() + { + Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public void SetLimit(Side side, int value) + { + Node.Call(MethodName.SetLimit, (int)side, value); + } + + public int GetLimit(Side side) + { + return (int)Node.Call(MethodName.GetLimit, (int)side); + } + + public new static class MethodName + { + public const string GetZoom = "get_zoom"; + public const string SetZoom = "set_zoom"; + + public const string GetSnapToPixel = "get_snap_to_pixel"; + public const string SetSnapToPixel = "set_snap_to_pixel"; + + public const string GetLimit = "get_limit"; + public const string SetLimit = "set_limit"; + + public const string GetLimitLeft = "get_limit_left"; + public const string SetLimitLeft = "set_limit_left"; + + public const string GetLimitTop = "get_limit_top"; + public const string SetLimitTop = "set_limit_top"; + + public const string GetLimitRight = "get_limit_right"; + public const string SetLimitRight = "set_limit_right"; + + public const string GetLimitBottom = "get_limit_bottom"; + public const string SetLimitBottom = "set_limit_bottom"; + + public const string GetLimitTarget = "get_limit_target"; + public const string SetLimitTarget = "set_limit_target"; + + public const string GetLimitMargin = "get_limit_margin"; + public const string SetLimitMargin = "set_limit_margin"; + } +} + +public class PhantomCamera3D : PhantomCamera +{ + public Node3D Node3D => (Node3D)Node; + + public delegate void TweenInterruptedEventHandler(Node3D pCam); + + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableTweenInterrupted; + + public LookAtMode LookAtMode => (LookAtMode)(int)Node.Call(MethodName.GetLookAtMode); + + public Camera3DResource Camera3DResource + { + get => new((Resource)Node.Call(MethodName.GetCamera3DResource)); + set => Node.Call(MethodName.SetCamera3DResource, value.Resource); + } + + public Vector3 ThirdPersonRotation + { + get => (Vector3)Node.Call(MethodName.GetThirdPersonRotation); + set => Node.Call(MethodName.SetThirdPersonRotation, value); + } + + public Vector3 ThirdPersonRotationDegrees + { + get => (Vector3)Node.Call(MethodName.GetThirdPersonRotationDegrees); + set => Node.Call(MethodName.SetThirdPersonRotationDegrees, value); + } + + public Quaternion ThirdPersonQuaternion + { + get => (Quaternion)Node.Call(MethodName.GetThirdPersonQuaternion); + set => Node.Call(MethodName.SetThirdPersonQuaternion, value); + } + + public float SpringLength + { + get => (float)Node.Call(MethodName.GetSpringLength); + set => Node.Call(MethodName.SetSpringLength, value); + } + + 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) + { + _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); + Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera3D() + { + Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public new static class MethodName + { + public const string GetLookAtMode = "get_look_at_mode"; + + public const string GetCamera3DResource = "get_camera_3d_resource"; + public const string SetCamera3DResource = "set_camera_3d_resource"; + + public const string GetThirdPersonRotation = "get_third_person_rotation"; + public const string SetThirdPersonRotation = "set_third_person_rotation"; + + public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; + public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; + + public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; + public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; + + public const string GetSpringLength = "get_spring_length"; + public const string SetSpringLength = "set_spring_length"; + } +} + +public class ActivePhantomCameraQueryResult +{ + private readonly GodotObject _obj; + + public bool Is2D => _obj.IsClass("Node2D"); + + public bool Is3D => _obj.IsClass("Node3D"); + + public ActivePhantomCameraQueryResult(GodotObject godotOject) => _obj = godotOject; + + public PhantomCamera2D? AsPhantomCamera2D() + { + return Is2D ? new PhantomCamera2D(_obj) : null; + } + + public PhantomCamera3D? AsPhantomCamera3D() + { + return Is3D ? new PhantomCamera3D(_obj) : null; + } +} + +public class PhantomCameraHost +{ + public readonly Node Node; + + // TODO: For Godot 4.3 + // public InterpolationMode InterpolationMode + // { + // get => (InterpolationMode)(int)Node.Call(MethodName.GetInterpolationMode); + // set => Node.Call(MethodName.SetInterpolationMode, (int)value); + // } + + public Camera2D Camera2D => (Camera2D)Node.Call(MethodName.GetCamera2D); + + public Camera3D Camera3D => (Camera3D)Node.Call(MethodName.GetCamera3D); + + public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween); + + public PhantomCameraHost(Node node) => Node = node; + + public ActivePhantomCameraQueryResult? GetActivePhantomCamera() + { + var result = Node.Call(MethodName.GetActivePhantomCamera); + return result.VariantType == Variant.Type.Nil ? null : new ActivePhantomCameraQueryResult(result.AsGodotObject()); + } + + public static class MethodName + { + public const string GetCamera2D = "get_camera_2d"; + public const string GetCamera3D = "get_camera_3d"; + + public const string GetActivePhantomCamera = "get_active_pcam"; + public const string GetTriggerPhantomCameraTween = "get_trigger_pcam_tween"; + + // TODO: For Godot 4.3 + // public const string GetInterpolationMode = "get_interpolation_mode"; + // public const string SetInterpolationMode = "set_interpolation_mode"; + } +} + +public static class PhantomCameraManager +{ + private static GodotObject? _instance; + + private static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); + + public static PhantomCamera2D[] PhantomCamera2Ds => + Instance.Call(MethodName.GetPhantomCamera2Ds).AsGodotArray() + .Select(node => new PhantomCamera2D(node)).ToArray(); + + public static PhantomCamera3D[] PhantomCamera3Ds => + Instance.Call(MethodName.GetPhantomCamera3Ds).AsGodotArray() + .Select(node => new PhantomCamera3D(node)).ToArray(); + + public static PhantomCameraHost[] PhantomCameraHosts => + Instance.Call(MethodName.GetPhantomCameraHosts).AsGodotArray() + .Select(node => new PhantomCameraHost(node)).ToArray(); + + 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"; + } +} \ No newline at end of file diff --git a/dev_scenes/3d/dev_scene_3d.tscn b/dev_scenes/3d/dev_scene_3d.tscn index 7add5bbb..023019ab 100644 --- a/dev_scenes/3d/dev_scene_3d.tscn +++ b/dev_scenes/3d/dev_scene_3d.tscn @@ -28,7 +28,12 @@ script = ExtResource("7_fioii") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [sub_resource type="Resource" id="Resource_6c6yi"] script = ExtResource("6_pmc8r") @@ -41,7 +46,12 @@ script = ExtResource("7_fioii") cull_mask = 1048575 h_offset = 0.0 v_offset = 0.0 +projection = 0 fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 [node name="Node3D" type="Node3D"] script = ExtResource("1_gnrfx") diff --git a/dev_scenes/3d/dev_scene_csharp_3d.tscn b/dev_scenes/3d/dev_scene_csharp_3d.tscn index dab817ba..7b44bb82 100644 --- a/dev_scenes/3d/dev_scene_csharp_3d.tscn +++ b/dev_scenes/3d/dev_scene_csharp_3d.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=14 format=3 uid="uid://brb8fl27ofqhv"] +[gd_scene load_steps=13 format=3 uid="uid://brb8fl27ofqhv"] [ext_resource type="Script" path="res://dev_scenes/3d/scripts/DevSceneCSharp3D.cs" id="1_fd2f4"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="2_m8s1w"] -[ext_resource type="Script" path="res://dev_scenes/3d/scripts/DevPlayer.cs" id="3_45c2y"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="3_xni2u"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="4_c7bav"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/camera_3d_resource.gd" id="5_jea0a"] @@ -65,7 +64,6 @@ size = Vector3(10, 1, 10) [node name="Player" type="CharacterBody3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25252, 0) -script = ExtResource("3_45c2y") [node name="MeshInstance3D" type="MeshInstance3D" parent="Player"] mesh = SubResource("CapsuleMesh_vcumb") diff --git a/dev_scenes/3d/scripts/DevPlayer.cs b/dev_scenes/3d/scripts/DevPlayer.cs deleted file mode 100644 index db6246bd..00000000 --- a/dev_scenes/3d/scripts/DevPlayer.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Godot; -using System; - -public partial class DevPlayer : CharacterBody3D -{ - public const float Speed = 5.0f; - public const float JumpVelocity = 4.5f; - - // Get the gravity from the project settings to be synced with RigidBody nodes. - public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle(); - - public override void _PhysicsProcess(double delta) - { - Vector3 velocity = Velocity; - - // Add the gravity. - if (!IsOnFloor()) - velocity.Y -= gravity * (float)delta; - - // Handle Jump. - if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) - velocity.Y = JumpVelocity; - - // Get the input direction and handle the movement/deceleration. - // As good practice, you should replace UI actions with custom gameplay actions. - Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); - Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized(); - if (direction != Vector3.Zero) - { - velocity.X = direction.X * Speed; - velocity.Z = direction.Z * Speed; - } - else - { - velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed); - velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed); - } - - Velocity = velocity; - MoveAndSlide(); - } -} diff --git a/dev_scenes/3d/scripts/DevSceneCSharp3D.cs b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs index b9228398..9a6a6a02 100644 --- a/dev_scenes/3d/scripts/DevSceneCSharp3D.cs +++ b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs @@ -5,9 +5,8 @@ public partial class DevSceneCSharp3D : Node3D { public override void _Ready() { - var pCamNode = GetNode("Player/PlayerCam"); - var pCam = pCamNode.AsPhantomCamera3D(); + var pCam = GetNode("Player/PlayerCam").AsPhantomCamera3D(); - GD.Print(pCam); + GD.Print(pCam.Node3D); } } From 143d56708e82edd391ee4c4942e7806dc25f2142 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Mon, 22 Jul 2024 23:03:27 -0600 Subject: [PATCH 4/8] WIP #286: fixed bugs in c# wrapper and added tests - added Engine singleton registration to PhantomCameraManager - fixed incorrect LookAtMode signal (not available in 2d) - added c# project files - created basic test runner and added some tests (wip) --- .gitignore | 2 - PhantomCamera.csproj | 12 +++ PhantomCamera.sln | 19 ++++ .../phantom_camera/scripts/PhantomCamera.cs | 57 +++++++----- .../managers/phantom_camera_manager.gd | 4 + dev_scenes/3d/scripts/DevSceneCSharp3D.cs | 2 + tests/scenes/test_runner.tscn | 6 ++ tests/scenes/test_scene_2d.tscn | 22 +++++ tests/scenes/test_scene_3d.tscn | 36 ++++++++ tests/scripts/TestPhantomCameraWrapper.cs | 91 +++++++++++++++++++ tests/scripts/test_phantom_camera.gd | 7 ++ tests/scripts/test_runner.gd | 15 +++ 12 files changed, 246 insertions(+), 27 deletions(-) create mode 100644 PhantomCamera.csproj create mode 100644 PhantomCamera.sln create mode 100644 tests/scenes/test_runner.tscn create mode 100644 tests/scenes/test_scene_2d.tscn create mode 100644 tests/scenes/test_scene_3d.tscn create mode 100644 tests/scripts/TestPhantomCameraWrapper.cs create mode 100644 tests/scripts/test_phantom_camera.gd create mode 100644 tests/scripts/test_runner.gd diff --git a/.gitignore b/.gitignore index e3f3cb15..51c71048 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,6 @@ export_presets.cfg .godot/ # CSharp Dev -*.csproj -*.sln *.DotSettings # Docs specific ignores diff --git a/PhantomCamera.csproj b/PhantomCamera.csproj new file mode 100644 index 00000000..5d5d3388 --- /dev/null +++ b/PhantomCamera.csproj @@ -0,0 +1,12 @@ + + + net8.0 + net7.0 + net8.0 + true + 11.0 + + + + + \ No newline at end of file diff --git a/PhantomCamera.sln b/PhantomCamera.sln new file mode 100644 index 00000000..4a0d21cc --- /dev/null +++ b/PhantomCamera.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhantomCamera", "PhantomCamera.csproj", "{74ABEBAD-177B-4436-87F5-58340D7F4EE5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {74ABEBAD-177B-4436-87F5-58340D7F4EE5}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/addons/phantom_camera/scripts/PhantomCamera.cs b/addons/phantom_camera/scripts/PhantomCamera.cs index e2401469..6b65e01d 100644 --- a/addons/phantom_camera/scripts/PhantomCamera.cs +++ b/addons/phantom_camera/scripts/PhantomCamera.cs @@ -60,13 +60,12 @@ public enum ProjectionType Frustum } -// TODO: For Godot 4.3 -// public enum InterpolationMode -// { -// Auto, -// Idle, -// Physics -// } +public enum InterpolationMode +{ + Auto, + Idle, + Physics +} public static class PhantomCameraExtension { @@ -79,6 +78,11 @@ public static PhantomCamera2D AsPhantomCamera2D(this Node2D node2D) { return new PhantomCamera2D(node2D); } + + public static PhantomCameraHost AsPhantomCameraHost(this Node node) + { + return new PhantomCameraHost(node); + } } public class PhantomCameraTween @@ -237,7 +241,6 @@ public abstract class PhantomCamera public delegate void BecameActiveEventHandler(); public delegate void BecameInactiveEventHandler(); public delegate void FollowTargetChangedEventHandler(); - public delegate void LookAtTargetChangedEventHandler(); public delegate void DeadZoneChangedEventHandler(); public delegate void TweenStartedEventHandler(); public delegate void IsTweeningEventHandler(); @@ -246,7 +249,6 @@ public abstract class PhantomCamera public event BecameActiveEventHandler? BecameActive; public event BecameInactiveEventHandler? BecameInactive; public event FollowTargetChangedEventHandler? FollowTargetChanged; - public event LookAtTargetChangedEventHandler? LookAtTargetChanged; public event DeadZoneChangedEventHandler? DeadZoneChanged; public event TweenStartedEventHandler? TweenStarted; public event IsTweeningEventHandler? IsTweening; @@ -255,7 +257,6 @@ public abstract class PhantomCamera private readonly Callable _callableBecameActive; private readonly Callable _callableBecameInactive; private readonly Callable _callableFollowTargetChanged; - private readonly Callable _callableLookAtTargetChanged; private readonly Callable _callableDeadZoneChanged; private readonly Callable _callableTweenStarted; private readonly Callable _callableIsTweening; @@ -296,7 +297,6 @@ protected PhantomCamera(GodotObject phantomCameraNode) _callableBecameActive = Callable.From(() => BecameActive?.Invoke()); _callableBecameInactive = Callable.From(() => BecameInactive?.Invoke()); _callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke()); - _callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); _callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke()); _callableTweenStarted = Callable.From(() => TweenStarted?.Invoke()); _callableIsTweening = Callable.From(() => IsTweening?.Invoke()); @@ -305,7 +305,6 @@ protected PhantomCamera(GodotObject phantomCameraNode) Node.Connect(SignalName.BecameActive, _callableBecameActive); Node.Connect(SignalName.BecameInactive, _callableBecameInactive); Node.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - Node.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); Node.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); Node.Connect(SignalName.TweenStarted, _callableTweenStarted); Node.Connect(SignalName.IsTweening, _callableIsTweening); @@ -317,7 +316,6 @@ protected PhantomCamera(GodotObject phantomCameraNode) Node.Disconnect(SignalName.BecameActive, _callableBecameActive); Node.Disconnect(SignalName.BecameInactive, _callableBecameInactive); Node.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); - Node.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); Node.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); Node.Disconnect(SignalName.TweenStarted, _callableTweenStarted); Node.Disconnect(SignalName.IsTweening, _callableIsTweening); @@ -473,10 +471,13 @@ public class PhantomCamera3D : PhantomCamera { public Node3D Node3D => (Node3D)Node; + public delegate void LookAtTargetChangedEventHandler(); public delegate void TweenInterruptedEventHandler(Node3D pCam); + public event LookAtTargetChangedEventHandler? LookAtTargetChanged; public event TweenInterruptedEventHandler? TweenInterrupted; + private readonly Callable _callableLookAtTargetChanged; private readonly Callable _callableTweenInterrupted; public LookAtMode LookAtMode => (LookAtMode)(int)Node.Call(MethodName.GetLookAtMode); @@ -516,12 +517,16 @@ public float SpringLength public PhantomCamera3D(GodotObject phantomCamera3DNode) : base(phantomCamera3DNode) { + _callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); + + Node.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); } ~PhantomCamera3D() { + Node.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); } @@ -554,7 +559,7 @@ public class ActivePhantomCameraQueryResult public bool Is3D => _obj.IsClass("Node3D"); - public ActivePhantomCameraQueryResult(GodotObject godotOject) => _obj = godotOject; + public ActivePhantomCameraQueryResult(GodotObject godotObject) => _obj = godotObject; public PhantomCamera2D? AsPhantomCamera2D() { @@ -569,7 +574,7 @@ public class ActivePhantomCameraQueryResult public class PhantomCameraHost { - public readonly Node Node; + public Node Node { get; } // TODO: For Godot 4.3 // public InterpolationMode InterpolationMode @@ -578,9 +583,9 @@ public class PhantomCameraHost // set => Node.Call(MethodName.SetInterpolationMode, (int)value); // } - public Camera2D Camera2D => (Camera2D)Node.Call(MethodName.GetCamera2D); + public Camera2D? Camera2D => (Camera2D?)Node.Get(PropertyName.Camera2D); - public Camera3D Camera3D => (Camera3D)Node.Call(MethodName.GetCamera3D); + public Camera3D? Camera3D => (Camera3D?)Node.Get(PropertyName.Camera3D); public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween); @@ -591,26 +596,28 @@ public class PhantomCameraHost var result = Node.Call(MethodName.GetActivePhantomCamera); return result.VariantType == Variant.Type.Nil ? null : new ActivePhantomCameraQueryResult(result.AsGodotObject()); } + + public static class PropertyName + { + public const string Camera2D = "camera_2d"; + public const string Camera3D = "camera_3d"; + } public static class MethodName { - public const string GetCamera2D = "get_camera_2d"; - public const string GetCamera3D = "get_camera_3d"; - public const string GetActivePhantomCamera = "get_active_pcam"; public const string GetTriggerPhantomCameraTween = "get_trigger_pcam_tween"; - // TODO: For Godot 4.3 - // public const string GetInterpolationMode = "get_interpolation_mode"; - // public const string SetInterpolationMode = "set_interpolation_mode"; + public const string GetInterpolationMode = "get_interpolation_mode"; + public const string SetInterpolationMode = "set_interpolation_mode"; } } public static class PhantomCameraManager { private static GodotObject? _instance; - - private static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); + + public static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); public static PhantomCamera2D[] PhantomCamera2Ds => Instance.Call(MethodName.GetPhantomCamera2Ds).AsGodotArray() diff --git a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd index 8a53e059..b5b19fc9 100644 --- a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd +++ b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd @@ -18,6 +18,10 @@ var phantom_camera_3ds: Array: ## Note: To support disable_3d export templates f return _phantom_camera_3d_list var _phantom_camera_3d_list: Array ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. +func _ready(): + if Engine.has_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME): + Engine.unregister_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME) + Engine.register_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME, self) func _enter_tree(): Engine.physics_jitter_fix = 0 diff --git a/dev_scenes/3d/scripts/DevSceneCSharp3D.cs b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs index 9a6a6a02..a191968c 100644 --- a/dev_scenes/3d/scripts/DevSceneCSharp3D.cs +++ b/dev_scenes/3d/scripts/DevSceneCSharp3D.cs @@ -8,5 +8,7 @@ public override void _Ready() var pCam = GetNode("Player/PlayerCam").AsPhantomCamera3D(); GD.Print(pCam.Node3D); + + } } diff --git a/tests/scenes/test_runner.tscn b/tests/scenes/test_runner.tscn new file mode 100644 index 00000000..c17a6eff --- /dev/null +++ b/tests/scenes/test_runner.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bu4xjlkrknuo5"] + +[ext_resource type="Script" path="res://tests/scripts/test_runner.gd" id="1_m0lqx"] + +[node name="TestRunner" type="Node"] +script = ExtResource("1_m0lqx") diff --git a/tests/scenes/test_scene_2d.tscn b/tests/scenes/test_scene_2d.tscn new file mode 100644 index 00000000..0d23ab61 --- /dev/null +++ b/tests/scenes/test_scene_2d.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=5 format=3 uid="uid://bx61t0ytiwtwm"] + +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="1_qr8fr"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="2_53jhm"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="3_aqfsy"] + +[sub_resource type="Resource" id="Resource_f0onm"] +script = ExtResource("3_aqfsy") +duration = 1.0 +transition = 0 +ease = 2 + +[node name="TestScene2D" type="Node2D"] + +[node name="Camera2D" type="Camera2D" parent="."] + +[node name="PhantomCameraHost" type="Node" parent="Camera2D"] +script = ExtResource("1_qr8fr") + +[node name="PhantomCamera2D" type="Node2D" parent="."] +script = ExtResource("2_53jhm") +tween_resource = SubResource("Resource_f0onm") diff --git a/tests/scenes/test_scene_3d.tscn b/tests/scenes/test_scene_3d.tscn new file mode 100644 index 00000000..d654f4a9 --- /dev/null +++ b/tests/scenes/test_scene_3d.tscn @@ -0,0 +1,36 @@ +[gd_scene load_steps=7 format=3 uid="uid://bry2ltsrujg02"] + +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="1_egs8c"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="2_fln48"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="3_ir72h"] +[ext_resource type="Script" path="res://addons/phantom_camera/scripts/resources/camera_3d_resource.gd" id="4_w22nl"] + +[sub_resource type="Resource" id="Resource_om1dn"] +script = ExtResource("3_ir72h") +duration = 1.0 +transition = 0 +ease = 2 + +[sub_resource type="Resource" id="Resource_omnwe"] +script = ExtResource("4_w22nl") +cull_mask = 1048575 +h_offset = 0.0 +v_offset = 0.0 +projection = 0 +fov = 75.0 +size = 1.0 +frustum_offset = Vector2(0, 0) +near = 0.05 +far = 4000.0 + +[node name="TestScene3D" type="Node3D"] + +[node name="Camera3D" type="Camera3D" parent="."] + +[node name="PhantomCameraHost" type="Node" parent="Camera3D"] +script = ExtResource("1_egs8c") + +[node name="PhantomCamera3D" type="Node3D" parent="."] +script = ExtResource("2_fln48") +tween_resource = SubResource("Resource_om1dn") +camera_3d_resource = SubResource("Resource_omnwe") diff --git a/tests/scripts/TestPhantomCameraWrapper.cs b/tests/scripts/TestPhantomCameraWrapper.cs new file mode 100644 index 00000000..a75c3835 --- /dev/null +++ b/tests/scripts/TestPhantomCameraWrapper.cs @@ -0,0 +1,91 @@ +using System.Diagnostics; +using Godot; +using PhantomCamera; + +namespace PhantomCameraTests; + +public partial class TestPhantomCameraWrapper: Node +{ + private PackedScene _scene2d; + + private PackedScene _scene3d; + + public override void _Ready() + { + _scene2d = GD.Load("res://tests/scenes/test_scene_2d.tscn"); + _scene3d = GD.Load("res://tests/scenes/test_scene_3d.tscn"); + } + + public void Test() + { + Test2D(); + Test3D(); + GD.Print("PhantomCameraWrapper tests complete"); + } + + private void Test2D() + { + var testScene = _scene2d.Instantiate(); + AddChild(testScene); + + // PhantomCameraManager Tests + Debug.Assert(PhantomCameraManager.Instance != null); + Debug.Assert(PhantomCameraManager.PhantomCamera3Ds.Length == 0); + Debug.Assert(PhantomCameraManager.PhantomCamera2Ds.Length == 1); + Debug.Assert(PhantomCameraManager.PhantomCameraHosts.Length == 1); + + // PhantomCameraHost Tests + var cameraHost = testScene.GetNode("Camera2D/PhantomCameraHost").AsPhantomCameraHost(); + Debug.Assert(cameraHost.Node != null); + Debug.Assert(cameraHost.Camera2D != null); + Debug.Assert(cameraHost.Camera3D == null); + Debug.Assert(cameraHost.TriggerPhantomCameraTween); + + var cameraQuery = cameraHost.GetActivePhantomCamera(); + Debug.Assert(cameraQuery != null); + Debug.Assert(cameraQuery.Is2D); + Debug.Assert(!cameraQuery.Is3D); + Debug.Assert(cameraQuery.AsPhantomCamera3D() == null); + + // PhantomCamera2D Tests + var camera = cameraQuery.AsPhantomCamera2D(); + Debug.Assert(camera != null); + GD.Print(camera.Node2D); + // TODO: finish implementing 2d tests + + RemoveChild(testScene); + } + + private void Test3D() + { + var testScene = _scene3d.Instantiate(); + AddChild(testScene); + + // PhantomCameraManager Tests + Debug.Assert(PhantomCameraManager.Instance != null); + Debug.Assert(PhantomCameraManager.PhantomCamera2Ds.Length == 0); + Debug.Assert(PhantomCameraManager.PhantomCamera3Ds.Length == 1); + Debug.Assert(PhantomCameraManager.PhantomCameraHosts.Length == 1); + + // PhantomCameraHost Tests + var cameraHost = testScene.GetNode("Camera3D/PhantomCameraHost").AsPhantomCameraHost(); + Debug.Assert(cameraHost.Node != null); + Debug.Assert(cameraHost.Camera2D == null); + Debug.Assert(cameraHost.Camera3D != null); + Debug.Assert(cameraHost.TriggerPhantomCameraTween); + + var cameraQuery = cameraHost.GetActivePhantomCamera(); + Debug.Assert(cameraQuery != null); + Debug.Assert(!cameraQuery.Is2D); + Debug.Assert(cameraQuery.Is3D); + Debug.Assert(cameraQuery.AsPhantomCamera2D() == null); + + // PhantomCamera3D Tests + var camera = cameraQuery.AsPhantomCamera3D(); + Debug.Assert(camera != null); + GD.Print(camera.Node3D); + // TODO: finish implementing 3d tests + + RemoveChild(testScene); + } +} \ No newline at end of file diff --git a/tests/scripts/test_phantom_camera.gd b/tests/scripts/test_phantom_camera.gd new file mode 100644 index 00000000..e5f101f7 --- /dev/null +++ b/tests/scripts/test_phantom_camera.gd @@ -0,0 +1,7 @@ +extends Node + +@onready var scene2d = load("res://tests/scenes/test_scene_2d.tscn") +@onready var scene3d = load("res://tests/scenes/test_scene_3d.tscn") + +func Test() -> void: + print("No GDScript tests implemented") diff --git a/tests/scripts/test_runner.gd b/tests/scripts/test_runner.gd new file mode 100644 index 00000000..fa4b5795 --- /dev/null +++ b/tests/scripts/test_runner.gd @@ -0,0 +1,15 @@ +extends Node + +var test_scripts: Array[Script] = [ + load("res://tests/scripts/test_phantom_camera.gd"), + load("res://tests/scripts/TestPhantomCameraWrapper.cs"), +] + +func _ready() -> void: + for script: Script in test_scripts: + var test: Object = script.new() + if test.has_method("Test"): + add_child(test) + test.Test() + remove_child(test) + get_tree().quit() From bcdf718c369585e58d1d3902af358cbfd9473143 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Tue, 23 Jul 2024 23:46:56 -0600 Subject: [PATCH 5/8] WIP #286: more tests and re-organized - fix several bugs from invalid getter/setters, properties, and types - added more tests - added LimitTarget query result type for working with TileMaps and CollisionShape2Ds - reorganized scripts since main script file had become quite large --- .../phantom_camera/scripts/PhantomCamera.cs | 618 +----------------- .../scripts/managers/PhantomCameraManager.cs | 34 + .../managers/phantom_camera_manager.gd | 5 +- .../scripts/phantom_camera/PhantomCamera.cs | 162 +++++ .../scripts/phantom_camera/PhantomCamera2D.cs | 156 +++++ .../scripts/phantom_camera/PhantomCamera3D.cs | 106 +++ .../phantom_camera_host/PhantomCameraHost.cs | 75 +++ .../scripts/resources/Camera3DResource.cs | 122 ++++ .../scripts/resources/PhantomCameraTween.cs | 58 ++ tests/scenes/test_scene_2d.tscn | 15 +- tests/scripts/TestPhantomCameraWrapper.cs | 102 ++- 11 files changed, 827 insertions(+), 626 deletions(-) create mode 100644 addons/phantom_camera/scripts/managers/PhantomCameraManager.cs create mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs create mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs create mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs create mode 100644 addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs create mode 100644 addons/phantom_camera/scripts/resources/Camera3DResource.cs create mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraTween.cs diff --git a/addons/phantom_camera/scripts/PhantomCamera.cs b/addons/phantom_camera/scripts/PhantomCamera.cs index 6b65e01d..2da8606f 100644 --- a/addons/phantom_camera/scripts/PhantomCamera.cs +++ b/addons/phantom_camera/scripts/PhantomCamera.cs @@ -1,72 +1,9 @@ -using System.Linq; -using Godot; - -#nullable enable +using Godot; +using PhantomCamera.Cameras; +using PhantomCamera.Hosts; namespace PhantomCamera; -public enum FollowMode -{ - None, - Glued, - Simple, - Group, - Path, - Framed, - ThirdPerson -} - -public enum LookAtMode -{ - None, - Mimic, - Simple, - Group -} - -public enum InactiveUpdateMode -{ - Always, - Never -} - -public enum TransitionType -{ - Linear, - Sine, - Quintic, - Quartic, - Quadratic, - Exponential, - Elastic, - Cubic, - Circ, - Bounce, - Back -} - -public enum EaseType -{ - In, - Out, - InOut, - OutIn -} - -public enum ProjectionType -{ - Perspective, - Orthogonal, - Frustum -} - -public enum InterpolationMode -{ - Auto, - Idle, - Physics -} - public static class PhantomCameraExtension { public static PhantomCamera3D AsPhantomCamera3D(this Node3D node3D) @@ -85,556 +22,7 @@ public static PhantomCameraHost AsPhantomCameraHost(this Node node) } } -public class PhantomCameraTween -{ - public Resource Resource { get; } - - public float Duration - { - get => (float)Resource.Call(MethodName.GetTweenDuration); - set => Resource.Call(MethodName.SetTweenDuration, value); - } - - public TransitionType Transition - { - get => (TransitionType)(int)Resource.Call(MethodName.GetTweenTransition); - set => Resource.Call(MethodName.SetTweenTransition, (int)value); - } - - public EaseType Ease - { - get => (EaseType)(int)Resource.Call(MethodName.GetTweenEase); - set => Resource.Call(MethodName.SetTweenEase, (int)value); - } - - public PhantomCameraTween(Resource tweenResource) => Resource = tweenResource; - - public static class MethodName - { - public const string GetTweenDuration = "get_tween_durartion"; - public const string SetTweenDuration = "set_tween_durartion"; - - public const string GetTweenTransition = "get_tween_transition"; - public const string SetTweenTransition = "set_tween_transition"; - - public const string GetTweenEase = "get_tween_ease"; - public const string SetTweenEase = "set_tween_ease"; - } -} - -public class Camera3DResource -{ - public readonly Resource Resource; - - public const float MinOffset = 0; - public const float MaxOffset = 1; - - public const float MinFov = 1; - public const float MaxFov = 179; - - public const float MinSize = 0.001f; - public const float MaxSize = 100; - - public const float MinNear = 0.001f; - public const float MaxNear = 10; - - public const float MinFar = 0.01f; - public const float MaxFar = 4000; - - public int CullMask - { - get => (int)Resource.Call(MethodName.GetCullMask); - set => Resource.Call(MethodName.SetCullMask, value); - } - - public float HOffset - { - get => (float)Resource.Call(MethodName.GetHOffset); - set => Resource.Call(MethodName.SetHOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); - } - - public float VOffset - { - get => (float)Resource.Call(MethodName.GetVOffset); - set => Resource.Call(MethodName.SetVOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); - } - - public ProjectionType Projection - { - get => (ProjectionType)(int)Resource.Call(MethodName.GetProjection); - set => Resource.Call(MethodName.SetProjection, (int)value); - } - - public float Fov - { - get => (float)Resource.Call(MethodName.GetFov); - set => Resource.Call(MethodName.SetFov, Mathf.Clamp(value, MinFov, MaxFov)); - } - - public float Size - { - get => (float)Resource.Call(MethodName.GetSize); - set => Resource.Call(MethodName.SetSize, Mathf.Clamp(value, MinSize, MaxSize)); - } - - public Vector2 FrustumOffset - { - get => (Vector2)Resource.Call(MethodName.GetFrustumOffset); - set => Resource.Call(MethodName.SetFrustumOffset, value); - } - - public float Near - { - get => (float)Resource.Call(MethodName.GetNear); - set => Resource.Call(MethodName.SetNear, Mathf.Clamp(value, MinNear, MaxNear)); - } - - public float Far - { - get => (float)Resource.Call(MethodName.GetFar); - set => Resource.Call(MethodName.SetFar, Mathf.Clamp(value, MinFar, MaxFar)); - } - - public Camera3DResource(Resource resource) => Resource = resource; - - public void SetCullMaskValue(int layerNumber, bool value) - { - Resource.Call(MethodName.SetCullMaskValue, layerNumber, value); - } - - public static class MethodName - { - public const string GetCullMask = "get_cull_mask"; - public const string SetCullMask = "set_cull_mask"; - public const string SetCullMaskValue = "set_cull_mask_value"; - - public const string GetHOffset = "get_h_offset"; - public const string SetHOffset = "set_h_offset"; - - public const string GetVOffset = "get_v_offset"; - public const string SetVOffset = "set_v_offset"; - - public const string GetProjection = "get_projection"; - public const string SetProjection = "set_projection"; - - public const string GetFov = "get_fov"; - public const string SetFov = "set_fov"; - - public const string GetSize = "get_size"; - public const string SetSize = "set_size"; - - public const string GetFrustumOffset = "get_frustum_offset"; - public const string SetFrustumOffset = "set_frustum_offset"; - - public const string GetNear = "get_near"; - public const string SetNear = "set_near"; - - public const string GetFar = "get_far"; - public const string SetFar = "set_far"; - } -} - -public abstract class PhantomCamera -{ - protected readonly GodotObject Node; - - public delegate void BecameActiveEventHandler(); - public delegate void BecameInactiveEventHandler(); - public delegate void FollowTargetChangedEventHandler(); - public delegate void DeadZoneChangedEventHandler(); - public delegate void TweenStartedEventHandler(); - public delegate void IsTweeningEventHandler(); - public delegate void TweenCompletedEventHandler(); - - public event BecameActiveEventHandler? BecameActive; - public event BecameInactiveEventHandler? BecameInactive; - public event FollowTargetChangedEventHandler? FollowTargetChanged; - public event DeadZoneChangedEventHandler? DeadZoneChanged; - public event TweenStartedEventHandler? TweenStarted; - 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); - set => Node.Call(MethodName.SetPriority, value); - } - - public FollowMode FollowMode => (FollowMode)(int)Node.Call(MethodName.GetFollowMode); - - public bool IsActive => (bool)Node.Call(MethodName.IsActive); - - public PhantomCameraTween TweenResource - { - get => new((Resource)Node.Call(MethodName.GetTweenResource)); - set => Node.Call(MethodName.SetTweenResource, value.Resource); - } - - public bool TweenOnLoad - { - get => (bool)Node.Call(MethodName.GetTweenOnLoad); - set => Node.Call(MethodName.SetTweenOnLoad, value); - } - - public InactiveUpdateMode InactiveUpdateMode - { - get => (InactiveUpdateMode)(int)Node.Call(MethodName.GetInactiveUpdateMode); - set => Node.Call(MethodName.SetInactiveUpdateMode, (int)value); - } - - protected PhantomCamera(GodotObject phantomCameraNode) - { - 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()); - - 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); - } - - public static class MethodName - { - public const string GetFollowMode = "get_follow_mode"; - public const string IsActive = "is_active"; - - public const string GetPriority = "get_priority"; - public const string SetPriority = "set_priority"; - - public const string GetTweenResource = "get_tween_resource"; - public const string SetTweenResource = "set_tween_resource"; - - public const string GetTweenOnLoad = "get_tween_on_load"; - public const string SetTweenOnLoad = "set_tween_on_load"; - - public const string GetInactiveUpdateMode = "get_inactive_update_mode"; - public const string SetInactiveUpdateMode = "set_inactive_update_mode"; - } - - 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 LookAtTargetChanged = "look_at_target_changed"; - public const string DeadZoneChanged = "dead_zone_changed"; - 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 class PhantomCamera2D : PhantomCamera -{ - public Node2D Node2D => (Node2D)Node; - - public delegate void TweenInterruptedEventHandler(Node2D pCam); - - public event TweenInterruptedEventHandler? TweenInterrupted; - - private readonly Callable _callableTweenInterrupted; - - public Vector2 Zoom - { - get => (Vector2)Node.Call(MethodName.GetZoom); - set => Node.Call(MethodName.SetZoom, value); - } - - public bool SnapToPixel - { - get => (bool)Node.Call(MethodName.GetSnapToPixel); - set => Node.Call(MethodName.SetSnapToPixel, value); - } - - public int LimitLeft - { - get => (int)Node.Call(MethodName.GetLimitLeft); - set => Node.Call(MethodName.SetLimitLeft, value); - } - - public int LimitTop - { - get => (int)Node.Call(MethodName.GetLimitTop); - set => Node.Call(MethodName.SetLimitTop, value); - } - public int LimitRight - { - get => (int)Node.Call(MethodName.GetLimitRight); - set => Node.Call(MethodName.SetLimitRight, value); - } - public int LimitBottom - { - get => (int)Node.Call(MethodName.GetLimitBottom); - set => Node.Call(MethodName.SetLimitBottom, value); - } - public NodePath LimitTarget - { - get => (NodePath)Node.Call(MethodName.GetLimitTarget); - set => Node.Call(MethodName.SetLimitTarget, value); - } - - public Vector4I LimitMargin - { - get => (Vector4I)Node.Call(MethodName.GetLimitMargin); - set => Node.Call(MethodName.SetLimitMargin, 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)); - Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - ~PhantomCamera2D() - { - Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - public void SetLimit(Side side, int value) - { - Node.Call(MethodName.SetLimit, (int)side, value); - } - - public int GetLimit(Side side) - { - return (int)Node.Call(MethodName.GetLimit, (int)side); - } - - public new static class MethodName - { - public const string GetZoom = "get_zoom"; - public const string SetZoom = "set_zoom"; - - public const string GetSnapToPixel = "get_snap_to_pixel"; - public const string SetSnapToPixel = "set_snap_to_pixel"; - - public const string GetLimit = "get_limit"; - public const string SetLimit = "set_limit"; - - public const string GetLimitLeft = "get_limit_left"; - public const string SetLimitLeft = "set_limit_left"; - - public const string GetLimitTop = "get_limit_top"; - public const string SetLimitTop = "set_limit_top"; - - public const string GetLimitRight = "get_limit_right"; - public const string SetLimitRight = "set_limit_right"; - - public const string GetLimitBottom = "get_limit_bottom"; - public const string SetLimitBottom = "set_limit_bottom"; - - public const string GetLimitTarget = "get_limit_target"; - public const string SetLimitTarget = "set_limit_target"; - - public const string GetLimitMargin = "get_limit_margin"; - public const string SetLimitMargin = "set_limit_margin"; - } -} - -public class PhantomCamera3D : PhantomCamera -{ - public Node3D Node3D => (Node3D)Node; - - public delegate void LookAtTargetChangedEventHandler(); - public delegate void TweenInterruptedEventHandler(Node3D pCam); - - public event LookAtTargetChangedEventHandler? LookAtTargetChanged; - public event TweenInterruptedEventHandler? TweenInterrupted; - - private readonly Callable _callableLookAtTargetChanged; - private readonly Callable _callableTweenInterrupted; - - public LookAtMode LookAtMode => (LookAtMode)(int)Node.Call(MethodName.GetLookAtMode); - - public Camera3DResource Camera3DResource - { - get => new((Resource)Node.Call(MethodName.GetCamera3DResource)); - set => Node.Call(MethodName.SetCamera3DResource, value.Resource); - } - - public Vector3 ThirdPersonRotation - { - get => (Vector3)Node.Call(MethodName.GetThirdPersonRotation); - set => Node.Call(MethodName.SetThirdPersonRotation, value); - } - - public Vector3 ThirdPersonRotationDegrees - { - get => (Vector3)Node.Call(MethodName.GetThirdPersonRotationDegrees); - set => Node.Call(MethodName.SetThirdPersonRotationDegrees, value); - } - - public Quaternion ThirdPersonQuaternion - { - get => (Quaternion)Node.Call(MethodName.GetThirdPersonQuaternion); - set => Node.Call(MethodName.SetThirdPersonQuaternion, value); - } - - public float SpringLength - { - get => (float)Node.Call(MethodName.GetSpringLength); - set => Node.Call(MethodName.SetSpringLength, value); - } - - 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()); - _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); - - Node.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - ~PhantomCamera3D() - { - Node.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); - Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); - } - - public new static class MethodName - { - public const string GetLookAtMode = "get_look_at_mode"; - - public const string GetCamera3DResource = "get_camera_3d_resource"; - public const string SetCamera3DResource = "set_camera_3d_resource"; - - public const string GetThirdPersonRotation = "get_third_person_rotation"; - public const string SetThirdPersonRotation = "set_third_person_rotation"; - - public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; - public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; - - public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; - public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; - - public const string GetSpringLength = "get_spring_length"; - public const string SetSpringLength = "set_spring_length"; - } -} - -public class ActivePhantomCameraQueryResult -{ - private readonly GodotObject _obj; - - public bool Is2D => _obj.IsClass("Node2D"); - - public bool Is3D => _obj.IsClass("Node3D"); - - public ActivePhantomCameraQueryResult(GodotObject godotObject) => _obj = godotObject; - - public PhantomCamera2D? AsPhantomCamera2D() - { - return Is2D ? new PhantomCamera2D(_obj) : null; - } - - public PhantomCamera3D? AsPhantomCamera3D() - { - return Is3D ? new PhantomCamera3D(_obj) : null; - } -} - -public class PhantomCameraHost -{ - public Node Node { get; } - - // TODO: For Godot 4.3 - // public InterpolationMode InterpolationMode - // { - // get => (InterpolationMode)(int)Node.Call(MethodName.GetInterpolationMode); - // set => Node.Call(MethodName.SetInterpolationMode, (int)value); - // } - - public Camera2D? Camera2D => (Camera2D?)Node.Get(PropertyName.Camera2D); - - public Camera3D? Camera3D => (Camera3D?)Node.Get(PropertyName.Camera3D); - - public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween); - - public PhantomCameraHost(Node node) => Node = node; - - public ActivePhantomCameraQueryResult? GetActivePhantomCamera() - { - var result = Node.Call(MethodName.GetActivePhantomCamera); - return result.VariantType == Variant.Type.Nil ? null : new ActivePhantomCameraQueryResult(result.AsGodotObject()); - } - - public static class PropertyName - { - public const string Camera2D = "camera_2d"; - public const string Camera3D = "camera_3d"; - } - - public static class MethodName - { - public const string GetActivePhantomCamera = "get_active_pcam"; - public const string GetTriggerPhantomCameraTween = "get_trigger_pcam_tween"; - - public const string GetInterpolationMode = "get_interpolation_mode"; - public const string SetInterpolationMode = "set_interpolation_mode"; - } -} - -public static class PhantomCameraManager -{ - private static GodotObject? _instance; - - public static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); - - public static PhantomCamera2D[] PhantomCamera2Ds => - Instance.Call(MethodName.GetPhantomCamera2Ds).AsGodotArray() - .Select(node => new PhantomCamera2D(node)).ToArray(); - - public static PhantomCamera3D[] PhantomCamera3Ds => - Instance.Call(MethodName.GetPhantomCamera3Ds).AsGodotArray() - .Select(node => new PhantomCamera3D(node)).ToArray(); - - public static PhantomCameraHost[] PhantomCameraHosts => - Instance.Call(MethodName.GetPhantomCameraHosts).AsGodotArray() - .Select(node => new PhantomCameraHost(node)).ToArray(); - - 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"; - } -} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs new file mode 100644 index 00000000..9d41647e --- /dev/null +++ b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs @@ -0,0 +1,34 @@ +using System.Linq; +using Godot; +using PhantomCamera.Cameras; +using PhantomCamera.Hosts; + +#nullable enable + +namespace PhantomCamera.Managers; + +public static class PhantomCameraManager +{ + private static GodotObject? _instance; + + public static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); + + public static PhantomCamera2D[] PhantomCamera2Ds => + Instance.Call(MethodName.GetPhantomCamera2Ds).AsGodotArray() + .Select(node => new PhantomCamera2D(node)).ToArray(); + + public static PhantomCamera3D[] PhantomCamera3Ds => + Instance.Call(MethodName.GetPhantomCamera3Ds).AsGodotArray() + .Select(node => new PhantomCamera3D(node)).ToArray(); + + public static PhantomCameraHost[] PhantomCameraHosts => + Instance.Call(MethodName.GetPhantomCameraHosts).AsGodotArray() + .Select(node => new PhantomCameraHost(node)).ToArray(); + + 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"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd index b5b19fc9..999df238 100644 --- a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd +++ b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd @@ -19,9 +19,8 @@ var phantom_camera_3ds: Array: ## Note: To support disable_3d export templates f var _phantom_camera_3d_list: Array ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. func _ready(): - if Engine.has_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME): - Engine.unregister_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME) - Engine.register_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME, self) + if not Engine.has_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME): + Engine.register_singleton(PHANTOM_CAMERA_CONSTS.PCAM_MANAGER_NODE_NAME, self) func _enter_tree(): Engine.physics_jitter_fix = 0 diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs new file mode 100644 index 00000000..a436950b --- /dev/null +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs @@ -0,0 +1,162 @@ +using Godot; +using PhantomCamera.Resources; + +// TODO: missing shared properties +// - get/set pcam_host_owner +// - get/set follow_target +// - get/set follow_targets +// - get/set follow_path +// - get/set follow_offset +// - get/set follow_damping +// - get/set follow_damping_value +// - dead_zone_width +// - dead_zone_height + +#nullable enable + +namespace PhantomCamera.Cameras; + +public enum FollowMode +{ + None, + Glued, + Simple, + Group, + Path, + Framed, + ThirdPerson +} + +public enum LookAtMode +{ + None, + Mimic, + Simple, + Group +} + +public enum InactiveUpdateMode +{ + Always, + Never +} + +public abstract class PhantomCamera // TODO: FollowTarget, LookAtTarget +{ + protected readonly GodotObject Node; + + public delegate void BecameActiveEventHandler(); + public delegate void BecameInactiveEventHandler(); + public delegate void FollowTargetChangedEventHandler(); + public delegate void DeadZoneChangedEventHandler(); + public delegate void TweenStartedEventHandler(); + public delegate void IsTweeningEventHandler(); + public delegate void TweenCompletedEventHandler(); + + public event BecameActiveEventHandler? BecameActive; + public event BecameInactiveEventHandler? BecameInactive; + public event FollowTargetChangedEventHandler? FollowTargetChanged; + public event DeadZoneChangedEventHandler? DeadZoneChanged; + public event TweenStartedEventHandler? TweenStarted; + 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); + set => Node.Call(MethodName.SetPriority, value); + } + + public FollowMode FollowMode => (FollowMode)(int)Node.Call(MethodName.GetFollowMode); + + public bool IsActive => (bool)Node.Call(MethodName.IsActive); + + public PhantomCameraTween TweenResource + { + get => new((Resource)Node.Call(MethodName.GetTweenResource)); + set => Node.Call(MethodName.SetTweenResource, (GodotObject)value.Resource); + } + + public bool TweenOnLoad + { + get => (bool)Node.Call(MethodName.GetTweenOnLoad); + set => Node.Call(MethodName.SetTweenOnLoad, value); + } + + public InactiveUpdateMode InactiveUpdateMode + { + get => (InactiveUpdateMode)(int)Node.Call(MethodName.GetInactiveUpdateMode); + set => Node.Call(MethodName.SetInactiveUpdateMode, (int)value); + } + + protected PhantomCamera(GodotObject phantomCameraNode) + { + 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()); + + 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); + } + + public static class MethodName + { + public const string GetFollowMode = "get_follow_mode"; + public const string IsActive = "is_active"; + + public const string GetPriority = "get_priority"; + public const string SetPriority = "set_priority"; + + public const string GetTweenResource = "get_tween_resource"; + public const string SetTweenResource = "set_tween_resource"; + + public const string GetTweenOnLoad = "get_tween_on_load"; + public const string SetTweenOnLoad = "set_tween_on_load"; + + public const string GetInactiveUpdateMode = "get_inactive_update_mode"; + public const string SetInactiveUpdateMode = "set_inactive_update_mode"; + } + + 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 LookAtTargetChanged = "look_at_target_changed"; + public const string DeadZoneChanged = "dead_zone_changed"; + public const string TweenStarted = "tween_started"; + public const string IsTweening = "is_tweening"; + public const string TweenCompleted = "tween_completed"; + public const string TweenInterrupted = "tween_interrupted"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs new file mode 100644 index 00000000..b7e4ef6b --- /dev/null +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs @@ -0,0 +1,156 @@ +using Godot; + +// TODO: missing 2d properties +// - get/set auto_zoom (2d only) +// - get/set auto_zoom_min (2d only) +// - get/set auto_zoom_max (2d only) +// - get/set auto_zoom_margin (2d only) +// - draw_limits (2d only) + +#nullable enable + +namespace PhantomCamera.Cameras; + +public class PhantomCamera2D : PhantomCamera +{ + public Node2D Node2D => (Node2D)Node; + + public delegate void TweenInterruptedEventHandler(Node2D pCam); + + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableTweenInterrupted; + + public Vector2 Zoom + { + get => (Vector2)Node.Call(MethodName.GetZoom); + set => Node.Call(MethodName.SetZoom, value); + } + + public bool SnapToPixel + { + get => (bool)Node.Call(MethodName.GetSnapToPixel); + set => Node.Call(MethodName.SetSnapToPixel, value); + } + + public int LimitLeft + { + get => (int)Node.Call(MethodName.GetLimitLeft); + set => Node.Call(MethodName.SetLimitLeft, value); + } + + public int LimitTop + { + get => (int)Node.Call(MethodName.GetLimitTop); + set => Node.Call(MethodName.SetLimitTop, value); + } + + public int LimitRight + { + get => (int)Node.Call(MethodName.GetLimitRight); + set => Node.Call(MethodName.SetLimitRight, value); + } + + public int LimitBottom + { + get => (int)Node.Call(MethodName.GetLimitBottom); + set => Node.Call(MethodName.SetLimitBottom, value); + } + + public Vector4I LimitMargin + { + get => (Vector4I)Node.Call(MethodName.GetLimitMargin); + set => Node.Call(MethodName.SetLimitMargin, 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)); + Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera2D() + { + Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public void SetLimitTarget(TileMap tileMap) + { + Node.Call(MethodName.SetLimitTarget, tileMap.GetPath()); + } + + public void SetLimitTarget(CollisionShape2D shape2D) + { + Node.Call(MethodName.SetLimitTarget, shape2D.GetPath()); + } + + public LimitTargetQueryResult? GetLimitTarget() + { + var result = (NodePath)Node.Call(MethodName.GetLimitTarget); + return result.IsEmpty ? null : new LimitTargetQueryResult(Node2D.GetNode(result)); + } + + public void SetLimit(Side side, int value) + { + Node.Call(MethodName.SetLimit, (int)side, value); + } + + public int GetLimit(Side side) + { + return (int)Node.Call(MethodName.GetLimit, (int)side); + } + + public new static class MethodName + { + public const string GetZoom = "get_zoom"; + public const string SetZoom = "set_zoom"; + + public const string GetSnapToPixel = "get_snap_to_pixel"; + public const string SetSnapToPixel = "set_snap_to_pixel"; + + public const string GetLimit = "get_limit"; + public const string SetLimit = "set_limit"; + + public const string GetLimitLeft = "get_limit_left"; + public const string SetLimitLeft = "set_limit_left"; + + public const string GetLimitTop = "get_limit_top"; + public const string SetLimitTop = "set_limit_top"; + + public const string GetLimitRight = "get_limit_right"; + public const string SetLimitRight = "set_limit_right"; + + public const string GetLimitBottom = "get_limit_bottom"; + public const string SetLimitBottom = "set_limit_bottom"; + + public const string GetLimitTarget = "get_limit_target"; + public const string SetLimitTarget = "set_limit_target"; + + public const string GetLimitMargin = "get_limit_margin"; + public const string SetLimitMargin = "set_limit_margin"; + } +} + +public class LimitTargetQueryResult +{ + private readonly GodotObject _obj; + + public bool IsTileMap => _obj.IsClass("TileMap"); + + public bool IsCollisionShape2D => _obj.IsClass("CollisionShape2D"); + + public LimitTargetQueryResult(GodotObject godotObject) => _obj = godotObject; + + public TileMap? AsTileMap() + { + return IsTileMap ? (TileMap)_obj : null; + } + + public CollisionShape2D? AsCollisionShape2D() + { + return IsCollisionShape2D ? (CollisionShape2D)_obj : null; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs new file mode 100644 index 00000000..a68f1b6e --- /dev/null +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs @@ -0,0 +1,106 @@ +using Godot; +using PhantomCamera.Resources; + +// TODO: missing shared properties +// - get/set follow_distance (3d only) +// - get/set auto_follow_distance (3d only) +// - get/set auto_follow_distance_min (3d only) +// - get/set auto_follow_distance_max (3d only) +// - get/set auto_follow_distance_divisor (3d only) +// - get/set look_at_target (3d only) +// - get/set look_at_targets (3d only) +// - get/set viewport_position (3d only) +// - get/set collision_mask (3d only) +// - get/set shape (3d only) +// - get/set margin (3d only) +// - get/set look_at_offset (3d only) +// - get/set look_at_damping (3d only) +// - get/set look_at_damping_value (3d only) + +#nullable enable + +namespace PhantomCamera.Cameras; + +public class PhantomCamera3D : PhantomCamera +{ + public Node3D Node3D => (Node3D)Node; + + public delegate void LookAtTargetChangedEventHandler(); + public delegate void TweenInterruptedEventHandler(Node3D pCam); + + public event LookAtTargetChangedEventHandler? LookAtTargetChanged; + public event TweenInterruptedEventHandler? TweenInterrupted; + + private readonly Callable _callableLookAtTargetChanged; + private readonly Callable _callableTweenInterrupted; + + public LookAtMode LookAtMode => (LookAtMode)(int)Node.Call(MethodName.GetLookAtMode); + + public Camera3DResource Camera3DResource + { + get => new((Resource)Node.Call(MethodName.GetCamera3DResource)); + set => Node.Call(MethodName.SetCamera3DResource, value.Resource); + } + + public Vector3 ThirdPersonRotation + { + get => (Vector3)Node.Call(MethodName.GetThirdPersonRotation); + set => Node.Call(MethodName.SetThirdPersonRotation, value); + } + + public Vector3 ThirdPersonRotationDegrees + { + get => (Vector3)Node.Call(MethodName.GetThirdPersonRotationDegrees); + set => Node.Call(MethodName.SetThirdPersonRotationDegrees, value); + } + + public Quaternion ThirdPersonQuaternion + { + get => (Quaternion)Node.Call(MethodName.GetThirdPersonQuaternion); + set => Node.Call(MethodName.SetThirdPersonQuaternion, value); + } + + public float SpringLength + { + get => (float)Node.Call(MethodName.GetSpringLength); + set => Node.Call(MethodName.SetSpringLength, value); + } + + 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()); + _callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); + + Node.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + Node.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PhantomCamera3D() + { + Node.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + Node.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + public new static class MethodName + { + public const string GetLookAtMode = "get_look_at_mode"; + + public const string GetCamera3DResource = "get_camera_3d_resource"; + public const string SetCamera3DResource = "set_camera_3d_resource"; + + public const string GetThirdPersonRotation = "get_third_person_rotation"; + public const string SetThirdPersonRotation = "set_third_person_rotation"; + + public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; + public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; + + public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; + public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; + + public const string GetSpringLength = "get_spring_length"; + public const string SetSpringLength = "set_spring_length"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs new file mode 100644 index 00000000..34cf663b --- /dev/null +++ b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs @@ -0,0 +1,75 @@ +using Godot; +using PhantomCamera.Cameras; + +#nullable enable + +namespace PhantomCamera.Hosts; + +public enum InterpolationMode +{ + Auto, + Idle, + Physics +} + +public class PhantomCameraHost +{ + public Node Node { get; } + + // TODO: For Godot 4.3 + // public InterpolationMode InterpolationMode + // { + // get => (InterpolationMode)(int)Node.Call(MethodName.GetInterpolationMode); + // set => Node.Call(MethodName.SetInterpolationMode, (int)value); + // } + + public Camera2D? Camera2D => (Camera2D?)Node.Get(PropertyName.Camera2D); + + public Camera3D? Camera3D => (Camera3D?)Node.Get(PropertyName.Camera3D); + + public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween); + + public PhantomCameraHost(Node node) => Node = node; + + public ActivePhantomCameraQueryResult? GetActivePhantomCamera() + { + var result = Node.Call(MethodName.GetActivePhantomCamera); + return result.VariantType == Variant.Type.Nil ? null : new ActivePhantomCameraQueryResult(result.AsGodotObject()); + } + + public static class PropertyName + { + public const string Camera2D = "camera_2d"; + public const string Camera3D = "camera_3d"; + } + + public static class MethodName + { + public const string GetActivePhantomCamera = "get_active_pcam"; + public const string GetTriggerPhantomCameraTween = "get_trigger_pcam_tween"; + + public const string GetInterpolationMode = "get_interpolation_mode"; + public const string SetInterpolationMode = "set_interpolation_mode"; + } +} + +public class ActivePhantomCameraQueryResult +{ + private readonly GodotObject _obj; + + public bool Is2D => _obj.IsClass("Node2D"); + + public bool Is3D => _obj.IsClass("Node3D"); + + public ActivePhantomCameraQueryResult(GodotObject godotObject) => _obj = godotObject; + + public PhantomCamera2D? AsPhantomCamera2D() + { + return Is2D ? new PhantomCamera2D(_obj) : null; + } + + public PhantomCamera3D? AsPhantomCamera3D() + { + return Is3D ? new PhantomCamera3D(_obj) : null; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/resources/Camera3DResource.cs b/addons/phantom_camera/scripts/resources/Camera3DResource.cs new file mode 100644 index 00000000..0d5dd399 --- /dev/null +++ b/addons/phantom_camera/scripts/resources/Camera3DResource.cs @@ -0,0 +1,122 @@ +using Godot; + +namespace PhantomCamera.Resources; + +public enum ProjectionType +{ + Perspective, + Orthogonal, + Frustum +} + +public class Camera3DResource +{ + public readonly Resource Resource; + + public const float MinOffset = 0; + public const float MaxOffset = 1; + + public const float MinFov = 1; + public const float MaxFov = 179; + + public const float MinSize = 0.001f; + public const float MaxSize = 100; + + public const float MinNear = 0.001f; + public const float MaxNear = 10; + + public const float MinFar = 0.01f; + public const float MaxFar = 4000; + + public int CullMask + { + get => (int)Resource.Call(MethodName.GetCullMask); + set => Resource.Call(MethodName.SetCullMask, value); + } + + public float HOffset + { + get => (float)Resource.Call(MethodName.GetHOffset); + set => Resource.Call(MethodName.SetHOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); + } + + public float VOffset + { + get => (float)Resource.Call(MethodName.GetVOffset); + set => Resource.Call(MethodName.SetVOffset, Mathf.Clamp(value, MinOffset, MaxOffset)); + } + + public ProjectionType Projection + { + get => (ProjectionType)(int)Resource.Call(MethodName.GetProjection); + set => Resource.Call(MethodName.SetProjection, (int)value); + } + + public float Fov + { + get => (float)Resource.Call(MethodName.GetFov); + set => Resource.Call(MethodName.SetFov, Mathf.Clamp(value, MinFov, MaxFov)); + } + + public float Size + { + get => (float)Resource.Call(MethodName.GetSize); + set => Resource.Call(MethodName.SetSize, Mathf.Clamp(value, MinSize, MaxSize)); + } + + public Vector2 FrustumOffset + { + get => (Vector2)Resource.Call(MethodName.GetFrustumOffset); + set => Resource.Call(MethodName.SetFrustumOffset, value); + } + + public float Near + { + get => (float)Resource.Call(MethodName.GetNear); + set => Resource.Call(MethodName.SetNear, Mathf.Clamp(value, MinNear, MaxNear)); + } + + public float Far + { + get => (float)Resource.Call(MethodName.GetFar); + set => Resource.Call(MethodName.SetFar, Mathf.Clamp(value, MinFar, MaxFar)); + } + + public Camera3DResource(Resource resource) => Resource = resource; + + public void SetCullMaskValue(int layerNumber, bool value) + { + Resource.Call(MethodName.SetCullMaskValue, layerNumber, value); + } + + public static class MethodName + { + public const string GetCullMask = "get_cull_mask"; + public const string SetCullMask = "set_cull_mask"; + public const string SetCullMaskValue = "set_cull_mask_value"; + + public const string GetHOffset = "get_h_offset"; + public const string SetHOffset = "set_h_offset"; + + public const string GetVOffset = "get_v_offset"; + public const string SetVOffset = "set_v_offset"; + + public const string GetProjection = "get_projection"; + public const string SetProjection = "set_projection"; + + public const string GetFov = "get_fov"; + public const string SetFov = "set_fov"; + + public const string GetSize = "get_size"; + public const string SetSize = "set_size"; + + public const string GetFrustumOffset = "get_frustum_offset"; + public const string SetFrustumOffset = "set_frustum_offset"; + + public const string GetNear = "get_near"; + public const string SetNear = "set_near"; + + public const string GetFar = "get_far"; + public const string SetFar = "set_far"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs new file mode 100644 index 00000000..b596f0a8 --- /dev/null +++ b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs @@ -0,0 +1,58 @@ +using Godot; + +namespace PhantomCamera.Resources; + +public enum TransitionType +{ + Linear, + Sine, + Quintic, + Quartic, + Quadratic, + Exponential, + Elastic, + Cubic, + Circ, + Bounce, + Back +} + +public enum EaseType +{ + In, + Out, + InOut, + OutIn +} + +public class PhantomCameraTween +{ + public Resource Resource { get; } + + public float Duration + { + get => (float)Resource.Get(PropertyName.Duration); + set => Resource.Set(PropertyName.Duration, value); + } + + public TransitionType Transition + { + get => (TransitionType)(int)Resource.Get(PropertyName.Transition); + set => Resource.Set(PropertyName.Transition, (int)value); + } + + public EaseType Ease + { + get => (EaseType)(int)Resource.Get(PropertyName.Ease); + set => Resource.Set(PropertyName.Ease, (int)value); + } + + public PhantomCameraTween(Resource tweenResource) => Resource = tweenResource; + + public static class PropertyName + { + public const string Duration = "durartion"; + public const string Transition = "transition"; + public const string Ease = "ease"; + } +} \ No newline at end of file diff --git a/tests/scenes/test_scene_2d.tscn b/tests/scenes/test_scene_2d.tscn index 0d23ab61..77c3b3c0 100644 --- a/tests/scenes/test_scene_2d.tscn +++ b/tests/scenes/test_scene_2d.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://bx61t0ytiwtwm"] +[gd_scene load_steps=7 format=3 uid="uid://bx61t0ytiwtwm"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd" id="1_qr8fr"] [ext_resource type="Script" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="2_53jhm"] @@ -10,6 +10,10 @@ duration = 1.0 transition = 0 ease = 2 +[sub_resource type="TileSet" id="TileSet_lpbxs"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_4v04e"] + [node name="TestScene2D" type="Node2D"] [node name="Camera2D" type="Camera2D" parent="."] @@ -20,3 +24,12 @@ script = ExtResource("1_qr8fr") [node name="PhantomCamera2D" type="Node2D" parent="."] script = ExtResource("2_53jhm") tween_resource = SubResource("Resource_f0onm") + +[node name="TileMap" type="TileMap" parent="."] +tile_set = SubResource("TileSet_lpbxs") +format = 2 + +[node name="Area2D" type="Area2D" parent="."] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +shape = SubResource("RectangleShape2D_4v04e") diff --git a/tests/scripts/TestPhantomCameraWrapper.cs b/tests/scripts/TestPhantomCameraWrapper.cs index a75c3835..14ac6043 100644 --- a/tests/scripts/TestPhantomCameraWrapper.cs +++ b/tests/scripts/TestPhantomCameraWrapper.cs @@ -1,6 +1,9 @@ using System.Diagnostics; using Godot; using PhantomCamera; +using PhantomCamera.Cameras; +using PhantomCamera.Managers; +using PhantomCamera.Resources; namespace PhantomCameraTests; @@ -28,13 +31,13 @@ private void Test2D() var testScene = _scene2d.Instantiate(); AddChild(testScene); - // PhantomCameraManager Tests + // PhantomCameraManager tests Debug.Assert(PhantomCameraManager.Instance != null); Debug.Assert(PhantomCameraManager.PhantomCamera3Ds.Length == 0); Debug.Assert(PhantomCameraManager.PhantomCamera2Ds.Length == 1); Debug.Assert(PhantomCameraManager.PhantomCameraHosts.Length == 1); - // PhantomCameraHost Tests + // PhantomCameraHost tests var cameraHost = testScene.GetNode("Camera2D/PhantomCameraHost").AsPhantomCameraHost(); Debug.Assert(cameraHost.Node != null); Debug.Assert(cameraHost.Camera2D != null); @@ -47,11 +50,98 @@ private void Test2D() Debug.Assert(!cameraQuery.Is3D); Debug.Assert(cameraQuery.AsPhantomCamera3D() == null); - // PhantomCamera2D Tests + // PhantomCamera shared tests var camera = cameraQuery.AsPhantomCamera2D(); Debug.Assert(camera != null); - GD.Print(camera.Node2D); - // TODO: finish implementing 2d tests + Debug.Assert(camera.Node2D != null); + + Debug.Assert(camera.FollowMode == FollowMode.None); + Debug.Assert(camera.IsActive); + + var priority = camera.Priority; + camera.Priority += 10; + Debug.Assert(camera.Priority == priority + 10); + + var tweenOnLoad = camera.TweenOnLoad; + camera.TweenOnLoad = !camera.TweenOnLoad; + Debug.Assert(camera.TweenOnLoad != tweenOnLoad); + + Debug.Assert(camera.InactiveUpdateMode == InactiveUpdateMode.Always); + camera.InactiveUpdateMode = InactiveUpdateMode.Never; + Debug.Assert(camera.InactiveUpdateMode == InactiveUpdateMode.Never); + + // TweenResource tests + var tweenResource = camera.TweenResource; + Debug.Assert(tweenResource.Resource != null); + + var tweenDuration = tweenResource.Duration; + tweenResource.Duration += 1.0f; + Debug.Assert((tweenResource.Duration - (tweenDuration + 1.0f)) <= float.Epsilon); + + tweenResource.Ease = EaseType.Out; + Debug.Assert(tweenResource.Ease == EaseType.Out); + + tweenResource.Transition = TransitionType.Sine; + Debug.Assert(tweenResource.Transition == TransitionType.Sine); + + var tweenResourceScript = GD.Load("res://addons/phantom_camera/scripts/resources/tween_resource.gd"); + var newTweenResource = new PhantomCameraTween(tweenResourceScript.New().As()) + { + Duration = 1.5f, + Ease = EaseType.In, + Transition = TransitionType.Cubic + }; + camera.TweenResource = newTweenResource; + + Debug.Assert((camera.TweenResource.Duration - 1.5f) <= float.Epsilon); + Debug.Assert(camera.TweenResource.Ease == EaseType.In); + Debug.Assert(camera.TweenResource.Transition == TransitionType.Cubic); + + // PhantomCamera2D tests + camera.Zoom = new Vector2(2, 2); + Debug.Assert(camera.Zoom.Equals(new Vector2(2, 2))); + + var snapToPixel = camera.SnapToPixel; + camera.SnapToPixel = !camera.SnapToPixel; + Debug.Assert(camera.SnapToPixel != snapToPixel); + + camera.LimitLeft = 2; + camera.LimitTop = 3; + camera.LimitRight = 4; + camera.LimitBottom = 5; + Debug.Assert(camera.LimitLeft == camera.GetLimit(Side.Left)); + Debug.Assert(camera.LimitTop == camera.GetLimit(Side.Top)); + Debug.Assert(camera.LimitRight == camera.GetLimit(Side.Right)); + Debug.Assert(camera.LimitBottom == camera.GetLimit(Side.Bottom)); + + camera.SetLimit(Side.Left, 5); + camera.SetLimit(Side.Top, 4); + camera.SetLimit(Side.Right, 3); + camera.SetLimit(Side.Bottom, 2); + Debug.Assert(camera.LimitLeft == camera.GetLimit(Side.Left)); + Debug.Assert(camera.LimitTop == camera.GetLimit(Side.Top)); + Debug.Assert(camera.LimitRight == camera.GetLimit(Side.Right)); + Debug.Assert(camera.LimitBottom == camera.GetLimit(Side.Bottom)); + + Debug.Assert(camera.GetLimitTarget() == null); + + var tileMap = testScene.GetNode("TileMap"); + camera.SetLimitTarget(tileMap); + var limitTarget = camera.GetLimitTarget(); + Debug.Assert(limitTarget != null); + Debug.Assert(limitTarget.IsTileMap); + Debug.Assert(limitTarget.AsTileMap() != null); + + var shape2D = testScene.GetNode("Area2D/CollisionShape2D"); + camera.SetLimitTarget(shape2D); + limitTarget = camera.GetLimitTarget(); + Debug.Assert(limitTarget != null); + Debug.Assert(limitTarget.IsCollisionShape2D); + Debug.Assert(limitTarget.AsCollisionShape2D() != null); + + // TODO: test LimitMargin + + // TODO: test signals RemoveChild(testScene); } @@ -83,8 +173,6 @@ private void Test3D() // PhantomCamera3D Tests var camera = cameraQuery.AsPhantomCamera3D(); Debug.Assert(camera != null); - GD.Print(camera.Node3D); - // TODO: finish implementing 3d tests RemoveChild(testScene); } From 96bd65c1b776f414198f1577cfc5b35527f00b17 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Wed, 24 Jul 2024 22:56:53 -0600 Subject: [PATCH 6/8] wip: #286 C# Wrapper - added missing shared camera properties - added missing phantom camera 2d properties - added missing phantom camera 3d properties --- .../scripts/phantom_camera/PhantomCamera.cs | 66 +++++-- .../scripts/phantom_camera/PhantomCamera2D.cs | 89 ++++++++- .../scripts/phantom_camera/PhantomCamera3D.cs | 176 ++++++++++++++++-- tests/scenes/test_scene_2d.tscn | 2 + tests/scripts/TestPhantomCameraWrapper.cs | 2 +- 5 files changed, 296 insertions(+), 39 deletions(-) diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs index a436950b..cbf69d80 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs @@ -1,17 +1,7 @@ using Godot; +using PhantomCamera.Hosts; using PhantomCamera.Resources; -// TODO: missing shared properties -// - get/set pcam_host_owner -// - get/set follow_target -// - get/set follow_targets -// - get/set follow_path -// - get/set follow_offset -// - get/set follow_damping -// - get/set follow_damping_value -// - dead_zone_width -// - dead_zone_height - #nullable enable namespace PhantomCamera.Cameras; @@ -41,7 +31,7 @@ public enum InactiveUpdateMode Never } -public abstract class PhantomCamera // TODO: FollowTarget, LookAtTarget +public abstract class PhantomCamera { protected readonly GodotObject Node; @@ -79,6 +69,30 @@ public int Priority public bool IsActive => (bool)Node.Call(MethodName.IsActive); + public PhantomCameraHost PhantomCameraHostOwner + { + get => new((Node)Node.Call(MethodName.GetPCamHostOwner)); + set => Node.Call(MethodName.SetPCamHostOwner, value.Node); + } + + public bool FollowDamping + { + get => (bool)Node.Call(MethodName.GetFollowDamping); + set => Node.Call(MethodName.SetFollowDamping, value); + } + + public float DeadZoneWidth + { + get => (float)Node.Get(PropertyName.DeadZoneWidth); + set => Node.Set(PropertyName.DeadZoneWidth, value); + } + + public float DeadZoneHeight + { + get => (float)Node.Get(PropertyName.DeadZoneHeight); + set => Node.Set(PropertyName.DeadZoneHeight, value); + } + public PhantomCameraTween TweenResource { get => new((Resource)Node.Call(MethodName.GetTweenResource)); @@ -136,6 +150,28 @@ public static class MethodName public const string GetPriority = "get_priority"; public const string SetPriority = "set_priority"; + + public const string GetPCamHostOwner = "get_pcam_host_owner"; + public const string SetPCamHostOwner = "set_pcam_host_owner"; + + public const string GetFollowTarget = "get_follow_target"; + public const string SetFollowTarget = "set_follow_target"; + + public const string GetFollowTargets = "get_follow_targets"; + public const string SetFollowTargets = "set_follow_targets"; + + public const string GetFollowPath = "get_follow_path"; + public const string SetFollowPath = "set_follow_path"; + + public const string GetFollowOffset = "get_follow_offset"; + public const string SetFollowOffset = "set_follow_offset"; + + public const string GetFollowDamping = "get_follow_damping"; + public const string SetFollowDamping = "set_follow_damping"; + + public const string GetFollowDampingValue = "get_follow_damping_value"; + public const string SetFollowDampingValue = "set_follow_damping_value"; + public const string GetTweenResource = "get_tween_resource"; public const string SetTweenResource = "set_tween_resource"; @@ -147,6 +183,12 @@ public static class MethodName public const string SetInactiveUpdateMode = "set_inactive_update_mode"; } + public static class PropertyName + { + public const string DeadZoneWidth = "dead_zone_width"; + public const string DeadZoneHeight = "dead_zone_height"; + } + public static class SignalName { public const string BecameActive = "became_active"; diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs index b7e4ef6b..e27ed2a4 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs @@ -1,11 +1,5 @@ -using Godot; - -// TODO: missing 2d properties -// - get/set auto_zoom (2d only) -// - get/set auto_zoom_min (2d only) -// - get/set auto_zoom_max (2d only) -// - get/set auto_zoom_margin (2d only) -// - draw_limits (2d only) +using System.Linq; +using Godot; #nullable enable @@ -21,6 +15,36 @@ public class PhantomCamera2D : PhantomCamera private readonly Callable _callableTweenInterrupted; + public Node2D FollowTarget + { + get => (Node2D)Node2D.Call(PhantomCamera.MethodName.GetFollowTarget); + set => Node2D.Call(PhantomCamera.MethodName.SetFollowTarget, value); + } + + public Node2D[] FollowTargets + { + get => Node2D.Call(PhantomCamera.MethodName.GetFollowTargets).AsGodotArray().ToArray(); + set => Node2D.Call(PhantomCamera.MethodName.SetFollowTargets, value); + } + + public Path2D FollowPath + { + get => (Path2D)Node2D.Call(PhantomCamera.MethodName.GetFollowPath); + set => Node2D.Call(PhantomCamera.MethodName.SetFollowPath, value); + } + + public Vector2 FollowOffset + { + get => (Vector2)Node2D.Call(PhantomCamera.MethodName.GetFollowOffset); + set => Node2D.Call(PhantomCamera.MethodName.SetFollowOffset, value); + } + + public Vector2 FollowDampingValue + { + get => (Vector2)Node2D.Call(PhantomCamera.MethodName.GetFollowDampingValue); + set => Node2D.Call(PhantomCamera.MethodName.SetFollowDampingValue, value); + } + public Vector2 Zoom { get => (Vector2)Node.Call(MethodName.GetZoom); @@ -56,13 +80,43 @@ public int LimitBottom get => (int)Node.Call(MethodName.GetLimitBottom); set => Node.Call(MethodName.SetLimitBottom, value); } - + public Vector4I LimitMargin { get => (Vector4I)Node.Call(MethodName.GetLimitMargin); set => Node.Call(MethodName.SetLimitMargin, value); } + public bool AutoZoom + { + get => (bool)Node2D.Call(MethodName.GetAutoZoom); + set => Node2D.Call(MethodName.SetAutoZoom, value); + } + + public float AutoZoomMin + { + get => (float)Node2D.Call(MethodName.GetAutoZoomMin); + set => Node2D.Call(MethodName.SetAutoZoomMin, value); + } + + public float AutoZoomMax + { + get => (float)Node2D.Call(MethodName.GetAutoZoomMax); + set => Node2D.Call(MethodName.SetAutoZoomMax, value); + } + + public Vector4 AutoZoomMargin + { + get => (Vector4)Node2D.Call(MethodName.GetAutoZoomMargin); + set => Node2D.Call(MethodName.SetAutoZoomMargin, value); + } + + public bool DrawLimits + { + get => (bool)Node2D.Get(PropertyName.DrawLimits); + set => Node2D.Set(PropertyName.DrawLimits, value); + } + public static PhantomCamera2D FromScript(string path) => new(GD.Load(path).New().AsGodotObject()); public static PhantomCamera2D FromScript(GDScript script) => new(script.New().AsGodotObject()); @@ -131,6 +185,23 @@ public int GetLimit(Side side) public const string GetLimitMargin = "get_limit_margin"; public const string SetLimitMargin = "set_limit_margin"; + + public const string GetAutoZoom = "get_auto_zoom"; + public const string SetAutoZoom = "set_auto_zoom"; + + public const string GetAutoZoomMin = "get_auto_zoom_min"; + public const string SetAutoZoomMin = "set_auto_zoom_min"; + + public const string GetAutoZoomMax = "get_auto_zoom_max"; + public const string SetAutoZoomMax = "set_auto_zoom_max"; + + public const string GetAutoZoomMargin = "get_auto_zoom_margin"; + public const string SetAutoZoomMargin = "set_auto_zoom_margin"; + } + + public new static class PropertyName + { + public const string DrawLimits = "draw_limits"; } } diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs index a68f1b6e..b8f0b821 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs @@ -1,22 +1,7 @@ -using Godot; +using System.Linq; +using Godot; using PhantomCamera.Resources; -// TODO: missing shared properties -// - get/set follow_distance (3d only) -// - get/set auto_follow_distance (3d only) -// - get/set auto_follow_distance_min (3d only) -// - get/set auto_follow_distance_max (3d only) -// - get/set auto_follow_distance_divisor (3d only) -// - get/set look_at_target (3d only) -// - get/set look_at_targets (3d only) -// - get/set viewport_position (3d only) -// - get/set collision_mask (3d only) -// - get/set shape (3d only) -// - get/set margin (3d only) -// - get/set look_at_offset (3d only) -// - get/set look_at_damping (3d only) -// - get/set look_at_damping_value (3d only) - #nullable enable namespace PhantomCamera.Cameras; @@ -33,7 +18,37 @@ public class PhantomCamera3D : PhantomCamera private readonly Callable _callableLookAtTargetChanged; private readonly Callable _callableTweenInterrupted; + + public Node3D FollowTarget + { + get => (Node3D)Node3D.Call(PhantomCamera.MethodName.GetFollowTarget); + set => Node3D.Call(PhantomCamera.MethodName.SetFollowTarget, value); + } + + public Node3D[] FollowTargets + { + get => Node3D.Call(PhantomCamera.MethodName.GetFollowTargets).AsGodotArray().ToArray(); + set => Node3D.Call(PhantomCamera.MethodName.SetFollowTargets, value); + } + + public Path3D FollowPath + { + get => (Path3D)Node3D.Call(PhantomCamera.MethodName.GetFollowPath); + set => Node3D.Call(PhantomCamera.MethodName.SetFollowPath, value); + } + public Vector3 FollowOffset + { + get => (Vector3)Node3D.Call(PhantomCamera.MethodName.GetFollowOffset); + set => Node3D.Call(PhantomCamera.MethodName.SetFollowOffset, value); + } + + public Vector3 FollowDampingValue + { + get => (Vector3)Node3D.Call(PhantomCamera.MethodName.GetFollowDampingValue); + set => Node3D.Call(PhantomCamera.MethodName.SetFollowDampingValue, value); + } + public LookAtMode LookAtMode => (LookAtMode)(int)Node.Call(MethodName.GetLookAtMode); public Camera3DResource Camera3DResource @@ -66,6 +81,91 @@ public float SpringLength set => Node.Call(MethodName.SetSpringLength, value); } + public float FollowDistance + { + get => (float)Node3D.Call(MethodName.GetFollowDistance); + set => Node3D.Call(MethodName.SetFollowDistance, value); + } + + public bool AutoFollowDistance + { + get => (bool)Node3D.Call(MethodName.GetAutoFollowDistance); + set => Node3D.Call(MethodName.SetAutoFollowDistance, value); + } + + public float AutoFollowDistanceMin + { + get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceMin); + set => Node3D.Call(MethodName.SetAutoFollowDistanceMin, value); + } + + public float AutoFollowDistanceMax + { + get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceMax); + set => Node3D.Call(MethodName.SetAutoFollowDistanceMax, value); + } + + public float AutoFollowDistanceDivisor + { + get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceDivisor); + set => Node3D.Call(MethodName.SetAutoFollowDistanceDivisor, value); + } + + public Node3D LookAtTarget + { + get => (Node3D)Node3D.Call(MethodName.GetLookAtTarget); + set => Node3D.Call(MethodName.SetLookAtTarget, value); + } + + public Node3D[] LookAtTargets + { + get => Node3D.Call(MethodName.GetLookAtTargets).AsGodotArray().ToArray(); + set => Node3D.Call(MethodName.SetLookAtTargets, value); + } + + public Vector2 ViewportPosition + { + get => (Vector2)Node3D.Call(MethodName.GetViewportPosition); + set => Node3D.Call(MethodName.SetViewportPosition, value); + } + + public int CollisionMask + { + get => (int)Node3D.Call(MethodName.GetCollisionMask); + set => Node3D.Call(MethodName.SetCollisionMask, value); + } + + public Shape3D Shape + { + get => (Shape3D)Node3D.Call(MethodName.GetShape); + set => Node3D.Call(MethodName.SetShape, value); + } + + public float Margin + { + get => (float)Node3D.Call(MethodName.GetMargin); + set => Node3D.Call(MethodName.SetMargin, value); + } + + public Vector3 LookAtOffset + { + get => (Vector3)Node3D.Call(MethodName.GetLookAtOffset); + set => Node3D.Call(MethodName.SetLookAtOffset, value); + } + + public bool LookAtDamping + { + get => (bool)Node3D.Call(MethodName.GetLookAtDamping); + set => Node3D.Call(MethodName.SetLookAtDamping, value); + } + + public float LookAtDampingValue + { + get => (float)Node3D.Call(MethodName.GetLookAtDampingValue); + set => Node3D.Call(MethodName.SetLookAtDampingValue, value); + } + + public static PhantomCamera3D FromScript(string path) => new(GD.Load(path).New().AsGodotObject()); public static PhantomCamera3D FromScript(GDScript script) => new(script.New().AsGodotObject()); @@ -102,5 +202,47 @@ public PhantomCamera3D(GodotObject phantomCamera3DNode) : base(phantomCamera3DNo public const string GetSpringLength = "get_spring_length"; public const string SetSpringLength = "set_spring_length"; + + public const string GetFollowDistance = "get_follow_distance"; + public const string SetFollowDistance = "set_follow_distance"; + + public const string GetAutoFollowDistance = "get_auto_follow_distance"; + public const string SetAutoFollowDistance = "set_auto_follow_distance"; + + public const string GetAutoFollowDistanceMin = "get_auto_follow_distance_min"; + public const string SetAutoFollowDistanceMin = "set_auto_follow_distance_min"; + + public const string GetAutoFollowDistanceMax = "get_auto_follow_distance_max"; + public const string SetAutoFollowDistanceMax = "set_auto_follow_distance_max"; + + public const string GetAutoFollowDistanceDivisor = "get_auto_follow_distance_divisor"; + public const string SetAutoFollowDistanceDivisor = "set_auto_follow_distance_divisor"; + + public const string GetLookAtTarget = "get_look_at_target"; + public const string SetLookAtTarget = "set_look_at_target"; + + public const string GetLookAtTargets = "get_look_at_targets"; + public const string SetLookAtTargets = "set_look_at_targets"; + + public const string GetViewportPosition = "get_viewport_position"; + public const string SetViewportPosition = "set_viewport_position"; + + public const string GetCollisionMask = "get_collision_mask"; + public const string SetCollisionMask = "set_collision_mask"; + + public const string GetShape = "get_shape"; + public const string SetShape = "set_shape"; + + public const string GetMargin = "get_margin"; + public const string SetMargin = "set_margin"; + + public const string GetLookAtOffset = "get_look_at_offset"; + public const string SetLookAtOffset = "set_look_at_offset"; + + public const string GetLookAtDamping = "get_look_at_damping"; + public const string SetLookAtDamping = "set_look_at_damping"; + + public const string GetLookAtDampingValue = "get_look_at_damping_value"; + public const string SetLookAtDampingValue = "set_look_at_damping_value"; } } \ No newline at end of file diff --git a/tests/scenes/test_scene_2d.tscn b/tests/scenes/test_scene_2d.tscn index 77c3b3c0..f2bd1424 100644 --- a/tests/scenes/test_scene_2d.tscn +++ b/tests/scenes/test_scene_2d.tscn @@ -33,3 +33,5 @@ format = 2 [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] shape = SubResource("RectangleShape2D_4v04e") + +[node name="Marker2D" type="Marker2D" parent="."] diff --git a/tests/scripts/TestPhantomCameraWrapper.cs b/tests/scripts/TestPhantomCameraWrapper.cs index 14ac6043..b4510af2 100644 --- a/tests/scripts/TestPhantomCameraWrapper.cs +++ b/tests/scripts/TestPhantomCameraWrapper.cs @@ -104,7 +104,7 @@ private void Test2D() var snapToPixel = camera.SnapToPixel; camera.SnapToPixel = !camera.SnapToPixel; Debug.Assert(camera.SnapToPixel != snapToPixel); - + camera.LimitLeft = 2; camera.LimitTop = 3; camera.LimitRight = 4; From 638ee89e78b4a47899467b1e22a8ed231877d8e4 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Mon, 14 Oct 2024 17:03:24 -0600 Subject: [PATCH 7/8] WIP #268: catching up to release 0.7.3 --- .gitignore | 1 + PhantomCamera.csproj | 2 +- .../scripts/phantom_camera/PhantomCamera.cs | 37 ++++++- .../scripts/phantom_camera/PhantomCamera2D.cs | 12 +++ .../scripts/phantom_camera/PhantomCamera3D.cs | 101 +++++++++++++++++- tests/scripts/TestPhantomCameraWrapper.cs | 7 ++ 6 files changed, 157 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 51c71048..f716d48c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ export_presets.cfg # CSharp Dev *.DotSettings +*.csproj.old # Docs specific ignores #/docs/ diff --git a/PhantomCamera.csproj b/PhantomCamera.csproj index 5d5d3388..30e9280b 100644 --- a/PhantomCamera.csproj +++ b/PhantomCamera.csproj @@ -1,4 +1,4 @@ - + net8.0 net7.0 diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs index cbf69d80..d6b1b820 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs @@ -98,6 +98,30 @@ public PhantomCameraTween TweenResource get => new((Resource)Node.Call(MethodName.GetTweenResource)); set => Node.Call(MethodName.SetTweenResource, (GodotObject)value.Resource); } + + public bool TweenSkip + { + get => (bool)Node.Call(MethodName.GetTweenSkip); + set => Node.Call(MethodName.SetTweenSkip, value); + } + + public float TweenDuration + { + get => (float)Node.Call(MethodName.GetTweenDuration); + set => Node.Call(MethodName.SetTweenDuration, value); + } + + public TransitionType TweenTransition + { + get => (TransitionType)(int)Node.Call(MethodName.GetTweenTransition); + set => Node.Call(MethodName.GetTweenTransition, (int)value); + } + + public EaseType TweenEase + { + get => (EaseType)(int)Node.Call(MethodName.GetTweenEase); + set => Node.Call(MethodName.GetTweenEase, (int)value); + } public bool TweenOnLoad { @@ -172,10 +196,21 @@ public static class MethodName public const string GetFollowDampingValue = "get_follow_damping_value"; public const string SetFollowDampingValue = "set_follow_damping_value"; - public const string GetTweenResource = "get_tween_resource"; public const string SetTweenResource = "set_tween_resource"; + public const string GetTweenSkip = "get_tween_skip"; + public const string SetTweenSkip = "set_tween_skip"; + + public const string GetTweenDuration = "get_tween_duration"; + public const string SetTweenDuration = "set_tween_duration"; + + public const string GetTweenTransition = "get_tween_transition"; + public const string SetTweenTransition = "set_tween_transition"; + + public const string GetTweenEase = "get_tween_ease"; + public const string SetTweenEase = "set_tween_ease"; + public const string GetTweenOnLoad = "get_tween_on_load"; public const string SetTweenOnLoad = "set_tween_on_load"; diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs index e27ed2a4..820578aa 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs @@ -136,6 +136,11 @@ public void SetLimitTarget(TileMap tileMap) Node.Call(MethodName.SetLimitTarget, tileMap.GetPath()); } + public void SetLimitTarget(TileMapLayer tileMapLayer) + { + Node.Call(MethodName.SetLimitTarget, tileMapLayer.GetPath()); + } + public void SetLimitTarget(CollisionShape2D shape2D) { Node.Call(MethodName.SetLimitTarget, shape2D.GetPath()); @@ -211,6 +216,8 @@ public class LimitTargetQueryResult public bool IsTileMap => _obj.IsClass("TileMap"); + public bool IsTileMapLayer => _obj.IsClass("TileMapLayer"); + public bool IsCollisionShape2D => _obj.IsClass("CollisionShape2D"); public LimitTargetQueryResult(GodotObject godotObject) => _obj = godotObject; @@ -220,6 +227,11 @@ public class LimitTargetQueryResult return IsTileMap ? (TileMap)_obj : null; } + public TileMapLayer? AsTileMapLayer() + { + return IsTileMapLayer ? (TileMapLayer)_obj : null; + } + public CollisionShape2D? AsCollisionShape2D() { return IsCollisionShape2D ? (CollisionShape2D)_obj : null; diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs index b8f0b821..8502c6bf 100644 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs +++ b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs @@ -164,7 +164,73 @@ public float LookAtDampingValue get => (float)Node3D.Call(MethodName.GetLookAtDampingValue); set => Node3D.Call(MethodName.SetLookAtDampingValue, value); } - + + public int CullMask + { + get => (int)Node.Call(MethodName.GetCullMask); + set => Node.Call(MethodName.SetCullMask, value); + } + + public float HOffset + { + get => (float)Node.Call(MethodName.GetHOffset); + set => Node.Call(MethodName.SetHOffset, value); + } + + public float VOffset + { + get => (float)Node.Call(MethodName.GetVOffset); + set => Node.Call(MethodName.SetVOffset, value); + } + + public ProjectionType Projection + { + get => (ProjectionType)(int)Node.Call(MethodName.GetProjection); + set => Node.Call(MethodName.SetProjection, (int)value); + } + + public float Fov + { + get => (float)Node.Call(MethodName.GetFov); + set => Node.Call(MethodName.SetFov, value); + } + + public float Size + { + get => (float)Node.Call(MethodName.GetSize); + set => Node.Call(MethodName.SetSize, value); + } + + public Vector2 FrustumOffset + { + get => (Vector2)Node.Call(MethodName.GetFrustumOffset); + set => Node.Call(MethodName.SetFrustumOffset, value); + } + + public float Far + { + get => (float)Node.Call(MethodName.GetFar); + set => Node.Call(MethodName.SetFar, value); + } + + public float Near + { + get => (float)Node.Call(MethodName.GetNear); + set => Node.Call(MethodName.SetNear, value); + } + + public Environment Environment + { + get => (Environment)Node.Call(MethodName.GetEnvironment); + set => Node.Call(MethodName.SetEnvironment, value); + } + + public CameraAttributes Attributes + { + get => (CameraAttributes)Node.Call(MethodName.GetAttributes); + set => Node.Call(MethodName.SetAttributes, value); + } + public static PhantomCamera3D FromScript(string path) => new(GD.Load(path).New().AsGodotObject()); public static PhantomCamera3D FromScript(GDScript script) => new(script.New().AsGodotObject()); @@ -244,5 +310,38 @@ public PhantomCamera3D(GodotObject phantomCamera3DNode) : base(phantomCamera3DNo public const string GetLookAtDampingValue = "get_look_at_damping_value"; public const string SetLookAtDampingValue = "set_look_at_damping_value"; + + public const string GetCullMask = "get_cull_mask"; + public const string SetCullMask = "set_cull_mask"; + + public const string GetHOffset = "get_h_offset"; + public const string SetHOffset = "set_h_offset"; + + public const string GetVOffset = "get_v_offset"; + public const string SetVOffset = "set_v_offset"; + + public const string GetProjection = "get_projection"; + public const string SetProjection = "set_projection"; + + public const string GetFov = "get_fov"; + public const string SetFov = "set_fov"; + + public const string GetSize = "get_size"; + public const string SetSize = "set_size"; + + public const string GetFrustumOffset = "get_frustum_offset"; + public const string SetFrustumOffset = "set_frustum_offset"; + + public const string GetFar = "get_far"; + public const string SetFar = "set_far"; + + public const string GetNear = "get_near"; + public const string SetNear = "set_near"; + + public const string GetEnvironment = "get_environment"; + public const string SetEnvironment = "set_environment"; + + public const string GetAttributes = "get_attributes"; + public const string SetAttributes = "set_attributes"; } } \ No newline at end of file diff --git a/tests/scripts/TestPhantomCameraWrapper.cs b/tests/scripts/TestPhantomCameraWrapper.cs index b4510af2..537dcca6 100644 --- a/tests/scripts/TestPhantomCameraWrapper.cs +++ b/tests/scripts/TestPhantomCameraWrapper.cs @@ -131,6 +131,13 @@ private void Test2D() Debug.Assert(limitTarget != null); Debug.Assert(limitTarget.IsTileMap); Debug.Assert(limitTarget.AsTileMap() != null); + + var tileMapLayer = testScene.GetNode("TileMapLayer"); + camera.SetLimitTarget(tileMapLayer); + limitTarget = camera.GetLimitTarget(); + Debug.Assert(limitTarget != null); + Debug.Assert(limitTarget.IsTileMapLayer); + Debug.Assert(limitTarget.AsTileMapLayer() != null); var shape2D = testScene.GetNode("Area2D/CollisionShape2D"); camera.SetLimitTarget(shape2D); From 39d774429c9985ed00b0560b97afd5f05894e6e5 Mon Sep 17 00:00:00 2001 From: Cody Bentley Date: Wed, 17 Jul 2024 00:00:43 -0600 Subject: [PATCH 8/8] WIP: Adding CSharp wrapper - Begin effort on PCam3D and some basic Godot extensions and PCam types --- .gitignore | 6 +- addons/phantom_camera/csharp/PCam3D.cs | 188 ++++++++++++++++++ addons/phantom_camera/csharp/PhantomCamera.cs | 35 ++++ 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 addons/phantom_camera/csharp/PCam3D.cs create mode 100644 addons/phantom_camera/csharp/PhantomCamera.cs diff --git a/.gitignore b/.gitignore index 95a3b9f6..644f7758 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,11 @@ export_presets.cfg .godot/ -# Docs specic ignores +# CSharp Dev +*.csproj +*.sln + +# Docs specific ignores #/docs/ node_modules cache diff --git a/addons/phantom_camera/csharp/PCam3D.cs b/addons/phantom_camera/csharp/PCam3D.cs new file mode 100644 index 00000000..d387cb47 --- /dev/null +++ b/addons/phantom_camera/csharp/PCam3D.cs @@ -0,0 +1,188 @@ +using Godot; + +namespace PhantomCamera; + +public class PCam3D +{ + public delegate void PCam3DEventHandler(); + public delegate void PCam3DTweenInterruptedEventHandler(Variant pCam3D); + + public event PCam3DEventHandler BecameActive; + public event PCam3DEventHandler BecameInactive; + public event PCam3DEventHandler FollowTargetChanged; + public event PCam3DEventHandler LookAtTargetChanged; + public event PCam3DEventHandler DeadZoneChanged; + public event PCam3DEventHandler TweenStarted; + public event PCam3DEventHandler IsTweening; + public event PCam3DEventHandler TweenCompleted; + public event PCam3DTweenInterruptedEventHandler TweenInterrupted; + + private readonly GodotObject _godotObject; + + private readonly Callable _callableBecameActive; + private readonly Callable _callableBecameInactive; + private readonly Callable _callableFollowTargetChanged; + private readonly Callable _callableLookAtTargetChanged; + private readonly Callable _callableDeadZoneChanged; + private readonly Callable _callableTweenStarted; + private readonly Callable _callableIsTweening; + private readonly Callable _callableTweenCompleted; + private readonly Callable _callableTweenInterrupted; + + public FollowMode FollowMode => _godotObject.Call(MethodName.GetFollowMode).As(); + + public LookAtMode LookAtMode => _godotObject.Call(MethodName.GetLookAtMode).As(); + + public bool IsActive => _godotObject.Call(MethodName.IsActive).As(); + + public int Priority + { + get => _godotObject.Call(MethodName.GetPriority).As(); + set => _godotObject.Call(MethodName.SetPriority, value); + } + + public Vector3 ThirdPersonRotation + { + get => _godotObject.Call(MethodName.GetThirdPersonRotation).As(); + set => _godotObject.Call(MethodName.SetThirdPersonRotation, value); + } + + public Vector3 ThirdPersonRotationDegrees + { + get => _godotObject.Call(MethodName.GetThirdPersonRotationDegrees).As(); + set => _godotObject.Call(MethodName.SetThirdPersonRotationDegrees, value); + } + + public Quaternion ThirdPersonQuaternion + { + get => _godotObject.Call(MethodName.GetThirdPersonQuaternion).As(); + set => _godotObject.Call(MethodName.SetThirdPersonQuaternion, value); + } + + public float SpringLength + { + get => _godotObject.Call(MethodName.GetSpringLength).As(); + set => _godotObject.Call(MethodName.SetSpringLength, value); + } + + public static PCam3D FromScript(string path) => GD.Load(path).AsPCam3D(); + + public PCam3D(GodotObject godotObject) + { + _godotObject = godotObject; + + _callableBecameActive = Callable.From(OnBecameActive); + _callableBecameInactive = Callable.From(OnBecameInactive); + _callableFollowTargetChanged = Callable.From(OnFollowTargetChanged); + _callableLookAtTargetChanged = Callable.From(OnLookAtTargetChanged); + _callableDeadZoneChanged = Callable.From(OnDeadZoneChanged); + _callableTweenStarted = Callable.From(OnTweenStarted); + _callableIsTweening = Callable.From(OnIsTweening); + _callableTweenCompleted = Callable.From(OnTweenCompleted); + _callableTweenInterrupted = Callable.From(OnTweenInterrupted); + + _godotObject.Connect(SignalName.BecameActive, _callableBecameActive); + _godotObject.Connect(SignalName.BecameInactive, _callableBecameInactive); + _godotObject.Connect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + _godotObject.Connect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + _godotObject.Connect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + _godotObject.Connect(SignalName.TweenStarted, _callableTweenStarted); + _godotObject.Connect(SignalName.IsTweening, _callableIsTweening); + _godotObject.Connect(SignalName.TweenCompleted, _callableTweenCompleted); + _godotObject.Connect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + ~PCam3D() + { + _godotObject.Disconnect(SignalName.BecameActive, _callableBecameActive); + _godotObject.Disconnect(SignalName.BecameInactive, _callableBecameInactive); + _godotObject.Disconnect(SignalName.FollowTargetChanged, _callableFollowTargetChanged); + _godotObject.Disconnect(SignalName.LookAtTargetChanged, _callableLookAtTargetChanged); + _godotObject.Disconnect(SignalName.DeadZoneChanged, _callableDeadZoneChanged); + _godotObject.Disconnect(SignalName.TweenStarted, _callableTweenStarted); + _godotObject.Disconnect(SignalName.IsTweening, _callableIsTweening); + _godotObject.Disconnect(SignalName.TweenCompleted, _callableTweenCompleted); + _godotObject.Disconnect(SignalName.TweenInterrupted, _callableTweenInterrupted); + } + + protected virtual void OnBecameActive() + { + BecameActive?.Invoke(); + } + + protected virtual void OnBecameInactive() + { + BecameInactive?.Invoke(); + } + + protected virtual void OnFollowTargetChanged() + { + FollowTargetChanged?.Invoke(); + } + + protected virtual void OnLookAtTargetChanged() + { + LookAtTargetChanged?.Invoke(); + } + + protected virtual void OnDeadZoneChanged() + { + DeadZoneChanged?.Invoke(); + } + + protected virtual void OnTweenStarted() + { + TweenStarted?.Invoke(); + } + + protected virtual void OnIsTweening() + { + IsTweening?.Invoke(); + } + + protected virtual void OnTweenInterrupted(Variant pCam3D) + { + TweenInterrupted?.Invoke(pCam3D); + } + + protected virtual void OnTweenCompleted() + { + TweenCompleted?.Invoke(); + } + + + public static class MethodName + { + public const string GetFollowMode = "get_follow_mode"; + public const string GetLookAtMode = "get_look_at_mode"; + public const string IsActive = "is_active"; + + public const string GetPriority = "get_priority"; + public const string SetPriority = "set_priority"; + + public const string GetThirdPersonRotation = "get_third_person_rotation"; + public const string SetThirdPersonRotation = "set_third_person_rotation"; + + public const string GetThirdPersonRotationDegrees = "get_third_person_rotation_degrees"; + public const string SetThirdPersonRotationDegrees = "set_third_person_rotation_degrees"; + + public const string GetThirdPersonQuaternion = "get_third_person_quaternion"; + public const string SetThirdPersonQuaternion = "set_third_person_quaternion"; + + public const string GetSpringLength = "get_spring_length"; + public const string SetSpringLength = "set_spring_length"; + } + + 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 LookAtTargetChanged = "look_at_target_changed"; + public const string DeadZoneChanged = "dead_zone_changed"; + public const string TweenStarted = "tween_started"; + public const string IsTweening = "is_tweening"; + public const string TweenInterrupted = "tween_interrupted"; + public const string TweenCompleted = "tween_completed"; + } +} \ No newline at end of file diff --git a/addons/phantom_camera/csharp/PhantomCamera.cs b/addons/phantom_camera/csharp/PhantomCamera.cs new file mode 100644 index 00000000..3ea3a960 --- /dev/null +++ b/addons/phantom_camera/csharp/PhantomCamera.cs @@ -0,0 +1,35 @@ +using Godot; + +namespace PhantomCamera; + +public static class GodotExtension +{ + public static PCam3D AsPCam3D(this GodotObject godotObject) + { + return new PCam3D(godotObject); + } + + public static PCam3D AsPCam3D(this GDScript godotScript) + { + return new PCam3D(godotScript.New().AsGodotObject()); + } +} + +public enum FollowMode +{ + None, + Glued, + Simple, + Group, + Path, + Framed, + ThirdPerson +} + +public enum LookAtMode +{ + None, + Mimic, + Simple, + Group +} \ No newline at end of file