Portrait System

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Message
Author
User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Portrait System

#1 Post 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.
Last edited by Tayruu on Thu Nov 21, 2019 12:43 am, edited 8 times in total.

nhguy03276
Regular
Posts: 41
Joined: Thu Jan 29, 2015 12:48 pm
Contact:

Re: Portrait System

#2 Post 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.

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#3 Post 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?)

User avatar
renoa-heartilly
Regular
Posts: 115
Joined: Sat Jul 17, 2010 2:37 am
Completed: مقهى الضائعون, Arabic Translations for Ren'py, Pretty Please
Projects: حور, BB project, dentist drama
Organization: stanza studio (creative circle)
Contact:

Re: Portrait System

#4 Post 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:
Attachments
idklmao.jpg

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#5 Post 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.

User avatar
Donmai
Eileen-Class Veteran
Posts: 1958
Joined: Sun Jun 10, 2012 1:45 am
Completed: Toire No Hanako, Li'l Red [NaNoRenO 2013], The One in LOVE [NaNoRenO 2014], Running Blade [NaNoRenO 2016], The Other Question, To The Girl With Sunflowers
Projects: Slumberland
Location: Brazil
Contact:

Re: Portrait System

#6 Post 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
Image
No, sorry! You must be mistaking me for someone else.
TOIRE NO HANAKO (A Story About Fear)

User avatar
PyTom
Ren'Py Creator
Posts: 16088
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: Portrait System

#7 Post by PyTom »

I just landed a fix for the animation timing problem.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#8 Post 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.

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#9 Post 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.

User avatar
Doodley
Newbie
Posts: 1
Joined: Fri Jan 08, 2016 1:03 am
Tumblr: doodleyoh
Deviantart: doodleyoh
Contact:

Re: Portrait System

#10 Post 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.

Green Lion
Newbie
Posts: 2
Joined: Tue Jun 21, 2016 5:11 am
Contact:

Re: Portrait System

#11 Post 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.

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#12 Post 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="".

User avatar
Tayruu
Regular
Posts: 141
Joined: Sat Jul 05, 2014 7:57 pm

Re: Portrait System

#13 Post 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)

AngelNun
Newbie
Posts: 3
Joined: Mon Jun 19, 2017 11:03 pm
Contact:

Re: Portrait System

#14 Post 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!!!!

User avatar
ChocoKeki
Newbie
Posts: 6
Joined: Wed Mar 26, 2014 7:13 pm
Location: Mexico
Contact:

Re: Portrait System

#15 Post 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?

Post Reply

Who is online

Users browsing this forum: No registered users