This project contains custom node types to easily implement a dialogue box with input and choices in Godot 3.2. It can automatically block the input.
It's designed to work with mouse, keyboard and multitouch, and in HTML5 deployments. For complex dialogue trees look at dialogic
Features:
- Rich text support (formatting, colors, effects, etc.) using BBCode
- Display test immediately or writing it at a given speed, mix speeds
- Allows the player to accelerate a dialogue (can be disabled)
- Place breaks and associate signals to them
- Supports keyboard, mouse and multitouch input
- Blocks the input while dilogues is open
- Decent unicode support (it includes Noto Sans font)
You can see the included demo for a complete usage example, but here's a textual documentation
The plugin is in the Godot asset library and you can install it directly from the editor. Go to AssetLib
and search for "blocking dialog boxes". You need only the addons folder.
After installing, enable it from project settings -> addons
Otherwise, this repository works as a Godot project that you can use to run the demo.
To only use the plugin in your game you need only the addons/blocking_dialog_box
folder. Copy it inside your addons
folder then enable the plugin under Project -> project settings -> addons
Activating the plugin will add 3 new node types:
- BlockingDialogBox
- BlockingInputBox
- BlockingListSelection
You need to insert these nodes in this order in your main scene.
Notice that they are independent and you can import only what you need, but you should keep the relative order for them to overlap correctly.
To use the dialogue nodes you need to retrieve a reference and call their functions.
For example to display text:
# this assumes your main scene is called Main
var bdb: BlockingDialogBox = get_node("/root/Main/BlockingDialogBox")
bdb.append_text("Hello world! (slow)\n", 90)
bdb.append_text("Immediate, and supports [shake]effects[/shake]\n", 0)
the dialog box will automatically open, intercept the input to scroll and close itself when done.
To detect breaks do this:
func your_function():
bdb.append_text("wait for input and rotate[break clockwise]\n", 10)
var direction: String = yield(bdb, "break_ended")
rotate_me(direction)
bdb.append_text("wait for input and rotate back[break counterclockwise]\n", 10)
direction = yield(bdb, "break_ended")
rotate_me(direction)
func rotate_me(direction: String):
if direction == "clockwise":
$AnimationPlayer.play("rotate")
if direction == "counterclockwise":
$AnimationPlayer.play_backwards("rotate")
the [break X]
tag will wait for the player input and then produce break_reached
and break_ended
signals containing the X
parameter string.
The player can accelerate a dialogue by pressing the input button (enter, or the mouse, or the multi-touch) using [set_skip 0]
will make a dialogue unskippable to the player (use it with caution!) while [set_skip 300]
will set back the default time skip 300 milliseconds upon input.
Here yield
is used to make the code more readable, but you can also manually connect and disconnect:
bdb.append_text("wait for input and rotate[break clockwise]\n", 10)
bdb.connect("break_ended", self, "rotate_me")
def rotate_me(direction: String):
bdb.disconnect("break_ended", self, "rotate_me")
# do something with the direction
this is less concise and prone to error if you forget to disconnect but allows you to trigger multiple events and manage signals independently.
Important note: you cannot always use yield
like this, for example it doesn't work on the selection element. In that case use signals explicitly. You can find examples in the included demo.
Hopefully in Godot 4 there will be await
with a nicer usage.
The input function is similar:
var bib: BlockingInputBox = get_node("/root/Main/BlockingInputBox")
bib.ask_input()
var name: String = yield(bib, "text_entered")
# here do something with the name
same to select an element from a list:
var bls: BlockingListSelection = get_node("/root/Main/BlockingListSelection")
bls.ask_value(['green', 'blue', 'arrakis'])
bls.connect("choice_made", self, "when_item_selected")
These three elements are designed to prevent inputs, so that for example when you choose an option in a list the UI actions don't move the character in background.
In general input is blocked if and only if the elements are displayed, and all the elements define a logic to close themselves and stop grabbing the input.
Using the keyboard the ENTER
key goes forward in a dialogue and stops breaks.
The input box decodes the character and allows to cancel the latest one using backspace
.
If IME is active (you can activate it globally using OS.set_ime_active(true)
) the input element supports it.
When you are on HTML5 and mobile (mobile detection is based on agent string, couldn't find a nicer way), window.prompt
is used instead to overcome the lack of a keyboard.
Element selection can be done with arrow keys, you can scroll faster using page up/down and select with enter
.
Left clicking advances in dialogue, scrolling the wheel moves the se3lection in the element list and clicking an element selects it. Text input still requires a keyboard.
Works like a mouse, except that you can swipe vertically in the element list to scroll and click on an element directly or clicking outside the box will select the currently selected element.
This project is MIT licensed, you can use it in your own games and edit it as you please. Contributions and feedback are welcome!