Since I'm doing some deep dives into customizing stuff (well, probably not that deep, it just feels that way since the documentation is at times a little light) I think I'll use this thread to walk through my thought process and code a bit so that if other people are looking for examples of stuff maybe they'll stumble upon it.
So, I was thinking about a couple of ways to add 'juice' to the experience. Juice, in this case, being little things that just makes the game feel a little cooler. I thought that a way to emphasize sprites as they're talking would be nice, so I started to think about ways to do that. Some VNs have the character jump or flash when they speak, but I ended up deciding on just having the character sprite 'grow' a little while speaking, then shrink when they're done.
Initially I did this by writing transforms between each section of dialogue.
Code: Select all
show landon:
xpos .6
yalign 1.0
with Dissolve(1.0)
show hotaru:
xpos .1
yalign 1.0
with Dissolve(1.0)
hide landon
show landon:
xpos .6
yalign 1.0
ease .5 zoom 1.02
l "Are you buckled?"
show landon:
xpos .6
yalign 1.0
ease .2 zoom 1
hide hotaru
show hotaru:
xpos .1
yalign 1.0
ease .5 zoom 1.02
h "Are you my mother?"
I did this for the two lines shown above, realized it was unwieldy, then forgot about the idea for a month. While reading on the forum, I saw someone bring up subroutines. I was like 'okay, that seems a little more like what I want', so I went back and made a subroutine that took the characters name and their position and slightly automated the previous code.
Code: Select all
label easein(char,xali=.5,yali=1.0):
transform zoomin:
xpos xali
yalign yali
ease .5 zoom 1.03
$ renpy.hide(char)
$ renpy.show(char,at_list=[zoomin])
return
label duoeasein(char, char2, xali1=.5, yali1=1.0, xali2=.5, yali2=1.0):
#(mostly the same as above, but more)
Code: Select all
show dude
with dissolve
call easein('dude',.13)
d "Hey, Yang!"
show hotaru
with dissolve
i "Once again, you’ll notice a theme. Knowing his name implies familiarity, which I generally prefer to avoid."
call duoeasein('hotaru','dude',.53,1.0,.13,1.0)
h "Oh hey... You."
I went ahead and started implementing that, this time I got about two scenes in before realizing, just like before, is was still too much.
At this point, I realized I was just going to need something that happened automatically between lines of dialogue. Even one line of code between each exchange was too many. So I went back to the Google mines... I saw a couple of things from that that seemed promising. The first thing I saw was character call backs, but for some reason at the time I didn't view that as elegant enough I guess (more likely, I was just kind of overwhelmed and it seemed too hard to grasp at the second so my brain blanked it) so I looked at the speaking config variable. The config.speaking_attribute option (which seemed less intimidating at the time, since it was just a single variable. How scary can a single variable be?) would make it so that when a character was saying a line, it would find an image with the correct set of words (in the example below, I used the attribute 'pulse', then had a sprite that was the characters name + pulse. So 'landon pulse' would be the image that it looked for when Landon was speaking)
Code: Select all
init python:
config.speaking_attribute = "pulse"
Code: Select all
define l = Character("Landon",color="#000000",image="landon")
transform easeincustom:
ease .5 zoom 1.03
pause .5
ease .3 zoom 1
image landon= "characters/landon/LandonSmall.png"
image landon pulse:
"characters/landon/LandonSmall.png"
easeincustom
This seemed kind of promising. I went ahead and implemented it and ran through a few scenes. It mostly looked good, but as always, there was something not to my liking. I had it set so that it would enlarge the sprite, pause for a fraction of a second, then shrink. So if a character had multiple lines of dialogue in a row, they'd kind of just keep pulsing. I thought this was kind of distracting and figured I may as well keep researching... Usually with enough determination, you can achieve the effect you want.
Finally I went back to character call backs. I found
this thread (which I must have absolutely seen before and just ignored the part about character callbacks. But it must have been the same thread I sourced the above code from since it's pretty much exactly what trooper6 recommended in the last post) and tested the code that Lord Hisu posted halfway through the thread. Obviously it wasn't exactly what I wanted, but it was close enough that I could see a clear path ahead of me: mainly what I saw and liked was that it had a memory and could determine when the speaker changes. So I knew I just needed to edit it so that it would make the character speaking grow when they start speaking, then shrink when the speaker changes.
Code: Select all
Def char_pulse(char, event_name, *args, **kwargs):
#A variable used to manually change a characters expression, for when I don't want the 'automatic' response that happens below
nonspeaking = kwargs.get('nonspeaking', None)
if event_name == "begin":
#If the speaker has changed and the last speaker wasn't the monologue
if store.speaking_char != "internal" and store.speaking_char != char:
#If the previously speaking character is literally on screen, then ease them out and close their mouth
if renpy.showing(store.speaking_char):
renpy.show(store.speaking_char,at_list=[easeoutcustom])
if store.speaking_char=='hotaru' and not nonspeaking:
mouth_expression[store.speaking_char] = "closed"
mouth[store.speaking_char] = mouth_expression[store.speaking_char] + facing[store.speaking_char]
#If the current speaker is the internal monologue, then we don't really need to do much. Just store them as a speaker.
if char == "internal":
store.speaking_char = char
return
#If the speaker has changed and they're currently on screen, then ease them in and open their mouth
if char != store.speaking_char:
store.speaking_char = char
if renpy.showing(char):
renpy.show(char,at_list=[easeincustom])
if char=='hotaru' and not nonspeaking:
mouth_expression[char] = "open"
mouth[char] = mouth_expression[char] + facing[char]
return
return
After getting it in, I continued iterating on it. Mainly: Lumping in rudimentary 'mouth flaps' (Basically just making the character open their mouth when speaking, if they have a mouth sprite. Only Hotaru has one coded in right now, so she's the only one that gets that treatment), as well as a bonus variable (nonspeaking) that I could throw in to manually show a different sprite if I wanted (so if someone was supposed to have a mouth open sprite, but instead I wanted to give them a yelling sprite or something). I'm still in the middle of iterating on it, but it's finally accomplishing what I wanted! Which is always cool.
(Well, it's looped, so it's not a perfect representation. But it gets the point across!)
Anyways, sort of a long post, but maybe someone will find this in a google search and I'll have said the magic words that makes them understand what they need to understand to get the effect they're trying to get! Next time maybe I'll talk about my character layering system that's been similarly frankenstein'd from various posts around the forums