Any way to do ATL/Style-changing effects on individual dialogue letters?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
paintbrushDjinn
Newbie
Posts: 2
Joined: Tue May 05, 2020 6:57 am
Contact:

Any way to do ATL/Style-changing effects on individual dialogue letters?

#1 Post by paintbrushDjinn »

Hello! I hope my goal makes some amount of sense, and isn't too much of an ask for a single thread. Basically, the long and short of what I'm trying to accomplish is that ultimately, I want to have a way I can apply visual effects to individual letters and words in dialogue text.

What I would like to do is add stuff like "shivering" letters (wherein they're moving back and forth quickly -- a little like the character select screen in Undertale), letters that change colour, and more complex stuff like glitching letters, or other more complicated, possibly variable dependent effects. The actual specifics of making each effect is, I recognize, at least partly covered by the ATL language (except for things like changing colour, as far as I can tell; I'm not really sure if there is a way to make ATL affect style properties, but I'm leaving that for later), which isn't the problem at the moment. The actual problem, however, has been trying to have this affect individual letters rather than an entire block of text.

Here has been what I've accomplished so far. In order to make sure I can apply a transform to dialogue, I went off the code figured out in the thread here: viewtopic.php?t=48419. Here was my version of it, adjusted so that instead, I was adding the what_transform argument instead of the window_transform one.

Code: Select all

# Within screens.rpy
...
transform Null_Transform:
	pass

screen say(who, what, what_Transform = Null_Transform):
	style_prefix "say"

	window:
		id "window"

		if who is not None:
			
			window: 
				id "namebox"
				style "namebox"
				text who id "who"
		
		text what at what_Transform:
			id "what"
...
And when I'm defining the character, I can just write this:

Code: Select all

define c = Character("Chara", show_what_transform=fancy_text_effect)
This is all fine, but this only works if I want to affect the entire dialogue string all at once, in the exact same way. But what I would like to do is have the effect happen individually to each character or glyph of the string, and I'm not sure if I've just missed a really obvious trick in the documentation that would allow me to do this.

The closest I can figure is trying to edit the say screen definition. I was thinking that if i could take the what object (which, from what i understand, is a string or a list of strings and displayables), unpack it into a list of characters, and then use the resulting list in place of "text what at what_Transform:" might be part of the way there, but I worry that this would either do nothing, or would return an error.

I do know that Ren'Py itself uses separate transforms on individual characters a little further in the screens.rpy file. The code for the skip indicator is as follows:

Code: Select all

...
            text "▸" at delayed_blink(0.0, 1.0) style "skip_triangle"
            text "▸" at delayed_blink(0.2, 1.0) style "skip_triangle"
            text "▸" at delayed_blink(0.4, 1.0) style "skip_triangle"


## This transform is used to blink the arrows one after another.
transform delayed_blink(delay, cycle):
    alpha .5

    pause delay

    block:
        linear .2 alpha 1.0
        pause .2
        linear .2 alpha 0.5
        pause (cycle - .4)
        repeat
...
...So I THINK I'm on the right track! However, the triangles for the skip indicator have no ID's, and thus do not need to worry about sharing one the way that what in the say screen does.

So my question is, specifically: Is there a way I could chain together separate text files consisting of single characters, each with their own separate transforms, so that they can share a single ID in the say screen? Or is there some other far easier way to do ATL effects on individual letters that I've just entirely missed?

I hope this all makes enough sense, and thanks for reading this far and for offering any help you can!

philat
Eileen-Class Veteran
Posts: 1910
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#2 Post by philat »

As far as I know, basically the answer is there is no simple way to do this. You can sort of hack your way around it for specific effects using text tags (e.g., viewtopic.php?f=8&t=23852 ) but there are limits. The internal workings of renpy's text display are at the engine level and rather inscrutable unless you have the time and knowledge to take the entire thing apart (which I certainly never have).

User avatar
MaydohMaydoh
Regular
Posts: 165
Joined: Mon Jul 09, 2018 5:49 am
Projects: Fuwa Fuwa Panic
Tumblr: maydohmaydoh
Location: The Satellite of Love
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#3 Post by MaydohMaydoh »

You could split a string and show each one with an atl, is the only way I can think of

Code: Select all

default random_text = "This is a random string"
hbox:
    for i in range(len(random_text)):
        text random_text[i] at some_effect
Dunno how well that would work, if it would even work at all.

philat
Eileen-Class Veteran
Posts: 1910
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#4 Post by philat »

MaydohMaydoh wrote: Wed May 06, 2020 1:46 am Dunno how well that would work, if it would even work at all.
Well, it can work. But the problem (and the reason I didn't mention it) is that by doing that you're automatically not using ren'py's built-in text system (the syntax, line-breaking, text tags, characters and all their assorted features like side images, etc.) which means that you're foregoing almost all of the benefit of using ren'py in the first place (as opposed to other engines that offer more freedom and less scaffolding, e.g., Unity).

User avatar
Imperf3kt
Lemma-Class Veteran
Posts: 3794
Joined: Mon Dec 14, 2015 5:05 am
itch: Imperf3kt
Location: Your monitor
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#5 Post by Imperf3kt »

Break it down.
What is the dialogue window? It's made of text that has been converted into an image, overlayed on top of the window background image, so...

I would hide the window, then apply ATL to each character and create an animated image that looks like a say window, but is in fact an image.
It'll be a long, complicated ATL transform, but at least it should work.
Warning: May contain trace amounts of gratuitous plot.
pro·gram·mer (noun) An organism capable of converting caffeine into code.

Current project: GGD Mentor

Twitter

User avatar
paintbrushDjinn
Newbie
Posts: 2
Joined: Tue May 05, 2020 6:57 am
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#6 Post by paintbrushDjinn »

Hello! Thank you to everyone for responding! I've been thinking about this all day and after some additional help, I believe I might have a better idea where to go with this. I was originally going to try and make an entirely new custom displayable that would hold the dialogue in the form of individual strings, each with an associated transform. However, I have no idea how this would end up interacting with ren'py's dialogue system, and I am admittedly not that sure in my OOP skills to try making that work while still having all the features I like from the regular text object.

I realized my best option is similar to the one mentioned here by some of you, and to make separate images for the affected characters, and display them instead where the normal dialogue would be. This would solve most of my problems, I think, although I end up with a couple others.

One option would be to figure out how to make a copy of the dialogue, laid out at the same location as the normal text, but replacing every string within it with a corresponding ATL image of each letter. the Say screen likely will not accept this as a what variable, as all the strings have been replaced with sequences of displayables related to the characters, but since it would still be a text object, I might be able to coax Ren'py into laying it out like dialogue text (I think the linebreaking algorithm should be available within renpy to do this?). This would be the ideal situation, since it would only make one copy of each character, and if i could code things well i might be able to give each string a complex transform based on its index (hopefully). however, while i think I know how to put a general transformed displayable into a text object (I have done so here, and it seems to work):

Code: Select all

image hieroglyph:
    "text.png" # an image i drew manually
    block:
        linear 1.0 yoffset 1.0
        linear 1.0 yoffset 0.0
        repeat
 
 ....
 # Other game code
 ....
 
    e "Once you add a story, pictures, and music, you can release it to the world!{image=hieroglyph}"
I can't seem to get this to work for a text displayable. Trying to write text letter = 'H' returns an error, as renpy seems to dislike text displayables being referred to at all; if i force renpy to define it with $ letter = Text("H"), and throw that into the ATL image, it returns the text "Image letter not found". Would I need to wrap this text image somehow? As well, like I mentioned earlier, I'm not entirely sure how to make this copy fit within the dimensions of the dialogue box.

My other options seem less viable in comparison, at least given the knowledge of ren'py that I personally have. Another idea was to make a Composite of each character, and then figure out how to calculate the positions of each letter in the dialogue on screen. I could then use that to position the different parts of the Composite. Alternatively, I could use the information I know I'm given by the dialogue to calculate where each letter would end up (using the font size, the string index of each letter, the kerning, and the size/width of the textbox.) However, I'm admittedly not entirely sure what kind of algorithm I'd need to calculate all that information, since fonts arent necessarily monospaced.

(I THINK another possibility branching off from this might be to make an Hbox and just fill this with individual Text objects I'm transforming. This might deal with horizontal placement, however I do not know how I would get ren'py to deal with vertical placement, or if the answer to that is to produce a set of hboxes, how renpy might decide how many hboxes to make, and what characters go into which).

My final option, which would be very messy, would be to make identical copies of the dialogue, but with added text tags that make all but one portion of it invisible. I doubt this would work, since the added text tags would likely not be accepted by the what id, and I wouldn't be able to position the text in the right way as a result (making this option lose its major bonus). Not only that, but if i were to do this so that each letter gets a copy of the text like this, I worry it could balloon the memory required to display this on the screen (even if most are invisible, a 180-character message would need 180 copies of the entire thing, which would mean 180^2 = 36400 characters now being displayed on the screen. I am admittedly not sure how renpy deals with invisible displayables, or if this is even on a scale where it would be a problem, but it certainly doesnt help its viability.)

----

The TL;DR of this update is more or less that, I think the best option would be the first one, ie. that I should produce a copy made up of image versions of each letter, with an ATL applied to each. However, all of these options seem to be dependent on knowing how to force them to fit the dimensions of the textbox. Using just the x, y, height and width of the textbox doesnt seem to work, as I dont know if ren'py is aware where to add linebreaks. Any thoughts?

That's all for my update on my attempts at this, thanks again for all your advice and suggestions!

EDIT: Brief update, I have been told that there IS in fact a way to pull the coordinates of individual glyphs from a text object in ren'py, which answers one part of my problem! I just need to find out where the dialogbox object is stored, and to see if i can pull them out of its virtual layout.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#7 Post by Remix »

Just to give you an idea of how Ren'Py handles the text...

1) It takes the string and divides it into styled portions (generally where text tags change styles)
2) Each letter of each portion is drawn in turn onto a canvas taking into account the separate Kerning values between each letter, the line height and offsets and the line breaking algorithm. Internally in the engine, each letter (glyph) now has its x,y,w,h bounds calculated from all the above.

