--- Title: Positioning Things in Ren'py Date: 2021-05-17 Category: dev Status: published Tags: python, gamedev --- As is common in Python, the mechanical process of displaying something on screen in Ren'py is at once easy to execute and deceptively complicated to execute correctly. The Ren'py documentation does a fine job of defining the specifications of position properties, but intuitively understanding how to use those properties can still be hard because it doesn't include much in the way of examples or elaboration, so here are some of those. ## Your basic properties These names come directly from [atl transform](https://www.renpy.org/doc/html/atl.html#list-of-transform-properties) on the documentation. Note that these are generally parallel with the [style properties](https://www.renpy.org/doc/html/style_properties.html#position-style-properties) of the same names. ### `pos` - tuple of `(position, position)` The position of the `anchor` relative to the top-left corner of the containing area (usually the whole screen). `pos` is a coordinate point in the form of `(position, position)`. This will look like `(0, 0)` or `(600, 300)` or `(0.5, 1.0)` or even `(-20, 0.5)` The first coordinate is the x coordinate (horizontal) and the second is the y coordinate (vertical). You can set these individually with `xpos` and `ypos`, which each take a single position. Note that pos takes a `position` rather than a "number". `position`s in Ren'py come in two forms, and have two different uses. If a position is an integer (a number without a decimal point) it's a number of pixels. `(100, 200)` is 100 pixels from the left, 200 pixels from the top. If a position is a `float` (a number with a decimal point), it's treated as a percentage. `1.0` is 100%, `0.0` is 0%, `0.5` is 50%. In `pos`, this percentage is of the total screen, so `(0.5, 1.0)` is 50% from the left, 100% from the top. **Note that 1.0 and 1 are treated differently!** It's critical that you understand this. You can mix and match position types. Here's `(0.5, 20)`: ### `anchor` - tuple of `(position, position)` The position of the `anchor` relative to the displayable image itself. `anchor` is a coordinate point in the form of `(position, position)`, like `pos`. Again, `xanchor` and `yanchor` manipulate the two numbers separately. For this example, let's say we have a simple rectangle with a `pos` of `(0.5, 0.5)`: This is the default, with `anchor = (0, 0)`. The anchor (still the red dot) is at `pos = (0.5, 0.5)`, the displayable (the grey rectangle) is positioned with its anchor at the *rectangle's* top-left `anchor = (0, 0)` Here's `anchor = (0.5, 1.0)`: Now it's completely centered, both relative to the screen and itself. A nice baseline for a visual novel might be `pos = (0.5, 1.0), anchor = (0.5, 1.0)`, which positions the entire displayable at the bottom center: Since `yanchor = 1.0`, the rectangle starts at the anchor and "goes up", instead of "going down". ### `offset` - tuple of `(int, int)` The last real value! `offset` is a simple one, it just nudges the displayable by the given number of pixels. And they're just integers this time, no need to worry about percentages. Again, `xoffset` and `yoffset` manipulate the two components separately. Here's a nice little offset figure: ```renpy pos = (0.5, 0.5) anchor = (0, 0) xoffset = 50 ``` ### The shortcut ones Like I said, that was the last real position property! The rest are all shorthand for changing those three. - `xcenter = x` sets `xpos` to `x` and xalign to `0.5` - `ycenter = y` sets `ypos` to `y` and yalign to `0.5` - `align = (x, y)` sets both `pos` and `anchor` to `(x, y)` --- You may have noticed an obvious pitfall here: there are a lot of different ways to display exactly the same image! Changing `anchor` by a few pixels looks the same as changing `offset`, but animates totally differently. That's why it's critical to keep the way you animate things consistent, and think through animations carefully rather than just bashing your code with a hammer until it produces an image that looks good. If you don't, you'll find it very easy to write yourself into a corner where animations don't work correctly and you can't get `ease` or other interpolations to do what you want, because you've accidently spread a single design decision across three sets of numbers. Here are my design recommendations, I suppose: - Keep a default align for characters of `(0.5, 1.0)` - Adjust `xpos` as needed to move them around the screen. - Have a default offset of `(0, 20)` so there's room for the character to move up a few pixels - Just use `offset` for animations (jumping up and down, sitting, general fx). You should always be able to clamp `offset` back to near-zero and still have the character in the right area. - If your characters are standing, you probably don't ever need to touch ypos or yanchor!