code Archives - Feniks Development https://feniksdev.com/tag/code/ Coding tutorials, tips, and tricks Thu, 11 Apr 2024 00:57:42 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.5 https://feniksdev.com/wp-content/uploads/2022/05/feniks_shawna-150x150.png code Archives - Feniks Development https://feniksdev.com/tag/code/ 32 32 How to make resizeable backgrounds in Ren’Py with Frame https://feniksdev.com/how-to-make-resizeable-backgrounds-in-renpy-with-frame/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-make-resizeable-backgrounds-in-renpy-with-frame https://feniksdev.com/how-to-make-resizeable-backgrounds-in-renpy-with-frame/#comments Tue, 02 Apr 2024 00:39:16 +0000 https://feniksdev.com/?p=3014 In this tutorial for beginners, learn how to make backgrounds that tile and stretch to fit their contents.

The post How to make resizeable backgrounds in Ren’Py with Frame appeared first on Feniks Development.

]]>
It’s common for UI to reuse the same background for various UI elements, just sizing it differently based on its contents. Often this background has a repeating or solid pattern on the inside so it can be stretched or tiled to accommodate many sizes.

The most basic example of this is a messenger bubble in a chat app. The rounded corners and little speech bubble triangle will be the same size for all messages, but depending on how long the text is, the bubble sides may get longer or taller to accommodate the contents.

Luckily, Ren’Py has a built-in displayable you can use to achieve this effect for your UI as well without creating a whole bunch of additional images. It’s called Frame (not to be confused with frame, which we covered in Ren’Py Screen Language Basics – Frames – though Frame displayables are often used as backgrounds for frame containers). If you’ve done any development in Android, you might also know this technique as “9-patch”, for reasons which will become clear shortly.

Difficulty Level: Beginner

This tutorial is designed to be understood as a standalone guide to Frame. In particular, the knowledge in this tutorial will be sufficient to let you swap out some images in the default GUI without needing to know anything else about screen language. That said, you may find it useful to read through more of my tutorials on screen language, in which case I recommend starting with Getting Started with Ren’Py Screen Language.

What is Frame?

At its core, Frame takes a displayable of some kind (generally an image) and slices it up in a 3×3 grid based on numbers you provide – hence the name “9-patch” for the 9 squares. The corners of the 3×3 grid (squares 1, 3, 7, and 9 in the image below) are preserved exactly as-is regardless of how big the Frame displayable will be, but the inner “+” of grid squares (squares 2, 4, 5, 6, and 8) will be either stretched (the default) or tiled to make the Frame as tall and wide as it needs to be.

This makes it incredibly powerful for UI, since you can reuse the same Frame for the background of dozens of UI elements regardless of their size, since the Frame will handle resizing the background to whatever you need.

How to set up Frame

Frame takes as few as 2 arguments to set up. An argument is just a value of some kind that you’re going to provide to Frame so it can slice up your image properly.

The one consistent argument you will provide a Frame is an image or displayable of some kind. This is the displayable you’re trying to stretch/tile to fill out a background. You should be able to slice this displayable up into a 3×3 grid to make it work for a Frame.

Next, you have three main options for providing the numbers for the 3×3 grid.

Method 1: Two numbers

If you provide two numbers after the displayable, those are interpreted as the x border and the y border. So, take the following example:

image my_background = Frame("myframe.png", 25, 30)

For this Frame, the left border will be 25 pixels from the left edge of the image and the right border will be 25 pixels from the right edge of the image. Similarly, the top border will be 30 pixels from the top edge and the bottom border will be 30 pixels from the bottom edge. Ren’Py will use these numbers to slice up the image “myframe.png” to turn it into a resizable image you can use as a background.

Method 2: Four numbers

If you provide four numbers after the displayable, those are interpreted as the (left, top, right, bottom) borders, respectively. You remember it by thinking of it going clockwise, starting at the left. For example:

A floral frame is shown on the right with text. The left shows a smaller rounded square the frame is constructed from.
image floral_background = Frame("flowers.png", 214, 230, 206, 217)

For this example, the left border is 214 pixels from the left edge, the top border is 230 pixels from the top edge, the right border is 206 pixels from the right edge, and the bottom border is 217 pixels from the bottom edge.

Method 3: A Borders object

You may also provide a Borders object right after the displayable, in which case the four numbers you provide act exactly like just providing 4 regular numbers would (so, left/top/right/bottom; see Method 2 above).

image speech_bubble = Frame("bubble.png", Borders(53, 47, 28, 29))

In this case, the left border is 53, the top border is 47, the right border is 28, and the bottom border is 29. Borders are used a lot in the default gui styles as an easy way of holding four numbers for padding or Frame borders, though personally I don’t use them myself and generally prefer to directly provide numbers as in methods 1 and 2.

Note

Note that the numbers you provide to Frame to split up the displayable for stretching/tiling do NOT have to correspond to the padding for the displayable. Sometimes they are the same or similar, but you can use Frame to stretch unusual and asymmetrical displayables as well. In such a case, often at least one of the borders is much higher than the others (e.g. messenger bubbles with a little triangle. The side with the triangle has a bigger border than the other sides.).

Tiling

By default, the displayable provided to Frame will be stretched to fit the required area. However, there are some background designs that should be tiled instead of stretched, for example, frames with a dotted line border, frames with a repeating pattern on the inside, frames with a particular outline (e.g. with wavy edges for a thought cloud), etc.

There are two possible methods Ren’Py will use for tiling a provided image.

Method 1: Regular tiling

To make the displayable tile, simply add tile=True to the end of your Frame declaration e.g.

image polka_dots = Frame("gui/dots.png", 0, 0, tile=True)

For this example, we have a polka-dot background that should be tiled when it’s resized to fit a larger area. Notably the borders are 0; this is perfectly valid if you don’t need to preserve the shape or style of the edges when the displayable is resized. In this case we’re using Frame primarily to tile the polka dots to fill out an area.

If you set tile=True for your Frame, Ren’Py will simply repeat the provided sections as many times as possible to fill out the area.

Method 2: Integer tiling

This method is very similar to the previous one, except instead of tile=True you’ll use tile="integer" e.g.

image thought_bubble = Frame("gui/thought.png", 20, 10, 20, 30, tile="integer")

As opposed to regular tile=True, tile="integer" means that Ren’Py will calculate how many repetitions of each “tile” it can create to fill the given area, and then rounds that to the nearest integer.

So, for example, if your image is 50×50 and you provided borders of (20, 20, 20, 20), then the tiles will be 10×10 (since 20 pixels are taken up on each side of the displayable). If you then use this Frame to fit an area that’s 67×92 pixels wide, there will be (68-(20*2))/10 = 2.7 tiles along the top and bottom edges, which will be rounded up to 3. Similarly, there will be (92-(20*2))/10 = 5.2 tiles along the left and right edges, which will be rounded down to 5.

To get the final displayable, Ren’Py calculates these numbers and then squashes or stretches the tiles to fit. So, for the earlier example, three 10×10 tiles along the top and bottom edges would usually take up 30×10, making for a final width of 30+20*2=70. But if you recall, the provided width was 67, not 70. So Ren’Py will slightly squish each of the tiles to be 9×10 instead, so the final width is 20*2+9*3=67.

Similarly, the five 10×10 tiles along the left and right sides would normally take up an area of 10×50, giving the displayable a final height of 20*2+50=90 instead of 92. Ren’Py will slightly stretch the tiles vertically to fill out this space, so each tile is 10×10.4 instead of 10×10.

Unlike with tile=True, tile="integer" will look different depending on how large the tiled sections are. Generally, you should try to reduce your image to have the smallest repeating section possible for the best adaptability.

Which tiling method to use

In short, if it’s okay to have half a tiled square as part of your tiled background, just do tile=True. If each tile needs to contain the same contents and can’t be cropped/split, then use tile="integer" (and ideally either have each tile be fairly small or use displayable sizes that are close to a round number of tiles for your tiled Frame).

Using Frame for buttons and frames

To use Frame in-game, you can declare it using the image statement and then use that as the background, or declare a Frame directly as the background property of a style or container.

Two scrolls are side by side. The first reads "By royal decree, you shall be executed on the morrow." The second reads "Signed, the King".
image history_scroll = Frame("scroll.png", 10, 120)
screen royal_decree():
    frame:
        # Uses your resizable scroll background
        background "history_scroll"
        # Since the scroll curls are fairly tall, this container
        # looks best with a minimum size
        yminimum 300
        text "By royal decree, you shall be executed on the morrow."

Note the background "history_scroll" line; this uses the history_scroll image declared just above the screen, which is a Frame.

A screenshot with a rectangle in the middle that has a dotted outline. Text in the rectangle reads "How to play. Rock beats scissors. Scissors beat paper. Paper beats rock."
screen information():
    frame:
        background Frame("big_outline.png", 25, 25, tile="integer")
        has vbox
        text "How to play" size 50
        text "Rock beats scissors"
        text "Scissors beat paper"
        text "Paper beats rock"

In this example, the background for the frame directly uses a Frame as the value rather than declaring it as an image and using that.

Summary

  • Frame allows you to declare an image which will resize itself to fit whatever space it is given. You can provide information to Frame to customize how the image is stretched or squished to fit the available space.
  • Frame images are either stretched (default) or tiled (tile=True or tile="integer") to expand to fill additional space. tile=True can result in half tiles; tile="integer" will guarantee full tiles but may squish or stretch the tiles to fit the space.
  • You can use Frame anywhere a displayable is used – so, as the displayable for background properties, in styles, added directly to screens with add, etc.

Bonus tool

You may have noticed I’m using some kind of tool in the various screenshots for this tutorial – this tool is available on my itch.io:

You can use the tool to find the borders, tiling, and padding for a Frame image, and then copy the result to your clipboard to use in your game.

The post How to make resizeable backgrounds in Ren’Py with Frame appeared first on Feniks Development.

]]>
https://feniksdev.com/how-to-make-resizeable-backgrounds-in-renpy-with-frame/feed/ 6
Ren’Py Screen Language Basics – Frames https://feniksdev.com/renpy-screen-language-basics-frames/?utm_source=rss&utm_medium=rss&utm_campaign=renpy-screen-language-basics-frames https://feniksdev.com/renpy-screen-language-basics-frames/#comments Mon, 18 Mar 2024 01:01:01 +0000 https://feniksdev.com/?p=2962 In this Ren'Py tutorial for beginners, learn about the frame container and its properties like background and padding.

The post Ren’Py Screen Language Basics – Frames appeared first on Feniks Development.

]]>
You’ve already learned about fixed, vbox, and hbox in Ren’Py Screen Language Basics – Basic Containers. One more crucial container you’ll often use when constructing UI is the frame container. But what is a frame in Ren’Py, and how do you use it?

Difficulty Level: Beginner

This tutorial is suitable for people new to Ren’Py screen language. You should start with Getting Started with Ren’Py Screen Language and read up to Ren’Py Screen Language Basics – Basic Containers at minimum to have a better basis for the concepts covered in this tutorial.

Vocabulary

As per usual, a quick vocabulary recap is in order:

Element – thing you’re displaying on a screen. Can be an image, text, a button, anything that goes in a screen.

Container – thing that organizes elements in some way. Usually contains multiple elements to organize them somehow.

Children – The elements inside containers are also referred to as the container’s children.

Overview of frames

A frame is a special sort of container that actually acts as a “decorator for containers” in a manner of speaking. While containers like fixed, vbox, and hbox organize their elements in some fashion but don’t have many other properties aside from size and position properties, frame has several of its own, most notably background and padding. It acts like a decoration which you can put other container types inside, like fixed/vbox/hbox.

has

To explain what makes frames unique, I will explain the concept of has, and how frames work. Frames have only one child, as opposed to fixed, hbox, and vbox seen earlier which take multiple children and organize them. Since a frame is more of a decoration than a container itself, it uses one of the existing container types as its child instead, so the child can do the organizing. This saves repeat code in the backend, and means that frame can be used to decorate lots of things. All elements added to the frame get passed along to its child for organization.

By default, a frame has a fixed as its child, and any children you put inside the frame are automatically added to the fixed child instead (so you can still add multiple elements to a frame, and it mostly acts as though it were a fixed with a background).

Now, for most purposes, this is as much knowledge as you need to work with frames. But for those of you looking for a bit more advanced knowledge, we will look at the has statement.

Understanding the has statement

As mentioned, the default child of a frame is a fixed. You can think of it kind of like:

frame:
    # Frame properties, like background and padding
    fixed:
        # All the frame's children go in here

has tells certain containers like frame what kind of child they have. Again, by default, it is a fixed, but you might change it to one of the following:

  • fixed
  • vbox
  • hbox
  • side
  • grid

To do so, inside your frame you will write has vbox where vbox is the container type you’d like frame to use.

# has vbox example
frame:
    has vbox
    text "Hello"
    text "World!"

For the above example, has vbox means the child of the frame is now a vbox, and thus, any elements that are inside the frame will be organized as if they were inside a vbox. This is almost the same as just a regular vbox, except it’s also got a background and some padding provided by the frame.

vbox:
    text "Hello"
    text "World!"

Now that you know frame by default contains a fixed, internally something like the following code:

frame:
    vbox:
        text "Hello"
        text "World!"

is actually:

frame:
    has fixed
    vbox:
        text "Hello"
        text "World!"

Since you don’t need the fixed in there, using has vbox like in the # has vbox example earlier to turn the contents of the frame into a vbox is cleaner. It also avoids one extra level of indentation, which is a bonus in my opinion!

Note that you can still apply properties to the frame’s child as if it were a separate screen element. For example:

frame:
    background "#000000"
    xysize (300, 600)

    has vbox
    align (0.5, 0.5)

    text "Hello"
    text "World"

