"Sensor" extra to reveal secrets in a part of the screen

Godot Version

4.2

Question

I am lost on how to implement this feature:

In a 2D game, there are invisible secrets hidden in the level.

The player can find a “sensor” extra and point it in some direction. This will change the view (i.e. to heat vision) and reveal the secret items, but only in the affected part of the screen.

That can be done with a mask or stencil, but you’re gonna have to give more details about your project if you want a better answer.

Thanks for the reply.

I think of having a “landscape” sprite with two textures - the usual texture and a second texture that contains the secret stuff. Then a (transparent) second sprite above should act as a “window to the secret world” and reveal the secret texture.

I am pretty new to shaders, but as far as I understand it, a 2D fragment shader is either a “material” which determines the looks of one sprite, or a “post processing” shader on a canvas layer that basically acts on a screenshot of the viewport.

What I would need is a shader that takes the current sprite and the sprite below into account. The sprite below sends two textures and the sprite above decides, which one is visible.

If you’re using shaders, I think the easy way would be to make a material for your secret-hiding sprite that takes a mask texture and the regular texture. You multiply the mask by the alpha of the regular, so wherever the mask is white, it renders the regular texture, and wherever it’s black, it renders nothing.
Then you can either swap the texture for the mask at runtime or even make it entirely at runtime in the same pass, a previous pass or a compute shader or many other options. Depending on the exact effect you want some will be easier than the others.

Thanks to Efi for showing me the direction. I want to document my solution, in case someone reads this later.

I read a bit about multiple render passes but in the end I did something more simple:

  • The secret item is a separate sprite.
  • I went for a flashlight-like cone effect. The player emits a signal that contains its current position and limits for the view cone.
  • The secret sprite’s shader material uses the global coordinates of the currently rendered pixel and the data from the player to calculate if it is in the cone.
shader_type canvas_item;

uniform vec2 player_position;
uniform vec2 view_cone_left;
uniform vec2 view_cone_right;

varying vec2 pixel_world_position;

void vertex() {
	pixel_world_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
}

void fragment() {
	vec2 pixel_relative_position = pixel_world_position - player_position;
	vec2 pixel_player_normal = vec2(- pixel_relative_position.y, pixel_relative_position.x);
	
	bool is_inside_left = dot(pixel_player_normal, view_cone_left) < 0.0;
	bool is_inside_right = dot(pixel_player_normal, view_cone_right) > 0.0;
	
	if (is_inside_left && is_inside_right) {
		COLOR = texture(TEXTURE, UV);
	} else {
		COLOR = vec4(0.0, 0.0, 0.0, 0.0);
	}
}

Together with a visualization of the view cone (another shader on a polygon 2D, basically just the sin of the distance to the player), the final effect looks like this.

1 Like

Excellent vector mask! Good job =D

However, I must add a thing: branching (if else) in shaders is “bad”. It make the compiler sad. It’s not predictable.

could instead be

COLOR = vec4(texture(TEXTURE, UV), float(is_inside_left && is_inside_right));

I think that’s valid. It is in GLSL.

1 Like