Why ColorRect break Area2d? - godot

ColorRect break Area2d
My scene:
root
└ Node2D
└ Area2D
├ Sprite
└ CollisionShape2D
Area2D.gd:
extends Area2D
func _ready():
var b = self.connect("input_event", self, "_on_Area2D_input_event")
print($"../..".print_tree_pretty())
func _on_Area2D_input_event(viewport, event, shape_idx):
if InputEventMouseButton and event.is_pressed():
print("Click")
Everything works. If you click on the sprite, it is written "Click".
If my scene:
root
└ Node2D
├ ColorRect #This background
└ Area2D
├ Sprite
└ CollisionShape2D
The script Area2D.gd is breaking! If you click on a sprite, "Click" is **not **written.
Why is this happening and how to fix it?

Controls (such as ColorRect) take priority to handle input. So a Control will have an opportunity to handle the mouse input before any Node2D (such as Area2D) even if the Node2D is in front.
To fix that, change the Mouse Filter (mouse_filter) property of the ColorRect to "Ignore" (MOUSE_FILTER_IGNORE), so it does not handle mouse input, and lets the Area2D handle it.

Related

Why does rock image does not move alongside the mouse when in motion although I called the node and got its position-Godot Game Engine

I am trying to get my rock.png to move to the side(+x axis) when my mouse is moving. You can see that _target is the position of the event and the event is the mouse moving.
I then got the node image and set it to a variable.
In the else statement I made the rock.position the position of the _target and gave it somee space away from the _target
I want this to move because my camera moves and I want it to move with the flow of the camera
``
extends Camera2D
const zone = 10
onready var rock = get_node("rocks_png_1")
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
var _target = event.position
if _target.length() < zone:
self.position = Vector2(640,350)
else:
self.position = _target.normalized() * (_target.length() - zone) * 0.5
rock.position = Vector2(int(_target)+40, int(_target)+20)
``
From the code I used above I get this error
Invalid get Index 'position' (on base: 'TextureRact')
I tried just using the same code as I used in my self.position for the camera that made it move, but when I try it for the rock.position it gives me an error that tells me I need a Vector?
Invalid get Index 'position' (on base: 'TextureRact')
The error is telling you that TextureRect does not have a position property.
What happens is that a TextureRect is a Control which are positioned differently from Node2D (Camera2D is a Node2D). You can try using rect_position with it. Although I think you would be better off using a Sprite instead of a TextureRect (Sprite is a Node2D).
Notice that your TextureRect is a child of the Camera2D in the scene tree. Well, a Node2D would move with its parent automatically. So changing it to Sprite would negate the need of the line of code you are using to move it.
… Actually, due to the way Camera2D works (it has drag and limits) you might still want do something (depending what your goal is). So, know that the get_camera_screen_center method will give you the actual visual center of the screen (which is not the target of the Camera2D, again due to drag and limits).

Moving an instanced player with move_and_slide

