Connect predefined signal (ScreenEntered) in static context

Godot Version

v4.4.stable.mono.official [4c311cbee]

Question

I have a VisibleOnScreenNotifier3D object whose ScreenEntered signal I’d like to connect to my Camera3D. The problem is that there will be several of these objects in my scene, so I’d like to connect the signal in a static context. I’ve done this previously using the following code for the debug info, but it won’t work here.

public partial class OwCamDebuginfo : Label
{
        public static event Action<OwCamDebuginfo> RequestDebugInfo;
        private static void InvokeRequestDebugInfo(OwCamDebuginfo me) => RequestDebugInfo?.Invoke(me);

        public override void _Ready()
        {
              InvokeRequestDebugInfo(this);
        }
}
public partial class OwCamera : Camera3D
{
       public override void _Ready()
       {
              OwCamDebuginfo.RequestDebugInfo += RequestDebugInfoEventHandler;
       }

       private void RequestDebugInfoEventHandler(OwCamDebuginfo you)
       {
              //...
       }
}

Is it possible to do this, or will I have to connect each object’s signal individually? The goal, by the way, is that when the object enters the screen, the camera will stop moving.

Hi!

You could use a custom static event on the class representing objects appearing on screen, and add an argument to the event so that anytime an object appears on screen, it triggers the static event and pass itself as an argument.
The camera then only needs to add a listener to the static event on ready, and you’re good to go!

For instance, this would be the class for objects triggering the event:

public partial class VisibilityNotifierEntity : Node
{
    public static event System.Action<VisibilityNotifierEntity> CustomScreenEntered;

    // Hook this to VisibleOnScreenNotifier3D signal.
    public void OnScreenEntered()
    {
        CustomScreenEntered?.Invoke(this);
    }
}

And the camera class would look something like this:

public partial class Cam : Camera3D
{
    public override void _Ready()
    {
        VisibilityNotifierEntity.CustomScreenEntered += OnNotifierScreenEntered;
    }

    private void OnNotifierScreenEntered(VisibilityNotifierEntity notifier)
    {
        // Do your camera stopping logic here.
    }
}

Just so you know, I didn’t test the code so you may have to adjust some stuff, but hopefully you get the idea.
Also, do not forget to remove the listeners when needed. Static events are useful but can lead to issues when listeners are not removed properly.

Let me know if this helps!

1 Like

Here’s what I have:

public partial class OwCamBound : VisibleOnScreenNotifier3D
{
    new public static event Action<OwCamBound> ScreenEntered;

    public void OnScreenEntered()
    {
        ScreenEntered?.Invoke(this);
    }
}
public partial class OwCamera : Camera3D
{
    public override void _Ready()
    {
        OwCamBound.ScreenEntered += BoundEntered;
    }
    
    private void BoundEntered(OwCamBound you)
    {
        GD.Print("hi");
    }

The problem is at the line OwCamBound.ScreenEntered += BoundEntered;. That line tries to link a non-static method to the receiver method in a static context. And if I make the method static, then it can’t access this instance, which defeats the point.

I’m also not completely clear on how the OnScreenEntered method is supposed to work, since from what I can tell it’s not a built-in method of the VisibleOnScreenNotifier3D class.

I tried adding a VisibleOnScreenNotifier3D as a child of the Camera3D itself right in front of it, and used GetNode<VisibleOnScreenNotifier3D>("test").ScreenEntered += BoundEntered; to connect the signal, and that worked, since that isn’t a static context.

Oh, I guess I should have not called my event ScreenEntered as the name is already used. Don’t use the new keyword, it may lead to some issues due to how it works (I don’t know how familiar you are with C# but I just think it should never be used).
Your code seems fine but maybe you should try with a custom name for the event.

That line tries to link a non-static method to the receiver method in a static context. And if I make the method static, then it can’t access this instance, which defeats the point.

Using a static event and linking a non-static method is perfectly valid. Are you talking about compilation/runtime error, or just the behaviour not working as intended without any technical issue?

The idea is simply to use a static event as a “bridge” between OwCamBound and OwCamera, so that the latest doesn’t have to hook itself to each instance. The camera doesn’t need to hook itself to each instance, it just needs to know that any OwCamBound appeared, which is why the static event is useful here. However, it still needs to do some behaviour based on the instance, which is why I’m passing it as an argument to the event.
The OwCamBounds do not say “Hey, I entered the screen”, it says “Hey, some instance entered the screen, and just in case, that’s me” :smile:

(Just trying to make it more clear in case my previous post was misleading somehow :sweat_smile: ).

I’m learning C# from Godot, haha. I do have limited prior experience with Java however. The problem was a compiler error.

I actually came up with a solution just now. It’s less elegant, but I tested it and it works by just connecting from OwCamera with OwCamBound.ScreenEnteredStatic += testmethod;

public partial class OwCamBound : VisibleOnScreenNotifier3D
{
    public static event Action<OwCamBound> ScreenEnteredStatic;

    public override void _Ready()
    {
        ScreenEntered += WhichEnteredScreen;
    }

    private void WhichEnteredScreen()
    //sends instance to a static method and signal that can be easily accessed by OwCamera
    {
        ThisEnteredScreen(this);
    }

    private static void ThisEnteredScreen(OwCamBound me)
    {
        ScreenEnteredStatic?.Invoke(me);
    }
}
1 Like

Well, your code is exactly what I had in mind, but my post was just so confusing since I used the same name for my custom event as the native one haha.

Your code is great, you can just write

private void WhichEnteredScreen()
//sends instance to a static method and signal that can be easily accessed by OwCamera
{
    ScreenEnteredStatic?.Invoke(this);
}

to make it less verbose, but the idea is exactly the same.

Sorry about the confusion, glad it now works!

1 Like

Oh thanks, I missed that I had an unnecessary step. No worries about the confusion, we got there eventually. :triumph:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.