diff --git a/Dialogue.gd b/Dialogue.gd index 9aff8fd..4c243eb 100644 --- a/Dialogue.gd +++ b/Dialogue.gd @@ -4,6 +4,10 @@ func _ready(): $RichTextLabel/Logo.hide() $VBoxContainer/ButtonTemplate.hide() + $Wol.connect('line', self, '_on_line') + $Wol.connect('option', self, '_on_option') + $Wol.connect('finished', self, '_on_finished') + func continue_dialogue(): if $Tween.is_active(): $Tween.remove_all() @@ -12,7 +16,7 @@ func continue_dialogue(): $Wol.resume() -func _on_Wol_line(line): +func _on_line(line): $RichTextLabel.bbcode_text = line.text $Tween.remove_all() @@ -26,7 +30,7 @@ func _on_Wol_line(line): $Tween.start() -func _on_Wol_options(options): +func _on_options(options): var button_template = $VBoxContainer/ButtonTemplate for option in options: @@ -45,7 +49,7 @@ func _on_option_selected(option): if not 'Template' in child.name: child.queue_free() -func _on_Wol_finished(): +func _on_finished(): $RichTextLabel.text = '' func _input(event): diff --git a/Dialogue.tscn b/Dialogue.tscn index 6e2a4cd..3e1745d 100644 --- a/Dialogue.tscn +++ b/Dialogue.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=12 format=2] +[gd_scene load_steps=11 format=2] -[ext_resource path="res://addons/Wol/Wol.gd" type="Script" id=1] [ext_resource path="res://addons/Wol/logo.svg" type="Texture" id=3] [ext_resource path="res://addons/Wol/font/Italic.tres" type="DynamicFont" id=4] [ext_resource path="res://addons/Wol/font/Regular.tres" type="DynamicFont" id=5] @@ -58,13 +57,6 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Wol" type="Node" parent="."] -script = ExtResource( 1 ) -path = "res://dialogue.yarn" -auto_start = true -variable_storage = { -} - [node name="Logo" type="TextureRect" parent="."] anchor_left = 1.0 anchor_top = 1.0 @@ -137,7 +129,3 @@ custom_styles/normal = SubResource( 1 ) text = "This is a dialogue option" [node name="Tween" type="Tween" parent="."] - -[connection signal="finished" from="Wol" to="." method="_on_Wol_finished"] -[connection signal="line" from="Wol" to="." method="_on_Wol_line"] -[connection signal="options" from="Wol" to="." method="_on_Wol_options"] diff --git a/ExampleScene.tscn b/ExampleScene.tscn new file mode 100644 index 0000000..492d74f --- /dev/null +++ b/ExampleScene.tscn @@ -0,0 +1,66 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/Wol/Wol.gd" type="Script" id=1] +[ext_resource path="res://Dialogue.tscn" type="PackedScene" id=2] + +[sub_resource type="RectangleShape2D" id=1] + +[node name="ExampleScene" type="Node2D"] + +[node name="Dialogue" parent="." instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 1024.0 +margin_bottom = 600.0 + +[node name="Wol" type="Node" parent="Dialogue"] +script = ExtResource( 1 ) +variable_storage = { +} + +[node name="Player" type="KinematicBody2D" parent="."] +position = Vector2( 360, 488 ) + +[node name="ColorRect" type="ColorRect" parent="Player"] +margin_left = -21.0 +margin_top = -52.0 +margin_right = 19.0 +margin_bottom = -12.0 +color = Color( 0, 0, 0, 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ColorRect2" type="ColorRect" parent="Player"] +margin_left = -8.0 +margin_top = -47.0 +margin_bottom = -28.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ColorRect4" type="ColorRect" parent="Player"] +margin_top = -23.0 +margin_right = 8.0 +margin_bottom = -19.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ColorRect3" type="ColorRect" parent="Player"] +margin_left = 7.0 +margin_top = -47.0 +margin_right = 15.0 +margin_bottom = -28.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"] +position = Vector2( -1, -9 ) +shape = SubResource( 1 ) + +[node name="StaticBody2D" type="StaticBody2D" parent="."] + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="StaticBody2D"] +polygon = PoolVector2Array( -3, 427, -40, 426, -40, 648, 838, 647, 838, 419, 787, 420, 788, 600, 2, 600, 0, 420 ) diff --git a/README.md b/README.md index a0a7299..bc19944 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Wol is actively maintained by [Bram Dingelstad](https://bram.dingelstad.works), ## Getting Started -This repo contains the source code for the Wol compiler. If you want to use it in a game engine other than Godot, you should get the appropriate package for your game engine. Check out [YarnSpinner-Unity](https://github.com/YarnSpinnerTool/YarnSpinner-Unity) for a Unity version of this project. +This repo contains the code for the Wol nodes & Yarn compiler. +If you want to use it in a game engine other than Godot, you should get the appropriate package for your game engine. +Check out [YarnSpinner-Unity](https://github.com/YarnSpinnerTool/YarnSpinner-Unity) for a Unity version of this project. ### Download from AssetLib Unfortunately, this option isn't available yet. Stay tuned! @@ -38,9 +40,9 @@ There are few things that need to be ironed out to be 100% feature compatible wi - [ ] Full support for [format functions](https://yarnspinner.dev/docs/syntax/#format-functions). - [ ] In-editor dialogue editor with preview. - [ ] Fully extend the documentation of this project. + - [ ] Replicate the files needed for the Yarn Spinner tutorial. - [x] Document the [Option](README.md#Option) object. - [x] Write the method descriptions for the `Wol` node. - - [ ] Write a basic "Hello World"-esque tutorial. - [x] Provide helpful anchors in the documentation. - [x] Porting to usable signals in Godot. - [x] Providing helpful errors when failing to compile. @@ -207,7 +209,7 @@ It has several properties that you can change either in-editor or using GDScript When getting an option from the `options` signal, use this function to let Wol node which option you want to select. Use `Option.id` for the `id` parameter. -## [Line](README.md#Line) +## `Line` _Inherits from [Object](https://docs.godotengine.org/en/stable/classes/class_object.html)_ An object holding all information related to a line in your dialogue. @@ -245,7 +247,7 @@ The [Line](README.md#Line) object is _the_ object that you're gonna be interacti Currently unimplemented. -## [Option](README.md#Option) +## `Option` _Inherits from [Object](https://docs.godotengine.org/en/stable/classes/class_object.html)_ An object holding information of an option in your dialogue. @@ -278,4 +280,193 @@ It has a reference to a [Line](README.md#Line) with it's `line` property so you # Tutorial -_The tutorial is currently under construction, stay tuned!_ +Welcome to Yarn Spinner! In this tutorial, you’ll learn how to use Wol in a Godot project to create interactive dialogue. + +We’ll start by downloading and installing Yarn Spinner. We’ll then take a look at the core concepts that power Yarn Spinner, and write some dialogue. +After that, we’ll explore some of the more advanced features of Yarn Spinner. + +## Introducing Yarn Spinner + +Wol & Yarn (Spinner) are tools for writing interactive dialogue in games - that is, conversations that the player can have with characters in the game. +Yarn Spinner does this by letting you write your dialogue in a programming language called Yarn. + +Yarn is designed to be as minimal as possible. +For example, the following is valid Yarn code: + +```yarn +Gregg: I think I might be sick. +Mae: True friendship: Letting your friend make you sick. +Gregg: True bros. +Mae: True bros. +``` + +Wol will take each line, one at a time, and deliver them to the game. +It’s entirely up to the game to decide what to do with the lines; for example, the game that these lines are from, +Night in the Woods, displays them in speech bubbles, one character at a time, and waits for the user to press a button before showing the next one. + + +## Quick Start + +We’ll begin by playing the example game that comes with Wol. It’s very short - about 2 minutes long. After that we'll make some small changes! + +1. Create a new empty Godot project. +2. Download and install Wol. Go to the [Getting started section](#README.md#Getting-Started), and follow the directions there. +3. Open the example scene. +4. Play the game. Use the left and right arrow keys to move, and the space bar to talk to characters. + +We’re now ready to start looking under the hood, to see how Wol & Yarn power this game. + +### The Yarn Editor + +Wol & Yarn Spinner stores its dialogue in .yarn (or .wol) files. These are plain text files, which means you can edit them in any plain text editor (Visual Studio Code is a good option, and Secret Labs offers a syntax highlighting extension to make it nice to use!) + +You can also use the Wol Editor, which is a tool in the Godot editor for working with Yarn code. This editor is useful because it lets you view the structure of your dialogue in a very visual way. + +### Reading Yarn + +In this section of the tutorial, we’re going to open the file Sally.yarn, and look at what it’s doing. + +#### Open Sally.yarn in your editor of choice. + +Yarn Spinner groups all of its dialogue into nodes. Nodes contain everything: your lines of dialogue, the choices you show to the player, and the commands that you send to the game. The Sally.yarn file contains four of them: Sally, Sally.Watch, Sally.Exit, and Sally.Sorry. The example game is set up so that when you walk up to Sally and press the spacebar, the game will start running the Sally node. + +#### Go to the Sally node. + +Let’s take a look at what that node contains. Here’s the entire text of it: + +```yarn +<> + Player: Hey, Sally. #line:794945 + Sally: Oh! Hi. #line:2dc39b + Sally: You snuck up on me. #line:34de2f + Sally: Don't do that. #line:dcc2bc +<> + Player: Hey. #line:a8e70c + Sally: Hi. #line:305cde +<> + +<> + [[Anything exciting happen on your watch?|Sally.Watch]] #line:5d7a7c +<> + +<> + [[Sorry about the console.|Sally.Sorry]] #line:0a7e39 +<> +[[See you later.|Sally.Exit]] #line:0facf7 +``` + +Take a second now to look at this code, and get a feel for its structure. + +### Lines and Logic + +We’ll now take a closer look at each part of this code, and explain what’s going on. + +```yarn +<> + Player: Hey, Sally. #line:794945 + Sally: Oh! Hi. #line:2dc39b + Sally: You snuck up on me. #line:34de2f + Sally: Don't do that. #line:dcc2bc +<> + Player: Hey. #line:a8e70c + Sally: Hi. #line:305cde +<> +``` + +The first line of code in this node checks to see if Yarn Spinner has already run this node. visited is a function that this example game has defined - it isn’t built into Yarn Spinner. It returns true if the node you specify has been run before. You’ll notice that this line is wrapped in << and >> symbols. This tells Yarn Spinner that it’s control code, and not meant to be shown to the player. + +If they haven’t run the Sally node yet, it means that this is the first time that we’ve spoken to Sally in this game. As a result, we run lines in which Sally and the player character meet. Otherwise, we instead run some shorter lines. + +Each line in Yarn Spinner is just a run of text, which is sent directly to the game. It’s up to the game to decide how it wants to display it; in the example game, it’s shown at the top of the screen. + +At the end of each line, you’ll see a #line: tag. This tag lets Yarn Spinner identify lines across multiple translations, and is optional if you aren’t translating your game into other languages. Yarn Spinner can automatically generate them for you. +Options + +Here’s the next part of the code. + +```yarn +<> + [[Anything exciting happen on your watch?|Sally.Watch]] #line:5d7a7c +<> + +<> + [[Sorry about the console.|Sally.Sorry]] #line:0a7e39 +<> +``` + +In the next part of the code, we do a check, and if it passes, we add an option. Options are things that the player can select; in this game, they’re things the player can say, but like lines, it’s up to the game to decide what to do with them. Options are shown to the player when the end of a node is reached. + +The first couple of lines here test to see whether the player has run the node Sally.Watch. If they haven’t, then the code adds a new option. Options are wrapped with [[ and ]]. The text before the | is shown to the player, and the text after is the name of the node that will be run if the player chooses the option. Like lines, options can have line tags for localisation. + +If the player has run the Sally.Watch node before, this code won’t be run, which means that the option to run it again won’t appear. + +The rest of this part does a similar thing as the first: it does a check, and adds another option if the check passes. In this case, it checks to see if the variable $sally_warning is true, and if the player has not yet run the Sally.Sorry node. $sally_warning is set in a different node - it’s in the node Ship, which is stored in the file Ship.yarn. + +```yarn +[[See you later.|Sally.Exit]] #line:0facf7 +``` + +The very last line of the node adds an option, which takes the player to the Sally.Exit line. Because this option isn’t inside an if statement, it’s always added. + +When Yarn Spinner hits the end of the node, all of the options that have been accumulated so far will be shown to the player. Yarn Spinner will then wait for the player to make a selection, and then start running the node that they selected. + +And that’s how the node works! +Writing Some Dialogue + +Let’s write some dialogue! We’ll add a couple of lines to the Ship. + + Open the file Ship.yarn. It contains a single node, called Ship - go to it. + +This code uses couple of features that we didn’t see in Sally: commands, and variables. +Commands + +Commands are messages that Yarn Spinner sends to your game, but aren’t intended to be shown to the player. Commands let you control things in your scene, like moving the camera around, or instructing a character to move to another point. + +Because every game is different, Yarn Spinner leaves the task of defining most commands to you. Yarn Spinner defines two built-in commands: wait, which pauses the dialogue for a certain number of seconds, and stop, which ends the dialogue immediately. + +The example game defines its own command, setsprite, which is used to change the sprite that the Ship character’s face is displaying. You can see this in action in the file Ship.yarn: + +```yarn +Player: How's space? +Ship: Oh, man. +<> +Ship: It's HUGE! +<> +``` + +You can learn how to define your own custom commands in Working With Commands. +Variables + +Variables are how you store information about what the player has done in the game. We saw variables in use in the Sally node, where the variable $sally_warning was used to control whether some content was shown or not. This variable is set in here, in the Ship node - it represents whether or not the player has heard Sally’s warning about the console from the Ship. + +Variables in Yarn Spinner start with a $, and can store text, numbers, booleans (true or false values), or null. If you try and access a variable that hasn’t been set, you’ll get the value null, which represents “no value”. +Adding Some Content + +#### Add some new dialogue. Add the following text to the end of the node: + +``` +Ship: Anything else I can help with? + +-> No, thanks. + Ship: Aw, ok! +-> I'm good. + Ship: Let me know! + +Ship: Bye! +``` + +#### Shortcut Options + +The `->` items that we just added are called shortcut options. Shortcut options let you put choices in your node without having to create new nodes, which you link to through the [[Option]] syntax. They exist in-line with the rest of your node. + +To use a shortcut option, you write a `->`, followed by the text that you want to display. Then, on the next lines, indent the code a few spaces (it doesn’t matter how many, as long as you’re consistent.) The indented lines will run if the option they’re attached to is selected. Shortcut options can be nested, which means you can put a group of shortcut options inside another. You can put any kind of code inside a shortcut option’s lines. + +Because shortcut options don’t require you to create new nodes, they’re really good for situations where you want to offer the player some kind of choice that doesn’t significantly change the flow of the story. + +Save the file, and go back to the game. Play the game again, and talk to the Ship. At the end of the conversation, you’ll see new dialogue. + +### Where Next + +The example game is set up so that when you talk to Sally, the node Sally is run, and when you talk to the Ship, the node Ship is run. With this in mind, change the story so that after you get told off by Sally, she asks you to go and fix a problem with the Ship. + +You can also read the Syntax Reference for Yarn Spinner. diff --git a/Sally.yarn b/Sally.yarn new file mode 100644 index 0000000..0101a35 --- /dev/null +++ b/Sally.yarn @@ -0,0 +1,55 @@ +title: Sally +tags: +colorID: 0 +position: 524,111 +--- +<> + Player: Hey, Sally. #line:794945 + Sally: Oh! Hi. #line:2dc39b + Sally: You snuck up on me. #line:34de2f + Sally: Don't do that. #line:dcc2bc +<> + Player: Hey. #line:a8e70c + Sally: Hi. #line:305cde +<> + +<> + [[Anything exciting happen on your watch?|Sally.Watch]] #line:5d7a7c +<> + +<> + [[Sorry about the console.|Sally.Sorry]] #line:0a7e39 +<> +[[See you later.|Sally.Exit]] #line:0facf7 +=== + +title: Sally.Watch +tags: +colorID: 0 +position: 512,430 +--- +Sally: Not really. #line:8c3f98 +Sally: Same old nebula, doing the same old thing. #line:24c418 +Sally: Oh, Ship wanted to see you. Go say hi to it. #line:df4eaf +<> +<> + Player: Already done! #line:1fea6c + Sally: Go say hi again. #line:5df323 +<> +=== + +title: Sally.Exit +tags: +colorID: 6 +position: 211,417 +--- +Sally: Bye. #line:60c282 +=== + +title: Sally.Sorry +tags: +colorID: 0 +position: 827,439 +--- +Sally: Yeah. Don't do it again. #line:d7df49 +=== diff --git a/Ship.yarn b/Ship.yarn new file mode 100644 index 0000000..cbc696f --- /dev/null +++ b/Ship.yarn @@ -0,0 +1,34 @@ +title: Ship +tags: +colorID: 0 +position: 721,130 +--- +<> + Ship: Hey, friend. #line:5837f2 + Player: Hi, Ship. #line:ship9 + Player: How's space? #line:ship10 + Ship: Oh, man. #line:ship11 + <> + Ship: It's HUGE! #line:ship12 + <> +<> + <> + Ship: Hey!! #line:ship13 + <> +<> + +<> + Player: Sally said you wanted to see me? #line:ship1 + <> + Ship: She totally did!! #line:ship3 + <> + Ship: She wanted me to tell you... #line:ship4 + Ship: If you ever go off-watch without resetting the console again... #line:ship5 + <> + Ship: She'll flay you alive! #line:ship6 + <> + <> + Player: Uh. #line:ship7 + <> +<> +=== diff --git a/TestScene.tscn b/TestScene.tscn new file mode 100644 index 0000000..9e03b5f --- /dev/null +++ b/TestScene.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://addons/Wol/Wol.gd" type="Script" id=1] +[ext_resource path="res://Dialogue.tscn" type="PackedScene" id=2] + +[node name="TestScene" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Dialogue" parent="." instance=ExtResource( 2 )] + +[node name="Wol" type="Node" parent="Dialogue"] +script = ExtResource( 1 ) +path = "res://dialogue.yarn" +auto_start = true +variable_storage = { +}