In this example, the properties background and xysize belong to the frame. They give it a black background (hex colour code #000000 – see below for more information on backgrounds in a section later) and a size of 300×600.

The vbox child of the frame, on the other hand, gets the property align (0.5, 0.5), which will cause the vbox to be perfectly centered inside the available space of the frame.

If you want to provide more properties to the frame, they have to go above the has statement. Anything below the has statement is a property applied to the child of the frame (in this case, the vbox).

You can also use this to provide properties for the default fixed child. For example:

frame:
    background "#ffffff"
    align (0.5, 0.5)
    has fixed
    fit_first True
    add "feniks.png"
    text "Hello, World!" align (0.5, 0.5)

Occasionally you might want to use properties like fit_first for the implicit fixed that is part of the frame; see: Ren’Py Size Properties – fill, fit, size_group, and area.

Frame vs Window

What’s the difference between frame and window? For all intents and purposes inside the Ren’Py documentation, it sounds like they’re basically identical.

Surprise – they are! They are genuinely identical in how they are handled internally. In fact, somewhat confusingly, while there is a Frame Python function to create a displayable, it has nothing to do with the frame container used in screens. (The Python equivalent of frame is actually Window, because as mentioned, frame and window are the same thing.)

So why would you use one over the other? Well, here’s the important difference – Ren’Py applies a whole lot of styling to any window containers. The default styles for a window come directly from all the styling for the dialogue window, aka the thing that shows dialogue to the player in-game.

This means window is really only suitable for 1) dialogue windows and 2) places where you want to replicate the style of the dialogue window (such as the default implementation of the input screen, which looks like the dialogue window, as seen above). Otherwise, you’ll be fighting against the default window styles, and why do that when frame is right there without the extra baggage?

In short: if you want a container with a background, you should be using frame everywhere except for the dialogue window, which can use window.

Screen Language Properties for Frames

There are two main property “groups” that are unique to frames, and by extension buttons (and their cousins textbutton, window, and label). For the purposes of this tutorial, I’m going to be using frames to talk about these containers generally, and we’ll be looking specifically at properties used by frame. The only difference between frame and button when it comes to these properties is that button has a few extra backgrounds based on whether the button is hovered, sensitive, selected, etc. We’ll be covering buttons properly in another tutorial!

Background and Foreground

The defining feature of frames is that they have a background property (which can be None so the background is invisible). They can optionally take a foreground as well. The background will display behind all of the frame’s children, and the foreground will display in front of all the frame’s children.

By default, frames use gui/frame.png as their background. You can replace this image in the gui/ folder, or manually declare the background any given frame will use.

Importantly: the size of a frame’s background does not affect the size of the frame element itself. This is in contrast to something like an imagebutton, where the size of the idle image becomes the size of the imagebutton unless you explicitly provide an xysize for it. Thus you can end up with slightly confusing scenarios, such as where your background is 200×200 pixels large, but your frame is only 120×50 to accommodate the contents of the frame, or it’s 300×250 because the contents take up more space than the frame’s background.

In the image above, the white dotted line is the foreground, and it represents the actual size of the container, compared to the background image. If the frame isn’t given a size, then its contents will inform how big it is – in this case, the size of the text is the size of the container. You can read more about the various size properties in Ren’Py Size Properties – xysize, maximum, and minimum.

Most commonly a frame’s background is resizable – that is, it resizes itself based on the size of the frame’s contents. The most common way to do this is to use the Frame displayable, which will be covered in detail in an upcoming tutorial.

Alternatively, you can also provide a colour as the background for a frame. I used this method in the earlier examples going over the has functionality. A colour can be provided in many different formats through Ren’Py’s Color class, but the easiest is to provide a hexadecimal code (or hex code for short) as a string. That looks like this:

Text in the center of the screen has a red background. It reads "This frame is red".
frame:
    background "#f00" align (0.5, 0.5)
    text "This frame is red"

Hex colour codes can be 3-digit (rgb, red/green/blue), 4-digit (rgba, red/green/blue/alpha), 6-digit (rrggbb), or 8-digit (rrggbbaa). Hexadecimal goes from 0-f, like 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. So, #f00 is red because there is f red, aka 100% red, and then 0 green and 0 blue.

You don’t have to memorize this or anything; it’s perfectly fine to look up colour codes when you need them. I like to use the 3-digit hex codes for quick testing, especially if I need to immediately see how big a frame is. A colour code like #f0f (magenta), is extremely visible and likely to clash with anything on-screen, so it’s easy to pick out when making a screen. Other useful colours that are easy to remember are #fff (white) and #000 (black).

Using colour codes for a frame’s background will always result in the background being automatically resizable depending on how big the frame’s child is, which depends on its properties and on how much space the frame contents take up.

Padding

Frames also get padding, which allows you to make sure the contents inside the frame aren’t right up against the edges of the background. If you think of the frame like a picture frame, the padding is how wide the borders of the frame are – if you have a wooden picture frame that’s 10×10″, and the wooden part of the frame is 1 inch wide all around, then you will have an 8×8″ area where you can put a picture inside (since you subtract 1 inch for the left frame border and 1 inch for the right -> 10-1-1=8).

A large square is in the middle of the screen. A 100px-wide border is drawn in brown all around it and the center is white.

There are multiple ways of setting padding for a frame, each described below.

Individual padding properties

To adjust the padding from each of the four directions individually, you can use the following properties: left_padding, top_padding, right_padding, and bottom_padding.

left_padding refers to the width between the left edge of the frame and the leftmost edge of the frame’s contents. Similarly, bottom_padding is the height between the bottom edge of the frame and the bottom of the frame’s contents (so, if you think of it as a picture frame, the bottom_padding is the height of the bottommost horizontal piece of the outer frame).

Each of these padding properties takes an integer number representing the size, in pixels, of that particular border.

X and Y padding properties

If your frame is symmetrical, you can also use the xpadding and ypadding properties. xpadding sets left_padding and right_padding to the same value, and ypadding sets top_padding and bottom_padding to the same value.

The padding property

Finally, there’s just padding, which operates similarly to how anchor is the combination of xanchor and yanchor (see Ren’Py Position Properties – Pos and Anchor). If you provide padding with two numbers, these are used as the xpadding and ypadding values.

frame:
    padding (12, 8)

So, in the above code, the frame has an xpadding of 12 (so, 12 pixels of padding on the left and right) and a ypadding of 8 (8 pixels of padding on the top and bottom).

You can also provide 4 numbers to padding, in which case they are read as (left_padding, top_padding, right_padding, bottom_padding). So, it starts with the left padding, and then declares the other padding properties in clockwise order.

frame:
    padding (100, 25, 8, 25)

In the above example, the top and bottom padding of the frame is 25, the left padding is 100, and the right padding is 8. These kinds of values are sometimes used for things like checkboxes, where the background is a checkbox square, but the text should start significantly farther to the right so it’s not actually on top of the checkbox square but beside it. Having a large left_padding value means that the contents of the button start much farther to the right.

Finally, frames are unique in that they may take a modal property, which is otherwise only used as a property for a screen as a whole.

modal is False by default, but if it is set to True, then it means that any “mouse events” that occur within the bounds of the frame – like clicking or hovering – are only passed to the frame itself. More practically, this means that if there was something like a button behind the frame, clicking on the frame will not cause the button to act like it was clicked, because the modal True property on the frame means it “absorbs” the mouse click event.

frame:
    background "#00f"
    modal True
    text "Any buttons behind this frame can't be clicked."

This is often used screen-wide for things like popups – in particular, the confirm screen uses modal True so that when the player has to confirm something, like “Are you sure you want to quit?” they can’t accidentally activate some button behind the confirmation prompt.

Besides just True/False, modal may also take a callable argument for more fine-tuned management over when it should absorb input or not. This is advanced usage that’s unnecessary for the vast majority of use cases, but for more, you can read about it here: https://www.renpy.org/doc/html/style_properties.html#style-property-modal

size_group

frame can also take the size_group property, which is covered in more detail in the tutorial Ren’Py Size Properties – fill, fit, size_group, and area

Summary

  • Frames (and buttons, labels, textbuttons) are a special type of container that comes with a background.
  • They can organize their children in multiple ways by using the has statement.
  • All frames can have a background and foreground property, padding properties, and the modal property to specify how it should look and behave. It also uniquely gets access to size_group as covered in Ren’Py Size Properties – fill, fit, size_group, and area

Next Steps

In the next tutorial in this series, we’ll take a look at the special Frame displayable, and how you can use it to make resizeable backgrounds for frame and other containers. Find that tutorial here: How to make resizeable backgrounds in Ren’Py with Frame. Follow me on itch.io to be notified of new tool releases as well!

The post Ren’Py Screen Language Basics – Frames appeared first on Feniks Development.

]]>
https://feniksdev.com/renpy-screen-language-basics-frames/feed/ 1
Incompatible Size & Position Properties in Ren’Py https://feniksdev.com/incompatible-size-position-properties-in-renpy/?utm_source=rss&utm_medium=rss&utm_campaign=incompatible-size-position-properties-in-renpy https://feniksdev.com/incompatible-size-position-properties-in-renpy/#respond Mon, 11 Mar 2024 01:00:23 +0000 https://feniksdev.com/?p=2912 Quickly reference incompatible size and position properties in Ren'Py, and the reasons behind each incompatibility.

The post Incompatible Size & Position Properties in Ren’Py appeared first on Feniks Development.

]]>
There are a few places here and there where I’ve mentioned that some size and position properties are incompatible with other properties in Ren’Py, often because one property is shorthand for setting multiple properties.

Here is a table of all those conflicts. You don’t have to read it through from start to finish, but I hope it will be good reference material if you’re ever wondering why Ren’Py complains about using certain properties together, or if you’re having trouble positioning and sizing screen elements.

Difficulty Level: Beginner

This article is intended largely as a reference after you’ve read Ren’Py Position Properties – Pos and Anchor, Ren’Py Position Properties – align, xycenter, and offset, Ren’Py Size Properties – xysize, maximum, and minimum, and Ren’Py Size Properties – fill, fit, size_group, and area.

Property NameConflictsExplanation
posxpos, ypos

align, xalign, yalign

xcenter, ycenter, xycenter

area
pos sets both xpos and ypos, so it can’t be used alongside them.

The next set of conflicting properties all set one or both of the pos properties along with the anchor property. They can’t both set the same thing, so only one of these properties will be what the actual pos value is.

area sets pos and xysize, so again, it can’t be used in combination with a pos property.
xpospos

xalign, xcenter, xycenter

area
pos sets xpos and ypos in one property, so it can’t be used alongside them

The next set of conflicting properties all set the xpos properties along with the anchor property. They can’t both set the same thing, so only one of these properties will be what the actual xpos value is.

area sets pos and xysize, so again, it can’t be used in combination with a pos property.
ypospos

yalign

ycenter, xycenter

area
pos sets xpos and ypos in one property, so it can’t be used alongside them

The next set of conflicting properties all set the ypos properties along with the anchor property. They can’t both set the same thing, so only one of these properties will be what the actual ypos value is.

area sets pos and xysize, so again, it can’t be used in combination with a pos property.
anchorxanchor, yanchor

align, xalign, yalign,

xcenter, ycenter, xycenter
anchor sets both xanchor and yanchor, so you can’t also use the individual xanchor and yanchor properties

For the other properties, it’s the same as pos; the conflicting xycenter and align properties set the anchor point of the image along with setting its pos, so only one of them can be actively applied at a time.
xanchoranchor

align, xalign

xcenter, xycenter
anchor sets both xanchor and yanchor, so you can’t also use the individual xanchor and property

For the other properties, it’s the same as xpos; the conflicting properties set the anchor point of the image along with setting its pos, so only one of them can be actively applied at a time.
yanchoranchor

align, yalign

ycenter, xycenter
anchor sets both xanchor and yanchor, so you can’t also use the individual yanchor and property

For the other properties, it’s the same as ypos; the conflicting properties set the anchor point of the image along with setting its pos, so only one of them can be actively applied at a time.
alignxalign, yalign

pos, xpos, ypos

anchor, xanchor, yanchor

xycenter, xcenter, ycenter

area
align lets you set a value for xalign and yalign, so it is incompatible with those individual properties.align sets both pos and anchor to the same value, so it’s incompatible with both those properties.

xycenter sets the anchor and pos of the element, and since align also sets the anchor and pos, they are incompatible.

area sets the pos of the element, and since align also sets the pos, they are incompatible.
xalignalign

pos, xpos

anchor, xanchor

xycenter, xcenter

area
align lets you set a value for xalign and yalign, so it is incompatible with those individual properties.xalign sets both xpos and xanchor to the same value, so it’s incompatible with both those properties, and pos and anchor which also set the xpos and xanchor values respectively.

xycenter sets the anchor and pos of the element, and since xalign also sets the xanchor and xpos, they are incompatible.

area sets the pos of the element, and since xalign also sets the xpos, they are incompatible.
yalignalign

pos, ypos, anchor, yanchor

xycenter, ycenter
area
align lets you set a value for xalign and yalign, so it is incompatible with those individual properties.yalign sets both ypos and yanchor to the same value, so it’s incompatible with both those properties, and pos and anchor which also set the ypos and yanchor values respectively.

xycenter sets the anchor and pos of the element, and since yalign also sets the yanchor and ypos, they are incompatible.

area sets the pos of the element, and since xalign also sets the xpos, they are incompatible.
xycenterxcenter, ycenter

pos, xpos, ypos

anchor, xanchor, yanchor

align, xalign, yalign

area
xycenter allows you to set both xcenter and ycenter in one property, so it can’t be used in combination with the individual properties.

xycenter sets both pos to the given value and the anchor to the center, so it’s incompatible with both those properties.

align sets the anchor and pos of the element, and since xycenter also sets the anchor and pos, they are incompatible.

area sets the pos of the element, and since xycenter also sets the pos, they are incompatible.
xcenterxycenter

pos, xpos

anchor, xanchor

align, xalign

area
xycenter allows you to set both xcenter and ycenter in one property, so it can’t be used in combination with the individual properties.

xcenter sets both xpos to the given value and xanchor to 0.5, so it’s incompatible with both those properties.

align sets the anchor and pos of the element, and since xcenter also sets the xanchor and xpos, they are incompatible.

area sets the pos of the element, and since xcenter also sets the xpos, they are incompatible.
ycenterxycenter

pos, ypos

anchor, yanchor

align, yalign

area
xycenter allows you to set both xcenter and ycenter in one property, so it can’t be used in combination with the individual properties.

ycenter sets both ypos to the given value and yanchor to 0.5, so it’s incompatible with both those properties.

