Making a transparent hole inside a rectangle

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Yeldham
:warning: Old Version Published before Godot 3 was released.

Back when I used Game Maker, I used surfaces to make holes inside sprites and primitives. My question is how can I make hole (draw_circle), inside a rectangle (draw_rect) in Godot.

:bust_in_silhouette: Reply From: Punpun

You’ll need to use a 2D node and the _draw() func.

This function is inherent of all CanvasItem nodes and draws things to the Viewport, it also has some unique methods that you’ll want to use, here is an example:

extends Node2D

func _ready():
	pass

func _draw():
	
	var pos = get_viewport().get_rect().size/2 #get center of the screen
	var rect_size = Vector2(80,60) #the size of your rectangle

	#because the rectangle origin is his top-left
    #does some correction to make it centered
	var rect_pos = pos - rect_size/2 
	
	#create your Rect2
	var rect = Rect2(rect_pos, rect_size )

	#drawn Rect
	draw_rect( rect, Color(1,1,1) )

	#drawn circle (origin is center by default)
	draw_circle( pos, 10, Color(1,.2,.5) )

Done, you just drew the Japanese flag with this, sorta, lol. For further reading: _draw()

I think I should have made my question more clearer, I want to make a hole in the sense of being able to see what’s behind the rectangle through the hole.

Yeldham | 2017-10-10 20:19

Uh… If there’s something like that in Godot, I don’t know. I know however this should be possible using shading language, but I’m no expert at it. But I have a “trick” for you.

Create a sprite, texture it and in it’s Material propriety, create a CanvasItemMaterial and set Shading Mode to Light Only, making it invisible.

Next, make a Light2D child, texture it with a white square image that engulfs the sprite, and set its Mode propriety to Mask. The main sprite now is visible as long as the mask is opaque.

Also make the same white square image but with an alpha hole at the center, make an AnimationPlayer with a track that scales the mask till a certain point, then in the main sprite script, put:

extends Sprite

var sprite
var mask
var anim

func _ready():

	sprite = get_node(".") #main sprite
	mask = get_node("Light2D")
	anim = get_node("AnimationPlayer")
	set_fixed_process(true)

func _fixed_process(delta):

	#create the hole
	if Input.is_key_pressed(KEY_SPACE):

		var hole = load("res://mask_hole.png") #load the mask with the hole	
		mask.set_texture(hole) #change the one without the hole for it
		anim.play("hole_expand") #plays the animation that scales the mask

In this example a hole will appear in the center of the mask, that’ll be scaling up, thus making a hole at the center of the main sprite too.

The problem lies in the fact that the mask can affect other sprites in the same light mask layer, so be sure to change both sprite light mask and the mask item mask to prevent this from affecting undesired sprites.

Although it’s probably not what you wanted, it should serve as a placeholder till you find a better method.

Punpun | 2017-10-10 22:43

I already have a placeholder method, by using a Polygon2D node, and an algorithm to draw circles:

var polygon_positions = PoolVector2Array()
var segments = 64
var viewport_size = get_tree().get_root().get_size()
var radius = (viewport_size.y / 2) + 29
for i in range(segments):
	var angle = (i * (PI * 2)) / segments
	polygon_positions.append(Vector2(cos(angle), sin(angle)) * radius)

polygon = polygon_positions
invert_border = viewport_size.x / 3

Yeldham | 2017-10-10 23:09