britzl / defold-richtext

Defold-RichText is a system to create styled text based on an HTML inspired markup language
MIT License
75 stars 12 forks source link

Fading one or more characters in at a time smoothly #40

Closed subsoap closed 3 years ago

subsoap commented 5 years ago

https://www.twitch.tv/finestko/clip/PrettiestPopularToadSquadGoals

An effect some visual novels has which looks nice.

2019-09-01 20_21_53-FinestKO - Steins;Gate 0 - Visual Novel Chill Stream - Twitch

britzl commented 5 years ago

Could this be achieved using the truncate() and characters() functions?

subsoap commented 5 years ago

@britzl most likely if truncate allowed per word truncation

britzl commented 5 years ago

Added per word truncation in 5.9.0

subsoap commented 5 years ago

How would you do this?

I can't set the previous node invisible, nor can I delete it because of the way truncate works. I can set their color.w to 0 I think. It would be more convenient to fade in one word at a time, but that's a different look. timer.delay seems like the wrong tool for this problem too.

britzl commented 5 years ago

I created an example of one way of solving this: https://github.com/britzl/defold-richtext/blob/master/example/example.gui_script#L246-L283

Take a look and see if you have any suggestions on how to improve it.

subsoap commented 5 years ago

This seems like the right solution!

WoW's interface was done in Lua. I wonder if their quest text used a similar technique? :)

Here are some visual / timer changes only. The timer.delay calls should be saved in a table which can then be canceled for each? If you click back before it finishes the script errors over a deleted node. Could add an event for clicking while fading to instantly show all text.

The improvement to the example in general is also good!

local function create_fade_in_example()
    local long_text = [[
    This is a text that should fade in character by character. This text is long to make it easier to see the visual effect.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis porta eu ligula in varius. 

    Vestibulum fringilla rhoncus vestibulum. Nullam rutrum diam eu quam volutpat, ut laoreet massa suscipit. 

    Nulla id eleifend erat. Donec commodo quam nec posuere efficitur. Vestibulum a justo ex. 

    Vestibulum facilisis, felis id semper scelerisque, magna metus pretium felis, vel fermentum ipsum mauris a quam.
    ]]
    local settings = { position = vmath.vector3(20, 900, 0), align = richtext.ALIGN_LEFT, width = 640 }
    local words = richtext.create(long_text, "Roboto-Regular", settings)

    local fade_duration = 0.44
    local fade_delay = 0.022
    coroutine.wrap(function()
        -- disable all words
        for i=1,#words do
            gui.set_enabled(words[i].node, false)
        end

        -- iterate over words and fade them in character by character
        for i=1,#words do
            local word = words[i]

            -- split into individual characters and fade in
            local characters = richtext.characters(word)
            for i,char in ipairs(characters) do
                fade_in(char.node, go.EASING_INOUTSINE, fade_duration, (i - 1) * fade_delay)
            end

            -- start a timer to delete the individual characters and re-enable original word
            local max_delay = (#characters - 1) * fade_delay

            timer.delay(fade_duration + max_delay, false, function()
                for _,char in ipairs(characters) do
                    gui.delete_node(char.node)
                end
                gui.set_enabled(word.node, true)
            end)

            fade_delay = fade_delay
            fade_duration = fade_duration

            -- wait before dealing with the next word
            delay(max_delay)
        end
    end)()

    return words
end
britzl commented 5 years ago

Yeah, this was a fairly quick test to see if this approach works. You need to keep track of the words and characters and timer so that it's possible to do cleanup.

Why do you set the delay and duration like this: ?

fade_delay = fade_delay
fade_duration = fade_duration
subsoap commented 5 years ago

Why do you set the delay and duration like this: ?

I have no idea! 🤪 Must have been testing something and did something weird, or maybe happened from a multiple cursor being active without noticing.