gdscript global variable value not changing after get_tree().reload_current_scene() - game-engine

I wanted the variable
first_playthrough
to become false so when the scene reloads, it won't show the text "Hello from Number Guesser" anymore. But it's still showing it.
Therefore, it's either: it never became false, or that it became false, but then went back to true.
The shortened version of the code:
extends Node
var first_playthrough = true
func _ready():
# this is here so it will show the message
first_playthrough_checker()
func first_playthrough_checker():
# problem here is that, the message below still shows even though i thought i set it to 'false' already.
if first_playthrough == true:
text_printer("Hello from Number Guesser!\n\n")
func _restart_game():
#I've tried everywhere else. Thought it would work here. i was wrong.
get_tree().reload_current_scene()
first_playthrough = false
One solution would be a persistent data storage.
But maybe for a simple game like this one, it isn't needed anymore?
What am i doing wrong here?
I'll post the whole script if needed.

Building on an answer from a different site where i posted the question as well.
After creating the singleton globals where first_playthrough is declared, i replaced all instances of the variable on the script into globals.first_playthrough.
So in the shortened version of the code, this looks like:
extends Node
# removed the declaration here already, since it's already declared in globals.gd
func _ready():
# this is here so it will show the message
first_playthrough_checker()
func first_playthrough_checker():
# message below doesn't show anymore after globals.first_playthrough becomes false.
if globals.first_playthrough:
text_printer("Hello from Number Guesser!\n\n")
func _restart_game():
#I haven't tested it but i suspect the line after reloading the scene will create a memory leak?
#So i changed globals.first_playthrough's value before reloading the scene instead.
globals.first_playthrough = false
get_tree().reload_current_scene()
The script works as intended now.
By learning to use singletons, i learned that:
Declaring a variable global to the class even in a single script
project doesn't make it actually global.
An object is only global if it is declared in the project as so.
I should keep in mind that I'm still using a framework.
With gdscript, I have 3 options to store persistent information. 2 of which are most recommended.
Singleton documentation link:
https://docs.godotengine.org/en/3.1/getting_started/step_by_step/singletons_autoload.html?highlight=autoload

Related

How do I require certain instance variables be provided at object creation?

Let's say I have a type of object in my game called oCharacter. All characters must have names, so I want to provide one when I construct the object. I can do that by using the _variables argument of instance_create_layer:
instance_create_layer(0, 0, "Instances", oCharacter, { name: "George" });
I could even make sure that I don't forget to do this by making a "constructor" function for characters and only instantiating them using that:
function character_create(_x, _y, _name) {
return instance_create_layer(_x, _y, "Instances", oCharacter, { name: _name });
}
But this approach has two problems.
The first is that I or another developer might forget about this convention and instantiate a character directly using instance_create_layer, forgetting to pass a name and setting up a runtime error further down the road.
The second (related) issue is that Feather doesn't know about this convention, so my Feather window is full of error messages about references to instance variables that aren't declared in the Create event - but I don't see how I can declare these variables in the Create event, as I'm expecting their value to be provided by the creator.
Is there some way of doing this that addresses these issues?
The first problem is just about setting rules about the code conventions within your team, if your team does not know about these conventions you want them to follow, then you should tell it them in a meeting.
For the second problem: Maybe you could create an empty/nullable variable in the Create Event? I'm afraid I'm not familiar with Feather
Personally I would do two things for this.
Create development standards for the team and put them in something like a Word document, wiki page, onenote, whatever makes the most sense for your team.
I would use a function to create the instance of the object (like you're doing there), and have some simple validation checks inside of the create event itself that will cancel it's creation (something like a guard clause) and output a debug message with a reminder.
It's not the most elegant solution but that should do the trick (assuming you haven't found something else by now haha)

How to make a class that inherits the same methods as IO::Path?

