Render minigame in SubViewport when tree is paused (using get_gravity)

Godot Version

v4.5.1.stable.arch_linux

Question

I have a mobile phone style ui element in my pause menu. One of the menu items you can choose from is a minigame. I add the scene for the minigame in a SubViewport as a child like this:

func _on_game_button_pressed() → void:
  var scene = load("res://elements/cellphone/games/doodle_jump/doodle_jump.tscn").instantiate()
  %GameContainer/SubViewport.add_child(scene)
  %GameContainer.visible = true

The tree in this scenario is set to paused, because I’m still in the pause menu.
The pause menu’s process mode is set to “When Paused”, I also tried “Always”.

All child nodes of the pause menu have their mode set to “Inherit”.

When I load the game into the SubViewport, I can move left and right. I do this by setting the velocity to a fixed value and calling move_and_slide. So at least that part seems to be working.

But the gravity is not applied to the character.

While writing this post I worked out that get_gravity, which I use like this in the _physics_process:

if not is_on_floor():
  velocity.y += get_gravity().y * delta

seems to be a Vector2(0, 0).

I can’t find any settings on the SubViewport or anywhere else that I could tick to enable gravity in the SubViewport. If that’s even what’s going on here lol

How do you pause the tree?

I have a PauseMenuHandler node that has its process_mode set to Always and just calls get_tree.paused = true, like this:

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("ui_cancel"):
		if get_tree().paused:
			%PauseMenuBackground.hide()
			await %MobilePhone.hide_self()
			get_tree().paused = false
		else:
			get_tree().paused = true
			%PauseMenuBackground.show()
			%MobilePhone.show_self()

The minigame runs fine when not paused. It’s the pausing of the tree that seems to set the resulting gravity from the get_gravity call to a 0 vector.

Let the minigame scene run for at least one frame before pausing the tree.

2 Likes

Wow, Thanks!

That (sadly) works! I’d rather have a different solution, but if it works like that, it works for now :smiley:

If I can manage to find a solution that feels less hacky, I’ll add that here :smiley:

You can just use a custom global variable to store gravity.

I just had another issue actually. Area collisions are also not working, since the whole tree is paused. I think I’ll try to pause the main part of the game in a different way, since we don’t have sub-tree pausing yet, because otherwise the minigame just doesn’t work correctly.

I’m now tracking the pause state of the game via a global variable Game.is_paused that when set emits the signals was_paused and was_unpaused.

I’m setting the global variable from the PauseMenuHandler and I’m also connecting to those signals from there. Then I set the process_mode of the main scene to “Disabled”. (and of all the tweens I’m using by calling their paused() function. might vary for each use-case if you have more things that need individual pausing. would have loved to avoid that, but oh well)

I’m also setting the process_mode of the menu to “Always”.

When unpausing I re-enable the process_mode of the main scene and disable the menu again.
(and play() the paused tweens again, if is_valid() is true, ofc)

EDIT:
Pausing the individual tweens I had to do because I’m creating them via get_tree().create_tween(). If I’m creating them not tying it to the global tree, but my main scene from where I create them via just create_tween(), then they pause without me having to interfere manually. Makes perfect sense. Didn’t think of that at first.