I am probably overthinking this or going about it the completely wrong way. Basically I want to use move_and_slide on the instanced player (aPlayer) where I've commented, #MOVE. This script is attatched to my Tilemap on an instanced Scene Level_Test.
Code in screenshot pared down for this post because it is not relevant and nonfunctional at this time.
extends TileMap
onready var aPlayer = preload("res://Scenes/Player.tscn").instance()
var cSpawn1 = Vector2.ZERO
func _ready() -> void:
aPlayer.position = cSpawn1
add_child(aPlayer)
func _physics_process(delta) -> void:
if Input.is_action_just_released("iPlayerInteract"):
_Interact()
else:
var vInputDirection = Vector2(Input.get_action_strength("iPlayerRight") - Input.get_action_strength("iPlayerLeft"), Input.get_action_strength("iPlayerDown") - Input.get_action_strength("iPlayerUp"))
if vInputDirection != Vector2.ZERO:
_Move(aPlayer, vInputDirection)
func _Interact():
pass
func _Move(vSelf, vDirection):
#MOVE
pass
The player.tscn scene as depicted in the screenshot is structured as follows:
Player (Node2d)
└ Body (KinematicBody2D)
├ Camera (Camera2D)
├ Collision (CollisionShape2D)
└ Sprite (Sprite)
This is what gets instanced here:
onready var aPlayer = preload("res://Scenes/Player.tscn").instance()
That sets aPlayer to the root node of the instanced scene, which is a Node2D.
And thus what you pass into _Move (_Move(aPlayer, vInputDirection)) is a Node2D, which do not have move_and_slide, and thus calling move_and_slide on it results in error.
Instead move_and_slide exists in KinematicBody(3D) and KinematicBody2D.
The simple and elegant solution is make the KinematicBoyd2D (Body) the root of the scene. There is a "Make Scene Root" in the contextual menu you get on secondary click on the Scene panel, which will do that.
Alternatively you could also reach into the scene with get_node, for example:
vSelf.get_node("Body").move_and_slide(...)
Or for example:
onready var aPlayer = preload("res://Scenes/Player.tscn").instance().get_node("Body")
Addendum: There is another way if you think about it. You can add a script to the root node (Player) and declare whatever functions you want there, including but not limited to a move_and_slide that delegates to the KinematicBody2D (Body).
I should also point out that move_and_slide will move the KinematicBody2D, but not its parent. So the position property on the Player node will no longer be the position of the KinematicBody2D. And, well, you avoid all the trouble if you make the KinematicBody2D the root.

Godot - Missing Nodes

This is difficult to explain with typing...
I have a GameController scene (Node2D) that holds 3 instanced scenes within:
Mouse (scenes/Mouse.tscn) - this just swaps the mouse cursor for a custom graphic
HeaderBar (scenes/HeaderBar.tscn) - this is the score/label that just sits up top
Messages (scenes/Messages.tscn) - this is the "popup" message box that displays text to the user
In the main scene (Level1.tscn) I instance the GameController scene and it "works" fine. The header bar is there with the score/label and the custom mouse cursor is there and the message box is there (but I have it hidden by default but if I toggle its visibility in the remote it will show up).
Here's where my confusion comes in...
If I attempt, in the GameController script, to manipulate any of those nodes in the GameController scene (the mouse, header, messages) they return as null and will throw an error. For example; if I try to update the score in the $HeaderBar/Control/score I get the following:
Invalid set index 'text' (on base: 'null instance') with value of type 'String'.
The code completion will autofill the node names as I type them (so it recognizes them in the group) but any attempt to reference/use them in script throws similar errors to above.
I'm very new to Godot so I'm sure it's just some misunderstanding I have on how this type of thing works but I'm stumped! Any insight is much appreciated!
UPDATE
I will try to simplify my explanation a bit (I have made some changes). Okay here is the object script:
extends StaticBody2D
onready var main = load("res://scenes/MainGame.gd").new()
func _ready():
pass
# mouse [left] clicked on object
func _on_trigger_input_event(viewport, event, shape_idx):
if Input.is_action_just_pressed("left-click"):
main.display_message("you [left] clicked on the object!")
the call to main.display_message() works. It does call the function but here is that function in the MainGame.gd
extends Node2D
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
func display_message(msg):
label.text = msg
box.visible = true
It errors out because label (and box) are null. If I call display_message from the _ready function (in the MainGame.gd) it works as it should. Call it from outside (in the Object.gd) and the nodes are null for some reason.
This instances the scene as expected:
onready var main = load("res://scenes/MainGame.gd").new()
However, this code will run when the instanced scene is added to the scene tree:
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
Yes, both onready variables and _ready will run when this is added to the scene tree.
And when do you add main to the scene tree? I don't see where.
Then when you do this:
main.display_message("you [left] clicked on the object!")
Both box and label will still be null (which is the default value).
Thus, add it to the scene tree, for example:
func _ready():
add_child(main)
Except that does not work either, does it? Look at the code again:
onready var main = load("res://scenes/MainGame.gd").new()
You are instancing a script. A script. Not a scene. It will not have its children nodes and so on. You want to instance a scene, for example:
onready var main = load("res://scenes/MainGame.tscn").instance()
However, I find it odd that you are doing this in a StaticBody2D. I don't think it makes sense that MainGame belongs to a StaticBody2D.
I suspect, you are instancing MainGame in multiple places expecting it be the same instance. But it is not the same instance, it is a new instance.
Here is where I suggest you make MainGame into a autoload, so there is a single instance that you can access from anywhere. However, perhaps that is not right either. Perhaps you should use a Signal Bus. See Why does this variable keep returning to its original value? which had a similar problem (they where trying to open a door, you are trying to show a message, but still).

