Godot Version
v4.6.2.stable.mono.official [71f334935]
Question
I’ve been working on a system that will keep my Camera3D within a box that changes depending on the camera’s rotation. To do this, I have four Node3Ds that each contain a VisibleOnScreenNotifier3D, along with a MeshInstance3D to keep track of them while debugging.
The way the code works is that a manager Node3D calculates the rectangle that circumscribes the walls of the room, then transforms each of the four Node3Ds to match, like this:
public partial class OwCamBoundManager : Node3D
{
//...
public void Bounds_Apply()
{
//this can't take Bounds_recalculate as an argument or else the camera won't be able to call it properly
var square_circum = Bounds_Recalculate();
//update bounds
for(var i = 0; i < 4; i ++)
{
var centroid = (square_circum[i] + square_circum[(i + 1) % 4]) / 2;
my_Bound_Groups[i].GlobalPosition = new Vector3(centroid.X, your_Bound_Markers[i].GlobalPosition.Y, centroid.Y);
my_Bound_Groups[i].GlobalRotation = new Vector3(0, GetViewport().GetCamera3D().GlobalRotation.Y, 0);
Vector2 new_scale;
BaseMaterial3D new_material = my_Bound_Meshes[i].GetSurfaceOverrideMaterial(0).Duplicate() as BaseMaterial3D;
if (i % 2 == 0)
{
new_scale.X = square_circum[i].DistanceTo(square_circum[i + 1]);
new_scale.Y = your_Bound_Markers[i].Scale.Z;
}
else
{
new_scale.X = your_Bound_Markers[i].Scale.X;
new_scale.Y = square_circum[i].DistanceTo(square_circum[(i + 1) % 4]);
}
my_Bound_Groups[i].Scale = new Vector3(new_scale.X, float.Epsilon, new_scale.Y);
new_material.Uv1Scale = new Vector3(new_scale.X, new_scale.Y, new_material.Uv1Scale.Z);
my_Bound_Meshes[i].SetSurfaceOverrideMaterial(0, new_material);
}
}
//...
}
Please NB:
- I’ve omitted the code Bounds_Recalculate() because I don’t believe it’s relevant to this issue, but I can supply it if necessary.
- The code that keeps the camera within these bounds was disabled to get these screenshots.
- The dark purple and green markers that define the rectangle to be circumscribed (one is circled green).
- That weird tearing: I don’t know what it is but it goes away if I pull back the camera and I was planning to ask about it some other time but I left it in in case it’s the reason my stuff isn’t working.
I’m getting behavior that indicates the notifiers aren’t being tracked properly when the camera is moved in a non-cardinal direction, i.e. not on a Vector3 of (±1f, 0f, ±1f). For example, the List that keeps track of how many are on screen using the following logic will report too many or too few: I had to move right well past the point at which the marker circled in green in the screenshot was no longer in-frame for it to report 3 instead of 4.
public partial class OwCamBoundManager : Node3D
{
//...
public List<VisibleOnScreenNotifier3D> Bounds_Visible = [];
public async override void _Ready()
{
//...
for (var i = 0; i < 4; i ++)
{
var current = my_Bound_Notifs[i];
current.ScreenEntered += () => Bound_OnScreen(current);
current.ScreenExited += () => Bound_OffScreen(current);
}
}
private void Bound_OnScreen(VisibleOnScreenNotifier3D emitter)
{
if (!Bounds_Visible.Contains(emitter))
{
Bounds_Visible.Add(emitter);
GD.Print(Bounds_Visible.Count);
}
else
{
throw new Exception("Camera Bound Manager attempting to add redundant bound to list.");
}
}
private void Bound_OffScreen(VisibleOnScreenNotifier3D emitter)
{
if (Bounds_Visible.Contains(emitter))
{
Bounds_Visible.Remove(emitter);
GD.Print(Bounds_Visible.Count);
}
else
{
throw new Exception("Camera Bound Manager attempting to remove absent bound from list.");
}
}
}
Am I not correctly applying the transforms to the notifiers? I’ve had a similar issue before, which prompted me to move toward this system, so I’m worried I’m not understanding the node correctly.
To clarify, the meshes are behaving exactly as intended, minus the tearing I mentioned.
