Ren'Py Simili 2.5D, parallax & layers

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
Hi!

In order to keep in touch with Daz assets I wanted to create another shit prototype, just to keep things fresh. But I come across something and I'm not even sure it's a problem (you tell me). Thait said it's quite difficult to explain, so bear with me for a minute. First thing first, the goal is to recreate simili-2.5D in Renpy (one could argue you shouldn't, but hey). So in order to do just that I use first that one (extremely helpful) , not only it can put a camera in a 3D space but also come with very handy features if you deal with several layers (I will come to that later). Plugin itself is far (a galaxy away) beyond my coding skills but I used it recklessly without any problems.

Now, second thing, in order to create my simili-2.5D, I also need to use a parallax script for every single scene. Before I go further here the script I use :

You don't have permission to view the spoiler content. Log in or register now.

Basically parallax use mouse tracking to move layer in XYZ positions. Script is great because it allow me to use transform to tweak every layers to create what I have in mind. And here the weird thing - everything work great. No problem except for one thing, if I open my super cool 3D camera plugin - as it can keep track of every layers. That's where it goes weird.

1WhRj2pzIb.jpg

Imagine you're playing the game and went 30 scenes in. Master is the usual renpy layer; l0 l1 l2 l3 and so on are what compose my master2 (parallax) layer. Basically for each "scene" it recreate a master2 layer without pruning the last one. No matter how hard I try to modify my parallax script with my shit coding skill, it just won't leave me alone. Trust me I tried various things, with no luck so far. So endless master2 (empty) layer for each scene is my problem. Those layers (except the one shown) are empty of images, and no memory usage so far, but I fell it's gonna bite me in the ass somehow. Here the thing: is Renpy keeping track of my layers or is just my 3D camera plugin, if problematic how could I purge them?
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,975
16,230
is Renpy keeping track of my layers or is just my 3D camera plugin, if problematic how could I purge them?
Two answers here.

Firstly, yes, Ren'Py is keeping track of your layers, at least if they are defined correctly. Secondly the 3D camera is not part of the plug-in, it's directly a Ren'Py feature ; what the plugin do is just letting you see and edit its parameters in real time.

As for your problem, it really looks like it's a full overdone code.

I haven't done things like that since ages, but I kind of remember that parallaxes just need a really basic algorithm. For each layer you define a moving factor for both X and Y axis, or one by axis, depending what you intent to do. Then, each time you move the main layer, you move the others accordingly to those factors. In your case, the factors would apply to the mouse movements.

For the usual 4 layers model, you would have this:
  • Far background, factor 0.2
  • Background, factor 0.5
  • Main layer, factor 1.0
  • Foreground, factor 1.3
The values are given for a generic parallax approach. The further if the background, the lower the value must be, and the closer is the foreground, the higher it must be.

Then, your own code is really limited ; no need for all the python things you shown:
Python:
init python:
    # The 'master' layer is used as main focus point.
    # Add the 'foreground' layer right after it.
    config.layers.insert( config.layers.index( "master" ) + 1, "foreground" )
    # and the 'background' one right before it.
    config.layers.insert( config.layers.index( "master" ), "background" )

# The transforms to apply to each layer for them to move.
# One by layer in your parallax system.
transform background:
    function backgroundPos

transform master:
    function masterPos

transform foreground:
    function foregroundPos

# The function used by the transform, once again, one by layer.
init python:
    def backgroundPos(trans, st, at):
        # The position of the content for this layer is now the position of the mouse * 0.5
        trans.xpos = int( store.AONmouse.x * 0.5 )
        return 0

    def masterPos(trans, st, at):
        trans.xpos = store.AONmouse.x
        return 0

    def foregroundPos(trans, st, at):
        trans.xpos = int( store.AONmouse.x * 1.3 )
        return 0

    # The user defined displayable that track the movement of the mouse.
    class AONMousePos(renpy.Displayable):
        x = 0
        y = 0

        def __init__(self, **kwargs):
            super(AONMousePos, self).__init__(**kwargs)

        def event(self, ev, x, y, st):
            if (x < 0 or y < 0): return None
            import pygame_sdl2 as pygame
            if ev.type != pygame.MOUSEMOTION: return None
            self.x = x
            self.y = y
            renpy.redraw(self, 0)

        # 1x1 pixel blank. Is mandatory.
        def render(self, width, height, st, at):
            rv = renpy.Render(1, 1)
            return rv


define AONmouse = AONMousePos()

# for test purpose.
image transparency = Solid( "#00000000" )

# The screen that will be used to track the mouse movements.
screen mouse():
    add AONmouse

