A downloadable asset pack for Windows, macOS, and Linux
This project is made to demonstrate an effect I'm calling, Auto Highlight. Its primary purpose is to allow you to subtly change sprites based on who is speaking in the dialogue, drawing attention to the speaker's sprite. All handled automatically, saving you from having to write ATL on every line of the script. The default implementation of this I've provided makes use of a small zoom and darkening of the sprite. But can easily be expanded to make use of other ATL properties based on the desired effect.
The main file of interest is the00auto-highlight.rpy. It provides several comments to explain how it works and what you'll need to do to get it running.Be sure to read the setup comments near the top to know what you have to do to implement it.
Originally this was a minor effect that I created for Where the Demons Lurk. But it ended up being useful for both Beyond the Harbor: r and Burrows. As a result, I began to consider that it might be useful for other projects as well. So, I decided to release it so everyone has access to it.
Default implementation makes use of the matrixcolor ATL property, which is only available on Renpy 7.4 and above. But can be modified to work with earlier versions if you desire.
The code for this project is provided under the MIT license.
Credits:
Luke and Kota sprites used with permission from https://minoh.itch.io/minotaur-hotel-sfw I do not claim ownership of the sprites. The sprites belong to their respective owners.
Demo script written by members of FVN in a voice call: https://discord.gg/GFjSPkh - FVN is an 18+ discord server. Do not join if you are a minor.
Beyond the Harbor: r Example from: https://harmoyena.itch.io/beyond-the-harbor - BtH:r is an 18+ VN with strong themes that are inappropriate for children
Burrows Example is from: https://itch.io/search?q=Burrows - Burrows is an 18+ VN with strong themes that are inappropriate for children
Status | Released |
Category | Assets |
Rating | Rated 4.9 out of 5 stars (52 total ratings) |
Author | Wattson |
Genre | Visual Novel |
Made with | Ren'Py |
Tags | Asset Pack,Ren'Py,sourcecode |
Code license | MIT License |
Average session | A few seconds |
Languages | English |
Inputs | Keyboard,Mouse |
Click download now to get access to the following files:
Log in with itch.io to leave a comment.
As kinda the opposite of onebulb's comment below, for my use case I needed the sprites to transition between being tinted when speaking but left fully untouched at baseline. My modification ended up looking like the following:
if is_talking: # Apply the talking transformation trans.matrixcolor = SaturationMatrix(1.0 + (curr_ease * sat_change)) * BrightnessMatrix(0 + curr_ease * bright_change) else: # Apply the not-talking transformation trans.matrixcolor = SaturationMatrix(1.0 + ((1 - curr_ease) * sat_change)) * BrightnessMatrix((1 - curr_ease) * bright_change)
Thanks for making this - I customized it for my WIP to make the non-speaking sprites simply "shadowed". This is what I use:
if is_talking: # Apply the talking transformation trans.matrixcolor = IdentityMatrix()else: # Apply the not-talking transformation trans.matrixcolor = TintMatrix("#646464") * SaturationMatrix(1.0)
Hello! First off, thank you so much for this. It's a really neat effect, and I appreciate all the work you did on it.
For the most part, it seems very easy and straightforward. However, I have a few characters who have player-defined sprites. Right now I am handling this with DynamicImage statements.
For example:
image mc base = DynamicImage("images/sprites/mc/mc[avatar].png")
image mc sad = DynamicImage("images/sprites/mc/mc[avatar]_sad.png")
etc
In part 2 of the setup where you demonstrate how to set up the sprites, your image statements rely on the At() statement. Is there a way to make this compatible with DynamicImage, or is that a no go? Right now I don't have the sprites set up as layered, but I could switch to that if that helps in some way.
hey wattson!! fantastic tool!
currently working on a project for my thesis, and i'm struggling to understand what went wrong haha
the auto highlight will work for only the prologue, but when i continue adding in the sprites for other chapters, it just stops working (it'll show the sprite but the sprite highlight just doesn't seem to want to work). i've followed the steps you wrote out but maybe i did something wrong...
any ideas of how i can try to fix it? thank you!
I've never had it flat out refuse to work. My best guess would be maybe your sprites are sharing names that are used to look up which to highlight. Having two sprites referring to the same one can often make it act screwy. Could be something else. but without more information about what's going on, that's my best guess.
Thank you for the plugin, it has helped make our game a whole lot better!
https://store.steampowered.com/app/3250420/Talewarden_Riders_of_the_New_Day/
Hi Wattson, thanks for the content!
I have a question with making a callback to a character when the player has input their own name. I've been trying a few things but nothing is working. Any ideas what I need to put instead of the %(P)s ? Thanks anyone in advance.
This is my code:
(WORKING FINE)define t = Character('Topaz', image='i_topaz.png', color="#d2be0a", callback=name_callback, cb_name='t')image i_topaz =At('i_topaz.png', sprite_highlight('t'))
(NOT WORKING DUE TO NOT RECONGNISING INPUT NAME.)
define P = Character('%(P)s', image='i_mc1l.png', color="#e712b2", callback=name_callback, cb_name='%(P)s')image i_mc1l =At('i_mc1l.png', sprite_highlight('%(P)s'))
I was dumb, the answer was in the code's comments. If anyone else wants to know how to do this, it just takes 2 lines.
Use this line so that no one will be highlighted during narration:
define narrator = Character(callback = name_callback, cb_name = None)
Then just define who you want highlighted like this:
"Then CHARACTER laughed out loud." (cb_name = "CHARACTER")
Hello! I was wondering if there was a way to adjust a sprite's zoom levels globally, while also using this plugin. I don't mean the zoom_change, but rather a zoom for the sprite overall. For example, I have the following code:
transform small: zoom 0.8
And I can apply that to any of the character sprites to make them a little smaller. However, once I enable the auto-highlight effect on a character as follows, the zoom stops working and the sprite reverts to it's original size.
show h stunned: small function SpriteFocus('haka')
If I take out the "function SpriteFocus('haka')" part, the "small" effect works again and the sprite becomes smaller. Is there any way I can have zoom effects work on sprites while the auto-highlighter is active?
Hello, great plugin, but I encountered a problem, at the beginning of each scene, when the dialogue starts, the picture kind of “jumps”, first a darkened picture is shown, then immediately a normal one, after which the dialogue runs normally, as it should. Any ideas what could be causing this and how to fix it?
Sample code below:
define g1 = Character("Первый Стражник", color="#be4c4c", image="guard1",callback=name_callback, cb_name="guard1")
image guard1 guard1a = At('guard1', sprite_highlight('guard1'))
show guard1 at left2
with Dissolve(0.2)
g1 guard1a "В райских гущах, а ты как думаешь?"
If I had to guess I would presume it's because when the scene starts, the character hasn't been speaking, so the speaking_char is probably either None or someone else. Then when the line starts, the character is then set as the speaking_char, so he lights up to normal. If you want them to start off already focused, you can manually set the speaking char by doing:
$ speaking_char = "guard1"
either before the transition or sometime during it. Which would then have the sprite start off in the talking state.
Hopefully that helps. But if it's some other issue, let me know and I'd be happy to help correct it.
Hey there! Thanks for this tool, but I was wondering how to apply shaders to this tool. I tried applying it under line 185 as suggested in the comments, but I get a "ATLTransform object is not iterable".
if is_talking: trans.matrixcolor = ... .... trans.shader = outline() # <- this is defined as a transform
Implementing the shader by hand also didn't work...
if is_talking: trans.matrixcolor = ... ... trans.shader = "remix.smoothstep_outline" # there is no outline
Hi there! Thank you for making such a fantastic effect for visual novels. I wanted to ask about an issue I've been running into.
Following instructions, I placed the auto-highlight file into the game, and changed the definitions to match the instructions. It works like an absolute charm on certain characters (here: mary and vein), but not on others (here: charlie and mona).
## Define characters
define charlie = Character("Charlie",callback=name_callback, cb_name = "charlie", image="charlie")
define mary = Character("Mary", callback=name_callback, cb_name = "mary", image="mary")
define mona = Character("Mona", callback=name_callback, cb_name = "mona", image="mona")
define vein = Character("Vein", callback=name_callback, cb_name = "vein", image="vein")...
## Image definitionslayeredimage mona: #mona's SPRITES at sprite_highlight('mona')
group exp: attribute base: "images/MONA/mona.png" default attribute shadow: "images/MONA/monashadow.png" ....
There are brief moments when the character's name is changed ($ mona = "???"), and this might be the problem. Even reverting the name to its original ($ mona = "Mona") still doesn't work. Are there any solutions to this issue?
Got it fixed! I made a minor mistake of not capitalizing the name when converting it to a different one.
Incorrect: $ mona = "???"
Correct: $ Mona = "???"
EDIT: It broke again... a capital letter will not change the character's name now, so I would have to either decide to keep the autohighlight with no name changes or name changes with no autohighlight aughhhhh
i tried changing the anim_length (to 1.0) and for some reason it only applies when the sprite is first shown, it goes back to the default (0.2) setting as i put another sprite over it.
i use individual sprites but all of them are defined.
i even set it to 0.5, wondering if it was too fast to no avail.
i use NVL, if that helps.
wondered if you could look into it.
thank you very much for this btw.
Hi. Sorry for the late reply. I tried replicating the issue on my end, but doing the same setup (or at least what I can suppose from your comment) yielded results that looked correct to me. My best guess for the issue is that something about how you defined the sprites is causing renpy to reset the anim_time value, which would cause the sprite_highlight function to snap to the highlighted value. Which maybe you're confusing for the .2 seconds given .2 is pretty fast. Though I could be wrong. This is just my best guess given the information.
I tested with NVL and that didn't seem to make much of a difference. If you'd want to show me your code so I could test it and see what the issue might be, I'd be happy to take a look when I have time. (My username on discord is wattson if you need to reach out). But yeah I'm not entirely sure what the problem might be beyond my guess. Hope this was helpful and a solution to your issue can be found!
thank you for replying!
i sent you a discord req, i'd greatly appreciate if you could take a look if you do have time for it.
i don't think it's the definition because it's the exact same for every sprite and it does work for whatever that i place on the screen first, though i'd be glad to send you the code to see for yourself.
Hello! I'm Zeil fromZeil Learnings, a YouTube channel about Ren'Py. Thank you for this! It was very helpful and easy to implement thanks to your awesome documentation! If you allow me, I would like your permission to upload a modified version of this on my itch.io (not using a layered image and more comments on script.rpy). I'll link it to this page, ofc. Additionally, I'd like to feature it in one of my future videos. (maybe 2-3 months from now). You may contact me on Discord if you have any questions: _zeil
For more context, an individual approached me seeking assistance with this. So I figured it's not easy for non-programmers to follow. Therefore, I want to make a modified version of it for people without programming experience.
Sincerely,
Zeil
Hi. Sorry for taking a bit to get back to you. But yeah I appreciate you wanting to edit it to be easier for non-programmers to read it. I will admit I did my best when writing the original code to try and make it approachable as I could. If you have ideas on how to make it more approachable, I'd be happy to take feedback and upload a modified version on here with credit to you for the help. But if you're not making significant changes to the functionality and just adding comments, I feel like it'd be best to just have one project up. Sent a friend request on discord so we can chat more about it. Thanks for your feedback.
- Wattson
Any advice on how to make this work on custom player names? It works for all of my other characters, but because I let the player choose their own name, the sprite doesn't highlight when they speak. Any thoughts?
define mc = Character("[mc_name]", callback=name_callback, cb_name="mc_light")layeredimage mc: at sprite_highlight('mc_light') always: "mc_base" group expressions: pos(70,62) attribute neutral default: "mc_neutral" attribute happy: "mc_happy" attribute sad: "mc_sad" attribute angry: "mc_angry" attribute surprised: "mc_surprised"
if blush: "mc_blush"layeredimage mchbrunch: at sprite_highlight('mc_light') always: "mc_brunch" group expressions: attribute neutral default: pos(163,125) "mc_neutral" attribute happy: pos(163,125) "mc_happy" attribute sad: pos(163,128) "mc_sad" attribute angry: pos(163,125) "mc_angry" attribute surprised: pos(163,125) "mc_surprised" if blush2: pos(165,115) "mc_blush2"
I cannot, for the life of me, get this to work with layered images. It only seems to work for me if I set the function SpriteFocus('sprite_name') per sprite call/update. Here's an example of one of my images and the associated character. I have no idea why this isn't working properly. It does not highlight at all (unless I manually set the sprite focus per call, which is tedious). Thoughts?
define Richard = Character("[Richard_name]", color="#5c00ff", callback = name_callback, cb_name = "ric", image='richard')
layeredimage richard: at sprite_highlight('richard') attribute normal default: "Richard" attribute angry: "Richard ANGRY" attribute annoyed: "Richard ANNOYED" attribute blush: "Richard BLUSH" attribute blush2: "Richard BLUSH 2" attribute crying: "Richard CRYING" attribute crying2: "Richard CRYING 2" attribute surprised: "Richard SURPRISED" attribute worried: "Richard WORRIED"
The highlight on the character "Mark" does not work here, but in all other dialogues with him it works. I tried changing the names of the files, as they only have differences in names, but it didn't help.
Right now I was thinking that it doesn't work because of the previous replica of this character. In front of her I have a sprite in the middle with two characters pushing each other and Mark is talking at this time. After that, the sprite of the two characters is removed and Mark speaks again. It is at this moment that highlight does not work on the character Mark.
It is here that the backlight and its animation on the character Mark do not work:
I tried to remove the "with dissolve" transition, but the highlight animation still didn't work.
As far as I can see from the sprites, the Van character is still darkened. And the highlight animation most likely does not work just because of the previous replica of the Mark character, since I noticed that after changing sprites with pushing two characters a little further than this moment, when the Van character has a replica, the highlight stays on it for a few milliseconds and goes to the Mark character when he speaks. I think I should come up with some kind of "stub" or something like that. Well, or leave it as it is. The rest works, I don't want to break everything because of this.
In any case, thank you for your time to this problem and many thanks for this script. This is very useful for people without Python programming skills.
In another .rpy file, I have a dictionary that maps character names (the string that is used for thecb_name parameter when defining a Character) to the path of their respective bleep files, which looks something like this:
## name_callback in game/00auto-highlight.rpy uses this dictionary ## to determine what bleeps to use per characterdefine bleeps_dict = {"ss" : "audio/bleep023.ogg", "vl" : "audio/bleep003.ogg", "ap" : "audio/bleep006.ogg"}
And in 00auto-highlight.rpy, I tweaked thename_callback function to look like this (please note that I defined a separate audio channel just for text bleeps):
def name_callback(event, interact=True, name=None, **kwargs): global speaking_char if event == "begin": speaking_char = name ## text bleeps if name in bleeps_dict: if event == "show": renpy.music.play(bleeps_dict[name], channel="bleeps", loop=True) elif event == "slow_done" or event == "end": renpy.music.stop(channel="bleeps", fadeout=1.0)
If there is a character who shouldn't use bleeps (I defined a separate Character object for my MC's thoughts, for example), then the "if name in bleeps_dict" line above can be changed to something like:
if name in bleeps_dict and name != "name_of_silent_character":
If there are several character who don't speak, it might be easier to define a list of names of silent characters and then use "...and name not in silent_characters".
I think this is all I needed to do. If you have any issues, please feel free to let me know!! ♡
(Also I'm really sorry if the code formatting above is a bit nasty to read with the indentations)
I'm happy to help!! I hope it works for your project.
I forgot to mention in my last reply that I defined a separate audio channel for the text bleeps (which affects the changes to the name_callback function). That's why I have a channel named "bleeps" in the code. This can be changed to any audio channel (premade or custom) where bleeps can work.
Please feel free to let me know if you have any more questions about my implementation!! <3 <3
You can also do this by taking your sprite and fading it out in Gimp. Then when you show your characters, you just show the one that is speaking and the others as the faded out versions. You can see how I did that in this game = https://rpgmaker.net/games/8212/
Yes, that does work. Though it has some issues. One is that it eats up a lot of unneeded file space. With your method, every image essentially requires twice as much file space. It saves a lot of file space if you do the color manipulation in the engine for basically free. And if you want to change the look of it a little, it's a lot easier this way, since you don't have to re-export every image. Albeit, the color part of this does require using Renpy 7.4, which doesn't work on some older devices.
The other issue is that then in your script, you have to write out every 'show' and 'hide' statement manually to swap between the two. Which is a fair amount of work and means you have to be sure you do it for every time who's speaking changes in the dialogue. Which might be a lot.
With my method, and a bit of setup, it does both of these effects automatically without any additional work needed or taking up additional file space. While I commend your method and think it is clever and certainly good for its time. I think this is a nice alternative for people going forward, if they wish to do it as well.