I want to build a class in Raku. Here's what I have so far:
unit class Vimwiki::File;
has Str:D $.path is required where *.IO.e;
method size {
return $.file.IO.s;
}
I'd like to get rid of the size method by simply making my class inherit the methods from IO::Path but I'm at a bit of a loss for how to accomplish this. Trying is IO::Path throws errors when I try to create a new object:
$vwf = Vimwiki::File.new(path => 't/test_file.md');
Must specify a non-empty string as a path
in block <unit> at t/01-basic.rakutest line 24
Must specify a non-empty string as a path
I always try a person's code when looking at someone's SO. Yours didn't work. (No declaration of $vwf.) That instantly alerts me that someone hasn't applied Minimal Reproducible Example principles.
So I did and less than 60 seconds later:
IO::Path.new
Yields the same error.
Why?
The doc for IO::Path.new shows its signature:
multi method new(Str:D $path, ...
So, IO::Path's new method expects a positional argument that's a Str. You (and my MRE) haven't passed a positional argument that's a Str. Thus the error message.
Of course, you've declared your own attribute $path, and have passed a named argument to set it, and that's unfortunately confused you because of the coincidence with the name path, but that's the fun of programming.
What next, take #1
Having a path attribute that duplicates IO::Path's strikes me as likely to lead to unnecessary complexity and/or bugs. So I think I'd nix that.
If all you're trying to do is wrap an additional check around the filename, then you could just write:
unit class Vimwiki::File is IO::Path;
method new ($path, |) { $path.IO.e ?? (callsame) !! die 'nope' }
callsame redispatches the ongoing routine call (the new method call), with the exact same arguments, to the next best fitting candidate(s) that would have been chosen if your new one containing the callsame hadn't been called. In this case, the next candidate(s) will be the existing new method(s) of IO::Path.
That seems fine to get started. Then you can add other attributes and methods as you see fit...
What next, take #2
...except for the IO::Path bug you filed, which means you can't initialize attributes in the normal way because IO::Path breaks the standard object construction protocol! :(
Liz shows one way to workaround this bug.
In an earlier version of this answer, I had not only showed but recommended another approach, namely delegation via handles instead of ordinary inheritance. I have since concluded that that was over-complicating things, and so removed it from this answer. And then I read your issue!
So I guess the delegation approach might still be appropriate as a workaround for a bug. So if later readers want to see it in action, follow #sdondley's link to their code. But I'm leaving it out of this (hopefully final! famous last words...) version of this answer in the hope that by the time you (later reader) read this, you just need to do something really simple like take #1.

Is Kotlin synchronized() not locking basic types?

class Notification(val context: Context, title: String, message: String) {
private val channelID = "TestMessages"
companion object ID {
var s_notificationID = -1
}
init {
var notificationID = -1
synchronized(s_notificationID) {
if (++s_notificationID == 0)
createNotificationChannel()
notificationID = s_notificationID
}
The above is being called simultaneously from two threads. A breakpoint in createNotificationChannel() clearly showed that sometimes s_notificationID equals 1.
However, if I change
synchronized(s_notificationID)
to synchronized(ID)
then it seems to lock fine.
Is synchronized() not locking basic types? And if so, why does it compile?
A look at the generated JVM bytecode indicates that the ID example looks like
synchronized(ID) { ... }
which is what you'd expect. However, the s_notificationID example looks more like
synchronized(Integer.valueOf(s_notificationID)) { ... }
In Java, we can only synchronize on objects, not on primitives. Kotlin mostly removes this distinction, but it looks like you've found one place where the implementation still seeps through. Since s_notificationID is an int as far as the JVM is concerned (hence, not an object) but synchronized expects an object, Kotlin is "smart" enough to wrap the value in Integer.valueOf on demand. Unfortunately for you, that produces wildly inconsistent results, because
This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
So for small numbers, this is guaranteed to lock on some cached object in memory that you don't control. For large ones, it may be a fresh object (hence always unlocked) or it might again end up on a cached object out of your hands.
The lesson here, it seems, is: Don't synchronize on primitive types.
Silvio Mayolo explained why it is not a good idea to synchronize on primitives (actually, I think the compiler should warn about this). But I believe there is another problem with this code, probably the main one that makes your synchronized blocks work in parallel.
The problem is that you replace the value of s_notificationID. Even if it would be an object, not a primitive, your synchronized blocks would still run in parallel, because each call to synchronized uses a different object. This is why in Java we usually synchronize on this and not on a field that we need to modify.
TL;DR The lesson here, it seems, is: Don't synchronize on primitive types.
synchronized(i) where i is Int, is actually synchronized(Integer.valueOf(i)).
Only in the range -128 to 127 this value is guaranteed to be a cached value.
Another fact is that ++i cannot be looked at as a mutation of the "object" i, but rather as replacing i by a new "object" with the value i+1.
Thank you broot & Silvio Mayolo for the above.
Experiments I did prove the above.
In my original code I have removed the ++ from
++s_notificationID. Amazingly or not, the lock worked now.
Now with that change I changed var s_notificationID = -1 to be var s_notificationID = -1000. Even more amazing, now the lock again stopped working.
Still, I think this anomaly of basic types undermines the attempt of Kotlin to see basic types as objects, and I think this should have been mentioned clearly in Kotlin documentation.

Why this error message appear?: invalid get index 'game_started' (on base: 'Node')

can you please help me, I have this script:
extends KinematicBody2D
var motion = Vector2(0, 300)
var sensitivity = 13
onready var player = get_node("../Player")
onready var enemy = get_node("../Enemy")
func _ready():
randomize()
reset_ball()
func _physics_process(delta):
if not get_parent().game_started:
return
if is_on_wall():
motion.x *= -1
if is_on_floor():
touch_someone(player)
if is_on_ceiling():
touch_someone(enemy)
move_and_slide(motion, Vector2(0, -1))
func touch_someone(node):
motion.y *= -1
motion.x = (position.x - node.position.x) * sensitivity
func reset_ball():
motion.x = rand_range(-300, 300)
nd when I run the game this error message appears
invalid get index 'game_started' (on base: 'Node').
This is the error
The error is telling you that there isn't a game_started defined in the parent node. There is a parent node, but it does not have a property game_started defined.
You probably intended it to be a property, but you forgot to define it, defined it somewhere else, or you did define it in the correct place, but instanced the node under the wrong parent. However, it is also possible you made it a method and simply forgot (). I don't know which is the case.
Regardless, in Godot, a common design pattern is to access down and signal up the scene tree. In this case, you are trying to access up the scene tree. This is problematic because the scene can't ensure where it will be instanced.
I'll give you a few approaches to solve this problem:
Check if game_started exists.
The change that will have a smaller impact on your architecture is to check if the parent node has game_started before trying to read it. Which looks something like this:
var parent = get_parent
if !"game_started" in parent or !parent.game_started:
return
However please notice that even if this gets rid of the error, it is not fixing the source of the problem. Since game_started is not defined in the parent anyway.
Furthermore, _physics_process is still getting called. There are ways to don't even have the call, which is better for performance. Will come back to that.
Let the parent access the child.
What you want to accomplish is to enable and disable _physics_process based on the value of game_started. There is a way to enable and disable _physics_process in the engine: set_physics_process.
Presumably there is some other code somewhere that sets game_started on the parent, in that moment it could call set_physics_process on the child to disable or enable _physics_process accordingly.
You may even use a property (with setget) to this effect.
For example:
var game_started:bool setget set_game_started
func set_game_started(new_value:bool) -> void:
if game_started == new_value:
return
for child in get_children():
child.set_physics_process(new_value)
game_started = new_value
With this, your script does not have to check game_started. One drawback is that the code only uses set_physics_process on the direct children. We could write a recursive version, however, we can do better!
Use pause.
As you can see in pausing games, you can use get_tree().paused to get or set if the game is paused. Which will stop _physics_process (among other methods) depending on the pause_mode.
Thus, you can have the parent update get_tree().paused...
Assuming, you also want a pause, you may follow this pattern:
var game_started:bool setget set_game_started
var game_paused:bool setget set_game_paused
func set_game_started(new_value:bool) -> void:
game_started = new_value
update_pause()
func set_game_paused(new_value:bool) -> void:
game_paused = new_value
update_pause()
func update_pause() -> void:
get_tree().paused = !game_started or game_paused
Then, for the nodes that should still work when the game is paused or not started, you can se pause_mode = PAUSE_MODE_PROCESS so they don't get _physics_process et.al. disabled. Then they can check game_started and game_paused to distinguish if the game is paused or not started.
A drawback is that this will affect nodes everywhere… If only game_started and game_paused could be available everywhere… Well, we can do that too!
Use an autoload.
Make an autoload (via project settings) of a scene with a node that has game_started et.al. So that it is available from everywhere. Then you can access it from everywhere.
You can do this combined with the paused as explained above. Which is what I would recommend.
Alternatively. Since you can access it from everywhere, you you could simply access it instead of the parent:
if !autoload_name.game_started:
return
This is the exception to the access down and signal up rule. Since the autoload is available everywhere and it does not depend on where or how the scene was instanced.
I remind you, is that paused and set_physics_process will avoid the call to _physics_process, and thus are more performant than checking this way.

Changing $*DISTRO values for testing

I need to test a feature that includes this line:
if $translate-nl && $*DISTRO.is-win
I have tried to reassign a value to $*DISTRO,
$*DISTRO='Windows 10';
but it says:
Cannot modify an immutable Distro (debian (9.stretch))␤
$*DISTRO is a dynamic variable, and it makes sense that it's not modified. That said, is there any other way that code can be tested (other than going to Windows, of course)
my $*DISTRO = ...
Hopefully modifying the original is unnecessary. It's generally unreasonable action-at-a-distance -- almost certainly so if someone has arranged for it to be an immutable value. This is the reason why global variables got such a bad reputation.
To elaborate on raiph's answer: the * in $*DISTRO marks it as a dynamic variable. You can re-declare it any scope, and code called from there will see the redeclared value:
{
my $*DISTRO = ...;
# coded called from here sees the redeclared value
}
# code called from here sees the original value
Now, the question remains, what do you put in place of these pesky ...?
In the simplest case, a mock that just has whatever the code under test needs:
{
my class Distro { has $.is-win }
my $*DISTRO = Distro.new( :is-win );
# call your test code here
}
If the code needs more attributes in Distro, just add them to the mock Distro class.
If the code needs a "real* Distro object, for some reason, you can instantiate the built-in one. The constructor .new isn't really documented, but the source code makes it pretty obvious what arguments it expects.
OK, I got the answer relatively fast. $*DISTRO is actually a read-only alias of PROCESS::<$DISTRO>
So we only need to do:
my $*DISTRO = Distro.new(:is-win,:release<11>,:path-sep('|||'),:auth<unknown>,:name<mswin32>,:desc<Test>,:version<v11>);
say $*DISTRO.is-win; #OUTPUT: «True␤»