align sets the anchor and pos of the element, and since ycenter also sets the yanchor and ypos, they are incompatible.

area sets the pos of the element, and since ycenter also sets the xpos, they are incompatible.
offset, xoffset, yoffsetN/Aoffset properties are compatible with all other mentioned properties.
xysizexsize, ysize

maximum, xmaximum, ymaximum

minimum, xminimum, yminimum

area

xfit, yfit, xfill, yfill, fit_first
xysize sets both the xsize and the ysize properties, so it is incompatible with them.

Setting xysize sets the maximum and minimum properties to the same value. Ergo, you can’t use both of them at the same time.

As with pos, area sets both pos and xysize, so you can’t use xysize with area.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
xsizexysize

maximum, xmaximum

minimum, xminimum

area

xfit, xfill, fit_first
xysize sets both the xsize and the ysize properties, so it is incompatible with them.

Setting xsize sets the xmaximum and xminimum properties to the same value. Ergo, you can’t use both of them at the same time.
As with pos, area sets both pos and xysize, so you can’t use xsize with area.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
ysizexysize

maximum, ymaximum

minimum, yminimum

area

yfit, yfill, fit_first
xysize sets both the xsize and the ysize properties, so it is incompatible with them.

Setting ysize sets the ymaximum and yminimum properties to the same value. Ergo, you can’t use both of them at the same time.
As with pos, area sets both pos and xysize, so you can’t use ysize with area.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
minimumxminimum, yminimum

xysize, xsize, ysize

area

xfit, yfit, xfill, yfill, fit_first
minimum sets both xminimum and yminimum, so it’s incompatible with those properties.

xysize sets minimum and maximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
xminimumminimum

xysize, xsize

area

xfit, xfill, fit_first
minimum sets both xminimum and yminimum, so it’s incompatible with those properties.

xysize and xsize set xminimum and xmaximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
yminimumminimum

xysize, ysize

area

yfit, yfill, fit_first
minimum sets both xminimum and yminimum, so it’s incompatible with those properties.

xysize and ysize set yminimum and ymaximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
maximumxmaximum, ymaximum

xysize, xsize, ysize

area

xfit, yfit, xfill, yfill, fit_first
maximum sets both xmaximum and ymaximum, so it’s incompatible with those properties.

xysize sets minimum and maximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
xmaximummaximum

xysize, xsize

area

xfit, xfill, fit_first
maximum sets both xmaximum and ymaximum, so it’s incompatible with those properties.

xysize sets xminimum and xmaximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
ymaximummaximum

xysize, ysize

area

yfit, yfill, fit_first
maximum sets both xmaximum and ymaximum, so it’s incompatible with those properties.

xysize sets yminimum and ymaximum to the same value, so they are incompatible.

area sets xysize, which as mentioned, sets both the minimum and maximum to that value.

fit and fill properties are incompatible with anything that sets size, as they also set the size.
xfillxmaximum, xminimum, xsize

xfit, fit_first

area
xfill sets the width of the displayable, so it’s incompatible with any other properties attempting to set the width.
yfillymaximum, yminimum, ysize

yfit, fit_first

area
yfill sets the height of the displayable, so it’s incompatible with any other properties attempting to set the height.
xfitxmaximum, xminimum, xsize

xfill, fit_first

area
xfit sets the width of the displayable, so it’s incompatible with any other properties attempting to set the width.
yfitymaximum, yminimum, ysize

yfill, fit_first

area
yfit sets the height of the displayable, so it’s incompatible with any other properties attempting to set the height.
areapos, xpos, ypos

align, xalign, yalign

xycenter, xcenter, ycenter

xysize, xsize, ysize

minimum, xminimum, yminimum

maximum, xmaximum, ymaximum

xfill, yfill, xfit, yfit, fit_first
area sets both xysize and pos, so it’s incompatible with any other properties that set those values or control size.
size_groupxfit, yfit, xfill, yfill, fit_firstsize_group is incompatible with the fit and fill properties because those properties can only apply to fixed containers, and size_group can only apply to frames, windows, labels, buttons, and textbuttons.

Note that while you can apply size properties like xminimum and ysize to a container with a size_group, it may not always affect the other containers with the same size_group in intuitive ways. Make sure you’re saving and testing your changes often!

Conclusion

And that’s all for this article! I hope this is a useful source to return to if you’re having trouble with error messages like “properties xalign and xpos conflict with each other” and aren’t sure why. In the next tutorial, we’ll start looking at frame, which lets you add backgrounds and padding to your containers. Find it here: Ren’Py Screen Language Basics – Frames

You might also be interested in my tool release from last week, which makes it simple to add blinking to character sprites! Find it on itch.io:

The post Incompatible Size & Position Properties in Ren’Py appeared first on Feniks Development.

]]>
https://feniksdev.com/incompatible-size-position-properties-in-renpy/feed/ 0
How to Resize Images in Ren’Py https://feniksdev.com/how-to-resize-images-in-renpy/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-resize-images-in-renpy https://feniksdev.com/how-to-resize-images-in-renpy/#respond Sun, 25 Feb 2024 15:18:33 +0000 https://feniksdev.com/?p=2874 In this tutorial for beginners, learn how to use the fit property to resize images while maintaining their aspect ratio.

The post How to Resize Images in Ren’Py appeared first on Feniks Development.

]]>
Have you ever wanted to resize images in Ren’Py while maintaining the aspect ratio? Luckily, there is a built-in property to help you do that, called fit. In this article, we’ll go over how to use fit and how different fit values work.

Difficulty Level: Beginner

If you haven’t already, you should be sure to read up on Ren’Py Size Properties – xysize, maximum, and minimum at minimum, and I recommend reading the follow-up Ren’Py Size Properties – fill, fit, size_group, and area as well.

While xfit and yfit are used for fixed containers, there is a special fit property which can be used with Transform or as part of ATL. Since ATL deals with displayables, not “elements”, I’ll be using “displayable” to refer to the thing you’re applying fit to.

fit causes the displayable to be sized in a particular way, typically preserving its original dimensions in some form. It is used in combination with xsize and ysize.

By default, fit begins as None. It can be one of several values, which I’ll explain in turn. For each of these examples, we will discuss an 800x400 pixel displayable called “feniks.png” which will have the fit property applied in various ways.

“contain”

If you use fit "contain", the displayable will try to be as large as possible without exceeding any provided dimensions (provided via xsize and/or ysize). It maintains the original aspect ratio of the displayable.

So, for our 800x400 pixel “feniks.png” displayable, if we applied the fit property with "contain", here’s what would happen:

add "feniks.png":
    fit "contain"
    xsize 400

Result: the width can be at most 400px, aka half the original width, so the height is scaled down to match. The resulting displayable is 400×200.

add "feniks.png":
    fit "contain"
    ysize 600

Result: The height can be at most 600px, aka 1.5x the current height. The width is scaled up to match. The resulting displayable is 1200×600.

add "feniks.png":
    fit "contain"
    xysize (900, 900)

Result: the displayable can be at most 900px in either dimension. The displayable is wider than it is tall, so the width will be 900px. The height will then be 450px to maintain the aspect ratio. The resulting displayable is 900×450.

“cover”

If you use fit "cover", the displayable will try to be as small as possible, while matching or exceeding any provided dimensions. It maintains the original aspect ratio.

add "feniks.png":
    fit "cover"
    xsize 400

Result: The width is at minimum 400px. To maintain the aspect ratio, the height is set to 800px. The resulting displayable is 400×800 (same as with “contain”).

add "feniks.png":
    fit "cover"
    ysize 600

Result: The height is at minimum 600px. To maintain the aspect ratio, the width is set to 1200. The resulting displayable is 1200×600 (again, same as “contain”).

add "feniks.png":
    fit "cover"
    xysize (900, 900)

This is where contain and cover differ. The minimum dimension we have is 900. Since “cover” will exceed or match the provided dimensions, we take the smallest dimension – the height – and set that as the baseline. So the height will be 900px, and thus the width will be 1800px to match. This results in a displayable that is 1800×900.

“fill” or None

If you have fit None or fit "fill", then the xysize are taken as exact dimensions which should squash/stretch the displayable to match. It does NOT maintain the original aspect ratio. This is the default behaviour. So:

add "feniks.png":
    fit "fill"
    xsize 400

Result: The xsize is set to 400. The height remains 400px. The resulting displayable is 400×400 (making it squished narrower than the original).

add "feniks.png":
    fit "fill"
    ysize 600

Result: The ysize is set to 600. The width remains 800px. The resulting displayable is 800×600 (making it stretched taller than the original).

add "feniks.png":
    fit "fill"
    xysize (900, 900)

Result: The width and height are set to 900px. The resulting displayable is 900×900, making it slightly wider and over twice as tall.

“scale-down”

“scale-down” is nearly the same as “contain”, but it will never increase the size of the displayable. So, for our example displayable, the width will never be more than 800px, and the height will never be more than 400px.

add "feniks.png":
    fit "scale-down"
    xsize 400

Result: The width is smaller than 400px, so it is selected and the height scaled to match the aspect ratio. The resulting displayable is 800×400 (same as “contain”).

add "feniks.png":
    fit "scale-down"
    ysize 600

Result: The provided height of 600px is larger than 400px. The displayable can’t exceed 800×400, so it remains 800×400.

add "feniks.png":
    fit "scale-down"
    xysize (900, 900)

Result: The width can’t exceed 800 and the height can’t exceed 400, so the resulting displayable remains 800×400.

“scale-up”

“scale-up” is nearly the same as “cover”, but it will never decrease the size of the displayable. So, for our example displayable the width will never be less than 800px, and the height will never be less than 400px.

add "feniks.png":
    fit "scale-up"
    xsize 400

Result: The width is smaller than 800px, but the displayable can’t get any smaller. The resulting displayable is 800×400.

add "feniks.png":
    fit "scale-up"
    ysize 600

Result: The height is larger than 400px. The height is set to 600 and the width scaled to match. The resulting displayable is 1200×600 (same as “cover”).

add "feniks.png":
    fit "scale-up"
    xysize (900, 900)

Result: The height is selected as the largest dimension and the width scaled to match. The resulting displayable is 1800×900 (same as “cover”).

Summary

ValueDescription
“contain”As large as possible, without exceeding any dimensions. Maintains aspect ratio.
“cover”As small as possible, without exceeding any dimensions. Maintains aspect ratio.
None or “fill”Exactly the size of the provided dimensions. Does not maintain aspect ratio.
“scale-down”As large as possible, while not exceeding any dimensions including the original dimensions of the displayable. Maintains aspect ratio.
“scale-up”As small as possible, while not being smaller than the original displayable. Maintains aspect ratio.

The ATL fit property is incredibly powerful for sizing and scaling displayables without losing their original dimensions. For example, maybe your character sprites are all slightly different sizes, and you want to be able to display them in a Character Bio screen. You can use fit "contain" with xysize to ensure all sprites fit inside the area designated to display their character sprites without worrying about what exact size they need to be scaled down to.

Next Steps

The next tutorial in this series will be on incompatible size and position properties from the last several tutorials so you can tell them apart, and then after that we will begin to look at other containers like frame.

You can also check out my recent tool releases on itch.io, which add Gradients and Circular Bars to Ren’Py!

And lastly, I also put out an article format of my recent talk at Visual;Conference. You can find it here: Character Customization 101 Talk

The post How to Resize Images in Ren’Py appeared first on Feniks Development.

]]>
https://feniksdev.com/how-to-resize-images-in-renpy/feed/ 0
Character Customization 101 Talk https://feniksdev.com/character-customization-101-talk/?utm_source=rss&utm_medium=rss&utm_campaign=character-customization-101-talk https://feniksdev.com/character-customization-101-talk/#comments Sat, 24 Feb 2024 16:50:17 +0000 https://feniksdev.com/?p=2733 Learn about tips and techniques to help you add character customization to your visual novel without ballooning your budget.

The post Character Customization 101 Talk appeared first on Feniks Development.

]]>
As some of you may already know, I recently gave a talk last month in the 2024 Visual;Conference. You can find it below on YouTube!

I’ve also included my notes and the code excerpts from my code in this article below. Enjoy!

Introduction

Let’s look at some of the pros and cons to character customization so you can decide whether it’s right for your story.

  • Replayability – Character customization can vastly improve replayability. Adding variations according to the aspects the player customized, whether that be their background, personality, gender, or appearance, provides an incentive to return to the same story to explore the lore, characters, narrative, and more.
  • Inclusivity – Character customization may also provide players with an avenue to express themselves, especially if they like to imagine themselves as part of the story. Allowing players to create a character who resembles them or who acts like they would can be very meaningful, especially when mainstream representation can be a rarity.
  • Unique experience – Have you ever watched someone play a game and thought, I wouldn’t have made that choice. I wouldn’t have said that. I liked the first hairstyle better than the one they picked. This can be a major motivator for a player to want to play a game themselves after learning about it. Character customization can be a primary source of this appeal.
  • Engagement – Customizable main characters are an excellent avenue for audience engagement. Since facts about the character are not set in stone, players are free to invent additional details to fill in the gaps. Many players will create their own original characters to roleplay as in the story and will create fanworks featuring them. More fanworks about your game expands its reach and can bring more people in to check it out, especially due to the previous point about customization offering unique experiences for each player.
  • Audience appeal – Finally, including a customizable main character can open up your game to new audiences. Players often want to play games with main characters similar to themselves – customization means a much wider range of people can do so.

However, there are also drawbacks to including character customization.

  • Extra work – For one, it’s additional work no matter which way you slice it. It’s work to include UI screens and choice menus for choosing pronouns, personality traits, appearance traits, and other information, and it’s work to write additional script variations to take the player’s customization choices into account.
  • If you’re including voiceover with a player with custom traits, it could require multiple takes on similar lines to account for different pronouns, backgrounds, appearances, and more.
  • If the main character’s appearance is customizable, it’ll involve more assets (or at minimum more involved asset creation) than if they had a set appearance, or you may make other sacrifices such as minimizing the main character’s appearance in CG art.

We’ll go over these drawbacks and what sorts of things you can do to mitigate them later on.

