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.

            jonSS There's a way to do it without using scripts in levels, from the same place you change the scene. You can pass player position to Player.level_started() and call it deferred from next_level scene:
            in player.gd:

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

            In next_level.gd:

            if ( levelGO == true ):
            	levelGO = false;
            	get_tree().change_scene_to_file(nextLevel);
            	Player.call_deferred("level_started", Vector2(plrnextX, plrnextY))

            This is then minimal intervention into your existing setup and it's more or less fully automated. You can as well add other arguments to level_started() if needed.

              xyz get_tree().get_current_scene().get_child(0) still returns the same null() value

              • xyz replied to this.

                xyz

                Ive commented all the func's ready() in the lvl_e's scripts,
                but the error comes from the ussual "get_tree().get_current_scene().get_child(0);" when you touch the next_level during the game

                • xyz replied to this.

                  jonSS Try it in 4.1. It works fine.
                  It's something related to 4.2, it's still beta after all but I'm not sure if it's a bug or they changed something in deferred call order. In any case the current_scene shouldn't be null there.

                    xyz in 4.2 rc1 they ve removed this function "change_scene_to_file"
                    from the signal " _on_area_2d_body_entered "

                    func _on_area_2d_body_entered(body):
                    	if ( timer > 0 ):
                    		return;
                    	if body.is_in_group("player"):
                    		levelGO = true;
                    		get_tree().change_scene_to_file(nextLevel); # error 

                    in 4.1 it gives you an error warning something like: 'change_scene_to_file' cant be used during an area enter collision
                    that is the reason why i have it on "func physics_process(delta)" in this case ive made a mistake and type "func _process(delta):" in next_level.gd, but its the same thing

                    • xyz replied to this.

                      jonSS I'm not following what they're doing with 4.2 but the problem is happening when calling change_scene_to_file() from either _process() or _physics_process():

                      func _process(delta):
                      	if Input.is_action_just_pressed("ui_accept"):
                      		print(get_tree())
                      		get_tree().change_scene_to_file("res://scene2.tscn")
                      		print(get_tree())

                      The scene gets properly changed but get_tree() returns null after the call. When doing this from _ready() everything is as expected. Looks like a bug.

                        xyz the log suggests using call_deferred(), when the func is on "area_enter" i just dont know how to use it..

                        It could be something to do with main scene ( res://levels/lvl_e1.tscn )... func ready only needs to be used on this scene, in the others y can use func _enter_tree(): ....
                        It problably is the same thing with "get_tree().change_scene_to_file(nextLevel);" if you can bypass the frist scene ( lvl_e1 ) the others could work

                        • xyz replied to this.

                          jonSS This is not specific to Area2D. It happens even if done plainly from _process(). Whatever it is, get_tree() should never return null here.

                          jonSS I reported it. The devs confirmed it as a regression bug that appeared in dev2. They added it to 4.2 milestone, so it'll surely be fixed.

                          As for your project, It'd be best to temporarily go back to 4.1 and use my last suggested solution with call_deferred() from next_level.

                            xyz I think the best solution maybe it would be to go back to the old way of changing a level... I cant rebember for sure, but in 3.5 the scene was instantiated, and the previous level scene removed... Maybe this way i could access the sub nodes in the new level scene...?! A google search on change level in gd4 always comes with "change_scene_to_file".

                            But, get_tree().change_scene_to_file(nextLevel); seems like one of those things that were made for a very specific thing, a certain type of game, or app for a specific user, or to make it easier to understand or quicker to type in code...?!

                            change_scene_to_file seems to come with the remove node included along with some toher trickets, in just one line of code

                            Note: The new scene node is added to the tree at the end of the frame. This ensures that both scenes aren't running at the same time, while still freeing the previous scene in a safe way similar to Node.queue_free(). As such, you won't be able to access the loaded scene immediately after the change_scene_to_file() call.

                            [Edit] get_tree() doesnt work because the scene is not there, or is already in the next or previous cicle...

                            • xyz replied to this.

                              jonSS The solution with deferred call I suggested works perfectly. It's fully automatic and, design-wise, it's a "natural" way to do it in your setup.

                              get_tree() doesn't work in 4.2 because of a core engine bug. The moral of the story is that you shouldn't quickly jump into using latest betas if're not well acquainted with engine's inner workings.

                              jonSS Hm, the plot thickens 🙂 Devs now claim that this timing change in change_scene_to_file() is in fact intentional. It won't be a deferred call from 4.2 on. It'll behave just like remove_child(), taking place immediately. The node running the script will not be in the scene tree after the call, so naturally get_tree() will return null. Imho this is better than it being deferred but it somewhat changes the way to handle scene swapping in cases like yours.

                              To make things bulletproof you should set things up so that there's no reliance on exact scene change timing:

                              • put all data that's relevant to camera/player initial setup into the level scene itself (instead into transitional next_level scene)
                              • notify the Player autoload about scene change form level itself by calling a dedicated level_started() function from level's _ready().
                              • do all level-related initialization of camera/player in that level_started() function.

                              That's all there is to it. If you set it up properly, this is fully automatic and should work regardless of change_scene_to_file() being deferred (v 4.1) or not (v 4.2)