hello,
I have this object to change levels, unfortunately the camera limitW / limitH isn't fast enought in some situations, i get small flickering.

**next_level.gd

func _process(delta):
	
	if ( timer > 0 ):
		timer -= 1*delta;
		return;
	
	if ( levelGO == true ):
		levelGO = false;
		if ( Player != null ):
			get_tree().change_scene_to_file(nextLevel);
			Player.position = Vector2(plrnextX, plrnextY);

func _on_area_2d_body_entered(body):
	if ( timer > 0 ):
		return;
	if body.is_in_group("player"):
		levelGO = true;

***mini_map.gd (GUI)

func _physics_process(_delta):
	
	if ( lvlName != get_tree().get_current_scene().name ):
		lvlName = get_tree().get_current_scene().name;
		lvl_ini_POS = get_tree().get_current_scene().map_POS;
		
		if ( Player != null ):
			lvlCamTile = get_tree().get_current_scene().get_child(0);
			Player.cam2D.limit_right = lvlCamTile.get_region_rect().size.x;
			Player.cam2D.limit_bottom = lvlCamTile.get_region_rect().size.y;

Iam using a node sprite with region enable to use has the level W / H
The problem here is...
( ex: the player is already on the next level and for 1 milisecond / cicle, the camera limits are still on the previous level )
if i want to stop that small flickering that happens on some ocasions i have to input the camera limits manually on the next level object, iam already writing the player X and Y. And camera limits adds extra confusion, of opening and closing tabs, some levels have 3 doors...

Is there a way to access the "get_tree().change_scene_to_file(nextLevel);" for a node in this case "lvlCamTile = get_tree().get_current_scene().get_child(0);"

nextLevel = res://levels/lvl_e7.tscn

Iam trying to avoid this in all next_level objects, has i have to input the values manually

