It lives
!!! Thank you so much for all of your help and detailed explanation; I’ve been trying to figure this out for a very long time.
Again, my understanding of this math is hazy at best, but it seems like this is unnecessary; the system wasn’t working when I did that, but once I got rid of that adjustment it worked.
Here’s the entire class
:
public partial class Camera_Overworld : CharacterBody3D
{
//VARIABLES THAT LINK NODES TOGETHER
public static Camera_Overworld Instance { get; private set; }
private SpringArm3D mySpringArm;
private Camera3D myCamera;
private List<CollisionShape3D> yourBounds_Colliders = [];
private List<Marker3D> your_RoomPoints = [];
//VARIABLES THAT DESCRIBE THE FIXED PROPERTIES OF THE CAMERA
private readonly float Fixed_Height_Above_Wall = 7;
//the camera bound system will STOP WORKING if this is changed!!!
[Export] private float Fixed_Angle = 37.5f;
[Export] private int[] Fixed_Sizes = [16, 48];
[Export] private int Fixed_Zoom_Seconds = 1;
[Export] private int[] Fixed_Speeds = [512, 1024, 2048];
//VARIABLES THAT DESCRIBE THE INITIAL STATE OF THE CAMERA
[Export] private bool Init_Zoomed = true;
[Export] private byte Init_Speed = 1;
//VARIABLES THAT DESCRIBE THE CURRENT STATE OF THE CAMERA
private bool[] States_Zoom = [false, false];
private float Counter_Zoom = 0;
private byte State_Move_Speed;
public override void _Ready()
{
Instance = this;
Initialize_Link("");
Fixed_Angle = Fixed_Angle * MathHandler.DegToRad;
States_Zoom[1] = Init_Zoomed;
if (Init_Zoomed) {myCamera.Size = Fixed_Sizes[0];}
else {myCamera.Size = Fixed_Sizes[1];}
State_Move_Speed = Init_Speed;
CallDeferred(MethodName.Initialize_Nodes);
}
public override void _PhysicsProcess(double delta)
{
if (Logic_Zoom((float) delta))
{
return;
}
Logic_Move((float) delta);
}
private void Initialize_Link(NodePath thisNode)
{
//this is a little janky but at a small number of nodes it's fine
if (thisNode == "")
{
mySpringArm = GetNode<SpringArm3D>("SpringArm3D");
myCamera = mySpringArm.GetNode<Camera3D>("Camera3D");
}
else
{
string node_path = Owner.GetPath() + thisNode.ToString()[2..];
if (node_path.Contains("Camera_Bounds"))
{
yourBounds_Colliders.Add(GetNode<CollisionShape3D>(node_path));
}
else if (node_path.Contains("Camera_Bound_Points"))
{
your_RoomPoints.Add(GetNode<Marker3D>(node_path));
}
else
{
throw new Exception("Camera_Overworld: An unexpected Node is emitting a signal toward Initialize_Link.");
}
}
}
private void Initialize_Nodes()
{
if (yourBounds_Colliders.Count != 4 || your_RoomPoints.Count != 5)
{
throw new Exception("Camera_Overworld: One or more linked Nodes have not been set.");
}
//reparent the node to the first instance of the player character node
Reparent(Player_Overworld_CharacterBody3D.ourPlayers[0]);
//move pivot point directly above player
Vector3 pos_player = GetParent<Player_Overworld_CharacterBody3D>().GlobalPosition;
GlobalPosition = new Vector3
(
pos_player.X,
your_RoomPoints[0].GlobalPosition.Y + Fixed_Height_Above_Wall,
pos_player.Z
);
GlobalRotation = new Vector3(0, 0, 0);
//set the properties of the springarm and camera
mySpringArm.SpringLength = (GlobalPosition.Y - your_RoomPoints[4].GlobalPosition.Y) / MathF.Tan(Fixed_Angle);
myCamera.GlobalRotate(Vector3.Left, Fixed_Angle);
Update_Bounds();
}
private void Update_Bounds()
{
//set the sizes of the bounds
for (int i = 0; i < 4; i ++)
{
float scale_x = 1f / 16f;
float scale_z = 1f / 16f;
switch(i)
{
case 0: scale_z = your_RoomPoints[3].GlobalPosition.Z - your_RoomPoints[0].GlobalPosition.Z; break;
case 1: scale_x = your_RoomPoints[0].GlobalPosition.X - your_RoomPoints[1].GlobalPosition.X; break;
case 2: scale_z = your_RoomPoints[2].GlobalPosition.Z - your_RoomPoints[1].GlobalPosition.Z; break;
case 3: scale_x = your_RoomPoints[3].GlobalPosition.X - your_RoomPoints[2].GlobalPosition.X; break;
}
BoxShape3D new_shape = new() {Size = new Vector3(scale_x, 1, scale_z)};
yourBounds_Colliders[i].Shape = new_shape;
}
//set the initial positions of the bounds
//https://forum.godotengine.org/t/mathematically-find-orthographic-camera-bound-knowing-size-angle-and-position/142503
float[] margins = [0, 0];
margins[0] = myCamera.Size / 2 * ViewHandler.GetAspectRatio(GetViewport());
margins[1] = (myCamera.Size / 2 - (GlobalPosition.Y - your_RoomPoints[0].GlobalPosition.Y) * Mathf.Cos(Fixed_Angle)) / Mathf.Sin(Fixed_Angle);
for (byte i = 0; i < 4; i ++)
{
float pos_x;
float pos_z;
if (i % 2 == 0)
{
pos_x = your_RoomPoints[i].GlobalPosition.X + margins[0] * (-1 + i);
pos_z = (your_RoomPoints[i / 2].GlobalPosition.Z + your_RoomPoints[3 - i / 2].GlobalPosition.Z) / 2;
}
else
{
pos_x = (your_RoomPoints[i - 1].GlobalPosition.X + your_RoomPoints[i].GlobalPosition.X) / 2;
pos_z = your_RoomPoints[i].GlobalPosition.Z + margins[1] * (2 - i);
}
yourBounds_Colliders[i].GlobalPosition = new Vector3(pos_x, GlobalPosition.Y, pos_z);
}
}
private bool Logic_Zoom(float delta)
{
if(!States_Zoom[0] && Input.IsActionJustPressed("cam_zoom_toggle"))
{
States_Zoom[0] = true;
if (States_Zoom[1])
{
SoundHandler.PlaySfx(this, "UI/Camera/Camera_ZoomFar");
}
else
{
SoundHandler.PlaySfx(this, "UI/Camera/Camera_ZoomClose");
}
return true;
}
else if (States_Zoom[0])
{
if (Counter_Zoom >= 1)
{
States_Zoom[0] = false;
States_Zoom[1] = !States_Zoom[1];
Counter_Zoom = 0;
return false;
}
else
{
Counter_Zoom += delta / Fixed_Zoom_Seconds;
if (States_Zoom[1])
{
myCamera.Size = Mathf.Lerp(Fixed_Sizes[0], Fixed_Sizes[1], Counter_Zoom);
}
else
{
myCamera.Size = Mathf.Lerp(Fixed_Sizes[1], Fixed_Sizes[0], Counter_Zoom);
}
return true;
}
}
return false;
}
private void Logic_Move(float delta)
{
Vector2 input = Input.GetVector("cam_move_left", "cam_move_right", "cam_move_forward", "cam_move_backward");
input = input.Normalized();
input *= Fixed_Speeds[State_Move_Speed] * delta;
Velocity = new Vector3(input.X, 0, input.Y);
MoveAndSlide();
}
}
The only weird thing is how the SpringArm3D behaves with the bottom (+Z) CollisionShape3D: you can see it only jitters on yourBounds_Colliders[3]. Once I get rotation in 90° increments implemented and working (fingers crossed), I’ll mark one of your replies as the solution, but let me know if you know what that jitter is about.
Here’s hoping it still works once I implement camera rotation at 45° increments: my plan is to keep yourBounds_Colliders in cardinal directions relative to the camera, and make it so the area you’re allowed to view is the rectangle at angle N that circumscribes the rectangle formed by your_RoomPoints. There’s a screenshot of an earlier attempt at that system in this thread.