- Sep 29, 2020
- 58
- 1,133
Hey!
Last night, another dev asked me to take a look at his gallery implementation, as he is not as experienced in coding and was only following a tutorial. The basic implementation was pretty lacking, and relied only on Ren'Py's 'has seen image' function, so the gallery items unlocked automatically, if an image was seen during gameplay.
His specification was that the gallery elements should be manually unlockable by him, in his code.
I modified his original solution and cleaned up the code a bit. I'm a software engineer by discipline but I'm not super well versed with the Ren'Py SDK and Python stuff. I mainly work with Kotlin, Java and C#.
The solution turned out to be the following:
First, a GalleryElement class which is the basic model of every single element in the gallery. It also holds the "unlock" function and the is_locked variable to define its locked state.
After that, we have a few utility methods for resizing the images (so that there is no need for separate image sizes for thumbnails and the high res image). Additionally, it holds the "unlock_gallery_element" method which should be called from... wherever... to unlock a GalleryElement:
After that, we have the screen definitions for the Gallery and "gallery closeup" screens:
The last file is supposed to hold everything related to the GalleryElements. I used constant variables for the name definitions since I wanted to implement compile safety.
And that's about it. It works like a charm! Well, that is UNTIL you close the game. If you open it up again. you just lost all the unlockables' states.
And I can't figure out why. Since I define the gallery_elements = [], shouldn't that be a persistent solution? Shouldn't it hold and save the state of all the elements inside that list?
I'm a bit confused and I'd appreciate some help figuring this out. For a while I thought the issue is that the GalleryElement's constructor defines the is_locked variable automatically. But it can't run again if I open up the game. That doesn't seem right.
What I'd like to avoid is having to create a seperate variable for each GalleryElement to track its locked/unlocked state.
Thank you for reading and thank you for the help!
Last night, another dev asked me to take a look at his gallery implementation, as he is not as experienced in coding and was only following a tutorial. The basic implementation was pretty lacking, and relied only on Ren'Py's 'has seen image' function, so the gallery items unlocked automatically, if an image was seen during gameplay.
His specification was that the gallery elements should be manually unlockable by him, in his code.
I modified his original solution and cleaned up the code a bit. I'm a software engineer by discipline but I'm not super well versed with the Ren'Py SDK and Python stuff. I mainly work with Kotlin, Java and C#.
The solution turned out to be the following:
First, a GalleryElement class which is the basic model of every single element in the gallery. It also holds the "unlock" function and the is_locked variable to define its locked state.
Python:
# The GalleryElement class is the basic model of a single element in the gallery
init python:
class GalleryElement:
def __init__(self, item_name, image_list, available_thumbnail, locked_thumbnail="default_locked_thumbnail"):
self.item_name=item_name
self.image_list=image_list
self.available_thumbnail=available_thumbnail
self.locked_thumbnail=locked_thumbnail
self.is_locked = True
def unlock(self):
self.is_locked = False
Python:
# This file is responsible for holding utility methods connected to the gallery features.
init python:
def max_scale(image, min_width=config.screen_width, min_height=config.screen_height):
current_width, current_height = renpy.image_size(image)
x_scale = float(min_width) / current_width
y_scale = float(min_height) / current_height
if x_scale > y_scale:
max_scale = x_scale
else:
max_scale = y_scale
return im.FactorScale(image, max_scale, max_scale)
def min_scale(image, max_width=config.screen_width, max_height=config.screen_height):
current_width, current_height = renpy.image_size(image)
x_scale = float(max_width) / current_width
y_scale = float(max_height) / current_height
if x_scale < y_scale:
min_scale = x_scale
else:
min_scale = y_scale
return im.FactorScale(image, min_scale, min_scale)
def unlock_gallery_element(item_name):
next((i for i in gallery_elements if i.item_name == item_name), None).unlock()
Python:
init python:
GALLERY_WIDTH = 2
GALLERY_HEIGHT = 2
GALLERY_ELEMENT_WIDTH = config.screen_width / (GALLERY_WIDTH + 1)
GALLERY_ELEMENT_HEIGHT = config.screen_height / (GALLERY_HEIGHT + 1)
MAX_ELEMENTS_PER_PAGE = GALLERY_WIDTH * GALLERY_HEIGHT
current_gallery_page = 0
current_closeup_page = 0
screen Gallery():
tag menu
add "black_background"
$start = current_gallery_page * MAX_ELEMENTS_PER_PAGE
$end = min(start + MAX_ELEMENTS_PER_PAGE - 1, len(gallery_elements) - 1)
# Create a grid for the images
grid GALLERY_WIDTH GALLERY_HEIGHT:
xfill True
yfill True
for i in range(start, end +1):
if gallery_elements[i].is_locked:
add gallery_elements[i].locked_thumbnail:
xalign 0.5
yalign 0.5
else:
imagebutton idle gallery_elements[i].available_thumbnail:
action Show("gallery_closeup", dissolve, gallery_elements[i].image_list)
xalign 0.5
yalign 0.5
for i in range(end +1, start + MAX_ELEMENTS_PER_PAGE):
null
# Create a grid for the image titles
grid GALLERY_WIDTH GALLERY_HEIGHT:
xfill True
yfill True
for i in range(start, end + 1):
hbox:
spacing GALLERY_ELEMENT_WIDTH - 70
xalign 0.5
yalign 0.1
$item_name = gallery_elements[i].item_name
text "[item_name]"
for i in range(end - start + 1, MAX_ELEMENTS_PER_PAGE):
null
# Add the 'Previous' and 'Next' buttons
if current_gallery_page > 0:
textbutton "Previous":
action SetVariable("current_gallery_page", current_gallery_page - 1)
xalign 0.1
yalign 0.98
if (current_gallery_page + 1) * MAX_ELEMENTS_PER_PAGE < len(gallery_elements):
textbutton "Next":
action SetVariable("current_gallery_page", current_gallery_page + 1)
xalign 0.9
yalign 0.98
#Return Button
textbutton "Return":
action Return()
xalign 0.5
yalign 0.98
screen gallery_closeup(images):
modal True
add images[current_closeup_page] at truecenter
if current_closeup_page > 0:
textbutton "Previous":
action SetVariable("current_closeup_page", current_closeup_page - 1)
xalign 0.1
yalign 0.98
background "black_background"
if current_closeup_page < len(images) - 1:
textbutton "Next":
action SetVariable("current_closeup_page", current_closeup_page + 1)
xalign 0.9
yalign 0.98
background "black_background"
textbutton "Return":
action [SetVariable("current_closeup_page", 0), Hide("gallery_closeup", dissolve)]
xalign 0.5
yalign 0.98
background "black_background"
Python:
# Define the names of the gallery items. These names will be used to unlock them and to display the name of the item as well.
define GALLERY_ITEM_NAME_1 = "Space & K/DA"
define GALLERY_ITEM_NAME_2 = "Maid Service"
define GALLERY_ITEM_NAME_3 = "Portrait of a girl"
# Declare the high resolution gallery images that are shown close-up when clicked on the image
image gallery_img_1 = max_scale("gallery/gallery_img_1.jpg")
image gallery_img_1b = max_scale("gallery/gallery_img_1b.jpg")
image gallery_img_2 = max_scale("gallery/gallery_img_2.jpg")
image gallery_img_3 = max_scale("gallery/gallery_img_3.jpg")
# Declare the lower resolution thumbnails images that are shown in the gallery
image gallery_thumbnail_1 = min_scale("gallery/gallery_img_1.jpg", GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT)
image gallery_thumbnail_2 = min_scale("gallery/gallery_img_2.jpg", GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT)
image gallery_thumbnail_3 = min_scale("gallery/gallery_img_3.jpg", GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT)
# Miscellanious declarations such as the default locked thumbnail, gallery background, etc.
image black_background = "#000"
image default_locked_thumbnail = min_scale("images/scene2/lockedaf1.jpg", GALLERY_ELEMENT_WIDTH, GALLERY_ELEMENT_HEIGHT)
# This block is responsible for declaring the elements which should be available in the gallery
init python:
gallery_elements = []
gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_1,["gallery_img_1", "gallery_img_1b"],"gallery_thumbnail_1"))
gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_2,["gallery_img_2"],"gallery_thumbnail_2"))
gallery_elements.append(GalleryElement(GALLERY_ITEM_NAME_3,["gallery_img_3"],"gallery_thumbnail_3"))
And I can't figure out why. Since I define the gallery_elements = [], shouldn't that be a persistent solution? Shouldn't it hold and save the state of all the elements inside that list?
I'm a bit confused and I'd appreciate some help figuring this out. For a while I thought the issue is that the GalleryElement's constructor defines the is_locked variable automatically. But it can't run again if I open up the game. That doesn't seem right.
What I'd like to avoid is having to create a seperate variable for each GalleryElement to track its locked/unlocked state.
Thank you for reading and thank you for the help!