func _process(delta):
	if ( timer > 0 ):
		timer -= 1*delta;
		return;
	if ( levelGO == true ):
		levelGO = false;
		if ( Player != null ):
			get_tree().change_scene_to_file(nextLevel);
			Player.position = Vector2(plrnextX, plrnextY);
			
			Player.cam2D.zoom =  Vector2( camZoom, camZoom );
			Player.cam2D.limit_right = camLimitX; ### trying to avoid this 
			Player.cam2D.limit_bottom = camLimitY; ### trying to avoid this 
  • xyz replied to this.

    jonSS change_scene_to_file() is a deferred call. It will take effect only after the whole of current frame finishes processing. Player and camera should wait one frame and then update itself according to newly loaded level.

      xyz But how do i access the next frame ? changing from scene lvl1 to lvl2... the problem here is that the camera limit isnt fast enough. In lvl1 the limit width is 0 to 500, in lvl2 0 to 1000, for a split second it shows the level 2 out of bounds.

      • xyz replied to this.

        It seems like you might want to think about a scene transition of some sort. It's not really worth trying to get that one frame in between the scene load looking perfect. It's best just to hide it with a transition. There's all different varieties you can do. Look for "godot scene transition" on youtube and there are tons of tutorials with different kinds.

        jonSS Set the camera directly. You can't do it immediately after calling change_scene_to_file() though. Wait one frame for it to actually execute.

        await get_tree().process_frame to wait 1 frame

        It always returns null error

        .

        ..

        Previously i was doing this: !to set the camera limit automatically!

        			lvlCamTile = get_tree().get_current_scene().get_child(0);
        			Player.cam2D.limit_right = lvlCamTile.get_region_rect().size.x;
        			Player.cam2D.limit_bottom = lvlCamTile.get_region_rect().size.y;

        i cant bypass that extra delta frame, the scene always returns null and i cant check for null...
        I think its the 'if levelGO == true' on top

        • Toxe replied to this.

          although i think that extra delta frame i have to wait to get the node and set the camera limit, is the split second i get when changing levels when the camera is out of bounds...
          this only happens on some ocasions, if lvl1 is 2000 width, and lvl2 500 width... for a split second when transitioning to lvl2 the camera is out of bounds.

          [edit]
          error

          print( get_tree().current_scene.scene_file_path )
          res://levels/lvl_e8.tscn
          res://levels/lvl_e7.tscn
          res://levels/lvl_e8.tscn
          res://levels/lvl_e7.tscn

          if ( playerlGO == true && get_tree().current_scene.scene_file_path != null ):

          E 0:00:04:0786 next_level.gd:37 @ _process(): Parameter "data.tree" is null.
          <C++ Source> scene/main/node.h:413 @ get_tree()
          <Stack Trace> next_level.gd:37 @ _process()

          Stack: invalid get index 'current_scene' ( on base:'null instance' )

          i cant check for null ?!?

          • xyz replied to this.

            jonSS If get_tree() returns null it means that the node you're calling it on is not in the scene tree. Which node are you running this script on?

              xyz its running on lvl1.tscn the ( next_level node ) its also on lvl1 going into lvl2 once the player touches it

              extends Node2D #next_level
              
              @export var nextLevel = "res://levels/lvl_a1.tscn";
              @export var plrnextX = 480;
              @export var plrnextY = 288;
              
              @export var camZoom = 1;
              @export var camLimitX = 480;
              @export var camLimitY = 288;
              
              var levelGO = false;
              var timer = 0.1; #0.1 seconds
              
              #func _ready():
              	#
              
              func _process(delta):
              	if ( timer > 0 ):
              		timer -= 1*delta;
              		return;
              	
              	if ( levelGO == true ):
              		get_tree().change_scene_to_file(nextLevel);
              		Player.position = Vector2(plrnextX, plrnextY);
              		Player.cam2D.zoom =  Vector2( camZoom, camZoom );
              		Player.cam2D.limit_right = camLimitX;
              		Player.cam2D.limit_bottom = camLimitY;
              
              		###lvlCamTile = get_tree().get_current_scene().get_child(0); # camera limit node
              		###Player.cam2D.limit_right = lvlCamTile.get_region_rect().size.x;
              		###Player.cam2D.limit_bottom = lvlCamTile.get_region_rect().size.y;
              
              		levelGO = false;
              	
              
              
              
              func _on_area_2d_body_entered(body):
              	if ( timer > 0 ):
              		return;
              	if body.is_in_group("player"):
              		levelGO = true;
              		#body.position = Vector2(plrnextX, plrnextY); #fuck not accurate !!!
              		#body.cam2D.zoom =  Vector2( camZoom, camZoom );
              • xyz replied to this.

                jonSS it doesn't matter in which tscn it is. The only important thing is the current state of the node the script is attached to. For some reason that node is not in the scene tree when you call get_tree(). You need to determine why it is not. Can you make a minimal reproduction project that just loads dummy scenes?

                jonSS Are you calling get_tree() from the same scene that you just unloaded by calling change_scene_*()? Because in that case you might have just pulled the rug from under your feet and your current scene is no longer in the scene tree.

                • xyz replied to this.

                  Toxe That's probably the case. It'd be a good idea for OP to make a clean scaffold project that just switches dummy scenes. Then test and try things with it to learn how scene processing/swapping is timed.

                  There it should be a flickering when going from level e1 to level e2 and e4 to e5
                  for a split second you can see a white rectangle with a red mark... that is the next level node showing up until the camera limit gets corrected

                  to stop the flickering y need to unComments these 2 line in ( next_level.gd )
                  #Player.cam2D.limit_right = camLimitX;
                  #Player.cam2D.limit_bottom = camLimitY;

                  I was trying to avoid this... because i have to input the camera limits manually for each next_level node on screen, its more confusing and takes more time

                  • xyz replied to this.

                    jonSS Remove camera limit updates from Player autoload (in miniMap node) and remove Player position/zoom update in next_level scene. Instead do all this in each level's _enter_tree() callback.

                      xyz In each lvl_e* script ? How do you use _enter_tree() would i have to use it multiple times for each node script... I was looking for a faster auto way of setting the camera limits, without having to write more code ? ( cant keep track of it then )

                      • xyz replied to this.

                        jonSS Then make a function in Player that will read any needed stuff from current level and adjust camera and player accordingly. Call this function from level's _enter_tree()

                        It's important to update all at the same time. Currently you have a mismatch because you set player position before the level is added and camera limits for new level only after the level is added. So there is one frame inbetween where player and level are not matched.

                        Btw no need to set camera limits every frame as you're currently doing. This is in fact causing the problem. Do it only once when level is loaded.

                        jonSS
                        in player.gd

                        func level_started():
                        	var lvl = get_tree().get_current_scene().get_child(0);
                        	cam2D.limit_right = lvl.get_region_rect().size.x;
                        	cam2D.limit_bottom = lvl.get_region_rect().size.y;
                        	position = lvl.player_initial_pos
                        	cam2D.zoom =  lvl.initial_zoom

                        In each level:

                        @export var player_initial_pos: Vector2
                        @export var initial_zoom: float
                        func _enter_tree():
                        	Player.level_started()

                        You can also put a dedicated positional marker node into level and let the level_started() function use its position for player starting position. That way you won't need a manually filled-in property.

                        Again, the important thing is to do all this at once as soon as level is added to the tree. So best to keep it all in one function and call that function exactly at right time.

                        EDIT: if there will be some initializer scripts inside level, it may be better to call Player.level_started() from level's _ready() instead from _enter_tree()

                          xyz thanks it worked
                          but using _enter_tree(): on lvl_e1.gd throws a nill error i had to use func _ready(): on other level e2, e3, e4, etc... _enter_tree(): seems to works.
                          I used the function in the camera2D instead, my player node script is kind of full:

                          *lvl_e1.gd

                          extends Node2D
                          var map_POS = Vector2(272, 112);
                          
                          func _ready():
                          	Player.cam2D.level_started()

                          *lvl_e2.gd

                          extends Node2D
                          var map_POS = Vector2(240, 128);
                          
                          func _enter_tree():
                          	Player.cam2D.level_started()

                          you can also put a dedicated positional marker node into level

                          Ive added a Marker2D into the nextLevel, but i cant move this node position when its on the level ?

                          the other nextLevel scenes on the level are duplicates. There are 2 nextLevel's in most levels the player either comes from the left or right ? I dont see how it can be done in an auto way ?
                          If i could move the Marker2D node in the editor for each nextLevel it would work, but the sub nodes cant be accessed in the editor

                          • xyz replied to this.

                            jonSS my player node script is kind of full

                            What do you mean by "full"? Just add this function to existing code. Having it in top node is better design-wise, and calling it looks simpler.

                            Your setup is kind of strange with Player being a sole autoload. It could be fully automated if level scenes were instantiated manually. This way, somebody needs to notify the autoload that scene was swapped so it can adapt itself to the new scene.

                            You don't need different scripts for each level. Use the same script just export the properties like map_POS and adjust them in editor. With this, you don't have to type any new code regarding camera/player initialization when you create a new level scene. It's fully automatic.