Skip to content

Commit

Permalink
WIP ramokz#286: more tests and re-organized
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
sircodemane committed Jul 24, 2024
1 parent 143d567 commit bcdf718
Show file tree
Hide file tree
Showing 11 changed files with 827 additions and 626 deletions.
618 changes: 3 additions & 615 deletions addons/phantom_camera/scripts/PhantomCamera.cs

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions addons/phantom_camera/scripts/managers/PhantomCameraManager.cs
Original file line number Diff line number Diff line change
@@ -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<Node2D>()
.Select(node => new PhantomCamera2D(node)).ToArray();

public static PhantomCamera3D[] PhantomCamera3Ds =>
Instance.Call(MethodName.GetPhantomCamera3Ds).AsGodotArray<Node3D>()
.Select(node => new PhantomCamera3D(node)).ToArray();

public static PhantomCameraHost[] PhantomCameraHosts =>
Instance.Call(MethodName.GetPhantomCameraHosts).AsGodotArray<Node>()
.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";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
162 changes: 162 additions & 0 deletions addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs
Original file line number Diff line number Diff line change
@@ -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";
}
}
156 changes: 156 additions & 0 deletions addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs
Original file line number Diff line number Diff line change
@@ -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<GDScript>(path).New().AsGodotObject());
public static PhantomCamera2D FromScript(GDScript script) => new(script.New().AsGodotObject());

public PhantomCamera2D(GodotObject phantomCameraNode) : base(phantomCameraNode)
{
_callableTweenInterrupted = Callable.From<Node2D>(pCam => TweenInterrupted?.Invoke(pCam));
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;
}
}
Loading

0 comments on commit bcdf718

Please sign in to comment.