The second major reason to not include character customization is that it just isn’t for all stories.  Character customization typically works best for stories with an obvious point of view character whom the player has some control over via choices or other gameplay mechanics. If the cast is more of an ensemble, the narrative is linear, or the main character has a plot-essential backstory and pre-determined personality, character customization may not be right for your story.

Pronouns

First, we’ll start with pronouns. Pronouns are one of the simplest ways to add character customization to a game without adding a ton of work for yourself.

First and foremost: use variables! Please don’t include a bunch of conditional statements like if pronouns == "she/her": etc. In many engines this will mean each line counts as a different one when skipping only previously seen text, so players will be halted all over the place when replaying if they choose different pronouns, which is a poor experience. Variables will keep your script readable and make your repeat players happy.

These next tips will focus primarily on English pronouns and conjugations, but the idea can be applied to other languages as well.

I highly suggest you set up the variables to be named they/them/their/theirs/themselves, since unlike he/him and she/her, all they/them pronouns have unique forms. This also results in very readable code, since for the most part it simply reads like a regular sentence.

You will also need to set up verbs as variables, since pronouns like they/them conjugate differently from most other pronouns. I suggest variables for is/are, has/have, was/were, and then special “s” and “es” variables that will take care of the majority of regular verb conjugations.

This can be extended to other languages; for example, you may declare variables for changing adjectives and verbs into their feminine forms in French.

class Pronoun():
    def __init__(self, neutral, masculine, feminine):
        self.neutral = neutral
        self.masculine = masculine
        self.feminine = feminine
    def __str__(self):
        global pronouns
        if pronouns == "she/her":
            return self.feminine
        elif pronouns == "he/him":
            return self.masculine
        elif pronouns == "they/them":
            return self.neutral
        else:
            return self.neutral
define they = Pronoun("they", "he", "she")
e "I think [they] left already."

In some engines, there will be a way to directly evaluate a value in dialogue at the time it is shown, rather than needing to declare a variable with a set value beforehand. In Ren’Py, we can use a class’s special string method (__str__) to achieve this. Without this, you would need to update perhaps dozens of variables when the player changes their pronouns, but with this, pronouns and related terms become a special kind of constant. It gets evaluated at runtime instead of when it’s declared, and in this example, it returns the right word based on one variable called “pronouns”. The on-screen example accounts for three different pronoun sets.

You might notice this logic is very similar to the earlier conditions that I mentioned were bad – the difference is when the logic happens. In a string method, it happens right when the value is shown in dialogue, and not when you declare it. This means you can easily update the player’s pronouns by switching just one variable, and all associated words and terms and verbs will simply work. You can even let the player update their pronouns in the middle of the game, or randomize which pronouns are used on every line, which allows for enormous flexibility on the player’s side with zero additional effort on your end.

define attractive = Pronoun("attractive", "handsome", "beautiful")
e "You're very [attractive]."

You can use this approach for words you might want to change based on the player’s pronouns also, with very few changes. This will allow you to declare honorifics, adjectives, terms of endearment, and more which are based around the player’s pronouns.

Where possible, consider also allowing the player to choose terms for themselves independently of which pronouns they choose. You can extend the class approach to facilitate this; I recommend giving each term an ID and storing the player’s preferences in a dictionary with the ID as the key. You can then use the class’s string method to search for the correct word in the dictionary, with the default term as a fallback.

If this all seems confusing, you’re in luck! I did the heavy lifting for Ren’Py users and made all this backend stuff already, which you can find for free on my itch.io. It includes the approach described here, with additional flexibility to allow players to enter their own pronouns and input custom terms. Once the backend is all set up, writing dialogue for a player whose pronouns and terms change every line looks identical to a player who only uses one set of pronouns.

Now as with any feature, there are some potential pitfalls with allowing the player to choose their pronouns and terms. One of these is voice acting. If you’re including full voice-acting in your game, at some point you are going to want to refer to the player character in the third person without recording dozens of variations.

There are some solutions to this. If you allow the player to change the main character’s name, consider keeping a consistent surname or nickname that the other characters can use to refer to them aloud. You can also use their job position or position of power to refer to them, or use relationship-based terms for characters close to the main character.

Some languages may have other natural-sounding ways around this also, such as the plethora of Japanese terms for referring to people older than you or in a position of authority. Or, if your game is not set on Earth, consider inventing gender-neutral ways of referring to the main character.

If your narration is unvoiced, you can clarify the topic of conversation in a line of narration, where you can substitute in variables, rather than in voiced dialogue. You might also assign some other character the point-of-view so their internal thoughts do not need to be voiced.

If you absolutely must use pronouns to refer to the player, I suggest just recording the line once using they/them.

Other than that, using variables in unvoiced dialogue is trivial! Create enough variables to write your script comfortably (maybe even using my tool), and you should be fine.

Appearance

Next, you can also offer character customization in the form of appearance. Allowing the player to change the appearance of the main character can take several forms. Let’s first go over a couple of common variants.

First off is text-only appearance customization. The main character has no set appearance, and they are never shown in-game. If the game has CGs, they are focused around other characters. On the upside, this approach does not require any additional art assets, and it allows for complete player freedom in deciding what the main character looks like. On the downside, the main character can’t be shown interacting with the other characters in CG art. This is most notable for games which involve romance, where interacting with the love interests is often a key part of CG art.

Our Life: Beginnings & Always

Next, a customizable character might only have some small part of them shown in-game. This is most often the main character’s hands, but is sometimes the back of their head, over their shoulder, an extreme closeup of their eyes, etc. Usually the game is still assumed to be from first-person perspective.

This approach allows for some limited interaction between the main character and other characters, but still leaves most of the details of their appearance up to the player’s imagination. Depending on which part of the main character you choose to show, you may need additional art assets to reflect the appearance the player has chosen for the main character, such as colour variations or different body types.

Some clever approaches to this kind of customization involve arranging for conditions which put sensible limits on the player’s appearance. For example, you may make your main character a knight, in which case, whenever they are shown you can show them in a suit of armour. You might show the main character’s hand cupping a love interest’s face while wearing gloves because they are outside during the winter to avoid needing skin colour or hand size variations.

Use harsh lighting or framing to avoid showing too much detail. Write your story so the main character is wearing a specific outfit or hat so their silhouette is distinct but leaves the details up to the player. Consider also splitting up CGs comic-book style with closeups on some key areas, such as hands, to show interaction, but keep the main focus of the art on the other characters.

Royal Order | Gilded Shadows

Third, the main character may have one or more pre-set appearances. If there is more than one pre-set appearance, it’s often connected to choosing a gender. I suggest not tying appearance to gender or pronouns, like you see for Royal Order in the top right; divorcing these associations adds a trivial amount of work (perhaps having three variables to track appearance, gender, and pronouns separately) and gives the player more ways to customize their experience.

Imperial Grace

Of the upsides, this approach allows the most interaction between the main character and other characters since they have a fully pre-determined appearance which can be shown in-game as a sprite or in CGs. Offering some colour or body type options still allows for some level of player control over the main character’s appearance.

However, this approach requires more assets if you would like to take advantage of showing the main character in-game, as you’ll need to account for all the customization options. It also places several restrictions on the main character’s appearance, resulting in something more akin to variants on the same character rather granting each player the ability to create unique characters.

One way to mitigate the amount of work this requires is through colorizing images in-engine. If your engine of choice supports shaders, such as Ren’Py or Unity, you can apply them to the same art asset to recolour it in several different varieties. For Ren’Py users, I’ve created a shader and accompanying tool to customize the look of recoloured images, which is available for free on my itch.io. Colorizing images in-engine will involve more planning beforehand to set up your base images properly, but the results can very closely match hand-coloured assets.

See my tutorials on the colorize shader: How to Format Images for Colorizing in Ren’Py and How to Colorize Images in Ren’Py

A Date with Death | Lord of the Wings

Finally, we have the upper echelons of appearance customization, which usually involves mixing and matching assets such as hair, clothing, and body types, as well as choosing colours for many options. These sorts of characters are almost always omitted from CG art due to the work that would be required to account for all the variations, though they may be shown in-game as profile pictures, side sprites, or even in-game sprites.

This approach can allow for an enormous amount of player expression and control over the main character, and the creation process can often be enjoyable in its own right. For players who are less artistically oriented, screenshotting the character they created to share on social media is simpler than drawing a character from scratch and can also help bring traffic to your game.

However, as you might expect, this approach also requires some of the most art assets and work to implement. It also often leads to tradeoffs in other art areas, specifically CGs. Often this kind of approach falls back on the techniques mentioned earlier, where the customizable main character is omitted from CGs or only a small part of them is shown.

If you’d like to use this approach, there are some ways you can make it easier on yourself. Many of these techniques are similar to what Shino talked about for asset reduction, so be sure to go through that talk for more ideas if you missed it (Link to talk). As mentioned earlier, you can colorize images in-engine to reduce the number of handmade colour variations you need.

You can also adopt a simplified art style for either the main character or potentially the whole game. Many art styles, especially exaggerated and cartoonish ones or pixel art can simplify several distinct facial features and body types into one stylized version. You might also choose an art style with fewer colours, such as a black-and-white comic style or a restricted palette to emulate an older game system.

If you’re showing the custom character in-game alongside other character art, consider placing some restrictions on what can be customized, such as shoulder width, clothing, or only allowing some facial features such as the nose and eye shape to be customized so you can reserve the eyebrows and lips for expressions.

Remember you can plan your game around this limitation – maybe your highly customizable character is in a sci-fi setting with mech suits, or they transform into a werewolf with a set appearance you can include in key art, or they have a special head-to-toe spy outfit for their job.

So, how do you actually include customizable appearances in your game?

default hair_color = "brown"
default hair_style = "long1"
default skin_color = "medium1"
default eye_color = "hazel"

layeredimage mc:

    always "mc/mc_base_[skin_color].webp"
    always "mc/mc_hair_back_[hair_color]_[hair_style].png"

    group eyes:
        attribute normal_eyes default "mc/mc_normal_eyes_[eye_color].png"
        attribute happy_eyes default "mc/mc_happy_eyes_[eye_color].png"
        attribute sad_eyes default "mc/mc_sad_eyes_[eye_color].png"

    always "mc/mc_hair_front_[hair_color]_[hair_style].png"

This looks for:

mc/mc_base_medium1.webp
mc/mc_hair_back_brown_long1.png
mc/mc_normal_eyes_hazel.png
mc/mc_happy_eyes_hazel.png
mc/mc_sad_eyes_hazel.png
mc/mc_hair_front_brown_long1.png

In Ren’Py you should make use of what are called dynamic images. These are image paths which include a variable. Like with variable substitution in dialogue, the variable’s value is substituted in to find the final image. You can then create buttons on the customization screen to change this variable to values corresponding to the file names. The layered image declared above can be shown with a regular show mc as with any other characters, and if you’ve set up your file names properly, it will pick out the right files.

init python:
    def colorize_image(st, at, img, variable, lookup):
        ## Fetch the variable's value
        value = getattr(store, variable)
        ## Use that to look up the RGBColorize object
        rgb = lookup[value]
        ## Apply the colorize to the image
        return At(img, rgb.transform), None

default skin_color = "medium1"
define mc_skin_colors = dict(
    light1=RGBColorize(...),
    light2=RGBColorize(...),
    medium1=RGBColorize(...),
    medium2=RGBColorize(...),
    dark1=RGBColorize(...),
    dark2=RGBColorize(...),
)
image mc_base = DynamicDisplayable(colorize_image, img="mc/mc_base.webp", 
    variable="skin_color", lookup=mc_skin_colors)

layeredimage mc:
    
    always "mc_base"

If you’re using shaders to colorize your images, for example if you’re using my Better Colorize shader, then you can use Dynamic Displayables instead. This works on a similar principle where you have a variable with colour names, but instead of corresponding to file paths they’ll correspond to a dictionary key to look up the colorize information (for my tool I format it into something called an RGBColorize object). Once you’ve got that set up, such as in the example, changing the variable skin_color to another value will automatically change it within the mc layered image. As with the previous method, you can show the layered image normally with a simple show mc command, and it will appear with the colour information you set up.

Background & Personality

Touchstarved

Finally, another avenue of character customization is allowing the player to customize the main character’s background and/or personality. Customizing the background may be as minor as deciding the reason why the main character is attending a magical academy or as major as choosing between multiple different backstories that will affect how the main character is treated within the game narrative and what choices are available to them.

To keep background customization from involving too much extra work, carefully consider where you will begin the story. If the player has the option to choose a background as a farmer or a blacksmith, don’t start the story too early in their life. This could mean you need two entirely different introductions on the farm and in the forge. Start your story where these backgrounds would converge, such as on the day a dragon razed the village and they had to pick up a sword to fight.

You can keep players feeling like their choice of background matters by including occasional unique choice options or lines of dialogue which reference their background. Perhaps the blacksmith has additional dialogue options when discussing weapons, but the farmer knows more about which wild plants are safe to eat while camping.

Sigh of the Abyss

For customizable personality, be sure to consider personality traits which will still get the main character where they need to be by the end of the story. A reluctant hero is just as much of a hero as an enthusiastic one, so long as there are enough motivators to get both to the finish line. Even if the main character needs to make a certain decision for the story to work, you can still provide players with differing reasons why they make that decision – perhaps they join a group of mercenaries out of a desire for safety in numbers, or maybe they join because they want to prove their strength in battle.

default player_background = "farmer"
label chapter3_scene2():

    menu:
        "I'll buy the sword (100G).":
            $ player_gold -= 100
            $ player_has_sword = True
            "Shopkeeper" "Thank you very much."
        "The balance is flawed near the guard, and the blade needs sharpening. It's clearly not worth more than 70G." if player_background == "blacksmith":
            $ player_gold -= 70
            $ player_has_sword = True
            "Shopkeeper" "You drive a hard bargain, but I can see you're someone of good taste."
        "Not this time, unfortunately.":
            "Shopkeeper" "That's too bad. Come back if you change your mind."

In most cases, selectable backgrounds and customizable personalities can be done entirely through regular variables. You can add conditions in dialogue or for choices to check for the player’s background. In the example on the right, only a player who chose a background as a blacksmith has the option to get a cheaper price on the sword. Be sure to watch Alaric’s talk (link to talk) for more on choice design!

