UPDATE 2: I have updated the Github with a fix that should allow for better compatibility with TextButton styling. Allowing you to have these effects applied to text buttons while still having hover, idle, insensitive styling applied. Though it comes with some caveats. For more on it, see the Advanced section of the readme on the GitHub.
UPDATE 3: Special thinks to user _ticlock for the suggestion of using imagebutton instead of textbuttons. I have added an example of this to the GitHub. I will continue to have the text.py available however for anyone who would like the automatic style inheritance that it allows for. But text.py can otherwise be considered completely optional!!
UPDATE 4:After a friend's suggestion, I have also made the code available for download off of itch.io for those who'd want to download it through there or donate. itch.io Completely optional though. Get it however you'd like.
UPDATE 5:After updating to Ren'py 7.4, I can confirm that all of my effects and code still work. So no need to fear about these breaking these if you decide to update.
UPDATE 6:Decided to update a few things. Bit more than I'd like to cram in this section. So I've made a devlog on Itch for those who'd like to read up on the changes. TL;DR Added a new glitch effect tag and improved the documentation on the various tag arguments.
UPDATE 7: Updated to version 3.0! Included is a new text tag that allows you to make use of ATL to create new effects! Can read about it on the itch post.
Some of you may have noticed a couple of days ago I was looking into how it might be possible to move text around the screen. I feel it's safe to say I found my answer... along with something much... much more powerful. To go over some of what is possible with this.
I bestow upon you what I'm dubbing: Kinetic Text Tags.
The code for this is available at this GitHub but if you'll indulge me I'd like to explain how it works so others can get the most out of it. Afterward, I will go over some of its current limitations and areas it can be further expanded. This is my first time sharing anything like this as well so please let me know if I could have explained things better or ways I can improve. But given the sheer diversity of what you can do with this, I'll keep this mostly conceptual. Comments on how to code this and create your own are available in the kinetic_text_tags.rpy itself.
- To explain this I will start from where I began and build up from there. For starters, the problem with getting text to move is that the Text class does a lot of work for positioning each character. While this saves us a lot of headache in terms of making dialogue, it is very stubborn when it comes to allowing letters to move freely.
- To overcome this, I created a custom displayable and use a text tag to apply it to the text. Going through every letter and applying a WrapperClass to a Text displayable that will handle one letter (or whatever length of text you'd like it applied to). This is then inserted back into the text string as an individual displayable.
- Sadly, the Text displayable within our WrapperClass, our single letter, doesn't react much to Transforms. thankfully, our WrapperClass can specify where it would like to place the Text's render in the blit call! Allowing us to manipulate its position! And the Text displayable that holds our Wrappers and the other letters on the say screen (or wherever this is applied) considers all of our Wrappers as unique displayables. And when the Text class has a displayable inside it, it only checks the size of the displayable and allocates enough space for it in the line it's drawing, allowing us to display our letter of text however we wish. But this comes with a problem.
- Sadly, our Text parent will send our Wrapper NONE of its style properties. Since our WrapperClass is registered as a displayable within it, it doesn't think to inform it of its style properties, because why would it, it's not a Text string. But even if we tried to mask ourselves as a text string somehow (which would be very hacky), we'd only end up being asserted a position again more than likely. Regrettably, the only way I work around this is to define the current style again WITHIN the text tag. But you might realize that even if we have those tags within our custom tag, we still need a way to apply it to every letter individually. Which is where we can be especially clever.
- The Text displayable class will interpret any string we give it and have it search for tags within it. Meaning that all we need to do to apply our style tags to our letters is to include the tags around our characters WITHIN the letter's string itself. So if we had the character "s" and wanted to make it bold, all we'd have to do to apply this is to make it "{b}s". The child Text class within WrapperClass will apply the style for us! We just need to keep track of each property to apply while we're dividing up our original string.
- But you might realize that if that's all we need to do to apply a style... well then our WrapperClass could change our Text class after making it. With that we could change not only the style of the text within our WrapperClass, but could even change the letters or even words we want it to display as well. And while the online Documentation doesn't mention this, the Text class does have a function .set_text(), which will apply all changes we could make. Giving us complete control over the Text!!
That's most of what I have to say about how it works. Feel free to let me know if you'd want any of this explained further. I've been looking a lot at how Renpy handles Text lately and while I'm still getting my head around some of it, I've started to get a good feel for how it all works. I go over how you can make your own effects more within the code itself so you can get a feel for how to make use of it. But here I'll go over some of the implementation details and things to be aware of when making your own that didn't fit well within the code.
Limitations, Notes and Misc.:- You also can nest your classes, applying multiple effects at the same time! However, it is advised that you don't apply too many at once!! As each letter will be a Text displayable within a Wrapper within a Text within a wrapper within a Text... The call stack would likely go pretty deep and can start to hurt performance depending on how intense your effects are. In some instances, it may be a better idea to just have one class that applies all the effects itself so as to improve performance. Or you can implement what I have called the Omega Tag. Which you send all of the effects you'd like stacked and allow it to nest them directly without needing to go through as many Texts. Making it so the callback will just be Wrapper > Wrapper > ... > Text instead. Still might make some lag but definitely a substantial improvement. Again, use whatever method works best for you.
- There are some style tags that should be skipped. Renpy's documentation says you should skip {p}, {w}, {nw} and {fast} tags. I also feel that the {cps}, {clear}, {#} and {k} tags also deserve to go through untouched. Finally, there were some tags I did not feel up for implementing and I leave to the reader to attempt to implement if you so choose. These include the {space}, {vspace}, {image}, {a}, {horiz}, {vert}, and ruby tags ({art}, {rt}, {rb}, etc). I also did not feel up for testing every kind of style that can be applied in the Style class. I don't doubt there are many more combinations possible with those, but I am not fully prepared to be that rigorous in my testing. Outlines seem like they might be bugged??? I don't know enough about them to fully make use of them and test them. Please let me know of any issues you find and I'll see if I can fix them.
- If you'd rather a list of text tags I currently have supported, it's {b}, {u}, {s}, {i}, {font}, {color}, {size}, {=custom_style}, {plain}, {outlinecolor} and {alpha} as well as my custom tags.
- It should be noted that when a line is sent to the History tab, the History tab will strip it of text tags. Including ours. However, this behavior can be changed. You can either redefine the gui.history_allow_tags directly or just change the line in the default history screen to You may also realize this can be used to have different text show up in the history tag than what is shown in the say screen. While I fear what some may do with this power, I can only advise restraint. Otherwise, we might end up conditioning players to check that screen a lot more than is comfortable and break immersion. Please don't ruin visual novel players for the rest of us. The history tab is meant to be an accessibility feature, not a weapon. Might be a fun puzzle, but I feel there is already enough power within this system to implement them in other ways.
Code: Select all
$ what = renpy.filter_text_tags(h.what, allow=[gui.history_allow_tags, "your_tag_here"])
- The online documentation never mentions it, but there are functions within renpy to support Matrix math that can be applied to renders. Allowing for projections and rotations in 3D space. The example rotation I included is based on the method viewtopic.php?f=51&t=59587 used since it is likely faster. But I have tested it with the matrix functions and they do work. Though there are some quirks to it I'm still working out. If I come up with anything using them I'll likely add them to the GitHub. But for now, I'll leave an example of the RotateText class using it in a large block comment for people to play with as it does allow for rotations in the x and y axis! I'll also mention in there what the other matrix functions are and their parameters for those interested but not wanting to dig into renpy to find them.
- I should acknowledge that if it is applied to a text button in the menu, it does not update the colors for hovering and such. The best workaround available is to use an imagebutton instead. Examples are included in the screens.rpy on the github.
- Style tag warning! If, for example, you have "Here is some {b}nice {bt}{b}wavy{/b} smaller{/bt}{/b} text" Where you have a bold begin outside of one of our custom text tags but want it to end inside the tag, you'll have to include an additional {/b} outside the tag like above to be sure that it ends it for the whole text. In you don't, the bold will apply for the rest of the text! So either be sure to end it again outside the tag or make it so your custom tag function will add the cancel tag back in regardless.
- I believe I attempted most of the effects I was interested in testing. I honestly can't name one that didn't work (even if rotation still needs some polishing). The only thing I thought of that I didn't try was wrapping the text in a Drag n Drop class. But I haven't messed with those much yet. So just something to try later. Though I feel I've only scratched the surface of what's possible with this and am super interested in seeing what people come up with.
- Renaming characters on the fly can already be done with a Character that has the dynamic flag. Please see the documentation for more info https://www.renpy.org/doc/html/dialogue ... er-objects. That said, I felt it was useful to show as an example.