Ren'Py Tutorial Customizeable & simple Renpy Gallery

recreation

pure evil!
Respected User
Game Developer
Jun 10, 2018
6,327
22,777
So I've been asked to make to make a small tutorial for my custom renpy gallery, well it looks like a lot, but it's actually quite simple, so don't let the size of the post discourage you, a lot of the code is probably not even necessary for most of you since I've included a gender choice option that is entirely optional ;)

The gallery itself is relatively simple, it's actually somewhat similar to the original renpy in a way, but much more customizeable and not as restricted.
One of it's main features is that you won't have to setup several pages once you add more content, you only set the grid size and the script does the rest.
Also once setup, you only need to add the image/scene names for every image/scene you want to show up in the gallery and nothing else. You can basically add an unlimited amount of images/scenes without any extra coding.

For this tutorial I expect you to at least have some basic renpy coding knowledge, I won't explain how to set up an imagebutton or something similar!
So lets start.


First thing we need is the function that determines if an image has been seen already:
Python:
init python:
    def galeryImg(imgID):
        if renpy.seen_image(imgID):
            return imgID
        else:
            return "nope.jpg"
This returns the image name if it has been seen in game, otherwise it will return a placeholder image (nope.jpg).
If you want to add another screen for scenes for example you can copy the code above and only have to change ID names:
Python:
    def galeryImgscene(imgIDscene):
        if renpy.seen_image(imgIDscene):
            return imgIDscene
        else:
            return "nope.jpg"
Next up is the list of images that you want in the gallery:
Python:
define nameByID = ["my image one",
                "my image two",
                "some other image",
                "yet another image"
                ]
In case of scenes you want to add the scene name, but also an image that represents the scene:
Python:
define nameByIDscene = [["my image one","my scene one"],
                ["my image two","my scene two"]
                ]
Note that in this case the image comes first, then the name of the scene!

Next we declare a few variables and images:
Python:
default gridSize = 9 #The gallery is a grid of 3 by 3 images, so this has to be 9, if you want to change the grid size you'll have to change the "grid" value and the gridSize value accordingly.
default page = 0 #The default value for the page the user is on. If there are more than 9(gridsize) images this scipt will automatically add more pages
default pageImageCount ="" #A placeholder variable, more on that later
#placeholder image
image my_image:
    "nope.jpg" with dissolve
We want the player to be able to end a replay, so we need to make an exit button:
Python:
screen endRep():
    zorder 666
    imagebutton auto "exit_%s.png" action EndReplay() yalign .99 xalign .99
I use different screens for images, scenes and also a "special images" gallery that is hidden until the player finishes the game, so I use another screen to choose which gallery the player wants to see:
Python:
#main gallery menu
screen gallery():
    style_prefix "galleryint"
    tag menu

    add "mm_anim" at zoomEffect #I use the background of the main menu here "mm_anim", this could be changed to a simple image or whatever you want to show in the background
    vbox:
      
        textbutton "Scenes" action ShowMenu("g_scenes")
        textbutton "Images" action ShowMenu("g_images")
        if persistent.gallery_special: #This is the special gallery as mentioned above, delete this and the line below if you don't use something like that
            textbutton "Special" action ShowMenu("g_special")

    textbutton "Return" action Return() xalign 0.5 yalign .99 text_outlines [ (0, "#333", 2, 2) ]
