Search code examples
viewportgodot

How to create a SubViewport + ViewportTexture programmatically in Godot?


I'm trying to create a SubViewport in combination with a ViewportTexture programmatically in Godot 4. My minimal example looks like this:

# This node is the root of my scene tree, and the only static node in the tree.
extends Node


func _ready():
    # Create the viewport, add something to it, and add it to the scene tree:
    # Note that setting UPDATE_ALWAYS doesn't help.
    var viewport = SubViewport.new()
    viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
    add_something_to_viewport(viewport)
    add_child(viewport)
    
    # Now that the viewport has a path, we can create a texture and connect it:
    var viewport_texture = ViewportTexture.new()
    viewport_texture.viewport_path = viewport.get_path()

    # Finally create a sprite that uses the viewport texture:
    var sprite = Sprite2D.new()
    sprite.centered = false
    sprite.texture = viewport_texture # <== this line causes debugger errors
    add_child(sprite)


func add_something_to_viewport(viewport: SubViewport):
    var label = Label.new()
    label.text = "Hello world"
    viewport.add_child(label)

(or with syntax highlighting for better readability)

enter image description here

Unfortunately the content of the viewport is not visible, and instead I'm seeing debugger errors like:

get_height: Viewport Texture must be set to use it
get_width: Viewport Texture must be set to use it

Solution

  • Initially I was suspecting this was a bug in Godot and raised this issue. A few helpful people pointed out that I simply misunderstood the role of setting the viewport_path of a ViewportTexture. Apparently this functionality is just used internally by the editor, and not the way you're supposed to connect a viewport texture with a viewport programmatically.

    Fortunately, the solution is much simpler and even cleaner than going via the node path. Instead of manually constructing the ViewportTexture, one can simply call Viewport.get_texture() to obtain the ViewportTexture instance directly. This works fine for me:

    extends Node
    
    
    func _ready():
        # Create the viewport, add something to it, and add it to the scene tree:
        var viewport = SubViewport.new()
        add_something_to_viewport(viewport)
        add_child(viewport)
        
        # Finally create a sprite that uses the viewport texture:
        var sprite = Sprite2D.new()
        sprite.centered = false
        sprite.texture = viewport.get_texture() # Just get the texture directly.
        add_child(sprite)
    
    
    func add_something_to_viewport(viewport: SubViewport):
        var label = Label.new()
        label.text = "Hello world"
        viewport.add_child(label)