Page 1 of 2

Portrait System

Posted: Tue Feb 03, 2015 6:13 pm
by Tayruu
This system has received a bit of an overhaul in the form of v2.0. The current post for that starts here.


Below is the original post for v1.0 of this system.
* * *
Portrait System
Version 1.3
Premise
A while ago I was confronted with a problem. I wanted to be able to use blink/lip-flap for my portraits, but quickly discovered this would lead to excessive amounts of code. I posted that there topic after struggling with the issue myself, and eventually got a reply. It's from there I worked slowly on getting the code to work for me.

Given I'm sure there are others that want this sort of thing, I worked on making the code Bryan Tsang provided into something that would work for others as well. Thus, a demo to show off this system.

Features
  • By specifying even just a single line, you can define a character portrait that will animate as required, instead of copying 20+ lines for every pose/image.
  • Includes a text-beep effect that can vary between characters.
  • Enables "introductory" animations and similar - short sequences that play when a character enters a pose.
  • Stores portrait, eyes, and mouth, all in one file per pose.
Example
The minimal, basic portrait code looks like this:

Code: Select all

image june idle = Portrait("june/idle.png", eyepos=(144, 192), moupos=(144,256), speaker="june")
The code works pretty straight forward - you have the file, the position of the eyes and mouth elements on the larger portrait, and the callback. The callback is something specified in Character() code, so that the message sound and lip-flap plays when it's expected to. The demo below also has a custom system for Character definitions, but this isn't necessary.

Download and Credits
Demo Available Here, along with some more information about how to use this system. (At least I hope. I don't think I'm that good at explaining things.)
The demo expands on some other ways to use the script than what is mentioned here.

Credit would be appreciated, given to myself - as Tayruu or Taylor, and Bryan Tsang/@bvtsang.

Re: Portrait System

Posted: Tue Feb 03, 2015 10:25 pm
by nhguy03276
Thank you for posting this link... I've been looking for something like this to cut down on the ATL coding and the number of individual png files.

Re: Portrait System

Posted: Thu Dec 03, 2015 8:21 pm
by Tayruu
Updated to version 1.1. If you don't want to download the demo again, you can just add the following line above return flatten_portrait:

Code: Select all

            flatten_portrait.reverse = renpy.display.draw.draw_to_virt
Someone reported that the portraits weren't resizing correctly if the window was resized or the game was switched to full screen. I don't know if this is actually the way to fix it, but it works for now. All I can tell is it's something to do with Flatten. Or the code from Flatten, since I'm not sure I can call it specifically. (I tried?)

Re: Portrait System

Posted: Fri Dec 04, 2015 12:57 pm
by renoa-heartilly
hello!
thank you very much for this! :mrgreen:
it will prove useful for the game i'm working on with live2d sprites!
but how do i go about making the portrait itself? how do i know how high to make the boxes with the eyes and mouth and how does Ren'Py know it?
also, i just downloaded the zip file a few minutes ago and i don't know if it's supposed to do that or if it's an issue with my version of renpy (9.66.7) but the transition looks like this to me:

Re: Portrait System

Posted: Fri Dec 04, 2015 6:34 pm
by Tayruu
It's possibly something in 6.99.7? I just upgraded from .6, and I'm seeing it now too. Wonderful.

Additionally re: the mouth/eye boxes, they are defined with eyesize=(128, 64), mousize=(128, 64). You can edit these to change the the size for all template parts - or even set them per image, though personally I'd recommend them remain consistent? The mouth template part is spaced 32px apart from the eye part, as noted with mouth_y_orign = self.eyes[3]*2 + 32.

The template image by default is 640px tall, and can be any width. However 128px to the side are taken by the eyes/mouth frames. The example portrait is 544px wide, the default width setting is 416px, leaving 128 for the frames.

Re: Portrait System

Posted: Fri Dec 04, 2015 7:28 pm
by Donmai
Ren'Py 6.99.8.912 here. Instead of downloading it again I just added the line, as Taylor suggested. Sometimes the animations seem to work, but sometimes... well, see for yourself.
screenshot.jpg

Re: Portrait System

Posted: Mon Dec 07, 2015 11:39 pm
by PyTom
I just landed a fix for the animation timing problem.

Re: Portrait System

Posted: Tue Dec 08, 2015 7:30 pm
by Tayruu
Observation: placing at char_fade after any portrait show (not just the initial one) seems to fix the fade/transform bug, but this isn't a clean fix. I'm pretty sure that you're supposed to be able to call an image and its transforms, and anything replacing that will always adhere to replace.

Meanwhile, looking for ways to use Flatten() as-is, and I think I have something...?

Code: Select all

            flatten_portrait = renpy.render(Flatten(portrait), self.portrait_width, 640, st, at)
            return flatten_portrait
