get_node crashing game when node is deleted - godot

First of all, I'm new to programming in general so I'm kind of assuming there's a simple answer to this question, I just couldn't seem to find it anywhere.
I'm making a simple platformer game with enemies that move toward the player. I used this code in the enemy's script underneath the physics process to get the player position:
player_position = get_parent().get_node("Player").get_position
However, upon the player being queue_freed when health reaches 0, the game crashes immediately and I get a null error due to there being no Player node. How can I work around this?

You could just set $Player.visibility to false instead of freeing, or you could check if the player exists first using get_parent().has_node("Player")

When you destroy the player, the physics process function is still trying to get the player node, even though it doesn't exist. So, as Lucas said, you could replace:
player_position = get_parent().get_node("Player").get_position
with something like...
if get_parent().has_node("Player"):
player_position = get_parent().get_node("Player").get_position
(Before setting player_position, it will check if the player node even exists)

I think you could use weakref (documentation here).
If you declare a weak reference:
var player_ref: WeakRef = null
and store that reference:
func store_player_node():
var player_ref = weakref(get_parent().get_node("Player"))
Then you can access that reference later:
if player_ref.get_ref():
player_position = player_ref.get_ref().get_position
Weakref has advantage over using get_parent().get_node("Player"). Let's imagine the following scenario:
Player dies and its node is removed from the parent node's children.
New node is created with name Player and added to the scene tree at the same place as the dead Player.
get_parent().get_node("Player") will return a node and the code will not crash. However, it will be a new Player node, not the old one, and I think this is usually undesired.
I hope this helps!

Related

How to Pinjoint2D darts(Kinematicbody2D) on moving wall(StaticBody2D) via gdscript

I have a shooting darts (KinematicBody2D) that will need to stick on a moving wall (staticbody2D).
I wants to let the dart stick on the wall, and change position according to how the wall move (currently my wall is moved by updating its position).
However, the dart does not follow fully the moving path of the wall.
I end up adding pinJoint2D, but setting the node via gdscript only give me an error
Invalid set index 'node_b' (on base: 'PinJoint2D') with value of type 'StaticBody2D (StaticBody2DWall.gd)'.
My code in dart node for setting up pinjoint2d goes as below:
var slide_count = get_slide_count()
if slide_count:
var collision = get_slide_collision(slide_count - 1)
var collider = collision.collider
lif collider.is_in_group("wall"):
$PinJoint2D.node_b = collider
Anyone please help. Please let me know if there's a better practice.
The node_b member is a node path, not a node. Try the following:
$PinJoint2D.node_b = collider.get_path()

Godot, GDScript - Play Animation on Right Click

Any insight onto why this code doesn't work?
When I right click, the game crashes and gives the error: "Invalid call. Nonexistent function 'play' in base 'Array'".
func _ready():
anim_Play = get_tree().get_nodes_in_group("AnimationPlayer")
func_input(event):
if Input.is_action_pressed("aim"):
anim_Play.play("AimSights")
I guess from your code that you are trying to get a reference to your AnimationPlayer node, it fails and you get an Array instead.
It happens because you are using get_nodes_in_group (which returns an Array of nodes in a group), instead of get_node, which returns a node.
Invalid call. Nonexistent function 'play' in base 'Array
Means your are trying the call the play method (found in AnimationPlayer) from an Array object, that does not exist.
You would get AnimationPlayer like
var anim_Play = get_node("./path/to/your/AnimationPlayer")
Response to your question
get_nodes_in_group(group) returns an Array of nodes that are both in the SceneTree and in group group.
Let's say there is one AnimationPlayer node in the group "AnimationPlayer". We'll fetch it like:
var anim_player = get_tree().get_nodes_in_group("AnimationPlayer")[0]
Notice the [0]. That is called an accessor. We access the array at element 0. Now, we can call play:
anim_player.play("AimSights")
Do note: it is an error to access a non-existent element of an array.
Recommendation
This seems like an inappropriate use of groups. I recommend you use a node path, like svarog suggested, if the animation player is in the same scene as the script.
Additionally, it will help to read or google about some fundamental programming concepts: specifically Objects and Arrays.
Lastly, read over the scenes and nodes page from Godot's documentation: https://docs.godotengine.org/en/3.1/getting_started/step_by_step/scenes_and_nodes.html
The whole getting started guide on the Godot's documentation is an invaluable resource for learning Godot. It will help you greatly and it's not too long of a read.
Good luck!