label parallax:

    # Mandatory during the test, else you've either the checkered background, or the full black one.
    # In prod, the image on the master layer should have the transparency values you need and this
    # shouldn't be needed anymore.
    scene transparency

    # For each layers, define the transform to apply.
    show layer background at background
    show layer master at master
    show layer foreground at foreground
    # Start tracking the mouse movements.
    show screen mouse

    # Fill the different layers with the corresponding image.
    show [some image for the background layer] onlayer background
    show [some image for the main layer]
    show [some image for the foreground layer] onlayer foreground
    "Move the mouse and enjoy"
    # Stop tracking the mouse movements.
    # /!\ The different layers will stay at their current position /!\
    hide screen mouse
/!\ While tested, this code is really RAW. /!\
It only move horizontally, and is limited in the movements you can do since it rely on the mouse position and assume that all layers starts a 0 for the X axis. But it show the spirit, and can serve as solid tweakable base for your own purpose.

Rich after our last discussion, I think that this can interest you.
 

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
Two answers here.

Firstly, yes, Ren'Py is keeping track of your layers, at least if they are defined correctly. Secondly the 3D camera is not part of the plug-in, it's directly a Ren'Py feature ; what the plugin do is just letting you see and edit its parameters in real time.

As for your problem, it really looks like it's a full overdone code.

I haven't done things like that since ages, but I kind of remember that parallaxes just need a really basic algorithm. For each layer you define a moving factor for both X and Y axis, or one by axis, depending what you intent to do. Then, each time you move the main layer, you move the others accordingly to those factors. In your case, the factors would apply to the mouse movements.

For the usual 4 layers model, you would have this:
  • Far background, factor 0.2
  • Background, factor 0.5
  • Main layer, factor 1.0
  • Foreground, factor 1.3
The values are given for a generic parallax approach. The further if the background, the lower the value must be, and the closer is the foreground, the higher it must be.

Then, your own code is really limited ; no need for all the python things you shown:
Python:
init python:
    # The 'master' layer is used as main focus point.
    # Add the 'foreground' layer right after it.
    config.layers.insert( config.layers.index( "master" ) + 1, "foreground" )
    # and the 'background' one right before it.
    config.layers.insert( config.layers.index( "master" ), "background" )

# The transforms to apply to each layer for them to move.
# One by layer in your parallax system.
transform background:
    function backgroundPos

transform master:
    function masterPos

transform foreground:
    function foregroundPos

# The function used by the transform, once again, one by layer.
init python:
    def backgroundPos(trans, st, at):
        # The position of the content for this layer is now the position of the mouse * 0.5
        trans.xpos = int( store.AONmouse.x * 0.5 )
        return 0

    def masterPos(trans, st, at):
        trans.xpos = store.AONmouse.x
        return 0

    def foregroundPos(trans, st, at):
        trans.xpos = int( store.AONmouse.x * 1.3 )
        return 0

    # The user defined displayable that track the movement of the mouse.
    class AONMousePos(renpy.Displayable):
        x = 0
        y = 0

        def __init__(self, **kwargs):
            super(AONMousePos, self).__init__(**kwargs)

        def event(self, ev, x, y, st):
            if (x < 0 or y < 0): return None
            import pygame_sdl2 as pygame
            if ev.type != pygame.MOUSEMOTION: return None
            self.x = x
            self.y = y
            renpy.redraw(self, 0)

        # 1x1 pixel blank. Is mandatory.
        def render(self, width, height, st, at):
            rv = renpy.Render(1, 1)
            return rv


define AONmouse = AONMousePos()

# for test purpose.
image transparency = Solid( "#00000000" )

# The screen that will be used to track the mouse movements.
screen mouse():
    add AONmouse

label parallax:

    # Mandatory during the test, else you've either the checkered background, or the full black one.
    # In prod, the image on the master layer should have the transparency values you need and this
    # shouldn't be needed anymore.
    scene transparency

    # For each layers, define the transform to apply.
    show layer background at background
    show layer master at master
    show layer foreground at foreground
    # Start tracking the mouse movements.
    show screen mouse

    # Fill the different layers with the corresponding image.
    show [some image for the background layer] onlayer background
    show [some image for the main layer]
    show [some image for the foreground layer] onlayer foreground
    "Move the mouse and enjoy"
    # Stop tracking the mouse movements.
    # /!\ The different layers will stay at their current position /!\
    hide screen mouse
/!\ While tested, this code is really RAW. /!\
It only move horizontally, and is limited in the movements you can do since it rely on the mouse position and assume that all layers starts a 0 for the X axis. But it show the spirit, and can serve as solid tweakable base for your own purpose.