3) Depending on the elapsed time since the dialogue was shown, Ren'Py then uses the cps setting to determine how many letters should be shown for this frame and crops each out from the donor canvas and draws each onto the target canvas

Somehow, you'd want to hook into the system at point 3 - It would mean becoming very familiar with the internal workings of Ren'Py Text though
Frameworks & Scriptlets:

User avatar
trooper6
Lemma-Class Veteran
Posts: 3712
Joined: Sat Jul 09, 2011 10:33 pm
Projects: A Close Shave
Location: Medford, MA
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#8 Post by trooper6 »

You can use custom text tags.
https://www.renpy.org/doc/html/custom_text_tags.html

I tend to make little test programs for some feature in the documentation, often times whenever a new feature is announced...but I am trying to slowly work thought all of the documentation so I can get to know renpy better. Anyhow, here is the little test program I made for custom text tags.

Code: Select all

## The script of the game goes in this file.

## Declare characters used by this game. The color argument colorizes the name
## of the character.
define e = Character("Eileen", who_color="#c8ffc8")

define COLORIZE_COLORS = [
        '#ff0000',
        '#ff8000',
        '#ffff00',
        '#00ff00',
        '#0000ff',
        '#8000ff',
        '#ff00ff'
    ]
init python:
    #Custom Text Tag
    def rainbow_tag(tag, argument, contents):
        rv = []
        for kind, text in contents:
            if kind == renpy.TEXT_TEXT:
                for i in range(0, len(text)):
                    rv.append((renpy.TEXT_TAG, "color={}".format(COLORIZE_COLORS[i%(len(COLORIZE_COLORS))])))
                    rv.append((kind, text[i]))
                    rv.append((renpy.TEXT_TAG, "/color={}"))
            else:
                rv.append((kind, text))
        return rv

    config.custom_text_tags["rainbow"] = rainbow_tag


