[SOLVED] Dynamic Character skin color change

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
uncoded
Regular
Posts: 27
Joined: Fri Apr 09, 2021 10:29 am
Contact:

[SOLVED] Dynamic Character skin color change

#1 Post by uncoded »

Hi,
I'm trying to implement a character creation room, where the player can do the usual stuff (change haircut, clothes, colors, ...). I'd like, however to implement color changes as a dynamic recoloring of some the character image layers.
I'm nearly there, but I yet have to figure out how to manage to :
  1. dynamically modify a Composite image layer, and
  2. use this Composite to create a Character and use it for the rest of the game
For now I can do one or the other, but not both.

What I've implemented is the character creation room (beautyparlor.rpy, see below), where the player can swap between haircut and clothes using hotspots of an imagemap (this works) and change skin tones using a slider (this works, but not as I'd like).
My screen has two Composite images : left one is added directly to the screen (skin tone updates fine), right one is a defined named variable ("girl") which is then added to the screen using its name (skin tone takes initial value, but doesn't update thereafter).
See screencap and code below :
SkinProblems.png

Code: Select all

## file: beautyparlor.rpy

init:
    default hairstyle = 1
    default hairstyle_max = 5
    default top_choice = 1
    default top_choice_max = 5
    default bottom_choice = 1
    default bottom_choice_max = 3

init -1 python:
    style.hue_bar = Style(style.bar)
    style.hue_bar.thumb = "color_cursor.png"
    style.hue_bar.hover_thumb = "color_cursor.png"
    style.hue_bar.base_bar = Frame("color_hues.png")
    style.hue_bar.hover_base_bar = Frame("color_hues.png")

init python:
    import colorsys

    class HSV(object):
        def __init__(self):
            self.hue = 0
            self.saturation = 255
            self.value = 255

        @property
        def hsv(self):
            return self.hue, self.saturation, self.value

        @property
        def rgba(self):
            # using HSV encoding allows to change RGB values with only one slider
            # colorsys.hsv_to_rgb takes 3 floats in range [0.0, 1.0]
            # and returns RGB values as 3 floats in range [0.0, 1.0]
            rgb = colorsys.hsv_to_rgb(
                float(self.hue)/255.0,
                float(self.saturation)/255.0,
                float(self.value)/255.0
                )
            # return RGBA values as 4 integers in range [0, 255]
            return int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255), 0

define skin = HSV()

# this image only gets skin color from the initial values defined in HSV.__init__
# they sadly won't be updated afterwards, although skin object values change
image girl = Composite(
    (467, 946),
    (0, 0), im.Twocolor("body/body.png", "#ffffff", skin.rgba),
    (0, 0), "clothes/bottom[bottom_choice].png",
    (0, 0), "clothes/top[top_choice].png",
    (0, 0), "body/eyebrows.png",
    (0, 0), "body/eyes.png",
    (0, 0), "body/mouth.png",
    (0, 0), "body/hair[hairstyle].png",
)


screen dress_character:

    imagemap:
        idle "dressup/idle.png"
        hover "dressup/hover.png"

        ## Hairstyle
        hotspot( 800,125, 100,100) action SetVariable("hairstyle", (hairstyle - 1) % hairstyle_max)
        hotspot(1200,125, 100,100) action SetVariable("hairstyle", (hairstyle + 1) % hairstyle_max)
        ## Top
        hotspot( 800,325, 100,100) action SetVariable("top_choice", (top_choice - 1) % top_choice_max)
        hotspot(1200,325, 100,100) action SetVariable("top_choice", (top_choice + 1) % top_choice_max)
        ## Bottom
        hotspot( 800,568, 100,100) action SetVariable("bottom_choice", (bottom_choice - 1) % bottom_choice_max)
        hotspot(1200,568, 100,100) action SetVariable("bottom_choice", (bottom_choice + 1) % bottom_choice_max)

        ## Quit
        hotspot(1107,9, 157,53) action Return()

    vbox align(0.5, 0.8):
        bar xalign 0.5 xmaximum 255 value FieldValue(skin, field='hue', style='hue_bar', range=255, step=1)

    text ("{size=-5}HSV: %s"%str(skin.hsv)) align(0.5, 0.84)
    text ("{size=-5}RGBA: %s"%str(skin.rgba)) align(0.5, 0.87)

    # CHARACTER ON THE RIGHT
    # this one I can (kind of) reuse later, but doesn't update well
    add "girl":
        pos(867, 86)
        zoom 0.33

    # CHARACTER ON THE LEFT
    # this one updates fine, but I didn't find a way to reuse it
    add Composite( (467, 946),
      (0, 0), im.Twocolor("body/body.png", "#ffffff", skin.rgba),
      (0, 0), "clothes/bottom[bottom_choice].png",
      (0, 0), "clothes/top[top_choice].png",
      (0, 0), "body/eyebrows.png",
      (0, 0), "body/eyes.png",
      (0, 0), "body/mouth.png",
      (0, 0), "body/hair[hairstyle].png",
      ):
        pos(50, 86)
        zoom 0.33
