Phaser3 Scenes transitions - phaser-framework

I'm new to Phaser3 and before starting a crazy project, I want to know how I should start, switch between scenes. I saw that there are several functions, start, launch, switch, run, resume, pause, etc.
Example, lets say I want to have 2 scenes, a Menu and a Game. I boot on the Menu and I want to go to the Game scene and if I click on a button then come back to the Menu scene.
I've achieved this by calling the start function, but I noticed that the all, init, preload and create functions are called every time and therefore I'm loading all the images, setting all the listener over and over again.
This seems wrong, should I be using the launch or switch functions and pausing and resuming? But how do I hide the previous scene?
Thanks in advance.

This question might be a little too broad, but with Phaser 3 in mind, it still depends upon what purpose your menu serves.
I think most games have a main menu that will generally be called when the game first starts, and then won't be called again.
If this is an in-game menu, where settings can be changed or part of the game can be reset/restarted, then it might not make sense to redirect to a completely different scene.
With Phaser 3's support of multiple scenes - with Dev Log #119 and Dev Log #121 probably being the best current sources of information - another option would be to start a new scene within the current scene to handle this.
However, if this is really just UI, there's nothing to stop you from creating an overlay, instead of spawning an entire scene.
If you're concerned about performance I might think about whether the entire menu needs to be called, or if a simplified menu would work. Also, make sure that you're preloading assets before you're in the menu and main game.
I personally use Boot > Preloader > Splash Screen > Main Menu > Main Game scenes, where the Preloader loads the majority of the assets I'll need. This has the downside of a longer initial load, but the upside of minimal loading after this point.
Scene Transitions
How I handle these in my starter templates is to add the scenes to the Scene Manager when creating the scene. Then I transition by start to the first scene.
this.scene.add(Boot.Name, Boot);
this.scene.add(Preloader.Name, Preloader);
this.scene.add(SplashScreen.Name, SplashScreen);
this.scene.add(MainMenu.Name, MainMenu);
this.scene.start(Boot.Name);
Then I simply keep starting the next scenes as needed.
this.scene.start(Preloader.Name);
For another game that uses multiple scenes I ended up creating the following function (TypeScript) to handle this:
private sleepPreviousParallelScene(sceneToStart: string): Phaser.Scene {
if (this.uiSceneRunning !== sceneToStart) {
// Make sure that we properly handle the initial state, when no scene is set as running yet.
if (this.uiSceneRunning !== "") {
this.scene.get(this.uiSceneRunning).scene.sleep();
}
const newScene = this.scene.get(sceneToStart);
newScene.scene.start();
this.scene.bringToTop(sceneToStart);
this.uiSceneRunning = sceneToStart;
return newScene;
} else {
return this.scene.get(this.uiSceneRunning);
}
}
In the game I was using this for, I was trying to replicate a standard tab interface (like what's see in the Dev Logs above with the file folder-like interface).

Ok, here is the deal. In phaser 3, the start function actually SHUTS DOWN the previous scene as it starts the new one. This is why you see the init, preload ext every time. If however, you 'launch' your scenes instead, then they will not go through the whole shut down, restart sequence. You can move them to the top or bottom, set to receive input or not, etc. HOWEVER, BEWARE! As of phaser 3.16, sleep or pause of a scene does NOT shut down the update. As a result, if you launch a scene, then sleep it, it basically does nothing other than maybe set the flag, saying it's asleep even though it really isn't. So any update processing will continue in any launched scene. You can short circuit the update with a flag of some sort at the beginning, (that's what I've resorted to), but the other gotcha is the camera size. All the scenes must therefore have the same camera size or they will render (even if "sleeping") and mess up your display.
So, bottom line, it's going to be messy until sleep and pause actually work.

Related

How to make the functions done for group nodes in a scene not change after changing scene?

I have put nodes in a group. And I have put a func on the scene script to make changes to the nodes in group. In that func,I made the nodes to queue free and stuffs like that. But when I change scene and come back to the previous scene,the queued free is back again,it is not queued free anymore. How do I make it not change even after changing scene?
Why information is lost
When you change the current scene with change_scene or change_scene_to the current scene is unloaded, and the new one is loaded and instanced.
Perhaps it helps to conceptualize that the instance of the scene is not the same as the scene in storage. So what happens is like opening a file in an editor, modifying and not saving it.
Alright, that is one solution if you really want to go that route: you can save a scene.
First create a PackedScene, for example:
var packed_scene := PackedScene.new()
Then tell it to pack the root node of the scene, for example:
packed_scene.pack(self)
Note: any Nodes that don't have their owner property set to the Node you passed here will be ignored. And no, add_child does not set the owner property.
If you have been paying attention, you know you can give a PackedScene to change_scene_to... But how do you keep it around when the scene changes?
There are a few ways. And the thing is: if we can keep information around, we might not need to save the scene.
Keep things around in autoloads
The scripts and scene you add to your autoloads (singleton) in project settings stay there (unless you explicitly remove them) even when you change scene.
Thus, a very simple way to keep information around is to have a script with some variables that you can write and read from anywhere in your project. And you can do that with an autoload.
Keep things around in resources
Godot caches resources. When you load the same resource in multiple places, you actually get the same object.
Well, you know you can create a custom Resource class. To do that, on the context menu of the FileSystem panel select "New Script", and in the script make a class that extends Resource, and give it a class_name.
Then you can create resources of that Resource from the context menu of the FielSystem panel by selecting "New Resource" and picking your Resource class when asked.
Everywhere you load one of those, you are going to get the same object. Yes, even if it is in another scene. So you can add variables to your Resource class, write them in one scene and read them in another.
I explain a more concrete example elsewhere.
Keep things around in storage
You can write to a file and read from a file. For example you can do this:
# save
var file = File.new()
file.open("user://a.sav", File.WRITE)
file.store_pascal_string(var2str(data))
file.close()
# load
file.open("user://a.sav", File.READ)
data = str2var(file.get_pascal_string())
file.close()
Or, if what you want to store is a Resource (be it a PackedScene, or some other Resource class including a custom one), you can use ResourceSaver and ResourceLoader:
# save
ResourceSaver.save("user://a.tres", resource)
# load
resource = ResourceSaver.load("user://a.tres")
Of course, you can also load resources with load, or preload. You may also be interested in Background Loading.
By the way, if you are going to save player progress, having all the player progress data in a single object makes sense. And if you are going to have all the data you are keeping all the player progress data in a single place, it makes sense it would be somewhere it is accesible all the time and stays around even when you change scenes, thus: put the player progress data in an autoload. Bonus: you can put the functions to save and load to a file there too.
Don't change scenes
Since changing scenes brings problems - consider another solution: don't.
You can load an scene:
var packed_scene := load("res://scene.tscn") as PackedScene
Then make an instance of it:
var scene := packed_scene.instance()
Then add it to your current scene tree:
add_child(scene)
Yes, it is a Node! It also means you can…
scene.queue_free()
Or you can simply remove it with remove_child which does not free it, so you can add it back later.
So you would be in control of what gets loaded or unloaded and when. Which is useful to keep stuff around (e.g. the UI, the player character, etc...). A drawback of doing it the way I describe here is get_tree().current_scene would not be useful to you anymore. See also Change scenes manually.

Area2D Not Triggering Object in Godot

Here is my code: (just so you know I am a beginner and I just started this week, though I do have knowledge with other languages and game engines)
func _on_Area2D_area_entered(area):
get_parent().get_node("Level 1/Area2D/Flag").rotation_degrees += 1
What I was trying to accomplish was that the Player GameObject would see if its in the area of the Flag, and if it is, the flag would rotate.
I am not sure where the issue is. I think it is probably in the second line. I provided a screenshot below if I did the setup wrong. I have looked at the other close questions asked on the same topic, but they did not answer my question.
The "Player" GameObject is the one with the script that contains the detection if its in the area2D.
If you want to check if the Area2D is positioned correctly during runtime enable Debug -> Visible Collision Shapes.
If you want to check if _on_Area2D_area_entered is running, add breakpoints (or use print).
Did you get an error?
If there isn't a Node there, this expression will cause an error in runtime:
get_parent().get_node("Level 1/Area2D/Flag")
If you want to be able to check, you can use get_node_or_null and is_instance_valid.
Since you didn't mention any error, I'm going to guess the method is not running.
If the method is not running, the most likely culprit is that - I'm guessing given then name of the method - you connected the "area_entered" signal but intended to connect the "body_entered" signal.
The "area_entered" signal will trigger when another Area2D enters the Area2D. But I only see one Area2D in your scene tree. On the other hand the "body_entered" will trigger when a PhysicsBody2D (e.g StaticBody2D, KinematicBody2D, RigidBody2D) enters the Area2D. In either case you get what entered as a parameter of the method.
Other reasons why the Area2D might not be detecting what you want include no intersection of collision_layer and collision_mask and monitoring being disabled.
And to dismiss a couple possible misconceptions:
The "area_entered" and "body_entered" trigger when the Area2D or PhysicsBody2D respectively enter the Area2D, not every frame they are inside. So rotation_degrees += 1 is not a rotation animation.
You will get notifications of anything that trigger the signals, not just the object to which you connected it. You may have to further filter, e.g. if body == self:.
For people arriving here from search, I want to link a similar case: Enemy is not affected by bullets. And also my full explanation of how to set up physic nodes.

can i correctly finish an android app in Kotlin, when i swipe it out?

I hope, I can formulate my question correctly and understandable.
When I write an Android App in Kotlin, I normally have a button to close the app and for example finish it with writing a file or something like that.
Now, sometimes I don't finish it with the button, but swipe it out. Then, the file is not wirtten..
Is there a Kotlin statement to catch the "swipe out" and perform some code? When I Inflate another view, at the end I have a dismiss-statement or dismiss.listener and can do some code.
example:
dialog.dismiss() or popupwindow.dismiss()
So question: is there a dismiss.app or something like that?
When your app's Activity is destroyed (either by the user swiping it away, or the system killing the app to free up some resources) it goes through the usual lifecycle steps, ending with onDestroy.
These steps also get pushed to any lifecycle-aware components that are observing that activity's lifecycle, including Fragments (like a DialogFragment) - so that will also get an onDestroy() call. Fragments can be destroyed at other times too, but you can look at the activity's lifecycle to see what's going on there if you need to.
But really, as a general rule you want to save data in something like onStop(), when the activity/fragment is going to stop being visible, i.e. it's going into the background. That's a good time to make sure you've saved all your important data and state, because the user may not be coming back, and you can't be sure onDestroy will be neatly called (e.g. there could be a crash, or the phone might suddenly lose power).
Don't rely on persisting data with the onSaveInstanceState() callback though - that's intended for saving UI state, and if the user backs out of the app / swipes it away, that's counted as a fresh start for the next time they load the app, so onSaveInstanceState won't be called (since the UI state isn't being saved). Use onStop instead (or onPause if you like - have a look at those links for more info on what the difference is)

posenet p5js position stops updating when I move to another tab

I'm coding with posenet and p5js. I made a grid on my screen which plays different audio fragments on different x ranges. This works very well. However, if I keep it running and open another tab in my chrome webbrowser, the x position gets stuck in one area (it doesn't update anymore) and the same audio fragment is repeated until you get crazy.
Is there a way I can prevent this?
Well, some code would be great to be honest, but variables shouldn't really get stuck if you open another tab and so on, are you sure you haven't just missssspelled some variable, or some function or did one of those random mistakes?
If it truly is the p5.js I guess just try checking everytime draw fires if your canvas is still focused.
I believe you can also have canvas.hasFocus... exept you'd have to declare createCanvas as a variable (cause canvas is basically just a button or a textArea to html and javascript and p5)
Also I think there's a command to only allow for one audio channel and stuff in js and p5...
function draw() {
if(document.hasFocus()){
// all of your other code even
// background() and stuff...
}
}

My sprites are being deleted when another sprite is added in pygame

Ok I am a complete noob to pygame and programming in general. However I have been working on a game where the player falls and dodges objects. The idea is that the player remains within the top 1/4 of the screen and everything scrolls upwards to give the illusion of moving. This works fine however the code below is where I try and get another wall to spawn. This should work by deleting the sprite when it goes off the screen and spawning a new sprite when the image starts to go off the screen. The -2 is how far it should cross until another is spawned and my logic was to check the size of the group. While it only contained one it would iterate through the next section of code defining a new wall sprite identical to the old one but positioned below it. However this causes the program to crash probably because the old sprite is deleted causing an infinite loop. So if this is true how do you define a new sprite with the same attribute whilst not killing the other?
Also I realised that the code wont even work multiple times because I used "left_wall" which wont be defined after its been deleted so I tried using "wall" as I iterate through the group defining each sprite as wall in the for loop so every time a new wall is added it should be defined as wall so that I works infinitely. however I this error message AttributeError: 'Game' object has no attribute 'wall'. So at this moment I am stuck, not really knowing what to do next.
edit: I forgot to take off "self" when using "wall" which was the cause of the attribute error however it is still crashing like I mentioned in the first paragraph which is probably due to the old sprite being deleted.
sorry if this is confusing or not a properly structured question (like I said, I am a noob).
for wall in self.left_wall_sprite:
wall.rect.y -= self.player.vel.y
if wall.rect.bottom < 0: # move left wall
wall.kill()
if wall.rect.top == -2: # a new wall needs to be spawned
while len(self.left_wall_sprite) < 2:
self.new_left_wall = self.left_wall
self.new_left_wall.rect.top = self.left_wall.rect.bottom
self.all_sprites.add(self.new_left_wall)
self.left_wall_sprite.add(self.new_left_wall) # add the new sprite to the wall group so that it runs through the same cycle
With only this part of the code, it is hard to tell for sure what is going on - but what can be detected is that you create a new wall section, but do not "leave things as you found them" - so, that the code will still try to move the old wall.
If you really need to create a new sprite, go all the way: once the condition is met for the change:
get a reference to the old wall
remove the old wall from all groups (the .kill method)
create a new wall (create a new instance - you are just making an
assigment to an existing instance).
insert the new instance in all relevant groups.
As the code is, you are just pointing to the same wall object (self.left_wall) so that "self.let_wall" and "self.new_let_wall" are just references to the samething, and when you change a parameter (like rect.top) in one side, you can see the same change in the other.
Without the code for the class of your wall objects, one can't now if everything is set for correctly creating a new instance, but that could typically be done by self.left_wall = Wall((wall.rect.bottom, wall.rect.left, wall.rect.width, wall.rect.height)) (if your class's __init__ will take a rect as first parameter). This replaces the old self.rect_wall (which should have a reference on the wall variable yet) - you then just add it to the new groups.
But it looks like you can just reuse the same sprite - so, jsut update the top coordinate as you are doing -no need to fiddle with group containment or anythng:
if wall.rect.bottom <= 0:
wall.rect.top = wall.rect.bottom # or "0" at once

Resources