Rich after our last discussion, I think that this can interest you.
Thank you for your time.

I think I got the general idea, but I cannot use some factor set in stone, I really need some flexibilty here. That being said I can try something in the same spirit (or so I believe).

That said I have to ask (sorry lol) : is it really a problem I mean? Everything works alright and Renpy didn't flinch once. About ~250 scenes and no fatigue so far (but I have a good machine). Is it worth it? Can I keep it under the carpet?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,975
16,230
I think I got the general idea, but I cannot use some factor set in stone, I really need some flexibilty here.
I used hardcoded values because it was a bit advanced, therefore I needed a proof of concept to be sure that what I had in mind effectively works. But you can perfectly replace them by a variable. You can even have more complex computation here, to have a none linear progression by example. Not sure it would looks good, but it's totally possible.


That said I have to ask (sorry lol) : is it really a problem I mean? Everything works alright and Renpy didn't flinch once. About ~250 scenes and no fatigue so far (but I have a good machine). Is it worth it? Can I keep it under the carpet?
A problem ? No, not really.
It's less optimized because it rely on overlays instead of using layers, and probably also a bit more slow due to the useless code. But we are talking about atomic values here, so nothing effectively noticeable. Even if the player was moving the mouse like a crazy cat, the difference would be below 0.001 second every second, so nothing impactful unless it do it for more than 15 minutes in a row. And well, even in this case it clearly wouldn't be the fault of the code.
It's just that I'm grumpy and dislike it when people use tons of Python to do something that can be done almost natively ;)

More seriously, code like this are the reason why games tend to be too generic. Most devs don't have much knowledge when it come to the codding part, and when they see tons of Python code, they give up because it's beyond their own capabilities. This while in fact, as I shown, outside of the mouse tracker, that is purely generic, the only code needed is some math in the transform. And this, everyone can do it. At worse you'll pass some time in try and error to find the computation that please you.
 

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
I used hardcoded values because it was a bit advanced, therefore I needed a proof of concept to be sure that what I had in mind effectively works. But you can perfectly replace them by a variable. You can even have more complex computation here, to have a none linear progression by example. Not sure it would looks good, but it's totally possible.




A problem ? No, not really.
It's less optimized because it rely on overlays instead of using layers, and probably also a bit more slow due to the useless code. But we are talking about atomic values here, so nothing effectively noticeable. Even if the player was moving the mouse like a crazy cat, the difference would be below 0.001 second every second, so nothing impactful unless it do it for more than 15 minutes in a row. And well, even in this case it clearly wouldn't be the fault of the code.
It's just that I'm grumpy and dislike it when people use tons of Python to do something that can be done almost natively ;)

More seriously, code like this are the reason why games tend to be too generic. Most devs don't have much knowledge when it come to the codding part, and when they see tons of Python code, they give up because it's beyond their own capabilities. This while in fact, as I shown, outside of the mouse tracker, that is purely generic, the only code needed is some math in the transform. And this, everyone can do it. At worse you'll pass some time in try and error to find the computation that please you.
Non-linear is intersting tho, never really tought about that :unsure:. Will need obvious context, but I think I could make it work eventually. Oh god don't give wrong ideas.

I got you tho but I'm more the visual guy (monkey that press the render button). Sadly, coding bore me to quasi instant death (and I try my best I swear, even make some kind of progress!). That said I don't really mind generic or not, visuals need to be alright (and they will be). Thank you again o/
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,566
7,382
Rich after our last discussion, I think that this can interest you.
Thanks for the ping. I was aware of the plugin - that was some of the first code I studied in trying to understand the 3D camera.

Actually, there's an even simpler way of creating layers than the code you showed - you can just do something like
Code:
define config.layers = ["background", "master", "transient", "screens", "overlay"]
(I know - this is basically what your code is doing by manipulating config.layers)
I tend to add the "background" layer an show a black image on it right at the front of the game so that I never have the dreaded "there's no image showing for a fraction of a second" issue in games.


no__name - As anne O'nymous said, with version 7.5 and 8.0 of Ren'py, Ren'py has a 3D camera (which isn't a big deal for what you're trying to do) and layers (which it's had for a long time). One of the newer things, however, is a function . This allows you to apply transforms directly to layers. Once you have images on the various layers (using what Anne showed you), you could then put the individual layers in motion, using different speeds to create the parallax effect. I'm using this particular feature in the latest version of Whores of Thrones (not released here yet) to allow the player to smoothly zoom in and out of animations so that they get closeups of, well, "the good bits." :D

