#making dialogue stop after you run out of the dialogue area

1 messages · Page 1 of 1 (latest)

fast vector
#

Hey, y'all! I was following this tutorial, but I've run into an issue.

https://www.youtube.com/watch?v=UhPFk8FSbd8

My inputs are all handled in physics_process, not unhandled_input like the tutorial. However, I don't want to change that. The player can move around once they've opened dialogue, which is fine with me, but I want the dialogue to close when the dialogue_interaction_finder (player's area2d) is no longer colliding with the NPC's dialogue area2d. How could I have it stop when they're no longer colliding?

here's what the code looks like in my player.gd:

        if Input.is_action_just_pressed("interact"):
        #Makes it so that something only happens if the collision shapes are overlapping
            var actionables = dialogue_interaction_finder.get_overlapping_areas()
            if actionables.size() > 0:
                actionables[0].action()
                return```

and in the dialogue_area:

```extends Area2D

@export var dialogue_resource: DialogueResource

#variable to choose the start of the dialogue
@export var dialogue_start: String = "this_is_a_node_title"

func action() -> void:
    DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)```

If you're relatively new to Godot (or just Godot 4) and want to set up branching dialogue then grab a copy of the repository below and follow along with this tutorial.

Starting project: https://github.com/nathanhoad/beginner_godot4_dialogue

Become a patron today: https://patreon.com/nathanhoad

0:00 Intro
0:35 Installing Dialogue Manager
2:51 ...

▶ Play video
shell vigil
#

area2d seems to have a area_exited signal that you could connect to.

fast vector
shell vigil
#

Depends very specifically on the structure of your nodes and how you reference to them and where.

But it could be akin to:

`
func _ready():
dialogue_interaction_finder.area_exited.connect(close)

func _unhandled_input(_event: InputEvent) -> void:
if Input.is_action_just_pressed("interact"):
#Makes it so that something only happens if the collision shapes are overlapping
var actionables = dialogue_interaction_finder.get_overlapping_areas()
if actionables.size() > 0:
actionables[0].action()
return

func close(area: Area2d):
dialogue_interaction_finder.queue_free()
`

In this example you'd probably end up destroying your dialog controller which you don't want.
But should serve to demonstrate the principle.

You'd probably want this functionality to be handled by the dialog controller itself

#

Also on the top left of the GUI when your game is running you can find the nodes instantiated in your root node tree and you mind find a way to path towards it in case it is not easily accessible.

fast vector
shell vigil
#

Just saw that the tutorial uses a plugin according to the docs:

https://github.com/nathanhoad/godot_dialogue_manager/blob/main/docs/API.md#methods
DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)

Returns the balloon node

So this should work in your dialogue_area:

`extends Area2D

@export var dialogue_resource: DialogueResource

#variable to choose the start of the dialogue
@export var dialogue_start: String = "this_is_a_node_title"

var balloonNode = null

func _ready():
self.area_exited.connect(_on_area_exited)

func action() -> void:
balloonNode = DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)

func _on_area_exited(area: Area2D):
balloonNode.queue_free()`

GitHub

A powerful nonlinear dialogue system for Godot. Contribute to nathanhoad/godot_dialogue_manager development by creating an account on GitHub.

#

You can also connect to signals via the GUI I bet there is a docs page on it

I'm also a godot beginner so I'm not sure how good of a practice it is to subscribe to your own signal.

I guess in your node that spawns the dialog you can also check has_overlapping_areas() and do the same queue_free() on the balloon if that methods returns false. in either _process or _physics_process()

fast vector
#

I appreciate you trying to help, but this isn't working. I followed the code to the T, and it's not getting rid of the dialogue window when the player exits the area.

shell vigil
#

Can you share you node tree and instead of destroying the node can you print something on area exit?

fast vector
#

The node tree for which scene? The dialogue_area scene is just the "Actionable" scene from the tutorial. It's a lone Area2D node. I've played it in the world scene as a child of a Sprite2D node (of an NPC), and assigned it a collision box.

The player node is extensive, but it's set up just like Coco is in the tutorial.

I tried changing this

       balloonNode.queue_free() 

to

    print("area exited")```

but it isn't printing anything, so that must be the issue? it's somehow not recognizing when the player's dialogue_interaction_finder isn't colliding, but that doesn't make sense to me, because the only way it's possible to start the dialogue is if the areas are overlapping (which means colliding, right?)
shell vigil
#

I pulled the finished tutorial I didn't go through all the code but for some reason the collider under "nathan" does not respond to any signals.
The collider extending from coco in the player controller does.

