Ren'Py Tutorial: Create an unlockable gallery

Ren'Py Tutorial: Create an unlockable gallery

So, you’ve got some cool CGs in your visual novel. You want to add in a gallery, so that players can look at the beautiful art at their leisure. It’s a great idea, but there’s just one problem: you’re not sure how to program it in. If this sounds like an issue that you’re facing, then this tutorial is for you!

Before I go on, I’d be remiss not to mention that the official Ren’Py documentation has some code for creating a gallery. I’m not going to claim that my code is better than what’s there, so if you find that it’s easier to read and use, then feel free to go ahead and use that page as a resource instead.

If not, then let’s get started. In the examples given here, we will be using the following setup:

  • The game has 12 CGs

  • Each CG is stored within a game/images/cg folder

  • Each CG is named in the format cgX.png, where X is a number used to identify them. For instance, the first CG unlocked is named cg1.png, the second is named cg2.png, and so on.

If your own setup is different, you’ll need to change the corresponding values in the code as we continue.

1. Set up your variables

Firstly, let’s create a variable called cgs_unlocked. In this tutorial, it’s a list of CGs that have been unlocked. We use the persistent. prefix to tell Ren’Py to remember the value of this variable across multiple playthroughs. The variable is empty here as we don’t want any CGs to start out unlocked.

default persistent.cgs_unlocked = []

 

Next, let’s create a function to unlock a CG:

init python:

    def unlock_cg(cg_id):

        if (cg_id not in persistent.cgs_unlocked):

            persistent.cgs_unlocked.append(cg_id)

 

When this is called, it will check whether the CG’s ID (more on this later) is in the list, and if not, add it. In addition to this, you might want to add in a function to remove an unlocked CG for debugging purposes:

init python:

    def lock_cg(cg_id):

        persistent.cgs_unlocked.remove(cg_id)

 

As a side note, I keep all of my python functions and variables within their own file, variables_python.rpy. It means that I have a single place where I can find them, and helps to keep my code organised.

2. Add in the Gallery Screen

The next thing to do is to add in the actual screen where we’ll view the gallery. This is the bulk of the coding, so I’ll split this up into multiple sections to make it more easy to digest. For this screen, I’ll be assuming that we have 12 unlockable CGs.

Add the following code to your game - if you’re not sure where to put it, add it to the bottom of screens.rpy. You might also want to consider having a separate file, such as custom_screens.rpy or even a single file called gallery.rpy.

screen gallery:

    tag menu

    default unlocked_cgs = persistent.cgs_unlocked

    default hover_item = None

    default viewing_cg = None

    default hiding_cg = False

    style_prefix "gallery"

 

    frame:

        text _("Gallery"):

            xalign 0.5

            yalign 0.1

 

        grid 4 3:

            xalign 0.5

            yalign 0.5

            spacing 10

 

            for i in range(1, 13):

                button:

                    xsize 300

                    ysize 169

                    background Frame("images/cgs/cg" + str(i) + ".png" if i in unlocked_cgs else (Solid("#0099FF") if hover_item is not i else Solid("#005e9d")))

                    action (SetScreenVariable("viewing_cg", i) if i in unlocked_cgs else NullAction())

                    hovered SetScreenVariable("hover_item", i)

                    unhovered SetScreenVariable("hover_item", None)

 

Let’s explain this. The first few lines are setting up variables needed for this screen, and their default values.

screen gallery:

    tag menu

    default unlocked_cgs = persistent.cgs_unlocked

    default hover_item = None

    default viewing_cg = None

    default hiding_cg = False

    style_prefix "gallery"

 

The next section places the text “Gallery” at the top of the page.

    frame:

        text _("Gallery"):

            xalign 0.5

            yalign 0.1

 

We then set up a grid of 12 images. The numbers 4 and 3 determine how many columns and rows the grid will have. If you only had, say, 6 images, you would need to ensure that numbers multiplied to get equal 6 - grid 2 3, grid 6 1, and so on.

        grid 4 3:

            xalign 0.5

            yalign 0.5

            spacing 10

 

We create a loop that will output the images. range(1, 13) will loop through numbers 1 to 12 - to use the 6 image example again, you would use range(1, 7) to access them.

            for i in range(1, 13):

 

Each item in the loop will display one of the following:

  • The CG corresponding to this number, if it has been unlocked. This assumes that your CGs are located in a folder called images/cgs and are named cg1.png, cg2.png, etc.

  • If a CG hasn’t been unlocked, a light blue rectangle will be added.

  • If we hover over a CG that hasn’t been unlocked, a dark blue rectangle will be shown in its place instead.

                button:

                    xsize 300

                    ysize 169

                    background Frame("images/cgs/cg" + str(i) + ".png" if i in unlocked_cgs else (Solid("#0099FF") if hover_item is not i else Solid("#005e9d")))

                    action (SetScreenVariable("viewing_cg", i) if i in unlocked_cgs else NullAction())

                    hovered SetScreenVariable("hover_item", i)

                    unhovered SetScreenVariable("hover_item", None)

 

