Masking Sprites in a dynamic shape which changes during playtime

Godot Version

4.6

Question

Hi! Creating a small game with some friends which plays in the style of Warioware. We want to have each minigame show up in their own ‘window’, and you play it in that window. What I’m currently having trouble with is keeping the minigame confined to that window visually. Currently it sticks out of the window, whereas I want it to stay within the confined window space and not render the parts outside of the window. Just like a desktop window it will be dynamically changed mid game.

I’ve tried using a clip children sprite2D as the parent of the minigame, but since the window can be resized I can’t change it without resizing the minigame too. I have also tried using a point light 2D, which works better though I am also struggling to change the size of the light dynamically in code.

From research, I feel the best solution would be a shader mask - but we’re all new to Godot and I’m not familiar with Shaders personally. Though if there is a simple way to go with either of the other solutions I am happy to hear those as well.

Unfortunately I cannot add an attatchment, but please ask if you need more information, and I’ll do my best to provide it. Thank you for the help!

Try having each mini-game in its own SubViewport.

To expand on the SubViewport suggestion, here is a setup that handles the resizable window without a shader:

  1. Put each minigame inside its own SubViewport, as a child of a SubViewportContainer.
    1. On the container, turn stretch off. With stretch on, resizing scales the minigame; with it off, the viewport just becomes a bigger or smaller “porthole” onto the same content.
    1. When the window is resized, set the viewport size from code. The minigame’s contents keep their position and scale, and everything outside the viewport is simply not rendered.
@onready var container: SubViewportContainer = $SubViewportContainer
@onready var viewport: SubViewport = $SubViewportContainer/SubViewport

func resize_window(new_size: Vector2i) -> void:
    container.size = Vector2(new_size)
    viewport.size = new_size

I tried the resize in a headless Godot 4.7 test: changing SubViewport.size from 200x150 to 400x300 left a child node at the same position, so the content is cropped, not scaled.

A simpler option to try first: put the minigame under a Control (a Panel works) with clip_contents = true. Per the docs that clips CanvasItem children, Node2D included, to the Control’s rectangle, and resizing the Control does not scale the children. I have not tested this one.

Disclosure: this answer was written with AI assistance.