The checked out project is not 100% functional it is missing resources and I don't plan to follow along with the tutorial.

But this should work:

In your player.gd

`
var dialogNode = null
func _ready() -> void:
animation_tree.active = true
actionable_finder.area_exited.connect(close)

func close(area:Area2D):
if dialogNode:
dialogNode.close()
dialogNode = null

func _unhandled_input(_event: InputEvent) -> void:
if Input.is_action_just_pressed("ui_accept"):
var actionables = actionable_finder.get_overlapping_areas()
if actionables.size() > 0:
dialogNode = actionables[0]
dialogNode.action()
input_vector = Vector2.ZERO
return`

in actionable.gd

`@export var dialogue_resource: DialogueResource
@export var dialogue_start: String = "start"
var balloon: Node

func _ready() -> void:
pass

func action() -> void:
balloon = Balloon.instantiate()
get_tree().current_scene.add_child(balloon)
balloon.start(dialogue_resource, dialogue_start)

func close():
balloon.queue_free()
`

For me the project is broken so I can't fully test it.

fast vector
fast vector
#

And what is is dialogNode?

shell vigil
#

Balloon is a scene from the tutorials creator repo, it might've been added later than you are in the tutorial or not mentioned in it.

instantiate "loads" the scene and then gets added to the world with .add_child()

In your case

in actionable.gd
balloon = DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)
should work just don't forget to make the balloon var at the top of your class.

dialogNode is a class property I created in player.gd (so don't forget to create it yourself as well in the player.gd)
It is set actionables[0] which is the actionable node that spawns the dialog text box.
Through the actionable we can find the dialogue text box. which is returned like in the snippet above.

fast vector
fast vector
#

Hey @shell vigil ! I found an unfortunate bug. So if the player chooses to end the dialogue while the dialogue areas are colliding, and then exits the dialogue area, the game will crash, because "Cannot call method 'queue_free' on a previously freed instance."

I tried adding an if balloon: statement before it queue_frees, but that doesn't work

    if balloon:
        balloon.queue_free()```

I still get the same error message. Do you have any ideas how we could prevent it from running the close function if the dialogue has already been closed?
shell vigil
#

Hmm my first guess would that that node is being freed elsewhere.

Are you calling close() on the ending of the dialogue as well because my gut is thinking that might be handled by the plugin itself when you reach the end of your dialougue or something like that.

hollow pebble
#

Maybe is_instance_valid(your_instance_here) or node.is_queued_for_deletion() would check for it.
But it'd be better to just not be running stuff unneccessarily of course.

fast vector
#

This is the entirety of the dialogue_area scene script:


@export var dialogue_resource: DialogueResource

#variable to choose the start of the dialogue
@export var dialogue_start: String = "this_is_a_node_title"

@onready var dialogue_area = $"."
var balloon: Node

func _ready() -> void:
    pass

func action() -> void:
    balloon = DialogueManager.show_example_dialogue_balloon(dialogue_resource, dialogue_start)

func close():
    if balloon:
        balloon.queue_free()

And in the player script there are these variables:

#to handle the dialogue window closing stuff
var dialogueNode = null```

this in the ready function:

```    dialogue_interaction_finder.area_exited.connect(close_dialogue)```

this is unhandled input:
```    if Input.is_action_just_pressed("interact"):
    #Makes it so that something only happens if the collision shapes are overlapping
        var actionables = dialogue_interaction_finder.get_overlapping_areas()
        if actionables.size() > 0:
            dialogueNode = actionables[0]
            dialogueNode.action()
            return```

and this function
```func close_dialogue(area: Area2D):
    if dialogueNode:
        dialogueNode.close()
        dialogueNode = null```
shell vigil
#

I'm a little to much wine in tonight to really help debug this, I can try and take a closer look tomorrow 😅

But I'd say place a debug point in your close queue free call.

Trigger the bug if it hits the breakpoint once check what the value of balloon is in the debug view if it is not null (which it shouldn't) the node is being freed elsewhere

fast vector
#

IDK if this is what you meant, this is the first time I've ever used a breakpoint intentionally lol. Looks like it is null?

shell vigil
#

Breakpoints are great.

But yeah it is null there probably a quick fix would be doing a null check in that function doesn't explain why it is even getting there but should solve it temporarily

fast vector
shell vigil
#

Usually I'd be val != null

For nodes like you did before should work in godot just moving the check one layer lower shouldn't let any null value slip through before the actual freeing of the node.

If balloon: #counts as a null check here