Sometimes there are actions one wants to take on a Node but one needs access to the Scene or for other reasons needs it to be added to the Scene first.
In Swing, I could use a hierarchy change listener to do this. Is there some type of notification or event when a Node is added to the hierarchy so that getScene will return non-null?
Add a ChangeListener to the node's sceneProperty to be notified to changes of the scene the node is attached to, including when the scene property changes from null to some value.
Related
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.
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.
I want to know how to move a view from top to bottom continuously without animation. I am asking this because I want to get the position of the view at every step so that I can check that if there is any collision between that view and any other view.
With animation you can move (not exactly move) a view from one position to another position (Animator class), but animation produces an illusion to the user that it is moving but it's position is fixed all the time. So this can't be done using animation?
Second approach is incrementing position of view. I applied this method in onCreate(). If I used it without Thread.sleep(50) then the activity doesn't show the view, if I applied it with Thread.sleep(50) then activity doesn't start for some period.
Property animation (subclasses of Animator class) actually move the view, as they update the actual property of the view. It is the view animations (subclasses of Animation class) that don't move the actual view and instead just where it appears to the user.
Source:http://developer.android.com/guide/topics/graphics/prop-animation.html
Quote: With the property animation system, these constraints are completely removed, and you can animate any property of any object (Views and non-Views) and the object itself is actually modified.
You also shouldn't start moving things around in the onCreate method as things are still initializing (onwindowfocuschanged is recommened). Also if you call thread.sleep, you are going the sleep the main UI thread, hence freezing the application for a time.
Solved the problem using ValueAnimator :-
CodeSnippet :-
va=ValueAnimator.ofFloat(0.0f,size.y);
va.setDuration(5000);
va.setRepeatCount(va.INFINITE);
va.setRepeatMode(va.REVERSE);
va.start();
va.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
bullet[0].setTranslationY((Float) va.getAnimatedValue());
Rect R11=new Rect(bullet[0].getLeft(),bullet[0].getTop()+(int)bullet[0].getTranslationY(),bullet[0].getRight(),bullet[0].getBottom()+(int)bullet[0].getTranslationY());
Rect R21=new Rect(ball.getLeft(), ball.getTop(), ball.getRight(), ball.getBottom());
if(R11.intersect(R21))
va.cancel();
}
});
I want to add a new View to a Layout dynamically by layout.addView(view) method. The new View is not supposed to be visible, but I want it's Display List to be created so it doesn't have to redraw (call onDraw method) itself when I decide to show it (by animation, for example fade in).
I run this code:
customView = new CustomView(this.getContext());
customView .setAlpha(0.0f);
this.addView(customView ); // 'this' is RelativeLayout instance in this case
onDraw method gets called for customView and everything is fine. However, when I change anything in my layout (push a button, scroll, anything that invalidates layout), the onDraw method for my customView is called second time. After that, it isn't called any more if I don't invalidate my customView (correct behaviour).
I don't know why Android behaves this way. I want it to create customView, call onDraw, create a Display List for it, and not call onDraw any more until I invalidate my view. To sum up, onDraw is supposed to be called once.
And it has nothing to do with initial invisibility of customView, if alpha is set to 0.5f, behaviour is the same.
My question is, how to make Android call onDraw only once?
And if Android really has to call onDraw twice, then what should I do to enforce it to do it in some code right after this.addView(view); without setting up any timers because THAT would be totally ugly.
The behavior you are describing is ok and is a part of the android framework for view object -
Drawing
Drawing is handled by walking the tree and rendering each view that
intersects the invalid region. Because the tree is traversed in-order,
this means that parents will draw before (i.e., behind) their
children, with siblings drawn in the order they appear in the tree. If
you set a background drawable for a View, then the View will draw it
for you before calling back to its onDraw() method. Note that the
framework will not draw views that are not in the invalid region. To
force a view to draw, call invalidate()
(Taken from the official google android API docs).
Meaning your custom view is contained by the same view that contains the button\scrollbar etc. If you don't want it to be rendered everytime the onDraw method is called for the subtree your view resides in you can set the view's boolean flag to false - use setWillNotDraw() to do that. (you should place it on the activity's onCreate in order to render the view set this flag to false (which is also the default) and use invalidate() whenever you want to render the view).You can read the official google docs for further information.
I have created an activity that builds a TableLayout dynamically in the onCreate event. But i would like to know if it's possible to know when the system has finished the activity rendering.
When the layout is created statically by XML, we can to do any action in the onLayout event, but seems that when it's created dinamically i have to wait more time or wait for another event until full TableLayout rendering is done.
Any ideas??
Regards