How do you display a part of the game screen in a subviewport?

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

I have a game board with minions on it and I want to be able to zoom in on a minion and display that zoomed in view in a subviewport.

My node structure is as follows:

TheNodeForMyGameLevel
->TheGameBoard
–>MyMinion
–>Panel
---->SubViewport (Viewport node)

Now I don’t know where to put the Camera2D. If I put it as a child of MyMinion, I can get the camera position and zoom correct, but it renders the camera’s view to the main viewport, which is not what I want. If I put it as a child of SubViewport (because I read that the camera renders to the closest ancestor viewport), I don’t know how to set the camera’s zoom and position to MyMinion. I lose all guidelines and indicators in the editor.

I’ve also tried various combinations of ViewportSprite, Viewport, Camera in different combinations and positions in the Node tree and gotten weird effects like the entire screen going grey, or white, but in all cases my SubViewport is always just a grey box. The above attempt is just the one that seems the most sane to me.

I feel like what I want to do is essentially really simple:

“Here is a box around a minion, now draw what you see in this other box on the screen.” But I can’t seem to be able to tell Godot to do that.

screenshot

Any help would be much appreciated. Thank you.

Could you please share an example project for that?
I could not replicate the solution.

DavidPeterWorks | 2018-04-23 17:27

:bust_in_silhouette: Reply From: quijipixel

First you must ensure that your subviewport share the same world that the parent viewport. You can set that using the function set_use_own_world(bool enable) of the subviewport object. In case you need to see the current world or do more code magic check the Viewports API.

Then, to keep your node structure, I think you’ll have to implement the movement of the camera by yourself. You can do so by moving its position. So, implement code in the Camera and add a function that receives the object it will follow, then you have several ways to get this objects position: You can poll this objects position inside a _process(delta) function or you can implement a homemade signal into the object, and connect that signal in the camera, getting all the objects movements:

# Code in Minion
signal moved

# Inside in the code, this is where the place where the minions move
move(new_position)
emit_signal('moved', get_global_pos())

Then code in the Camera2D a connection to this signal, and every time the minion moves, the camera will update its position.

# Code in Camera2D
var following = null
func follow_minion(minion)
    if following != null:
        stop_following_minion()
    following = minion
    following.connect("moved", self, "followed_moved")

func followed_moved(new_pos):
    set_global_pos(new_pos)

Remember to implement code to stop this signal from shooting if you want to move the Camera to another minion or object.

func stop_following_minion()
    if following != null:
        disconnect("moved", self, "followed_moved")
        following = null

I didn’t have the time to test this. Tell me if it works, I’ll try to squeeze some time to try it out.

Not working yet, but gave me some clues. I did some more experimentation.

screenshot

In the box on the left, here is my node structure:
TheNodeForMyGameLevel
->TheGameBoard
–>MyMinion
–>Panel
—>SubViewport (Viewport node)
---->Camera2D
---->TestMinion

I drew a circle around the camera position so I could see it, then moved it -90 in the x direction. The TestMinion is at (0,0). SubViewport is set to not use own world. As you can see, it still looks like the SubViewport has a completely separate world, and the camera seems to be “stuck inside the SubViewport”.

For the box on the right:
TheNodeForMyGameLevel
->TheGameBoard
–>MyMinion
—>Camera2D
–>Panel
—>SubViewport (Viewport node)

And in the Camera2D script:

func _ready():
    var vp = get_parent().get_parent().get_node("Panel/SubViewport")
    self.set_custom_viewport(vp)
    self.make_current()

This was just a guess as the description for set_custom_viewport is blank in the docs and I couldn’t really understand the code. But obviously this did not work either.

Hope you are having better luck than me. Ok, I’ll keep plugging away at it.

AngryTomato | 2017-08-21 08:18

I found the way to do it. You can find the answer to this in the split_screen_platformer demo. You need to create two viewports, the main Viewport where you’ll put your game scene, and a support Viewport to show the minion. Put your game level scene as child of the main viewport. Then you have to set the viewports world from the main viewport to the support viewport:

get_node("minion_panel/viewport").set_world_2d( get_node("main_panel/viewport").get_world_2d() ) 

Then you have to create a Camera2D to as child of the minion you want to follow. (or you could use one camera and implement something like my first suggestion). Use the method set_custom_viewport from the Camera2D API to assign the support viewport and you should get that working.

quijipixel | 2017-08-21 15:54

Yep, that works, although I have other problems now but I’ll post in another question. Thanks so much!

Btw, others who are looking, the split_screen_platformer is not added to the project list by default. You need to import it from the demo subfolder in Godot.

AngryTomato | 2017-08-25 20:30