XaiJu
flinpaltwell
flinpaltwell

patreon


Ren'Py Tutorial: How to make a Gallery

So it turns out, making a gallery in Ren'Py is kind of a PAIN IN THE ASS.

The amount of documentation on it is surprisingly limited, for being such a core aspect of visual novels. I found many people online opted to build their own from scratch, but I was rather of the "fuck that" mentality. Furthermore, I had some of my own goals in how I wanted my gallery to function.

Here was what I wanted to accomplish:

- Make the Gallery using PyTom's built-in "Gallery" tool.
    (A lot of people online complain about the built-in Gallery tool and how it doesn't work and stuff. They are wrong. The documentation just has a simple typo.)

- Include grayed-out/blurred thumbnails of the images you haven't unlocked yet, instead of a generic "lock" symbol or some shit

- ^ Do the above thing WITHOUT manually creating a custom thumbnail for every image, neither through imagery or code

- Brighten any thumbnails I hover over

- Programmatically, automatically add new images into the Gallery when I simply make a new gallery button, to save work for future me


I accomplished all of the above except for the last thing. But I did figure out how I would go about doing that last thing, and I may implement it eventually. I'll provide my notes on that at the end.

For now, let's talk about how to make a gallery!

FIRST, I would just direct you to the official documentation on it: https://www.renpy.org/doc/html/rooms.html

There is a TON of useful syntax there. I recommend you read all the way to the end of the first giant code block. However, do you see that last line of code? The one that says:

textbutton "Return" action Return() xalign 0.5 yalign 0.5
Well, de-indent this ^ line once. Otherwise, the example code won't work. lol
    (This may have been fixed by the time you read this)

If you follow all of that, you'll get a functional gallery. But locked images will all share the same generic thumbnail. I wanted to hint at what people haven't seen yet!

Let's study this line of code:

<code><code>add g.make_button("dark", "gal-dark.png", xalign=0.5, yalign=0.5)</code></code>

In particular, look at the first two arguments: "dark" and "gal-dark.png." The first, "dark," is an arbitrary button label. You name this whatever the name of that button is in the gallery code.
The second, "gal-dark.png," is the name of that button's thumbnail if you have it unlocked. This is a custom-made tiny version of the normal image. In other words, you need to make a tiny thumbnail image of every single button you wanna have in your gallery.

FUCK THAT.

First off, that means I need to export out a thumbnail version of every unique CG I ever wanna show in the gallery, which is a LOT of work! Second, that means the game's file size gets bloated up with a bunch of stupid thumbnail images. It's not that big a deal, but FUCK THAT.

Did you know you can use Ren'Py code to dynamically create new images based on your current images? You can layer shit together, make one piece grayscale while another is blurred, tinted purple, and rotated. Here's an example:

<code><code>image thing_night = im.MatrixColor("thing.jpg", im.matrix.tint(1, 1, 1.2) * im.matrix.brightness(-1.4) * im.matrix.contrast(0.26) * im.matrix.saturation(1.2))</code></code>

^ The above code takes "thing.jpg" and makes a nighttime version of it! For free! Isn't that sick as fuck? This could cut your file size nearly in half!

Anyways, so I started out doing it this way. I'd define a bunch of "do this image except scale it down" to make all the thumbnails for "free." It was still slow going. It still sucked.

So then I was like, FUCK THIS.

After hunting around for days, I finally found/figured out the code that changed it all:

<code><code>init -10 python:
    def thmbnl(f):
          return Transform(f, zoom = 0.1852)
     config.displayable_prefix["thmbnl"] = thmbnl</code></code><code><code>     def thmbnl_u(f):
          return Transform(f, blur = 3, zoom = 0.1852, matrixcolor = SaturationMatrix(0))
    config.displayable_prefix["thmbnl_u"] = thmbnl_u</code></code>

Basically, this automatically makes a thumbnail version (and a blurred, grayscale thumbnail version) of literally every image in the game, IF I decide to invoke the thumbnail's existence somewhere. Now, the first line.

init -10 python:

Right off the bat, what the fuck. -10? Basically, python init blocks all happen in a certain order, and -10 is kind of an arbitrary "I want this to happen before most other things" number. It could be -20, -3, -856, etc. There may be other blocks you wanna have happen before this one too, though.

def thmbnl(f):

Just the function definition. The letters "thmbnl" can be whatever you want. We'll reference them in a bit.

return Transform(f, zoom = 0.1852)

Scale the image way down, essentially making it a tiny thumbnail. You'll need to experiment with the number 0.1852 to see if that makes a good-sized thumbnail for your resolution (my game's resolution is 1920x1080, which is higher than most VN's).

config.displayable_prefix["thmbnl"] = thmbnl

Again, both instances of "thmbnl" can be whatever you want. I chose that for "thumbnail," but this will basically be your prefix that you use on ANY IMAGE you want to use. I'll show you how it's used later.

The rest of the code is the exact same thing again, except it makes a blurred, grayscale thumbnail of all images in the game. Note that the quality of the blur is a LITTLE shitty if you do it this way, but I didn't mind much.

Anyway, you remember the screen code from the documentation? Check this shit out:

<code><code>add g.make_button("Stella norm", "thmbnl:Stella norm", "thmbnl_u:Stella norm", xalign=0.5)
add g.make_button("SF_1", "thmbnl:SF_1", "thmbnl_u:SF_1", xalign=0.5)
add g.make_button("SF_5", "thmbnl:SF_5", "thmbnl_u:SF_5", xalign=0.5)</code></code>

And THAT'S IT. In all of these cases, I defined an image (image SF_1 = "something.jpg") somewhere, and now if I say "thmbnl:SF_1" anywhere, it automatically makes a thumbnail version!

This is black magic! Be impressed!! Be impressed!!

Oh, and that third argument, the "thumbnl_u:SF_1" is the image it shows if you haven't unlocked the image yet. Hence, the grayed-out blurred thumbnail.

Phew! What's next? Brighten any thumbnails I hover over. That's easy:

<code><code>init python:
    g = Gallery()
     g.navigation = True
     g.hover_border = "#ffffff40"</code></code>

That last line is the key. It just puts a semi-transparent white block over anything you hover over. You could probably figure out something prettier, but it's good enough for me.

And that just leaves the last thing. Basically, I rather hate the screen code in the documentation example, because it's super repetitive and tedious. I'm already going into the Gallery and saying "I want this button for this image, this button for this image," so why not have the screen intelligently figure out how to load all those buttons in?

I ended up failing to figure this one out. But I started to come close before I burned out. This was the commented note I left myself:

<code><code>#$ names = g.buttons.keys()
#$ names.sort(key=lambda x: x.index)
#$ rv = g.buttons["Stella norm"]
#if "Stella norm" in g.buttons:      # This turned up True, so this is a check that works
#    centered "Button names: [names]"
#    centered "Index of Stella norm: [rv.index]"

^ This code is if you feel like figuring out how to tell all the Gallery buttons down below to automatically get displayed in the Gallery screen. "names" winds up being a sorted list of all the button names that is ALMOST perfectly in order. That "almost" annoys the fuck outta me (Blank got sorted to the end and SF_5 came before SF_1, for NO REASON).</code></code>

In essence, I believed that if I could sort all the buttons in the order that I define them in the Gallery, I could just check the first letter of my button names and have a screen that sorts all the "S" buttons into a Stella page (in order), and do that for every character (which is how I'm sorting my CGs). In reality, a couple buttons turned up out of order and it bothered me too much to feel comfortable trying to implement this.

If that last bit confuses you, don't worry about it.
If ALL OF THIS CONFUSES YOU, I'M FUCKING SORRY!!! I may try to do a more robust video lesson or something in the future. A Patreon post is probably not the best way to do this.

Oh, uh, the end.


More Creators