carenalgas / popochiu

Godot plugin to make point-and-click adventure games, inspired by tools like Adventure Game Studio and PowerQuest.
https://carenalgas.github.io/popochiu/
MIT License
181 stars 18 forks source link

Improve UX for dialog creation and management #260

Open stickgrinder opened 1 month ago

stickgrinder commented 1 month ago

Benefit description

Even if, from a design perspective, the dialogs in Popochiu work well, the user/developer experience for them in the editor has been left behind a bit.

The two main offenders here are the array of dialog items in the dialog inspector

immagine

and the scripting API that exposes a single _option_selected() method, which forces the developer into writing a match statement of sort to process all the logic behind the options of a dialog:

func _option_selected(opt: PopochiuDialogOption) -> void:
    # Use match to check which option was selected makes
    # for a VERY long and unmaintainable method
    match opt.id:
        "MessyRoom":
            await D.say_selected()
            await C.Popsy.say("Errr... sorry, I forgot to tidy up!")
            await C.player.say("OK, but it's better not to leave toy cars around.")
            await C.player.say("Someone can step over them and fall.")
            await C.Popsy.say("Can you help me tidy up?")
            await E.wait(1.0)
            await C.player.say("You little lazy rascal!")
            turn_off_options(["MessyRoom"])
            turn_on_options(["AskBored"])
        "AskBored":
            await D.say_selected()
            await C.Popsy.say("Yes! I want my toy car!")
        "Bye":
            await D.say_selected()
            stop()
        _:
            # By default close the dialog. Options won't show after calling
            # stop()
            stop()

Solution description

Configuring the dialog options with an array may not be THAT terrible, but providing a custom widget for the inspector that masks out some properties of the raw godot array, and gives some cosmetic order to the input form will surely do the trick.

For the script, the best option is to add a function named after the dialog option, for every single options. In the example above these should be:

func _opt_messy_room_selected():
    pass

func _opt_ask_bored_selected():
    pass

func _opt_bye_selected():
    pass

func _opt_fallback():
   pass

The _option_selected() function may still have a place maybe (but we must consider carefully the chain of calls to ensure no double management of the same option happens).

Also, we need to think about possible collisions or forgery of the function name (that's why I removed the _selected from the _opt_fallback() function, so nobody can create a colliding function by creating an option named fallback.

Exclusions

The only thing that's not easy at all is to delete a callback when an option gets deleted. Renaming the options or creating new ones are easy to manage, but deleting something may be both imprecise: we should delete the function starting from the func keyword, to the line before the next func keyword, but also comments on top of the first func... we may leave some garbage or delete too much stuff? Also deleting logic may be undesireable... say someone wants to move an option to another dialogue tree and we remove the logic entirely?

For these reasons, it seems a good idea to put a TODO comment on top of the function name, like

TODO: This dialog option is orphan and must be deleted or moved somewhere else.

Implications

It won't be easy to write a migration that will remap all the dialogs already present in 2.0 projects. This is a good reason to allow using _option_selected() alongside the new methods.

We need to find a way to fall back to the old match statements or continue to the new functions without creating odd behaviors.

a-ivanov commented 4 weeks ago

Hi all!

First of all thanks for a great library!

My 2 cents on current dialog creation way: it is rather low level. More points on developer experience:

So maybe we can use some kind of "frontend" for dialog creation and editing which solves above points and then adapt it to work with Popochiu. Popular solutions today are:

Each of them has own text based description language plus editor UI with syntax highlight and more.

I found Dialogue Manager smaller and more focused on dialog interaction, so here is working POC integration (haven't found any Popochiu integrations with external libraries, so mine is raw in structure and setup, sorry if the sample game repo is not a right place for this). It is self-sufficient, just checkout and play. The dialog logic gonna look smth like this.

If you like it I can make another request after discussion with corrections. Love to hear your opinion on this 😊

stickgrinder commented 4 weeks ago

Hi @a-ivanov, thanks for your nice words.

Yes, we know that this part requires more love. The localization topic is first in line in the roadmap, we know it's a huge missing feature. We will leverage the getText-based features of Godot and provide string export/import plus other goodies.

As for specific DSLs or other tools, it's a long discussed topic and we had our share of evalutation, but there are strong arguments to sustain the "dialog lines as clickables" architecture.

If you refer to the interface of those plugins, we may take some inspirations of course, but we'll get there in steps probably, mostly for we really need to do more smaller and frequent releases. 2.0 is taking forever to get to a stable point and we want our delivery to be consistent :)

a-ivanov commented 4 weeks ago

@stickgrinder I see your point. Well, if you have any ideas or tasks in mind - I am happy to help.