label start():
    #http://www.renpy.org/doc/html/custom_text_tags.html#custom-text-tags
    "Creating a custom text tag is a something doable!"
    "I've made a rainbow text tag, let's check it out."
    "{rainbow}Here I am turning all the text rainbow.{/rainbow}"
    "Maybe I only want to turn one word {rainbow}rainbow{/rainbow}."
    "And of course, you can nest text tags."
    "{rainbow}For example, all this text is rainbow. And this word is {size=40}big{/size}{/rainbow}."
    "Okay, that's it."

    return

A Close Shave:
*Last Thing Done (Aug 17): Finished coding emotions and camera for 4/10 main labels.
*Currently Doing: Coding of emotions and camera for the labels--On 5/10
*First Next thing to do: Code in all CG and special animation stuff
*Next Next thing to do: Set up film animation
*Other Thing to Do: Do SFX and Score (maybe think about eye blinks?)
Check out My Clock Cookbook Recipe: http://lemmasoft.renai.us/forums/viewto ... 51&t=21978

User avatar
fullmontis
Regular
Posts: 129
Joined: Sun May 05, 2013 8:03 am
Deviantart: fullmontis
itch: fullmontis
Location: Italy
Contact:

Re: Any way to do ATL/Style-changing effects on individual dialogue letters?

#9 Post by fullmontis »

A simple and hacky way of doing this is by using screens:

Code: Select all

transform crazy(delay_start):
    yanchor 0.5
    zoom 1.5
    pause delay_start
    block:
        ease 0.6 zoom 0.5
        ease 0.6 zoom 1.5
        repeat

screen crazy_string(string):
    hbox:
        xpos 264
        ypos 590
        for i in range(len(string)):
            text string[i] at crazy(0.1*i)

label show_crazy(t):
    window hide
    show screen crazy_string(t)
    pause
    hide screen crazy_string
    window auto  
And use it in code like this:

Code: Select all

    call show_crazy("Let's get crazy!")
And that's how it looks:
prova.gif
It's really buggy thought, since you would have to manually add window background and the character name/side image, the string isn't added to the history, text tags don't work, text doesn't wrap, and so on. It can't be used in the say statement in place of the what because it requires a text element.

Still, this is probably the easiest way to do this, and it allows you to use ATL directly. You can use ATL on the single letters and do rather cool things, so maybe for one odd flavoured line could still work.

Post Reply

Who is online

Users browsing this forum: Sugar_and_rice