UI Considerations

Our Life: Now & Forever

Besides coding in these customization systems themselves, you will also need to consider how to present the customization to players in-game.

For pronouns, you should set up the common pronouns like she/her, he/him, and they/them for the player to simply select. If you allow custom pronouns, include example sentences with the word substituted in so players can see how it will be used (since most people don’t know the grammatical terms for it). Importantly, do not start the sentence with the target word, because this will often lead players to incorrectly capitalize it. Typically you should respect the player’s capitalization for these words rather than forcing it to lowercase, since for example, some players may enter their name to be used in lieu of pronouns.

Don’t forget to let players choose which conjugation is used for verbs as well, which you can see in the bottom of the screenshot with the title “Which sounds more natural?”

For advanced customization options, such as custom terms, you should aim for a multi-tiered approach to common use cases. For example, a common use case is a player who would like to use he/him pronouns and have all masculine terms like Mr., son, boy, and brother. To make this simpler for this player, when he selects he/him pronouns without diving into the extra options for custom or multiple pronouns, we simply set the default for all of the terms to be masculine as well so this player doesn’t have to go into the advanced options or change any of the setup for this page.

The next tier is for players who want some pronoun customization and have general preferences for which terms are used. For example, there may be a player who uses she/her and they/them, but only wants characters to use neutral terms like child and sibling. For these players, there is a fast option to use only neutral-leaning terms.

Finally, for players who want the maximum amount of control over their experience, there are the advanced options. Even here, there are shortcut options to mass-set many terms to one of the three pre-defined types. Many players do not need to see or use these options, but for players who want the most control over how the script refers to them, this screen allows them to do so.

This same principle can be applied to appearance options – even if you’re using a shader to colorize images, be sure to provide some pre-set colours that players can mix and match without bring up a complicated colour picker. This will allow players who simply want to pick a few harmonious colours to advance through your customization screens faster to get to the story, but still offers advanced freedom for those who seek it out.

Lastly, wherever possible, spread out customization options into later parts of the script. If the player’s background doesn’t come up until three scenes into the story, especially if you have a lot of other customization options, consider allowing them to choose their background at that point in the story. Allow the player’s personality to be shaped through choices instead of at the start. This becomes more important the more options you have to avoid delaying the player too long before they even know what the experience will be like.

Conclusion

There are so many ways to add character customization to your game that don’t have to be giant time and energy sinks while still offering a ton of benefits. I hope this presentation has given you ideas on where to start.

And that’s all for the talk! I answer a few questions at the end in the video if that interests you also – there are human-made subtitles and a transcript available on YouTube if you need them.

The post Character Customization 101 Talk appeared first on Feniks Development.

]]>
https://feniksdev.com/character-customization-101-talk/feed/ 3
Ren’Py Size Properties – fill, fit, size_group, and area https://feniksdev.com/renpy-size-properties-fill-fit-size_group-and-area/?utm_source=rss&utm_medium=rss&utm_campaign=renpy-size-properties-fill-fit-size_group-and-area https://feniksdev.com/renpy-size-properties-fill-fit-size_group-and-area/#respond Wed, 24 Jan 2024 01:25:16 +0000 https://feniksdev.com/?p=2698 In this beginner tutorial on Ren'Py screen language, learn about the fill, fit, size_group, and area properties, what they do, and when to use them.

The post Ren’Py Size Properties – fill, fit, size_group, and area appeared first on Feniks Development.

]]>
The next four size properties we’ll look at in this tutorial are less common, but may still come in handy as you learn to create screens with Ren’Py.

Difficulty Level: Beginner

If you haven’t already, you should start with the first part of this tutorial on xysize, minimum, and maximum, here: Ren’Py Size Properties – xysize, maximum, and minimum. Or, if you’re new to my screen language series or screen language in general, be sure to start at Getting Started with Ren’Py Screen Language.

Once again, in these tutorials I use element to refer to the thing you’re trying to change the size of, and container to refer to the space the element is positioned inside. The container is by default the entire screen. element can be anything; an image, a bit of text, a button, etc.

fill

Fill does not have a “combined” property, only xfill and yfill. Generally, it is only for containers. xfill and yfill won’t work directly on images added using add or text added using text.

This property causes the container to expand to fill up as much space as it can in the specified direction (so, horizontally for xfill and vertically for yfill). Instead of providing a number, xfill and yfill are set to False by default, which means they only take up as much space as they need to render on the screen properly (or that they will be whatever size they are provided using xysize, minimum, or maximum as covered in the previous tutorial). If this property is set to True instead, the container will take up as much space as possible in the given direction.

In the default GUI, for example, xfill True is used for the dialogue window to specify that it should be the width of the whole screen. This property can also be useful to create things such as separators between elements in containers like hbox and vbox, or any kind of banner or background.

Note that if you put a container with xfill True inside a container without a set size, for example, the container will effectively also be xfill True, taking up all the possible width of the container’s container. Since in this case the container doesn’t have a size, it tries to be only as large as it needs to be to contain everything inside it. But one of the things inside it is trying to be as big as it can, which causes the container to also take up as much space as it can. To prevent this, you can give the container a set size or at least a maximum size so the element is restricted from getting too large.

Here is an example of a vbox which has a solid line of colour as a separator between the text elements:

vbox:
    xsize 300
    text "Health: [hp]"
    fixed:
        xfill True ysize 5
        add "#ffffff" # white
    text "Mana: [mp]"

Note that add "#ffffff", aka the add statement with a hex colour code, adds a rectangle of the provided colour, much like you saw earlier with the red and orange messenger bubbles. This rectangle will be as large as possible, so ysize 5 is needed to restrict its height. xfill True on the fixed container ensures the container (and thus the white line) will be as wide as possible.

The line will be 300×5 pixels, since the vbox has xsize 300, so the maximum width the line can be is 300 pixels. But if you change the width of the vbox, you won’t have to update the widths of all the different separator lines, since they’ll just scale with the size of the vbox.

Note that if the xsize 300 wasn’t there for the vbox, the line would take up the whole screen:

fit

Only fixed containers have the fit properties xfit and yfit. Like with fill, there is no combined “fit” property, just the two separate ones. However there’s actually a special fit property for Transforms which does something mostly unrelated to xfit and yfit as described below, which I will cover in a shorter tutorial after this one.

fit properties are False by default. If set to True, xfit True will cause the fixed to shrink such that it is only as wide as it needs to be to contain the rightmost element. Similarly, yfit True will size the fixed such that it’s only as tall as it needs to be to fit the bottommost element.

Above, the first image has xfit True yfit True and the second image does not. By adding a background, you can see that this affects the size of the final image.

fixed:
    xfit True yfit True
    add "feniks"
    add "music_note" xpos 850 ypos 30

Note that moving the music note over further (xpos 1050) will continue to expand the container so it fits:

This is mostly useful if you’ve got a bunch of elements inside the fixed of various sizes and need to ensure it is only as large as it needs to be to contain them all. Typically, if you position an element inside of a fixed with properties like xpos or ypos, the fixed will become as large as possible (often the size of the screen). Using xfit and yfit will prevent this and make sure the container doesn’t expand to be unnecessarily large.

fit_first

Fixed containers also have a special property, fit_first. This is one of four values: False, the default, means it isn’t used, so the size of the fixed will depend on any other sizing properties applied to it, like xfill/yfill, xysize etc., as well as the size of all the elements inside the fixed. fit_first True means the fixed will fit the first element inside it (aka it’ll be the size of the element at the top of the fixed’s list of elements, code-wise). So if you’ve got two things in your fixed like so:

fixed:
    fit_first True
    add "feniks.png"
    text "Hello! This line will wrap when it reaches the end of the image."

then the fixed will be the size of "feniks.png", however large that image is. The size of text "Hello!" will not matter at all, because it is the second element in the fixed, and the fixed is only concerned with the size of the first element.

fit_first also takes the string “width” or “height”, as in fit_first "width" or fit_first "height". This is similar to setting fit_first to True, except it will only fit the width or the height of the first item (and not the full size of it). So, if we used fit_first "height" for the above example, if text "Hello!" is wider than the image “zoran.png”, then the fixed will be at least as wide as it needs to be to accommodate the width of the text, and it will be exactly as tall as the height of “feniks.png” since fit_first "height" will fit the height of the first element, which is “feniks.png”. We can see this if we put the fixed inside a vbox:

A bird is at the left of the screen. Above it, the text "Hello! This line will not wrap when it reaches the end of the image" is written. It does not wrap. Below the bird, the text reads "This is below the image."
vbox:
    fixed:
        fit_first "height"
        add "feniks.png"
        text "Hello! This line will not wrap when it reaches the end of the image."
    text "This is below the image."

Note: Recall that just because a container is a certain size internally doesn’t mean that it will crop or resize its contents to reflect that size. If you put another image inside a container with fit_first True which was larger than the first image that the container is fitting, it will simply cover the original image and display outside the bounds of the container.

size_group

size_group is fairly uncommon, but crucially it is used in the default Ren’Py GUI, so it’s important to understand what it does so you can modify it (or understand when to remove it).

size_group takes a string, which will be the name of a group (you make up this string to be the group name). Any containers which have a size_group with this same string name will belong to the same size group. All containers with the same size_group will be the exact same size.

Notably, size_group can only be used with windows, frames, labels, buttons, and textbuttons. For the purposes of demonstration using the tools you’ve learned so far, textbutton is pretty similar to text.

In practice, it looks like this:

There are two lines of text. The top reads "Hello!" and the bottom reads "It's time to learn about size groups!"
vbox:
    textbutton "Hello!" size_group "mygroup"
    textbutton "It's time to learn about size groups!" size_group "mygroup"

For the above example, both lines of text have the same size_group. By default, that means that they will both be the size of the largest item, so in this case, text "Hello!" will have a bunch of extra space so it’s the same length as text "It's time to learn about size groups!". You can see this the most clearly if it’s turned into an hbox:

There are two lines of text. The left one reads "Hello!" and the right reads "It's time to learn about size groups!". "It's time to learn about size groups!" is also shown semi-transparently below the text "Hello!" to show that it is the same width making up the gap between the two text lines.
hbox:
    text "Hello!" size_group "mygroup"
    text "It's time to learn about size groups!" size_group "mygroup"

If we put the “It’s time to learn about size groups!” text below “Hello!”, you can see that all the space to the right is because of how long that text is.

In the default Ren’Py GUI, this is used in a very similar manner to make all the navigation buttons (by default on the left side of menu screens like “Start”, “Load”, “Preferences” etc) the same width. This can give a more unified look to the buttons, particularly if you give them a background. With a size group, the backgrounds will all be the same size for a more cohesive look as opposed to each button having a background be only as wide as it needs to be to contain the text.

Of course, if you change the navigation menu to be in an hbox instead of a vbox, this size_group can be undesirable, because it makes all the buttons the same width and can cause the spacing to look uneven between each button (since something like “Start” will have more spacing to its left and right than a word like “Preferences” if all elements are the same size). Thus in such cases you may decide to get rid of the size_group property, unless you are using a particular button style, like seen below.

Four buttons are lined up left-to-right reading "Start", "load", "preferences", and "quit". In the top row, the buttons are all the same width but do not have a background. In the second row, the buttons are all the same size with a background. In the third row, the buttons are all different sizes with a background.

In the above image, the first two rows have the same size_group across all buttons. You can see the uneven spacing in the first row looks more natural in the second. In the third row, the buttons do not have a size_group and thus have different widths.

area

The last size property we’ll look at is actually a combination of size and position, hence why it’s last. area takes four numbers inside parentheses, separated by commas. Those numbers correspond to (xpos, ypos, xsize, ysize) or more colloquially, (x, y, width, height) of the displayable. Since it is setting both the pos and xysize of the element, it is incompatible with those properties or any properties that set them, like align or xycenter. Unlike with the properties seen above, almost any element can use area, not just containers.

area is most useful for something like the mousearea element, where it can be used to quickly specify a location for the mousearea to apply to. This (x, y, width, height) format is also used in other places around Ren’Py – hotspots are also set up as (x, y, width, height), as are crop areas.

In general this is one of the less common position/size properties, and it’s usually more common to set the pos and xysize individually. In-game, it usually looks like the following:

An orange square sits positioned offset from the top left corner and reads "Hello, world!"
fixed:
    area (100, 100, 500, 400)
    add "#ff8335" # Orange
    text "Hello, world!"

This declares a fixed that is 500×400 and positioned with its top-left corner at the position (100, 100).

Summary

  • xfill and yfill are used to tell a container to take up as much space horizontally and vertically as possible, respectively. It only applies to screen elements that change their size, so, containers like fixedhbox, etc.
  • fixed containers have the xfit and yfit properties. This property tells the container to try to only be as wide/tall as it needs to be to contain its children.
  • fixed containers also have the fit_first property. This can be set to fit_first "width" to make the fixed as wide as its first child, fit_first "height" to make it as tall as its first child, or fit_first True to make it the size of its first child.
  • size_group can be given to windows, frames, labels, buttons, and textbuttons. It takes the name of a group as a string. Any screen elements which have the same size_group will be the same size.
  • area takes four numbers which correspond to the (xpos, ypos, xsize, ysize) of the element.

Next Steps

The next article in this series will be on the ATL fit property, which is unrelated to the xfit and yfit explained here, but can help you make images which conform to particular dimensions (especially useful for things like profile screens or gallery thumbnails, where you may be resizing existing art). After that, the next screen language topic is on frame!

Next week I’ll be foregoing tool/tutorial releases as I’m giving a talk at Visual;Conference on the 27th! Find more information on how to attend at https://vnconf.com/. After the event, the talk will be added to YouTube and I’ll link it here for you to watch.

The post Ren’Py Size Properties – fill, fit, size_group, and area appeared first on Feniks Development.

]]>
https://feniksdev.com/renpy-size-properties-fill-fit-size_group-and-area/feed/ 0
Ren’Py Size Properties – xysize, maximum, and minimum https://feniksdev.com/renpy-size-properties-xysize-maximum-and-minimum/?utm_source=rss&utm_medium=rss&utm_campaign=renpy-size-properties-xysize-maximum-and-minimum https://feniksdev.com/renpy-size-properties-xysize-maximum-and-minimum/#respond Tue, 09 Jan 2024 01:09:20 +0000 https://feniksdev.com/?p=2670 In this beginner tutorial on Ren'Py screen language, learn about the size properties xysize, minimum, and maximum, and when to use them.