Where my real problem lies, however, is :
  • When I want to use the result afterwards, I cannot : I cannot use the Composite "on the left", even if it updates properly, because it is unnamed -plus, I'm not sure if Ren'Py allows to retrieve variables that are local to a screen (does it ?).
  • What I can do is use the Composite "on the right" (variable "girl" is found) to create a Character, but I get a placeholder instead of what I want. (solved)
  • The best I can do is to display the image "girl" directly on screen, and it displays more or less properly (although user-chosen skin color is not there, as seen before) ; however, an image is not a Character ... So now I have to add this image on every screen where the "girl" character speaks, which is kind of a hassle. (solved)
Please see code below for my attempt :

Code: Select all

## file: script.py

label start:
    define mc = Character("Girl", image='girl')

    call screen dress_character
    show girl:
        pos(800, 0)
        zoom 0.45

    "Yes, that's what I look like ... But where is my skin tone ?"

    show mc

    mc "... And why doesn't my Character image display ?"

    return
If you'd like to test what I have, please take a look at the standalone project SkinProblems:
SkinProblems.zip
(2.46 MiB) Downloaded 21 times
Any help or reaction is welcome.

Thanks in advance !

obligatory kudos: To write what you see, xela's post and LunaLucid's template were of great help. Of course, "girl" image resources I used are Konnet's creations.
Thank you all !
Last edited by uncoded on Sat Apr 10, 2021 2:15 pm, edited 2 times in total.
🐾

uncoded
Regular
Posts: 27
Joined: Fri Apr 09, 2021 10:29 am
Contact:

Re: Dynamic Character skin color change

#2 Post by uncoded »

Okay, I at least understood the root of one of my two problems : Character mc has a placeholder instead of an image because Ren'Py doesn't enforce any strict coupling between characters and images. The characters talk, the images are displayed, not the other way around. You can display any combination of images and characters. So it's perfectly normal to display image that float around in your scope without any relation (other than the one you, the writer, impose yourself) to characters whatsoever.
Well, shame on me to not having understood that I guess. :| At least this is clarified.

My main problem subsists however, and I need to answer one of these questions :
  1. why doesn't girl update when I recolor the skin ?
  2. how can I get a permanent handle on the anonymous image that does update after I add it to my screen, so I can reuse it ?
I played with layered images and image manipulators, and I got to the same point :
  • changing haircut no problem ;
  • changing hair color either works but I cannot reuse it, or doesn't work after initialization.
I'm currently trying to find a way to dig inside the Screen I got with renpy.get_screen, in the hopes of finding my anonymous, properly updated image inside it to maybe add it to globals or something, but this seems too much far-fetched !
Maybe at the end of my screen lifecycle I can clone the anonymous image and save it do disk, but this solution would be kind of ... meh.

Another solution would be to update girl with the correct skin color (even if I'd have to do it each time I display it), but I've yet to figure out how to modify the layers of a Composite image (or a layeredimage for that matter) after their declaration ...

Any advice ?
Thanks in advance !
🐾

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2401
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Dynamic Character skin color change

#3 Post by Ocelot »

uncoded wrote: Sat Apr 10, 2021 10:10 am why doesn't girl update when I recolor the skin ?
Image manipulators are from ancient times and are not dynamic. After you created them, it is over.
uncoded wrote: Sat Apr 10, 2021 10:10 am how can I get a permanent handle on the anonymous image that does update after I add it to my screen, so I can reuse it ?
Fun fact: eve if you could, it would be of no use for you, because image is not updated, instead, when screen is reevaluated, a new image is created. This is why it is "updated" - it is ismply replaced with newly created image. If you store that image in screen-local varable, it will stop updating.
uncoded wrote: Sat Apr 10, 2021 10:10 am Any advice ?
I am not an expert in composite images, but I managed to do what you need by replacing definicion of girl image:

Code: Select all

init python:
    def transform_girl(disp, a, t):
        disp.set_child(im.Twocolor("body/body.png", "#ffffff", skin.rgba))
        return 0.05

image girl = Composite(
    (467, 946),
    (0, 0), Transform(child=im.Twocolor("body/body.png", "#ffffff", skin.rgba), function=transform_girl),
    (0, 0), "clothes/bottom[bottom_choice].png",
    (0, 0), "clothes/top[top_choice].png",
    (0, 0), "body/eyebrows.png",
    (0, 0), "body/eyes.png",
    (0, 0), "body/mouth.png",
    (0, 0), "body/hair[hairstyle].png",
< < insert Rick Cook quote here > >

uncoded
Regular
Posts: 27
Joined: Fri Apr 09, 2021 10:29 am
Contact:

Re: Dynamic Character skin color change

#4 Post by uncoded »

That's ... exactly what I needed ! :shock:
Plus, your solution (is that it?) seems to give me the ability to implement in the future many different animations I could control directly. :idea:

Many, many thanks @Ocelot ! I owe you one ! 🎩
🐾

Post Reply

Who is online

Users browsing this forum: Majestic-12 [Bot]