Healthbars in Phaser 3

I am making a top down zombie shooter in Phaser 3.
I am using the moveToObject function to make zombies follow the player. Now I want to make healthbars for the zombies. I have read that you can use containers to make the enemies and healthbars move together but I am having trouble to move the container with moveToObject.
Is it possible to move a container with the moveToObject function or should I use something else instead?
It looks like the moveToObject method looks for an individual item's velocity and according to the API Docs, Container doesn't hold an overall velocity. But each object added to the container should have a velocity.
The quick way to fix this is to iterate through your container's objects and tell each item to moveToObject. I'm adapting an example from Phaser Labs here:
var block = this.physics.add.image(600, 300, 'block');
var clown2 = this.physics.add.image(20, 200, 'clown');
var clown = this.physics.add.image(200, 300, 'clown');
var container = this.add.container(10, 200, [clown, clown2]);
for (var x = 0; x < container.list.length; x++) {
this.physics.moveToObject(container.list[x], destination, 200);
}
This might not be the cleanest solution but it should help accomplish what you want to do. The Phaser API docs say moveToObject works with a GameObject, which includes Container. This might be worth reporting as a bug to see if there is a way the base code can be fixed to allow containers to work. You can report the issue here if you'd like.

Teleporting node

I am trying to teleport my player node(kinematicbody2d) when it hits my finish node (area2d) from the side of the Finish node
BTW I'm using godot 3
What I tried:Change the location using get_node("player").set_pos and get_node("player").location
code:
extends Area2D
func _on_Finish12_body_entered(body):
if body.get_name() == "player":
print("%s touched the finish on level %s" % [body.get_name(), get_tree().get_current_scene().get_name()])
get_node("player").position = Vector2(1504, 1896)
pass
So what I need:
The playing being teleported to 1504, 1896
There's a lot of unknowns here that can be the problem
Is the player's location updated in other parts of the code? and if so, could it be possible you did move to 1504, 1896 but then immediately get clobbered by said code?
What is the current behavior when you apply the new position? Does your player move at all? Does it go somewhere unintended?
Does your print statement execute?
Have you tried using move_and_slide/move_and_collide for the kinematicBody to check for collision?
Just a few ideas on how to go about figuring it out.
This is what works with Area and KinematicBody (i.e. 3D):
extends Area
func _on_Area_body_entered(body):
body.look_at_from_position(spawn, Vector3(0,0,0), Vector3(0,0,0))
with spawn being an empty spatial to define the point in space to teleport to.

AUDIO_OUTPUT_FLAG_FAST denied by client OR E/MediaPlayer: Error (1,-19)