Why am I not able to reassign this basic variables value (GODOT game engine)

I have a simple variable in an autoload called " var Current_Scene" , it has no value assigned to it ( which means its automatically null.
In my game when you click a button, "Current_Scene"s value is SUPPOSED to change to a string called "Main Street". When I check if the value changed in debug it says "Current_Scenes" value is still null. Even when I experimented by presetting "Current_Scenes" value to the string "cat" , After I tried to change it to "Main Street" , debug said it was STILL cat. Idk what I'm missing?
below is my code that checks if my value was properly reassigned: (inside of Scene 2)
func _ready():
if SceneChange.Current_Scene != "Main Street":
print(SceneChange.Current_Scene)
below is my code inside of my autoload that sets up the variable: (inside of an Autoload)
var Current_Scene
below is my code for the button that I want to change my variables value: (inside of a different, Scene 1)
func _on_Button_pressed():
#starts world clock when start button is clicked
WorldClock._WorldTimerStart()
SceneChange.RemoveScene()
SceneChange.enter_main_street()
SceneChange.Current_Scene = "Main Street"
Thank you for any help!
I don't have your scene loading code (RemoveScene+enter_main_sceneimplementation), so I just used get_tree().change_scene(.... I was not able to reproduce the problem this way so maybe it is in your scene loading/changing code. Please provide your implementation of these methods to elaborate further or try to change it to get_tree().change_scene() and see if the problem is fixed.
My assumption is that the difference between our cases is that your implementation calls _ready() callback on second scene BEFORE SceneChange.Current_Scene = "Main Street" line is executed. If I use get_parent().add_child(secondScene.instance()) instead of get_tree().change_scene(), that would be the case for me. To fix that swap the lines of code that do the string assignment and adding packed second scene as a child node of a current one.
My implementation:
# Autoload named 'Global'
extends Node
var global_string = "cat"
# Script attached to the root node of "MainStreet.tscn"
extends Node2D
func _ready():
if Global.global_string != "Main Street":
print(Global.global_string)
# Script attached to the button on the first scene:
extends Button
func _on_Button_pressed():
get_tree().change_scene("res://MainStreet.tscn")
Global.global_string = "Main Street"
Fix in case second scene is added with add_child:
var secondScene = load("res://MainStreet.tscn")
func _on_Button_pressed():
Global.global_string = "Main Street" # should go first
get_parent().add_child(secondScene.instance()) # calls _ready() on second scene
As this Godot tutorial proposes, you could also postpone you scene change with call_deffered("your_method_that_changes_scene").
My Godot version is 3.2.3.

How do I change sprites in scripts?

I'm trying to make a dating sim as a easy first game programming-wise. I don't know how to change the character sprites inside the scripts.
character_sprite.gd
extends Sprite
var char_tex = load("res://Sprites/Lu2.png")
func _ready():
set_texture(char_tex)
func _input(event):
if event is InputEventMouseButton:
char_tex = load("res://Sprites/Lu1.png")
update()
Just set the texture property to the desired texture. You could also preload the textures and then just switch them instead of loading them again.
extends Sprite
var char_tex = load("res://Sprites/Lu2.png")
func _ready():
set_process_input(true)
texture = char_tex
func _input(event):
if event is InputEventMouseButton:
texture = load("res://Sprites/Lu1.png")
The problem in your example was that you only assigned a new image to the char_tex variable, but that doesn't change the texture of the sprite. The texture will still refer to the previous image until you assign the new one with texture = or set_texture. Gdscript is relatively similar to Python in this regard, so I recommend taking a look at Ned Batchelder's talk Facts and myths about Python names and Values.

Resources