portrait refers to the base im.Crop portrait. But portrait_render - what 1.1 has in flatten_portrait - refers to the renders and blits combining the parts of the graphic. But... they still show up and animate... sometimes?

In 6.99.8.912 it seems to work. In 6.99.7, it only works during the transform (and the dialogue immediately after a new pose). I think.

EDIT: It also works in 6.99.6.739, and the transform/fade bug doesn't exist either.

Re: Portrait System

Posted: Tue Dec 22, 2015 9:25 am
by Tayruu
I'm not sure if it's worth posting about, but I've updated to version 1.2. (Or perhaps it should be 1.1a?). I've determined as much the current Release of Renpy causes the alpha overexposure weirdness renoa-heartilly is seeing - as 6.99.6 and 6.99.8.955 don't have this problem.

The update itself simply replaces the flatten code with the block in the above post, now that I've learnt to do that. Whoops. > .>
Though I also try rewording some of my advanced notes on the web page.

Re: Portrait System

Posted: Fri Jan 08, 2016 1:13 am
by Doodley
This is really neat! I just whipped a basic thing together using my own assets and the way its all done is really something. The method its animated is particularly special -- I've done commissions for animated talking portraits in the past, which involved a lot of separate mouth/blink layers with their own shading that was all worked into a long gif in Photoshop. Here it's handled in a single png and usable in a game! Never would have thought something like that possible.

Gigantic thanks for this! I'm just a beginner, but this is a huge way to get started and really gets me motivated knowing I don't have to mess with a bunch of different sprites and long code. You'll definitely be credited if/when its used in future stuff.

Re: Portrait System

Posted: Sat Jul 09, 2016 11:25 am
by Green Lion
I have a question: how can i make the animated portrait spirite be shown as a side image like for exemple:


image june concern = Portrait("june/concern.png", eyepos=(144, 208), moupos=(144,272), speaker="june")

define june concern = Character ( 'June' , color="#ffc0c0", window_left_padding=300,
show_side_image="june concern", xalign=0.0, yalign=1.0))

Because it doesnt work that way to me.

Re: Portrait System

Posted: Sun Jul 10, 2016 5:59 pm
by Tayruu
I have no experience with side images, so I'm not sure how they work.

What problem do you have? Does the image not display? Does it not animate entirely/in-part? Do you receive an error?

According to the documentation, the correct format is image="".

Re: Portrait System

Posted: Thu Jan 26, 2017 7:30 pm
by Tayruu
So the recent version of Ren'py seemed to do something that broke callback for the text-beep. I'm not really sure why, but I've updated the demo and here's the solution:
Original code:

Code: Select all

    # Text blip and speaker set
    def text_effect(file, speaker, event, **kwargs):
        if event is "show":
            pass
            if file and _preferences.text_cps is not 0:
                renpy.music.play(file, channel="text")
        elif event is "slow_done" or event is "end":
            speaking = None
            renpy.music.stop(channel="text")
        renpy.invoke_in_new_context(speaker_curry(speaker, event))
    text_effect = renpy.curry(text_effect)
Update:

Code: Select all

    # Text blip and speaker set
    def text_effect(file, speaker, event, **args):
        if event == "show_done":
            pass
            if file and _preferences.text_cps != 0:
                renpy.music.play(file, channel="text")
        elif event == "slow_done" or event == "end":
            speaking = None
            renpy.music.stop(channel="text")
        renpy.invoke_in_new_context(speaker_curry(speaker, event))
    text_effect = renpy.curry(text_effect)

Re: Portrait System

Posted: Tue Jun 20, 2017 1:07 am
by AngelNun
Thanks for this wonderful Portrait script, its a blessing to bring life to VNs!

I have two petitions because I still need help.

Would you mind doing a 1980x1080 version of the demo (full hd)?

Could you show us how to use this portrait system to show Lotus or a simple duel of characters casting magic with the hands? thanks you in advance!!!!

Re: Portrait System

Posted: Sun Aug 18, 2019 12:12 am
by ChocoKeki
Thank you so much for this, it's fantastic and I'm definitely gonna use it... probably in all my projects lol

Generally, I haven't had any problems with it (I could figure out how to change the default sizes and make my assets for example), but I was wondering if there's a way to include more than one expression per image with this system, so that I don't have to create many new images with the same base sprite but different eye and mouth frames. Also, can I make it so that, in one expression, the characters' eyes remain closed/only one specific frame is shown? How about setting a different mouth frame (say, a smile or grin) as the default for when the character isn't talking but when they do, a different set of frames is used (in a closed-half open-open cycle)?
I've tried to figure it out myself, but since I'm a beginner that doesn't know much about programming, I haven't been able to do anything close to it on my own.

Would a different system have to be created (or would this code have to be heavily modified) in order to achieve what I described?