Let’s continue. As you add the following code, ensure that the indentation of it lines up with the grid 4 3 line above.

 

        bar value StaticValue(len(unlocked_cgs), 12):

            xalign 0.25

            yalign 0.8

 

        text _(str(len(unlocked_cgs)) + "/12 (" + str(int(len(unlocked_cgs) / 12 * 100)) + "%) Unlocked"):

            xalign 0.25

            yalign 0.85

 

These lines are optional, and will add in a progress bar and percentage display for how many CGs have been unlocked. Again, if you have more or less than 12 images in your gallery, change the 12s in the above lines to match the number of images you have.

 

        if (hover_item is not None):

            text ("Locked" if hover_item not in unlocked_cgs else "View larger"):

                xalign 0.5

                yalign 0.95

 

    if (viewing_cg is not None):

        image "images/cgs/cg" + str(i) + ".png":

            if hiding_cg:

                at transform:

                    alpha 1.0

                    linear 1.0 alpha 0.0

            else:

                at transform:

                    alpha 0.0

                    linear 1.0 alpha 1.0

            xsize 1920

            ysize 1080

   

    if (hiding_cg):

        timer 1.0:

            repeat False

            action [

                SetScreenVariable("viewing_cg", None),

                SetScreenVariable("hiding_cg", False)

            ]

 

    textbutton _("Back"):

        xalign 1.0

        yalign 1.0

        action (ShowMenu("main_menu") if viewing_cg is None else SetScreenVariable("hiding_cg", True))

 

These last lines might look a bit intimidating, but they’re fairly simple. In order:

When we hover over a CG, display text to either clarify that it’s locked, or let people know that they can click to enlarge it.

        if (hover_item is not None):

            text ("Locked" if hover_item not in unlocked_cgs else "View larger"):

                xalign 0.5

                yalign 0.95

 

If we’ve clicked on a viewable CG, display it on the screen at 1920x1080 size. Obviously, you can change these values to make the image smaller if you’re targeting a smaller screen size.

    if (viewing_cg is not None):

        image "images/cgs/cg" + str(i) + ".png":

            if hiding_cg:

                at transform:

                    alpha 1.0

                    linear 1.0 alpha 0.0

            else:

                at transform:

                    alpha 0.0

                    linear 1.0 alpha 1.0

            xsize 1920

            ysize 1080

 

If we’ve clicked to stop viewing an enlarged CG, wait for a second before hiding it. This, combined with some lines above fade out the CG gracefully.

    if (hiding_cg):

        timer 1.0:

            repeat False

            action [

                SetScreenVariable("viewing_cg", None),

                SetScreenVariable("hiding_cg", False)

            ]

 

Display a back button in the bottom right-hand corner. This will either hide the CG if it’s displaying, or return to the main menu.

    textbutton _("Back"):

        xalign 1.0

        yalign 1.0

        action (ShowMenu("main_menu") if viewing_cg is None else SetScreenVariable("hiding_cg", True))

 

There! Not too complex when it’s broken down, is it? There’s still plenty here that could be updated - different text colours and fonts, the placement of items, and additional effects - but it’s a bit beyond the scope of this tutorial.

3. Unlock your CGs

If you’ve added this gallery into your game, you’ve probably noticed that it’s not showing any images right now. This is expected, as we haven’t actually unlocked any CGs yet. Thankfully, the process of unlocking CGs is fairly easy, as we already wrote some of the code earlier.

Let’s say that you have a CG, as a file named cg1.png. Your game script might look something like this:

alice "Something dramatic is about to happen!"

scene cg1

bob "My god! The spaceship is taking off!"

 

To add this CG to the gallery, simply change it to the following:

alice "Something dramatic is about to happen!"

scene cg1

$ unlock_cg(1)

bob "My god! The spaceship is taking off!"

 

4. Make the gallery accessible

The last step is to make the gallery accessible from the main menu. In order to do this, find the following line in your screens.rpy file:

screen navigation():

 

Then, within the indentation of the vbox block, add in the following:

if (main_menu):

    textbutton _("Gallery") action ShowMenu("gallery")

 

And you’re done!


 

There’s plenty of different things that you could change here if you wanted - updating the display of the gallery, changing the values you’re adding to the list of the unlocked CGs, or removing images from the gallery that haven’t been unlocked. I hope that this helps you if you’ve been struggling with any other tutorials! Still having difficulty, or have improvements to suggest? Contact me, and let me know your thoughts.

 


Related News