The post Ren’Py Size Properties – xysize, maximum, and minimum appeared first on Feniks Development.

]]>
Since we’ve covered position properties (Ren’Py Position Properties – Pos and Anchor), next we’ll look at all the various size properties you can use when constructing screens for Ren’Py.

As a refresher, in these tutorials I use element to refer to the thing you’re trying to change the size of, and container to refer to the space the element is located inside. The container is by default the entire screen. element can be anything; an image, a bit of text, a button, etc.

Difficulty Level: Beginner

This tutorial builds on information learned in earlier tutorials on screen language, particularly position properties Ren’Py Position Properties – Pos and Anchor and Ren’Py Position Properties – align, xycenter, and offset. You can start with Getting Started with Ren’Py Screen Language if you’re new to screen language in general.

Floats, Integers, and None

Like with positions, floats and integers are treated differently when provided to size properties. Integers are considered exact pixel values. So, a width of 300 is treated as exactly 300 pixels wide, regardless of the size of the element’s container. Meanwhile, a width of 0.5 is treated as a percentage of the width of its container, specifically, 50%. So if the container is 300 pixels wide, a width of 0.5 for the element will cause it to be 150 pixels wide.

For the most part, you will probably be using exact pixel values (integers) to specify the size of your element. The main exception to this is using None. Most size properties begin as None since they conflict with one another, kind of like how align and pos conflict with one another because align sets pos and anchor.

Uniquely, however, if all size properties are None, an element usually tries to only be as large as it needs to be. So, text will only be as large as required to display every letter, for example. If you’re trying to get a container to be only as large as its contents (for example, a messenger bubble containing text), make sure xsize and ysize are set to None, whether that means explicitly setting it or not changing the default value from None (maximum and minimum will come for free in such a case, as you’ll see below).

A Note on Size Properties

When you set the size of a container, with a few exceptions, Ren’Py does not treat this as a hard boundary when it comes to displaying the final container to the screen – specifically, elements inside the container will not be cropped to the size of the container. The exception to this is a special container called a viewport (which does crop its children) and resizable elements, such as the Solid, Frame, and Tile displayables (which will resize themselves to fit the available space only). Many of these will be covered in later tutorials.

Internally, elements will treat their sizes appropriately for layout purposes, but if you put a giant image (e.g. 700×700) inside a small container (e.g. 200×200), the image will still display at 700×700 and will not be cropped off at the 200×200 mark, or resized down. You would need to specify the size of the 700×700 image (not just the container) to resize it, or crop the image to the right size yourself (which you can do in-engine with crop).

xysize

xysize, as the name suggests, sets the size of an element directly. As with position properties, size properties also come in two varieties – xsize for controlling the width of the element, and ysize for controlling the height of the element. xysize will let you supply two values, the xsize and the ysize respectively e.g. xysize (100, 200) for an element that’s 100 pixels wide and 200 pixels tall.

This property is used for setting the exact width and/or height of an element. You don’t necessarily need both an xsize and a ysize; sometimes, for example, you might want something to be exactly 500 pixels wide, but it can be as tall as it needs to be based on its contents. In that case, you can just set the xsize and leave the ysize out or set it to ysize None.

Below are some examples. I’ve included a coloured orange square in the images to visibly show the boundaries of the container.

# fixed with set width and height
fixed:
    xysize (350, 350)
    text "Hello, world!" align (1.0, 1.0)

# Vbox with a set width, but it's as tall
# as it needs to be to hold the text
vbox:
    xsize 500
    text "Hello!"
    text "World!" xalign 1.0

# Hbox with a set height, but it's as wide
# as it needs to be to hold the text
hbox:
    ysize 120
    text "Helloooo"
    text "Wooooorld" yalign 1.0

Note: You may also occasionally see the size property used in code snippets you find online. The size property is an older version of the xysize property with more restrictions on where it can be used. size and xysize largely work in the same way, but you should always use xysize instead of size in modern Ren’Py (in all situations).

Minimum

In some cases, you want to let the element adjust its size based on its contents, but there should also be some minimum size so that it doesn’t cause your UI to look funky. For example, say you want to make a popup to inform the player of various things. Even if the message is very short like “Done!”, you probably want it to be some minimum size so that it doesn’t look weirdly squished just because there isn’t enough space to take up the whole popup area. But, if the popup message is very long, the popup area should have a maximum so the text wraps around instead of overflowing.

In this case, you should use minimum (and/or its x/y variants, xminimum and xmaximum). Note that this property is incompatible with xysize; if you set xysize, you are giving it an explicit size, so the minimum size becomes irrelevant. The xysize is the size, for all possible contents.

An example for the popup (once again with an orange square showing where the boundaries of the container are):

screen popup(popup_message):
    fixed:
        xsize 600 yminimum 400 align (0.5, 0.5)
        text "[popup_message]" align (0.5, 0.5)

This makes a fixed container which is at least 600×400 pixels large. If the popup_message variable has a lot of text in it, then it’s possible the height will become larger than 400, but it will never be smaller than that. The xsize is fixed at 600, so it won’t get smaller or larger width-wise.

Maximum

On the opposite end, we have maximum, which restricts the maximum size an element can be, but not how small it can be. As with minimum, it is incompatible with xysize, since xysize directly sets the size, whereas minimum and maximum only place restrictions on it. In fact, xysize actually just sets both minimum and maximum to the same thing, much like what we saw with align setting both pos and anchor to the same value back in Ren’Py Position Properties – align, xycenter, and offset. If the minimum and the maximum width of an element are the same, then that’s just the width of the element.

Maximum is often useful in combination with minimum to restrict the area a given element can take up while still letting it expand or shrink a little to fit the contents. One use case of maximum is if you are making a phone messaging system and you want to display dialogue in little speech bubbles. There is a maximum width for the bubble – it obviously shouldn’t go past the width of the screen/should start wrapping the text before it reaches that point – but it shouldn’t have a set size either, because you want the bubble to only be as wide and tall as it needs to be to fit the text inside it.

Now, I haven’t introduced frames quite yet (it’s coming soon in this series!), but you can think of it like a fixed with a background. Here, I’ll give it a hexadecimal (hex) colour code for the background property, which gives the frame a rectangular background in that particular colour.

Thus, the messaging system may have a container that looks a bit like this:

vbox:
    frame:
        background "#f93c3e" # Red
        xmaximum 500
        text "A bubble message."
    frame:
        background "#ff8335" # Orange
        xmaximum 500
        text "This is a much longer text bubble message, which will wrap."
Two blocks of text are on top of each other. The first is red and reads "A bubble message". The second is orange and reads "This is a much longer text bubble message, which will wrap". The latter bubble has three lines of text instead of one long one.

xmaximum 500 will cause the text to start wrapping if it becomes longer than 500 pixels wide on one line. Since the frame was not given a more specific size – recall that by default all size properties are None, so the frame doesn’t have a minimum or a set size, just an xmaximum – it will shrink to fit the text as best it can.

Summary

  • All size properties start as None, so the element typically is only as big as it needs to be to display on the screen properly.
  • Integers are exact pixel sizes, and floats are percent sizes relative to the element’s container
  • xysize/xsize/ysize will set the size of the element to exactly the provided value. It’s the same as setting minimum and maximum to the same value.
  • minimum/xminimum/yminimum will set the minimum size of the element to the provided value, but provides no restrictions on how large the element can get in a particular direction
  • maximum/xmaximum/ymaximum will set the maximum size the element can grow to be in a particular direction, but provides no restrictions on how small the element is

Next Steps

To keep any one tutorial from being too large, I’m splitting up explanations of the various size properties into multiple tutorials. This is the first part. In the next part, we’ll look at fillfitsize_group, and area. Find it here: Ren’Py Size Properties – fill, fit, size_group, and area.

You might also consider checking out my latest tool on itch.io, an Extended Music Room for Ren’Py!

The post Ren’Py Size Properties – xysize, maximum, and minimum appeared first on Feniks Development.

]]>
https://feniksdev.com/renpy-size-properties-xysize-maximum-and-minimum/feed/ 0
Ren’Py Position Properties – align, xycenter, and offset https://feniksdev.com/renpy-position-properties-align-xycenter-and-offset/?utm_source=rss&utm_medium=rss&utm_campaign=renpy-position-properties-align-xycenter-and-offset https://feniksdev.com/renpy-position-properties-align-xycenter-and-offset/#comments Sun, 17 Dec 2023 22:35:41 +0000 https://feniksdev.com/?p=2630 In this beginner tutorial on Ren'Py screen language, learn about the position properties align, anchor, and offset.

The post Ren’Py Position Properties – align, xycenter, and offset appeared first on Feniks Development.

]]>
Welcome back to part II of the position properties series. Learn how to use the combination properties align and xycenter to quickly position elements on the screen, and how to use offset for animations and adjustments.

Difficulty Level: Beginner

This tutorial builds off of concepts covered in earlier tutorials on screen language, particularly the previous part on pos and anchor. If you haven’t read part I, I highly recommend you do so here: Ren’Py Position Properties – Pos and Anchor. The position properties covered in this tutorial are combinations of the properties introduced in that tutorial, so if you don’t already understand those, you’ll have a harder time understanding this content.

As a quick review, I will be using element to refer to the thing you’re trying to position and container to refer to the space where you’re positioning the element (which may just be the screen as a whole).

Align

Now with knowledge of pos and anchor under your belt, you’re ready to understand align. align combines anchor and pos. It sets both of them to the same value. This is really excellent shorthand for several examples you saw before – namely, the following have align equivalents:

# Top left corner of the element against the top left
# corner of the container (default position for screen elements).
pos (0.0, 0.0) anchor (0.0, 0.0)
# SAME AS
align (0.0, 0.0)

# Top right corner of the element against the top right
# corner of the container.
pos (1.0, 0.0) anchor (1.0, 0.0)
# SAME AS
align (1.0, 0.0)

# Bottom left corner of the element against the bottom 
# left corner of the container ("left" sprite position).
pos (0.0, 1.0) anchor (0.0, 1.0)
# SAME AS
align (0.0, 1.0)

# Bottom right corner of the element against the bottom 
# right corner of the container ("right" sprite position).
pos (1.0, 1.0) anchor (1.0, 1.0)
# SAME AS
align (1.0, 1.0)

# Center of the element in the center of the container
pos (0.5, 0.5) anchor (0.5, 0.5)
# SAME AS
align (0.5, 0.5)

# The default sprite position, center
pos (0.5, 1.0) anchor (0.5, 1.0)
# SAME AS
align (0.5, 1.0)

Common Problems and Misconceptions

You’re using align wrong

And now, here is my most important tip, which I am putting right up front and center because it is an extremely common coding mistake in Ren’Py:

If you are using values other than 0.00.5, and 1.0 with align, you should almost certainly be using a different position property and NOT align.

But why? align is used all over in the default Ren’Py styles. You’ll also see it used with other values (incorrectly in my opinion) in lots of code examples all over various Ren’Py tutorials and games.

align is NOT well-suited to positioning things that are not centered or aligned to the edge of their container. Again, you’ll note that in all the examples in the last section, align is equivalent because both anchor and pos have the same value. That’s all align does – it sets the anchor and pos to the same thing. It will conflict with these properties if you use them together, in fact.

In the case that you’re centering something or lining it up against the edges (aka aligning it to the edges), align works excellently since you usually want those two values to be the same, so it makes for useful shorthand. That said, it does not extend well outside of these situations.

Doesn’t it move it to ___?

Take, for example, a common misconception: xalign 0.25 will move a sprite 25% of the way across the screen. Let’s assume this means it will put the center of the sprite 25% across the screen, as that’s likely what you wanted.

Remember, xalign 0.25 is equivalent to xpos 0.25 xanchor 0.25. So let’s do some math to see where that ends up. I’ll use very easy numbers to simplify the math as much as possible: Our container is the screen, which is 1000×1000 pixels. Our sprite is 100×100 pixels.

So first, xanchor 0.25 means that this sprite’s x-anchor point is 25% of the way right of the left edge of the sprite. So the x-anchor point is 25 pixels to the right relative to the top-left corner of the sprite.

Next, xpos 0.25 means that the anchor point is put 25% of the way across the container. So that point on our 1000×1000 screen is 250 pixels from the screen’s left edge.

All good so far, right? But when we put the anchor point 250 pixels across the screen, the left edge of the sprite is actually at (250-25) aka 225 and the center of the sprite is at (250-25+50) aka 275. 225 of 1000 means that the left edge of the sprite is 22.5% across the screen, and the center of the sprite is 27.5% across the screen. Neither of which is the 25% we were hoping for! Additionally, if I have sprites of different sizes – say, my sprite was (200, 200) instead of (100, 100) – it would end up somewhere entirely different (in fact, a 200×200 sprite at xalign 0.25 would end up with its center 30% across the screen instead of 25%. The math is (1000*0.25)-(0.25*200)+(200/2)=250-50+100=300).

Again, remember – align is setting BOTH anchor and pos to the same thing. It’s pretty rare that you want to have an anchor point 25% across the image when the image is also moving 25% across the screen. More likely, you wanted to move the center of the image 25% across the screen, or possibly the left or right edges 25% across the screen, neither of which you’ll get with align. One of the biggest problems with using align with values other than 0.00.5, and 1.0 is that it can result in elements of different sizes ending up in vastly different locations as well, since the final position is relative both to the size of the element as well as the size of the container. When you’re trying to center the image, or line up its edges with the edges of the container, this is not an issue, but in most other cases, it is.

Why isn’t my sprite moving?

In short: your sprite isn’t moving because it has the same dimensions as your screen (e.g. your sprite image is 1920×1080 and your game is 1920×1080) and you’re trying to use align to move it.

The longer version:

Ren’Py’s default positions, leftcenter, and right, use xalign 0.0xalign 0.5, and xalign 1.0 respectively. This means that it moves the left edge of the image against the left edge of the screen, the center of the image to the center of the screen, and the right edge of the image against the right edge of the screen, respectively.

