Optimal Implementation of Animated Character Sprites

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
Loutanina
Newbie
Posts: 2
Joined: Sun Apr 22, 2018 1:12 pm
Deviantart: Loutanina
Contact:

Optimal Implementation of Animated Character Sprites

#1 Post by Loutanina »

Hi all, I'm currently working on my first Ren'Py game, I first tried Tyranobuilder but after my UI drastically and annoyingly changed after an update I decided the engine wasn't too stable and switched to Ren'Py. As you can imagine, I'm a noob when it comes to coding, my main focus is art and story.
So I have these sprites:
Image Image
(annnnd I've noticed some things I need to clean up)

As you can see my character has various different states:
- Braided vs. loose hair
- Sweater vs. Nightgown
- Beatup vs. Alright

Now based on how the image's layers originally work, I made four bases:
- Sweater with Braids
- Sweater with Loose Hair
- and the same with the Nightgown

And I cut up all eyebrows, eyes, and mouths based on this tutorial:
https://www.youtube.com/embed/08HKfXMr1kY

How do I go about switching between the four different bases? I can't just have two true or false statements about the same body part, right?
Plus, how do I go about switching between the bruised eye and the non-bruised eye most efficiently? Do I make individual folders for "HappyBruised" "SideglanceBruised", etc. etc?
I have a bruised version of every kind of eye expression, so if they could remain in the same folder that'd be great. Plus my other issue is that in this tutorial, the blink animation is just one frame and the blink is outside of the expression folders. My blinks are custom for each expression and I also have a longer eyeroll animation.

Implementing all of this in code would cost a lot of time and I'm starting to wonder if .webm videos would be quicker. But if I were to use .webms I'm worried that it would take up more space, plus I'm a bit worried that if I were to use webms the talking wouldn't stop correctly as opposed to this method?

Thank you in advance.

kivik
Miko-Class Veteran
Posts: 786
Joined: Fri Jun 24, 2016 5:58 pm
Contact:

Re: Optimal Implementation of Animated Character Sprites

#2 Post by kivik »

Amazing artwork!

There are different ways of doing it, the easiest of which is probably to use LiveComposite Dynamic Images. Unfortunately this isn't in the documentation yet but in the changelog: https://www.renpy.org/doc/html/changelo ... mic-images

Dynamic images are basically images with filenames that contain variables. LiveComposite can put multiple filenames (with variables) into a single image.

So you'll need some variable names specified for the character's different states:
hair = "braided" / "loose"
bruised = "bruised" / "normal"
mood = "happy" / "sad" / "angry"
clothing = "gown" / "sweater"
etc.

It's up to you whether you sort them into folders or not. I'm going to assume you have a folder for each of the layers.

If you're doing this to multiple characters, then you're best making them object attributes, but we can come back to that later if you need help with that.

Now your LiveComposite can look something like this:

Code: Select all

# this will make Ren'py load the "speaking" version of your image when character is talking
config.speaking_attribute = "speaking"

image eileen = LiveComposite(
    (300, 600), # (width, height)
    (0, 0), "base/[clothing].png", # keep at (0, 0) if all your images are the same dimensions, it's the left top coordinate otherwise.
    (0, 0), "face/[mood]-[bruised].png", # or gif if eye blinks are incorporated?
    (0, 0), "hair/[hair].png",
    )

# speaking version of the image
image eileen speaking = LiveComposite( 
    (300, 600), # (width, height)
    (0, 0), "base/[clothing].png", # keep at (0, 0) if all your images are the same dimensions, it's the left top coordinate otherwise.
    (0, 0), "face/speaking-[mood]-[bruised].gif",
    (0, 0), "hair/[hair].png",
    )
With this setup, you can be extremely lazy and only ever show 1 version of your character image, and the variables will decide what your character looks like. There're also variations on how to animate the blinking and talking using separate animated layers - however it's a bit more complicated to do variation of bruised / non-bruised eyes versions with ConditionSwitches I think. So I've gone for the simplest approach. Perhaps start here and see if you need more a complex solution?

Loutanina
Newbie
Posts: 2
Joined: Sun Apr 22, 2018 1:12 pm
Deviantart: Loutanina
Contact:

Re: Optimal Implementation of Animated Character Sprites

#3 Post by Loutanina »

Thank you very much!


The hair and clothes are both on the base, so there are four bases.

Luckily all my other characters wear masks so I won't have to deal with inputting animations. (Unless I end up adding animations anyway lol)

I adjusted the code a bit. Originally I just put all the different types of mouths, eyes and eyebrows in folders rather than putting the expressions together. So I separated them based on that.

Code: Select all

# this will make Ren'py load the "speaking" version of your image when character is talking
##config.speaking_attribute = "speaking"
##config.bruised_attribute = "bruised"

default iris_clothing = "SweaterBraids"
default iris_mood = "NeutralBig" #The Moods are: Angry, Calm, Eyeroll, Glance, Happy, HappyClosed, Neutral, NeutralBig, Nostalgic, Sad, Shock, Smug, Surprise, Thinking)



image iris = LiveComposite(
    (530, 630), # (width, height)
    (0, 0), "Iris/Base/[iris_clothing].png", # keep at (0, 0) if all your images are the same dimensions, it's the left top coordinate otherwise.
    (0, 0), "Iris/Eyebrows/[iris_mood]/eyebrows.png", # or gif if eye blinks are incorporated?,
    (0,0), "Iris/Eyes/Normal/[iris_mood]/eyes.png", #or gif if eye blinks are incorporated?,
    (0,0), "Iris/Mouths/[iris_mood]/mouth.png"
    )

# speaking version of the image
image iris speaking = LiveComposite(
    (530, 630), # (width, height)
    (0, 0), "Iris/Base/[iris_clothing].png", # keep at (0, 0) if all your images are the same dimensions, it's the left top coordinate otherwise.
    (0, 0), "Iris/Eyebrows/[iris_mood]/eyebrows.png", # or gif if eye blinks are incorporated?,
    (0,0), "Iris/Eyes/Normal/[iris_mood]/eyes.png", #or gif if eye blinks are incorporated?,
    (0,0), "Iris/Mouths/[iris_mood]/speaking-mouth.png"
    )

# bruised version of the image
image iris bruised = LiveComposite(
    (530, 630), # (width, height)
    (0, 0), "Iris/Base/[iris_clothing].png", # keep at (0, 0) if all your images are the same dimensions, it's the left top coordinate otherwise.
    (0, 0), "Iris/Eyebrows/[iris_mood]/eyebrows.png", # or gif if eye blinks are incorporated?,
    (0,0), "Iris/Eyes/Bruised/[iris_mood]/eyes.png", #or gif if eye blinks are incorporated?,
    (0,0), "Iris/Mouths/[iris_mood]/mouth.png"
    )
Some questions about this:
- 1. Are the bruised/speaking variable in each others way? Do they annul each other and how can I make sure that they won't?
- 2. I had an error starting the game up with "##config.speaking_attribute = "speaking", even though I hadn't declared that my character should be speaking in the script? Do I need to add a "$ speaking = False"?
- 3. I thought .gifs didn't work? If they did it would be very convenient for the eyes and mouth.
- 4. Concerning the eyes in particular, I have different animations for the eyes with a different amount of frames, so one has 3 images, it goes from 1 to 3 and 3 to 1. And I have one that consists out of 4 images. Not just that, I also have an eyeroll animation of 11 frames. I could cut it out altogether if coding it will be too much of a pain though. Following the tutorial that I watched, I'm not sure how to properly implement that, seeing as it deals with a constant amount of frames for the eyes.
-5. Scaling is kind of an issue, my portrait is too big, I made it wanting it to be big and wanting to scale it down how do I do this in livecomposite? Or do I need to do this in a separate line?
- 6. have a question about the width/height in this line:

Code: Select all

LiveComposite(
    (530, 630), # (width, height)
Rather than it being width and height it seems to be about the positioning of the image? (xalign 1.0, yalign 1.0) gives a syntax error. I'd like to pin my height at a certain y-position, have it raise slightly when the character is talking, and move it to the side when another character is on screen manually later. How can I do this?

I hope this isn't too much and I'm not asking dumb things haha orz. Either way thank you very much for your time and quick reply, appreciate it!

kivik
Miko-Class Veteran
Posts: 786
Joined: Fri Jun 24, 2016 5:58 pm
Contact:

Re: Optimal Implementation of Animated Character Sprites

#4 Post by kivik »

- 1. Are the bruised/speaking variable in each others way? Do they annul each other and how can I make sure that they won't?
The approach I went for was to make bruised a variable, so that if you show iris - it automatically knows whether to show the bruised or unbruised version. I haven't played with the speaking variant with different images, but I expect if you have an iris bruised speaking image - then they won't conflict. Personally I'd do:

Code: Select all

image iris bruised = LiveComposite(
    (530, 630), # (width, height)
    (0, 0), "Iris/Base/[iris_clothing].png",
    (0, 0), "Iris/Eyebrows/[iris_mood]/eyebrows.png",
    (0,0), "Iris/Eyes/[iris_bruised]/[iris_mood]/eyes.png",
    (0,0), "Iris/Mouths/[iris_mood]/mouth.png"
    )
if iris_bruised holds either "Normal" / "Bruised" values.
- 2. I had an error starting the game up with "##config.speaking_attribute = "speaking", even though I hadn't declared that my character should be speaking in the script? Do I need to add a "$ speaking = False"?
Apologies I missed the define keyword:

Code: Select all

define config.speaking_attribute = "speaking"
- 3. I thought .gifs didn't work? If they did it would be very convenient for the eyes and mouth.
Ah, my bad. I thought it does when I glimpsed the video you linked. Ignore that then.
- 4. Concerning the eyes in particular, I have different animations for the eyes with a different amount of frames, so one has 3 images, it goes from 1 to 3 and 3 to 1. And I have one that consists out of 4 images. Not just that, I also have an eyeroll animation of 11 frames. I could cut it out altogether if coding it will be too much of a pain though. Following the tutorial that I watched, I'm not sure how to properly implement that, seeing as it deals with a constant amount of frames for the eyes.
This is a bit beyond me, but looks like you can do it by creating images that are animated for the eyes, then reference them in the LiveComposite. If I just do the eyes for example:

Code: Select all

image iris eyes:
    "Iris/Eyes/[iris_bruised]/[iris_mood]/eyes.png"
    choice:
        4.5
    choice:
        3.5
    choice:
        1.5
    "Iris/Eyes/[iris_bruised]/[iris_mood]/eyes_closed.png"
    .25
    repeat

image iris = LiveComposite(
    (530, 630), # (width, height)
    (0, 0), "Iris/Base/[iris_clothing].png",
    (0, 0), "Iris/Eyebrows/[iris_mood]/eyebrows.png",
    (0,0), "iris eyes", 
    (0,0), "Iris/Mouths/[iris_mood]/mouth.png"
    )
I've not tested that code, but my impression from the changelog about dynamic images is that that should work - I've just never tried it.
-5. Scaling is kind of an issue, my portrait is too big, I made it wanting it to be big and wanting to scale it down how do I do this in livecomposite? Or do I need to do this in a separate line?
Easy, you break down the image declaration:

Code: Select all

image iris:
    LiveComposite(all the code inside)
    zoom 0.75
- 6. have a question about the width/height in this line:

Code: Select all

LiveComposite(
    (530, 630), # (width, height)
Rather than it being width and height it seems to be about the positioning of the image? (xalign 1.0, yalign 1.0) gives a syntax error. I'd like to pin my height at a certain y-position, have it raise slightly when the character is talking, and move it to the side when another character is on screen manually later. How can I do this?
You'd do this with transforms. For when the character is talking, you'd add the transform to the speaking version of the image, which you may need someone to confirm. I think you can do it this way:

Code: Select all

transform talking:
    yalign 0.9 # or play with ypos and yanchor

image iris speaking:
    At(LiveComposite(livecomposite codes here),talking) # note no quotes around talking
Hope all these works - I desperately need to sleep now!

Post Reply

Who is online

Users browsing this forum: Google [Bot]