In my game you can chose the gender of the mc and because of the way I implemented it in the game I had to make an extra option to chose the gender for scenes here as well (ignore this if you don't need it):
Python:
screen g_scene_gender(curr_scene):
    modal True
    style_prefix "choice"
    vbox:
        textbutton _("Male") action [ Hide("g_scene_gender"), Replay(curr_scene, scope={"pc_name":persistent.pc_mname, "pcthink_name":persistent.pc_mname, "pcgender":"man", "pcmd":persistent.pcmdm}) ]
        textbutton _("Female") action [ Hide("g_scene_gender"), Replay(curr_scene, scope={"pc_name":persistent.pc_fname, "pcthink_name":persistent.pc_fname, "pcgender":"woman", "pcmd":persistent.pcmdf}) ]
        textbutton _("Cancel") action Hide("g_scene_gender")
You'll obviously have to set the persistent variables in the game for it to function. Of course you don't need this if you don't have a gender choice in the game.

Now to the actual gallery:
Python:
screen g_images():
    style_prefix "gallery"
    tag menu
    add "mm_anim" at zoomEffect #Again the main menu background
    default page = 0
    grid 3 3: #Change this value if you want to change the grid size, make sure to change the gridSize variable accordingly
        for i in range(page*gridSize,(page+1)*gridSize):
            if i < len(nameByID):
                imagebutton:
                    idle galeryImg(nameByID[i])
                    hover LiveComposite(  #I'm using a live composite here to overlay the gallery image with a pure white/transparent image for a hover effect, you can also just use a color instead
                        (1920, 1080),
                        (0, 0), nameByID[i],
                        (0, 0), im.Alpha("h.jpg",0.3)
                        )
                    action Show("g_image_full", my_image=nameByID[i])
                    sensitive renpy.seen_image(nameByID[i])
                    at btnZoom
            else:
                imagebutton:
                    idle im.Alpha("h.jpg",0)
                    at btnZoom
    $ pageImageCount = nameByID
    use changePages
The scene gallery is basically the same, just that I had to add a condition for gender specific scenes, this is of course only necessary if you have a gender option:
Python:
screen g_scenes():
    style_prefix "gallery"
    tag menu
    add "mm_anim" at zoomEffect
    default page = 0
    grid 3 3:
        for i in range(page*gridSize,(page+1)*gridSize):
            if i < len(nameByIDscene):
                if persistent.pc_mname and persistent.pc_fname: #Delete this part if you don't have a gender choice
                    imagebutton:
                        idle galeryImgscene(nameByIDscene[i][0])
                        hover LiveComposite(
                            (1920, 1080),
                            (0, 0), galeryImgscene(nameByIDscene[i][0]),
                            (0, 0), im.Alpha("h.jpg",0.3)
                            )
                        action Show("g_scene_gender", curr_scene=nameByIDscene[i][1])
                        sensitive renpy.seen_label(nameByIDscene[i][1])
                        at btnZoom
                  
                else:
                    imagebutton:
                        idle galeryImgscene(nameByIDscene[i][0])
                        hover LiveComposite(
                            (1920, 1080),
                            (0, 0), galeryImgscene(nameByIDscene[i][0]),
                            (0, 0), im.Alpha("h.jpg",0.3)
                            )
                        action [ Replay(nameByIDscene[i][1], scope={"pc_name":persistent.pc_name, "pcgender":persistent.pcgender})] #Delete the gender part here if you don't have a gender choice
                        sensitive renpy.seen_label(nameByIDscene[i][1])
                        at btnZoom
            else:
                imagebutton:
                    idle im.Alpha("h.jpg",0)
                    at btnZoom
                  
    $ pageImageCount = nameByIDscene
    use changePages
If you don't have a gender choice delete the second if part and only leave the else part (without the else of course). Don't delete the second else!
Important is that you have a persistent variable for the mc name.

Now to the part where we display the actual image:
Python:
screen g_image_full(my_image):
    style_prefix "special"
    modal True
    add my_image xalign 0.5 yalign 0.5
    textbutton "Return" action [ Hide("g_image_full") ] xalign 0.5 yalign .99 text_outlines [ (0, "#333", 2, 2) ]
This simply displays the image, nothing special here.

Once the game grows, so will the gallery, this means we have more images then we can fit on one screen, so we want to be able to change pages:
Python:
screen changePages:
    if len(pageImageCount) > (page+1)*gridSize:
        imagebutton:
            idle "gui/galery_right_idle.png"
            hover "gui/galery_right.png"
            action SetScreenVariable("page", page+1)
            xpos 1604
            yalign .5
            yfill True
            xfill True
    if page > 0:
        imagebutton:
            idle "gui/galery_left_idle.png"
            hover "gui/galery_left.png"
            action SetScreenVariable("page", page-1)
            xalign 0
            yalign .5
            yfill True
    textbutton "Return" action [ Hide("g_scene_gender"), ShowMenu("gallery")] xalign 0.5 yalign .99 text_outlines [ (0, "#333", 2, 2) ] #Delete the gender part here if you don't use it
I'm using two images with arrows to indicate the page direction change, both images are 316x1080px (for a 1080p screen). For some reason I saved them in the gui folder instead of the images folder like all the others, no idea why, so keep that in mind when you save your images.

Lastly you've probably noticed that I used some custom styles, so we have to create them as well:
Python:
style gallery_grid:
    spacing 50
    xpos .5
    ypos .5
    xanchor .5
    yanchor .5
    xmaximum 200
style special_text:
    outlines [ (absolute(3), "#bbb", absolute(1), absolute(1)) ]
  
style galleryint_vbox is vbox
style galleryint:
    xfill True
    yfill True
style galleryint_vbox:
    xalign .5
    yalign .5
  
style galleryint_text_button:
    yanchor .5
    xalign .5
style galleryint_button is default:
    properties gui.button_properties("choice_button")
style galleryint_button_text is default:
    properties gui.button_text_properties("choice_button")
  
style galleryint_button is button:
    xalign 0.5
style galleryint_button_text is button_text:
    size 60
    color "#333"
    hover_color "#ccc"
My gallery styling is very simplistic, so this is the part where you can edit most of the styling.

And that's it, you can copy most of the code and only change the image names (or name your images the same) for buttons and of course the persistent variable names and it should work out of the box, just remember that some of the code is for a gender choice and edit it when necessary (make sure to read the comments in the code as well).

As I said, my design is very simple, but you can change almost everything to your liking:

1618038403224.png
1618037644743.png
1618038525189.png


There's probably room for improvements! I'm far from being a pro, so I invite everyone with a bit coding experience to give feedback and suggestions.
 

Dilly_

Member
Game Developer
Oct 2, 2020
343
3,680
Thanks for sharing and for adding an explaination to each code section. Will definitely be saving this to use as a resource.
 
  • Like
Reactions: recreation

doe50818

Member
Apr 5, 2020
244
542
Thanks for sharing!

I understand what some of the code does, but I have no idea what to substitute imgIDscene with. If I just replace it with the name of an image I have DEFINITELY seen because it's literally the first image of the entire VN, the statement automatically wants to jump to else, so that's the wrong approach.

So what is imgIDscene? Looks to me like some ad-hoc variable name you picked for something that has to be defined first, but you have no definition for it in your example code... or is it nameByIDscene by any chance and you renamed the variable mid-tutorial? I'm really confused...


Never mind, downloaded your game to see a populated gallery to actually be able to discern variable names from example names (that need to be actually substituted with image names and scene labels), that cleared the air completely... sorry, I'm new to this.
 
Last edited:
  • Like
Reactions: recreation

Aggelus@

Member
Feb 14, 2020
380
119
Hello friend, could you make a model for us to download?
It would be easier to understand with something already ready and simpler.
Thanks.
 
  • Like
Reactions: rambo25