Of course, if the image is the same size as the screen, that means the left edge of the image is at the left edge of the screen. The right edge of the image is already at the right edge of the screen. So no align values will do anything to move the image, because it is already aligned to every possible value by virtue of being the same size as the screen.

How do you fix this?

A sprite with little transparent space around them.

The best answer is usually to trim the transparent space off of your character sprite so it isn’t the same width as the screen anymore. If, for whatever reason, your sprite must be the width of the screen (maybe they’ve got big wings or some fancy magic SFX layers), then you should use yalign 1.0 in combination with xanchor 0.5 xpos 200 where 200 can be a float like 0.25 (to put the center 25% across the screen) or a pixel value like 200 to put the center that many pixels across the screen. You may also use xcenter, covered below. Do not use xalign to move the sprite.

For images that aren’t sprites but are the size of their container, you’ll generally want to use some combination of pos and sometimes anchor if you need the anchor to be somewhere like the center of the image.

What if I want relative screen positions, like 25% across the screen?

It’s also a common misconception that only align takes float values that act as a percentage of the container area, but if you’ve seen the previous tutorial, you’ll know that isn’t true; all position properties can take floats to act as percentages or integers to act as exact pixel values.

What will happen if the player views my game at a different size?

Occasionally some people will end up under the misconception that align is the only property which will properly position screen elements for different screen sizes. This is Falseevery positional property in Ren’Py accounts for the virtual screen size when you move it around, regardless of whether you’re using integers (e.g. xpos 400) or floats (e.g. xanchor 0.5).

As mentioned, align is a combination of anchor and pos. It doesn’t have any special properties to deal with resizing the game window – every positional property will correctly scale to suit the size the game is currently being displayed at.

xycenter

The next property is similar to align in that it combines anchor and pos, but in the case of xycenter (and its single-axis equivalents xcenter and ycenter), they set anchor to 0.5 and pos to whatever value you provide.

So, for example, the following are equivalent:

# Horizontally centered 25% across the screen
xanchor 0.5 xpos 0.25
# SAME AS
xcenter 0.25

# Vertical center of the element is 400 pixels down the screen
yanchor 0.5 ypos 100
# SAME AS
ycenter 100

This is an excellent property to use for creating new positions for sprites outside of the default leftright, and center. It moves the center of the sprite to the position you provide, which is much more predictable and consistent across image sizes than using align would be. So, for example, here are two new positions you can declare:

# Center the sprite 25% across the screen
transform midleft:
    yalign 1.0
    xcenter 0.25
# Center the sprite 75% across the screen
transform midright:
    yalign 1.0
    xcenter 0.75

You can use these positions in-game via show ashwin at midleft or show zoran at midright.

An image with two silhouettes, one centered 25% across the screen and one centered 75% across the screen.

Offset

The last major tool in your positional properties toolset is offset. Like other positional properties, it has variants xoffset and yoffset for x-axis and y-axis positioning respectively, or offset to set them both in one property. Offsets are particularly helpful if you’re nudging an image slightly out of position, especially if you’re using align.

Like with all positional properties, floats act as a percentage of the container, and integers are exact pixel values. Moreso with offsets than with anything else, it’s also important to note that negative numbers move the element left/up and positive numbers move it right/down relative to its starting position.

For example, say you wanted to position some text at the bottom right corner of the screen, but 10 pixels away from the exact edge of the screen:

The text "To be continued" is in the bottom right corner of the screen
text "To be continued":
    align (1.0, 1.0) # Bottom right corner
    offset (-10, -10) # 10 pixels to the left & up

align (1.0, 1.0) will align the text in the bottom right corner, and offset (-10, -10) will move it 10 pixels to the left and 10 pixels up. pos wouldn’t work here because align (1.0, 1.0) is equivalent to anchor (1.0, 1.0) pos (1.0, 1.0), so trying to set pos would mean that only one of the provided positions would “win”. It would also be annoying to calculate something like (width_of_screen - 10) just to get an xpos for the text, so offset lets you retain the original position calculation (bottom right corner) and just, well, offset it a little.

If you aren’t setting an explicit position for an element (for example, if you’re using a container format like vbox to position elements relative to other elements) and still need to nudge it from its starting position, it’s generally better to use pos to move it rather than offset, since offset is best used to make tweaks to an existing position. It’s particularly helpful for ATL transforms, since offset will just tweak the existing position so you don’t have to know where the image is located on-screen to make it do things like gently float up and down.

Summary

  • align sets both anchor and pos to the same value.
    • This makes it great for aligning elements to the left, right, top, bottom, or center of their container.
    • It’s generally a bad idea to use it with values other than 0.00.5, and 1.0 because of how it takes into account the sizes of both the element and its container to get a final position. Most of the time, just pos or xycenter would be a better choice.
  • xycenter sets the anchor to the center of the image (0.5) and the pos to the value you provide. This has the effect of centering the image at the given coordinates. This makes it a great choice for the x position for custom sprite positions.
  • offset is used to adjust the element a little away from its current position. It’s best used only if you have pos set for some other reason (e.g. you’re using align to line the element up with its container edge) and just need to nudge it a few pixels away. Otherwise, use pos to make position adjustments so you can save offset for things like animations.

Next Steps

Hopefully now you can make more educated decisions on which positional properties you should use to move elements around when you’re coding your UI screens and sprite positions for your game. When in doubt, pos is usually a good choice to move things around. If you’d like a more interactive way of understanding the concepts taught in both part I and this part, may be interested in this tool from bobcgames.

You might also be interested in my tool from last week, released over on itch.io! It’s a shader that programmatically adds outlines to images in code rather than needing to save individual images with outlines.

The post Ren’Py Position Properties – align, xycenter, and offset appeared first on Feniks Development.

]]>
https://feniksdev.com/renpy-position-properties-align-xycenter-and-offset/feed/ 2
Ren’Py Position Properties – Pos and Anchor https://feniksdev.com/renpy-position-properties-pos-and-anchor/?utm_source=rss&utm_medium=rss&utm_campaign=renpy-position-properties-pos-and-anchor https://feniksdev.com/renpy-position-properties-pos-and-anchor/#comments Sun, 03 Dec 2023 22:28:49 +0000 https://feniksdev.com/?p=2547 In this beginner tutorial on Ren'Py screen language, learn about the position properties pos and anchor and how to combine them.

The post Ren’Py Position Properties – Pos and Anchor appeared first on Feniks Development.

]]>
Do you need to move a character sprite around to a new position? Relocate where a button is on-screen? What do pos and anchor and align and offset and all those properties mean, and how do you use them? This subset of tutorials on screen language will teach you about position properties used in Ren’Py screen language, and which one you should use in your game.

Difficulty Level: Beginner

This tutorial series does not expect any prior knowledge of Ren’Py screen language or coding. I recommend you start with Getting Started with Ren’Py Screen Language if you haven’t yet read it.

Vocabulary

If you’ve seen the previous tutorial in this series, Ren’Py Screen Language Basics – Basic Containers, these terms will be familiar. If you’re coming here to learn more about positioning sprites, you can read on. If you’re looking to learn more about screen language in general, consider starting with Getting Started with Ren’Py Screen Language.

I’m going to be using two main words to refer to moving things around: elements and containers.

An element is anything you’re moving around on the screen. It might be a button, a character sprite, an image, text, or a collection of any of those.

container is the space inside which you’re positioning the element. For a character sprite, the container is generally the entire screen. You can also manually specify various containers to group elements together – in the default Ren’Py UI, for example, the Start/Load/Preferences/Quit etc buttons are grouped into a container called a vbox (short for vertical box) so the buttons are stacked on top of each other from top to bottom. An element’s position is always relative both to the size of the element itself as well as its container (see Ren’Py Screen Language Basics – Basic Containers for more on that!).

What are position properties?

These are properties of screen language elements which move them around. Need a button in the middle of the screen? Need to move your sprite to a custom position? These are the properties you’ll use to move it there.

A “property” as it relates to screen language simply refers to information about the element that tells it how to look or how to behave. You can read more about it here.

Floats vs Integers

The most important thing to remember about position properties in Ren’Py is that they all treat floats and integers differently (see Simple Variable Types in Ren’Py if you aren’t sure what floats and integers are – in short, floats = decimal points, integers = whole numbers).

If a number has a decimal point in it anywhere (aka it is a float), it is treated as a percentage of its container (or, if it isn’t explicitly in a container, a percentage of the screen size). So 0.5 is treated like 50%.

Conversely, if it is an integer, it is treated as an exact pixel value. There is a special method you have to use if you want to use fractions of pixels as exact positions – otherwise, as soon as you add a decimal point, you’re dealing with percentages relative to the size of the container and/or the size of the element you’re moving.

Note: If you would like to position an element at an exact percent of a pixel, you can do so with absolute(10.5) where 10.5 is the pixel position. This is exceedingly uncommon and is mostly mentioned as a point of interest. It will not be touched on in this tutorial.

Starting Position

Before we get into the different positioning properties, know that Ren’Py considers the default for all position properties to be (0, 0), which corresponds to the top-left of the element you’re positioning. Coordinates come in (x, y) pairs. So, the position (0, 0) is the top-left corner of the screen if you’re positioning an element without a container.

An image labelling the corners of an image with their positions. The top left is (0, 0), the top right is (1920, 0), the bottom left is (0, 1080), and the bottom right is (1920, 1080).

Positive numbers will move the element to the right and down. So, something at a position of (200, 300) in the game will be 200 pixels from the left edge of the screen and 300 pixels from the top edge of the screen. Similarly, negative numbers move the element left and up relative to their starting position.

An image labelling the corners of an image with their positions. The top left is (0.0, 0.0), the top right is (1.0, 0.0), the bottom left is (0.0, 1.0), and the bottom right is (1.0, 1.0).

Position and Anchor

Position and anchor will make up the bulk of what you use to move elements around on the screen. It’s very important to understand how they work, because most of the other positioning properties act as some combination of these two properties.

Pos

First up is pos. Pos is short for position. It comes in two main flavours: xpos, for moving things left-to-right (along the x-axis), and ypos, for moving things top-to-bottom (along the y-axis). There is also a property that lets you set both xpos and ypos at the same time: pos. If you are using pos, you have to provide two numbers, separated by a comma. The first number is the xpos and the second is the ypos e.g. pos (200, 300) which positions the top-left corner of the image 200 pixels from the left and 300 pixels down from the top. pos (200, 300) is equivalent to xpos 200 ypos 300.

By default, the starting position for a screen element is the top-left corner of its container.

Anchor

Next is anchor. It also comes in two main varieties: xanchor, which moves the anchor on the x-axis (left-to-right), and yanchor which moves the anchor on the y-axis (top-to-bottom). anchor lets you set both at the same time, much like pos e.g. anchor (0, 0).

By default, the anchor for a screen element is its top-left corner (0, 0).

Understanding pos and anchor

While position seems pretty intuitive to understand – it’s just the position where the element is – what, exactly, is anchor?

Let’s think of it in terms of something you may be more familiar with. Instead of positioning an element on a screen, you are trying to pin a photo onto a cork board. You have three things: a cork board, a push pin, and a photograph. Let’s pretend that 1mm is equal to 1 pixel on a computer screen.

  • The cork board is the screen, or the container you’re trying to position the element inside.
  • The photograph is the element.
  • Where you put the pin on the photo is the anchor of the photograph.
  • Where you push the pin into on the cork board is the pos of the photograph.

By default in Ren’Py, the push pin always starts in the top left corner of the photo, so to speak. If you want the top-left corner of the photo 200mm from the left side of the cork board, you will put it at xpos 200. If you also want the top-left corner 300mm down from the top of the board, you will put it at ypos 300.

The top left corner of the image is positioned at a point labelled "200" from the left and "300" from the top of the bulletin board. The pin is in the top left corner.

What if you want the center of the photo at 200mm x 300mm?

This means you need to move where the pin is relative to the photo. The pin will stay at the point (200, 300) on the cork board – you just need to center the photo around that point as well. This means you need to change the anchor of the photo.

Except in some rare cases, an anchor point is usually one of three values: 0.00.5, or 1.0. Note that 1.0 is very different from 1 for positioning properties – see Floats vs Integers. This is because usually you want to position elements relative to one of their edges, or relative to the center.

To set the anchor point of the photo to the center of the photo, you can use anchor (0.5, 0.5) (aka xanchor 0.5 yanchor 0.5). Remember that floats mean percentages, so 0.5 means that the anchor is 50% of the way across the photo and 50% of the way down the photo, putting it at the exact center.

The center of an image is positioned 200 from the left edge and 300 from the top edge. There is a pin at the center at the position (200, 300) also.

Thus, a more concrete screen language example of positioning the center of the photo at position (200, 300):

screen cork_board():
    add "photo":
        pos (200, 300)
        anchor (0.5, 0.5)

As mentioned, anchor points are usually 0.00.5, or 1.0. With this in mind, here are the most common anchor properties and where they correspond to on the element. Remember that the red circle “pin” is the anchor point on the photo.

anchor (0.0, 0.0) # Top left corner
anchor (0.0, 1.0) # Bottom left corner
anchor (1.0, 0.0) # Top right corner
anchor (1.0, 1.0) # Bottom right corner
anchor (0.5, 0.5) # Exact center

anchor (0.0, 0.5) # Middle of the left edge
anchor (0.5, 0.0) # Middle of the top edge
anchor (1.0, 0.5) # Middle of the right edge
anchor (0.5, 1.0) # Middle of the bottom edge

Pos with floats

Unlike anchor, in which it’s very rare to use integer values to specify an anchor point (since it would be an exact pixel position, and it’s rare to need an anchor point somewhere in the nebulous middle of an image rather than at its exact center or along the edges), pos commonly uses both float and integer values. As mentioned, something like pos (450, 100) will put the anchor point of the image 450 pixels to the left of its starting position and 100 pixels down.

However, you can also use floats with pos to specify a position as a percentage of the size of the container it’s in. While it’s much more common to use 0.00.5, and 1.0 than other values, it’s not uncommon to see values like 0.25 or 0.75, for example, particularly in combination with an anchor point of 0.5 so you can do things like position the center of the image 25% of the way across the screen (aka xanchor 0.5 xpos 0.25).