I am creating a game using Libgdx. I have a lot of small sounds files in the MP3-format and since it is so many I do not preload them. I only load the sound I want to play when it is to be used, like this:
actorSound = Gdx.audio.newSound(Gdx.files.internal(sound));
The code above works great, but the rest of my sounds do not unfortunately. The actor above has its own class and plays a different sound every time it is touched.
When I try to play sounds in my Gamescreen I get the following error:
AUDIO_OUTPUT_FLAG_FAST denied by client
All the audiofiles have the same properties and have been recorded using the same microphone & Audacity. The files are all 44100Hz and only a few kb in size each.
I wonder why the sounds the Actor plays work and the other sounds do not?
I decided to try to change the non-working sounds to music instead and now they play fine - for a little while that is. I can play a full game, return to the menu and start a new game again. The second time I start a game I only get 3 sounds from the Gamescreen and then it is silent except for the sounds from the actor. The error that appears looks like this:
E/MediaPlayer: Error (1,-19)
I load the Music just the same way as the Sound:
gameSound = Gdx.audio.newMusic(Gdx.files.internal(soundeffect));
I have looked into the two errors by reading posts like these:
AUDIO_OUTPUT_FLAG_FAST denied by client
Mediaplayer error (-19,0) after repeated plays
But I'm not sure what to do or change to solve my problem. I would prefer Sound if that is possible, but Music is an acceptable workaround...
When it comes to Music it is probably as suggested in the URL, that I do not release the media players. I am not sure how to do that?
When I leave the GameScreen for another screen, like the MenuScreen or RewardScreen, I dispose Music first. The other screens use Music as well and the sounds are loaded when needed. When I change back to the GameScreen I dispose again and then start a new game...
Any ideas or suggestions? Any help is greatly appreciated.
I added AssetManager as suggested, and I now have a loding screen that loads all the sounds. I load them as sounds and not music, which is how I prefer it.
The sounds work well in the actual game but once I get to the reward screen, only the first sound plays and after that the app crashes with the following error:
06-02 07:47:09.774 6208-6282E/AndroidRuntime: FATAL EXCEPTION: GLThread 2935
Process: PID: 6208
java.lang.NullPointerException: Attempt to read from field 'com.badlogic.gdx.assets.AssetManager
Assets.Assets.manager' on a null object reference
at DelayedSounds(RewardsScreen.java:538)
at RewardsScreen.Update(RewardsScreen.java:567)
at Screens.RewardsScreen.render(RewardsScreen.java:577)
at Game.render(Game.java:46)
at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:459)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1562)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1262)
06-02 07:47:13.823 6208-6208 E/AndroidGraphics: waiting for pause
synchronization took too long; assuming deadlock and killing
The DelayedSounds method looks like this:
public void DelayedSounds(){
timer = timer + Gdx.graphics.getDeltaTime();
if(playitem == true && timer > 2){
itemsound = "vinster/" + item + ".mp3";
assets.manager.get(itemsound, Sound.class).play(volume);
//sound = Gdx.audio.newMusic(Gdx.files.internal(itemsound));
//sound.setVolume(volume);
//sound.play();
playitem = false;
}
if(playkeep == true && time > 3){
assets.manager.get("dialog/VINSTEN.mp3", Sound.class).play(volume);
//sound = Gdx.audio.newMusic(Gdx.files.internal("prizes/prize.mp3"));
//sound.setVolume(volume);
//sound.play();
playkeep = false;
}
}
As can be seen I use AssetManager now, and have commented out my old code for testing purposes. I define AssetManager in my main class and then pass it around to all other classes that uses sounds.
The RewardsScreen will only play the first sound and then it crashes on a NULL object reference as it seems.
If I change my code back to using Music in the RewardsScreen it works fine (see the code that is commmented out)
The sound I try to play is exactly the same in both cases. Assets class that handles the loading of all my assets has the sounds included and since I still get a NULL object I assume one or more items fails to load?
I search the logs and find this where it loads the sounds:
06-02 08:05:09.360 9844-9888/E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library "libwvm.so" not found
06-02 08:05:09.395 9844-9888/ E/NdkMediaExtractor: sf error code: -1010
06-02 08:05:09.395 9844-9888/ E/SoundPool: Unable to load sample
Maybe this is related to my problem?
I load all the sounds in the same manner and most definitely seem to work, the loading is the regular:
manager.load("prizes/cash.mp3", Sound.class);
I still find the AUDIO_OUTPUT_FLAG_FAST denied by client in my logs but now the sounds are playing instead of being rejected.
Any more ideas about how to solve this?
Your link having enough information for your bug.
It's not good way to create Resource instance each time in Game, Create once and user All over your game, if possible use AssetManager.
Like create Music instance in onCreate() method of your game.
gameSound = Gdx.audio.newMusic(Gdx.files.internal(soundeffect));
Music having play(), resume() and many other helpful methods.
You can also take a look of this, it may be helpful.

Resources