As a very simple example, suppose that you had layers a, b and c, and you wanted to shift them all, with c being closer, so it should move farther. You could do something like this:

Code:
transform shift_horizontally_to(amount):
    xpos 0
    linear 1.0 xpos amount

...

    $ renpy.show_layer_at([shift_horizontally_to(100)], layer="a", reset=False)
    $ renpy.show_layer_at([shift_horizontally_to(200)], layer="b", reset=False)
    $ renpy.show_layer_at([shift_horizontally_to(300)], layer="c", reset=False)
This would apply a transform to each of the layers with the first layer moving 100 pixels, the second 200 pixels and the third 300 pixels, all over a one-second period of time. Since the layers themselves shift, this "drags along" any image that's displayed on that layer.

You could create a mouse-tracking effect by doing the same type of thing without the linear interpolation - just plug the mouse coordinate in (with zero being center of screen, negative to the left, positive to the right), and use a fraction of the value on the farther-away layers so that they move less. This would track the mouse at "full speed," of course. If you wanted to limit the maximum speed that a layer moves (so that if you move the mouse suddenly it takes a fraction of a second for the layer to "catch up") it's a big trickier, but I've seen this done effectively using a custom displayable.

Actually, if I was trying to create the parallax effect, I'd actually probably do it without layers and just use a custom displayable, but that's because I'm a hard-core programmer, which I completely realize that you're not.

If you'd like to discuss a "reusable solution" for this, so that you don't have to custom-code every single scene, I'd be happy to - either in this thread, or via PM. Sounds like a fun challenge. :D
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,975
16,230
Actually, there's an even simpler way of creating layers than the code you showed
It was the modder in me talking here.
Relying on the position of the "master" layer ensure that you'll always have the default layers present, and in the right order. But yeah, like he's the author of the game, he's also in control of the version of Ren'Py he's using. Therefore the direct assignation is way less showing off than my code ;)
 

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
ay long time!

@no__name - As @anne O'nymous said, with version 7.5 and 8.0 of Ren'py, Ren'py has a 3D camera (which isn't a big deal for what you're trying to do) and layers (which it's had for a long time).
Yes and no. You could move those seven layers individually - or just move the camera from A to B.

One of the newer things, however, is a function . This allows you to apply transforms directly to layers. Once you have images on the various layers (using what Anne showed you), you could then put the individual layers in motion, using different speeds to create the parallax effect
Are you kidding me lmao?! How I missed that?
So much time wasted :HideThePain:
 

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
Rewrote parallax script from scratch and get rid of non-pruned layers. Thanks again all o/
Went further into experimentating but get mixed feelings about it.

Aesthetically it can work well (sometime really well tho), the more you stack layers the more convincing effect. It also force you to clutter scene and find tricky camera angle for every renders to multiply layers (and find difficult compromise, which is fun).

Render wise, I use no DoF at all & do it directly with some lens blurr in PS in order to save (plenty) of time. In perfect world, one could argue to make good use of a zdepth map, but feel it's just too much for a one man army.

But all in all, I'm not conviced about making a whole VN like that. There is some real eye fatigue in everything simili-2.5D, not all shot deserve it and really feel like pushing a gimmick too far without adding much. Maybe I should have taken a different approach, more sprite-driven VN with a fixed camera angle would work better (more redundancy, less fatigue).

That's said it's quite a funny thing, I can totally see taking advantage of this in a casual VN for some carefully selected scenes.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,975
16,230
But all in all, I'm not conviced about making a whole VN like that. There is some real eye fatigue in everything simili-2.5D, not all shot deserve it and really feel like pushing a gimmick too far without adding much.
Not just eye fatigue, it also tend to goes on the nerves when what you look at is (relatively) always in movement.


That's said it's quite a funny thing, I can totally see taking advantage of this in a casual VN for some carefully selected scenes.
Totally. All those scrolled view of the girl would be so much more interesting even with just two layers (background and the girl). But I guess that the first one to do it will do it every time a new girl enter the scene and make everyone hate it.
 

Deleted member 1121028

Well-Known Member
Dec 28, 2018
1,716
3,308
Not just eye fatigue, it also tend to goes on the nerves when what you look at is (relatively) always in movement.
Yeah got that :WeSmart:. Kinda got evicerated from my comparses (more than usual lol).

Totally. All those scrolled view of the girl would be so much more interesting even with just two layers (background and the girl). But I guess that the first one to do it will do it every time a new girl enter the scene and make everyone hate it.
A 3rd one is bare minimum ihmo, that's where faking depth really occur. Can work with 2 indeed - but nah bro.