Tip: If you find yourself using more than two decimal points for a positional property (anchor, pos, align, offset, etc), you should probably be using an exact pixel value instead. So, xpos 0.3452 is probably better expressed as something like xpos 663 (exact value will depend on the size of the container/size of the screen). It’s much, much easier to add 1 or 2 to an integer position rather than adding on several extra decimal places to try to nudge an image over a couple pixels.

Common pos and anchor combinations

Some common combinations for various positions include:

# Top left corner (aka the default position)
pos (0.0, 0.0) anchor (0.0, 0.0)

# Bottom edge of the element against the bottom of the container, centered
# (aka the default `center` position of character sprites)
pos (0.5, 1.0) anchor (0.5, 1.0)
# Left edge of the element against the left edge of the container,
# bottom edge of the element against the bottom of the container
# (aka the position `left` as in `show eileen at left`)
pos (0.0, 1.0) anchor (0.0, 1.0)
# Right edge of the element against the right edge of the container,
# bottom edge of the element against the bottom of the container
# (aka the position `right` as in `show eileen at right)
pos (1.0, 1.0) anchor (1.0, 1.0)

# Just offscreen to the left, where the right edge of the image is against
# the left edge of the container so it's just offscreen, aligned to the bottom
# (aka the position offscreenleft)
pos (0.0, 1.0) anchor (1.0, 1.0)
# Just offscreen to the right, where the left edge of the image is against
# the right edge of the container so it's just offscreen, aligned to the bottom
# (aka the position offscreenright)
pos (1.0, 1.0) anchor (0.0, 1.0)

# Precisely centered (aka the position truecenter)
pos (0.5, 0.5) anchor (0.5, 0.5)

Summary

And that covers the basics! pos and anchor will let you position an element just about anywhere you need it; they’re the fundamentals that make up the rest of the position properties you’ll see in part 2 aside from offsetpos is much more common than anchor; aside from centering elements or aligning them to the edges, you’ll generally use anchor a lot less than pos.

Next Steps

In the next part in the series we’ll go over the remaining position properties alignxycenter, and offset: Ren’Py Position Properties – align, xycenter, and offset. As per usual, check out my itch.io for tool releases also!

The post Ren’Py Position Properties – Pos and Anchor appeared first on Feniks Development.

]]>
https://feniksdev.com/renpy-position-properties-pos-and-anchor/feed/ 6
How to Format Images for Colorizing in Ren’Py https://feniksdev.com/how-to-format-images-for-colorizing-in-renpy/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-format-images-for-colorizing-in-renpy https://feniksdev.com/how-to-format-images-for-colorizing-in-renpy/#respond Sun, 26 Nov 2023 15:20:23 +0000 https://feniksdev.com/?p=2467 Learn how to format images for colorizing in Ren'Py, including layer splits and how to set up colors for RGB color sets.

The post How to Format Images for Colorizing in Ren’Py appeared first on Feniks Development.

]]>
If you would like to colorize images in your game, there are some best practices to separate image layers and set them up properly for colorizing. This tutorial will cover how to format existing or new images to get the best results for colorizing using Better Colorize for Ren’Py.

If you haven’t already, be sure to start with How to Colorize Images in Ren’Py for the basics on colorizing images using the Colorize Tool.

Difficulty level: Intermediate

The concepts covered in this tutorial largely pertain to image manipulation rather than code. You are expected to understand concepts such as image layers, blend modes, and clipping masks. These are only briefly covered during explanations.

Step 1: Layer splits

To start, wherever possible, layers that you wish to colorize should be separated from other, unrelated layers. For example, it is much simpler to have the hair as a separate layer which you can recolour, as opposed to it being attached to the body layer. Similarly, it is much easier to recolour skin when it is not attached to clothing layers. If you would not want the whole image to be recoloured with the same colours, it is often a good idea to split up those parts until it’s down to just one part to recolour.

Here are some examples of good layer splits:

This is a good layer split for recolouring the skin. The skin layer is separate from the clothing layer. You can recolour the skin layer separately without affecting the clothes, eyes, hair, etc., because they are separate from the skin you’re recolouring.

These are good layer splits for recolouring any of the images. On the far left, the skin layer doesn’t have any extra layers attached to it that we wouldn’t want to colour with skin colours. The hair being split up into front and back layers makes it simpler to display it behind the sprite regardless of the outfit or pose of the base skin layer. And lastly, the hair shading that goes between the skin and hair layers is also separated from the hair, so it can be shaded with the skin colours instead of the hair colours.

There are some combinations you may be able to work around, but which will require additional image editing. For example, if you have a “hair shadow” layer included with your hair layer, you will need to make use of one of the RGB colour sets at minimum so you don’t end up recolouring the (skin-toned) shadows with the hair colours.

Here you can see that some skin shadow layers are included directly on the same layer as the hair and clothing. This can be worked around if required, but it is generally better to have the shading on a separate layer so you can colour the hair separately from the skin shadow.

Special cases

There are also images which require specific setup for RGB channels if you aren’t going to split them up into separate layers, the most notable of which are mouth and eye images.

Typically, an eye layer is made up of the eye whites, eyelashes, iris, and optionally the eyelid or other skin shading. For recolouring, you will want the skin to be recoloured with the skin recolouring, the iris with the iris recolouring, and the eye whites and lashes to be constant colours. In order to achieve this, you must either 1) have all these parts on different layers to recolour separately, or 2) use the RGB colour sets to split up the different parts for recolouring.

Similarly, a mouth layer typically consists of the lips, which should use the lip recolouring, and may optionally include surrounding skin or shadows which should use the skin recolouring, and potentially tongue or teeth which should not be recoloured. These must also be either split up onto their own layers to recolour separately or use the RGB colour sets to split the image up into different parts for recolouring.

Other layers which may require either being split up or adjusted for the RGB colour sets:

  • Clothing layers with patterns, multiple colours, or accessories
  • Hands, particularly if you are recolouring the underside of the hand and supporting darker skin colours, as the palm will need to be recoloured separately. You may also consider recolouring the nails separately if you’d like to add nail polish.
  • Hair layers with ombre effects or dyed with roots (can be achieved with the RGB channels)

Layers which you may think need to be split up but will actually be fine for recolouring:

  • Art styles with line art – you can specify a colour and threshold for the line art like any other colour.
  • Art styles which use multiple colours for shading, e.g green hair which has both a dark green shadow and also purplish shadows – you may need to use the RGB channels to differentiate these in grayscale, but it is possible to shade with different colours so long as they can be told apart in the base image

Step 2: Colours

The first and easiest way to prepare an image layer for recolouring is to simply make it grayscale. You can do this in whichever image editor you like. If you are not using the RGB colour sets, then you will be able to colorize from a non-grayscale image as well, though you will need to choose your threshold numbers from that image and remember to take the greatest of the three RGB values (since they are likely to all be different if they are not a shade of gray). You can also technically use the shader to tweak existing colours, but that’s a topic for another day.

Using RGB channels to separate areas

There are two primary ways to use RGB channels for recolouring. In this tutorial, we’ll look at how you can use them to separate out parts of an image you want to use different colour sets for. Let’s look at how you can use them to split up eye layers for recolouring.

First, make sure you’ve separated the layer as much as you can. Here we have just the eyes – no eyebrows, mouth, etc. We will have a third channel that we don’t use by the end of this which could potentially be used for eye + brow combinations, but it’s easier if they’re separated.

Next, make a new layer on top of the eyes in an image editor such as Photoshop or GIMP. Pick a paintbrush tool and set your foreground colour to #0000FF (the colour is very important!). This is a dark blue. That means that anything we colour in using this colour can use the blue colour set when we colorize it using the colorize tool.

While this will be covered in more detail in the tutorial on blending with RGB colour sets, a quick rule of thumb is that the more blue the base image is, the more of the blue colour set it will use to colorize the image. The same goes for the other colour sets also. That’s why we use #0000FF – it’s the bluest colour we can possibly get, so it guarantees 100% of the blue colour set will be applied instead of the gray, red, or green ones.

Carefully paint over the iris only on your new layer. You may consider setting the layer opacity to 50-70% so you can see where you’re colouring. Remember to set the opacity back to 100% when you’re done. It should look like this:

Now hide the layer with the blue on it and make a new layer again above the base eyes. Set your paintbrush to the colour #FF0000.

Now we’re going to paint over the parts that have a skin colour to it, as pointed out on the image earlier in the tutorial. That includes the edges of the lashes on both sides. Don’t worry about going outside the lines of the lashes – we’ll mask it out later. Using your brush on the new layer, paint red over the left and right parts of the lashes that look more skin-coloured.

You can see I’m not terribly careful about going outside the lines into the transparent space, but I do make sure I’m not colouring onto the eye whites.

Note: If you have an eyebrow layer combined with the eyes, then you may want to use a green layer (#00FF00) to colour over the eyebrows. The remaining steps will apply to the red, blue, and green layers, in such a case.

Now make the blue layer visible again. While this next step is not strictly required for this example eye image, since the red and blue don’t overlap, it can be crucial for other images so we’ll be doing it regardless. Set both the blue layer and the red layer’s blend modes to lighten and make sure they’re at 100% opacity if you were adjusting that earlier. That will give you an image that looks something like this:

The importance of this step will be more apparent when using RGB channels to add additional colour depth to your colorized image, but here it makes sure the red and blue channels mix properly where they overlap. If we didn’t do this step and just skipped right to merging the layers together, the places where the colours overlap would only show the colour of the layer that’s on top.

Nex, merge the red and blue layers together (NOT the base image layer! Just the two new colour layers you made). You’ll get something that looks like this. If your colour layers overlapped, you may see magenta in those areas.

Now click on your original base eye image and make it grayscale if it isn’t already. In Photoshop, which I’m using, that’s Image -> Adjustments -> Desaturate. Clip the combined colour layer to the eye layer below – again, in Photoshop, this is done by right clicking the layer and hitting “Create Clipping Mask”.

Now your image should look like this:

Notice that the red outside of the eye boundaries has been clipped away so it fits the shape of the eyes again.

Finally, set the combined colour layer to “Multiply” blending.

This will allow you to see the darker parts of the original eye layer again, which we’ll use for our thresholds when we’re colorizing the image. And that’s all! Save this image and pop it into the colorize tool.

To colorize the image, you’ll use the same techniques to colour pick as demonstrated in How to Colorize Images in Ren’Py. However, you will be using the RGB channels also to colorize the iris and the skin blending separately from the eye whites and eyelashes.

Important: if your base grayscale image contains black (#000000), you must use the gray colour set to colorize that part of the image black again. None of the RGB colour sets can colorize black, since it has no red, green, or blue – its RGB is (0, 0, 0). However, the colour white (#FFFFFF) in a base grayscale image can be colorized with the colour channels if painted over, because using the multiply blending mode as specified above means that white will instead become the colour you painted over it with. See the white highlights of the eyes above in the RGB base image – they are #0000FF instead of #FFFFFF due to the blending, which means they’ll be colorized with the blue colour set.

Our colour sets will be used as follows:

  • Gray colour set: to colorize the eye whites and eye lashes. Notably this includes colorizing anything that is pure black also, so we need to include black as one of our swatches.
  • Blue colour set: to colorize the iris.
  • Red colour set: to colorize the skin shading
  • Optional – if you used the green colour set for eyebrows as described above, you would use the green colour set also. In this example, it is not used.

To start using the RGB colour sets, click the ✗ next to the word “Gray” above the swatch hexcode input box. This will change it to a checkmark and turn on the RGB colour sets.

Now click on “Gray▼” to see a dropdown for the different colour sets. Select the blue set. Initially, it will look just like the gray set.

You can now employ the techniques in How to Colorize Images in Ren’Py to select colours for just the iris. Remember that when you’re looking at the RGB values of the base image to figure out thresholds, you need the greatest of the first three values – for example, the threshold for the highlight in the iris is 255, because the colour is (0, 0, 255). I’ve achieved the results below using the colours #ffffff, #ffaae1, #f65b9a, #3c0833, #000000, and the thresholds 255, 212, 168, 34, 0. Notice in particular how the eye highlights, which were #0000FF in the base image, can be colorized back to white here.

Finally, select the Red colour set from the dropdown. This will colorize the skin parts, which are currently still gray. I’ve used the colours #ffffff, #fcb89b, #5d2c2f, #000000, and the thresholds 255, 203, 68, 0.

And that’s how you can use the RGB colour sets to separate areas on the same image for colorizing! As before, you can copy out the colours and thresholds the Copy Colors button in the top right corner to get an RGBColorize object which you can apply in-game as a transform.

You can use this same technique to separate out colour sets to colorize lips without recolouring the teeth also, to separate hair shading layers from the hair itself, or for anything you’d like to use several distinct colours with.

Summary

  • You should split up layers for colorizing by separating them based on if you would use the same set of colours to colorize them or not. For example, you wouldn’t use hair colours to colorize the shadow the hair casts on the skin of the character, so the hair shadow should be on a separate layer from the hair
  • Similarly, accessories and clothing items should generally be separated into different layers for colorizing (or for leaving as-is in their original colours)
  • Layers you want to recolour should typically be desaturated so they are grayscale
  • If you need to colorize some areas of an image separately but can’t/don’t have them on separate layers, such as the iris of an eye vs the whites, or teeth vs lip colour, you can use red, green, or blue to paint over the areas and “section them off”.
    • The colour layers should then be set to “lighten” blend mode and merged
    • The final combined layer should be clipped to the original base layer
    • Finally, turn the colour layer on “multiply” blend mode and save it

Conclusion

In the next tutorial, we’ll go over techniques using the RGB channels to add additional depth to colorized images. This is particularly helpful for recolouring skin layers and for art styles which use multiple colours to shade hair layers. It is also helpful for more complicated recolouring, such as hair with an ombre gradient or grown-in roots, eyes with central heterochromia, and more. Be sure to check out my itch.io for more Ren’Py tools!

The post How to Format Images for Colorizing in Ren’Py appeared first on Feniks Development.

]]>
https://feniksdev.com/how-to-format-images-for-colorizing-in-renpy/feed/ 0