Ren'Py Positioning screen relative to mouse

Sagisu

Newbie
Nov 12, 2017
30
25
Hi.
I'm struggling with one task and after many tries I can't find solution to it. I'm trying to show many screens (doesn't matter if on other large screen or using label for that) that will track current position of mouse and relate to that (eg. change positions), while still maintaining possibility to have buttons inside used. Is something like that even possible to do with Renpy?

I tried to refresh mouse coordinates and screen positions in label (call screen in while loop), but it just swallow all input, so other screens are unusable.
I tried to add to one main screen empty displayable that checks mouse coordinates and repositions all screens (had to use restart_interaction for that), but it also behaves strangely with buttons.
There's no problem to do that with displayables, but I would need to create whole UI (with buttons and such) from scratch like that.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
I'm struggling with one task and after many tries I can't find solution to it. I'm trying to show many screens (doesn't matter if on other large screen or using label for that) that will track current position of mouse and relate to that (eg. change positions), while still maintaining possibility to have buttons inside used. Is something like that even possible to do with Renpy?
Hmm...

I haven't tested it, but something like this should do the trick:
Python:
init python:

    class MousePos(renpy.Displayable):

        def event(self, ev, x, y, st):
            renpy.redraw(self, 0)
            if not -1 < x < config.screen_width or not -1 < y < config.screen_height: return None
            store.mouseX = x
            store.mouseY = y
            renpy.restart_interaction()

        def render(self, width, height, st, at):
            print( "rendered" )
            rv = renpy.Render(1, 1)
            return rv

default mouseX = 0
default mouseY = 0

screen myTest():

    add MousePos()

    frame:
        #  Replace 100 by the width of your content
        if mouseX < config.screen_width - 100:
            xpos mouseX
        else:
            #  Replace 100 by the width of your content
            xpos config.screen_width - 100

        #  Replace 100 by the height of your content
        if mouseY < config.screen_height - 100:
            ypos mouseY
        else:
            #  Replace 100 by the height of your content
            ypos config.screen_height - 100

        text "blablabla"
I know that you can name the displayable and access its attributes, but as I remember it's relatively tricky, so I played it safe by using external variables to store the position.

If someone have a better solution, I'm interested.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Just to be clear...

You want the screen to move with the mouse, as the mouse moves?
-or-
You want the initial position of the screen to be relative to the mouse position, but stay in place after it appears?

Because I can imagine the first one causing problems, especially if you have buttons within the screen you want to press (but if they move with the screen... the mouse will never be able to reach them).

There's also the additional problem of constraining the displayed screen within the overall dimensions of the screen. Otherwise, you could potentially spawn a screen so far on the right that you wouldn't be able to interact with it (assuming the screen is spawned relative to the cursor with the top left corner being the spawn point).

I don't strictly know an answer... but if I decide to play with it - I'd at least like to know what the goal is.
My initial thought though is something that utilizes the functions. Though I'm no expert.
A seems to point towards using ... As I say, I'd need to "play" with it a bit to really figure out what might work.
Edit: Anne's post seems to be CDDs too... erm... I think.
 

Sagisu

Newbie
Nov 12, 2017
30
25
I haven't tested it, but something like this should do the trick
Yea.. that won't do the trick, I've tried =)

You want the screen to move with the mouse, as the mouse moves?
-or-
You want the initial position of the screen to be relative to the mouse position, but stay in place after it appears?
The first one. Yea, sorry I didn't mention that it won't be exact mouse coordinates.

I've got similar to @anne O'nymous approach, but my class for cursor tracking looked like that:
Python:
    mouse_x = 0
    mouse_y = 0

    class TrackCursor(renpy.Displayable):
        def __init__(self, **kwargs):
            super(TrackCursor, self).__init__()

        def render(self, width, height, st, at):
            rv = renpy.Render(1, 1)
            return rv

        def event(self, ev, x, y, st):
            mouse_x = -x / 30
            mouse_y = -y / 30
To say it short: I'm trying to get the parallax view, but I'd like to also add that parallax effect to menu box with buttons:
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
Yea.. that won't do the trick, I've tried =)
In between I came back home, tried... and the code I gave works perfectly.
You don't have permission to view the spoiler content. Log in or register now.


Python:
        def event(self, ev, x, y, st):
            mouse_x = -x / 30
            mouse_y = -y / 30
The event method is useful only if Ren'Py call it frequently enough. This while changing the value of mouse_x and mouse_y is useful only if Ren'Py refresh the screen.

There's two things more in my own code, and they are what make mine works, while yours don't.
 

Sagisu

Newbie
Nov 12, 2017
30
25
It won't work if you'll try to use any UI with that code. Yea, I forgot about
Code:
   (...)
   hover = ev.type == pygame.MOUSEMOTION
   if hover:
      (...)
      renpy.restart_interaction()
   (...)
for refreshing screens, but as I said - any hovering or transitions won't work with it, I've tried and it surely was really nice, but I would need to create all buttons and all of that by myself, without using Renpy (and that could be performance nightmare).
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
It won't work if you'll try to use any UI with that code.
Hmm...
You don't have permission to view the spoiler content. Log in or register now.

The displayable do only two things, updating two variables each time the mouse move and forcing Ren'Py to refresh, dot.

You can do whatever you want with it, you can even use it to make a shooter or a vertical scroller... And putting it in an overlay, you can just forget about it, and just assume that there's a fairy who magically give you the position of the mouse each time you need it.

There's absolutely nothing that you cannot do with this code... But well, it you know better than those who answer you, why are you even asking ?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
I can't help but feel there is an "apples and oranges" problem going on here.

With each of you both being right, but at the same time both being wrong - because we're not all talking about the same thing.

Perhaps it's the distinction between the relative position of an individual displayable -vs- your aim to do that to a whole screen/window/frame while retaining the relative position of all the sub-components like imagebuttons and whatever else is within the outer container/screen.

Perhaps if your game is in a state where RenPy could perform a full build, without any game breaking errors or files missing - it might be worth building the package, uploading the .zip file somewhere like and sharing that link - so that we can see the same problem you're encountering with the same code and same images you are dealing with. More specifically, with the actual parallax perspective you're trying to implement.

Despite Anne's somewhat blunt conclusion, his code (especially once he say's he's tested it) is usually spot on. I even recall him solving someone elses' parallax problem a while ago. So if his code works for him and not for you... then we're missing something and that something is invariably a miscommunication somewhere along the line.

So if you can share the whole thing, that might go a long way toward figuring out where the discrepancy lies.
Otherwise we're left with trying to guess the specific point we're overlooking, while iterating through trial and error.
 

Sagisu

Newbie
Nov 12, 2017
30
25
The displayable do only two things, updating two variables each time the mouse move and forcing Ren'Py to refresh, dot.
I don't get it.. I letarally added your code for getting mouse position (maybe mine was wrong), but the problem still occurs: no hovering over buttons, no sound effects for over/click buttons, no transition animations working, while moving cursor (watch rain in windows):
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
I don't get it.. I letarally added your code for getting mouse position (maybe mine was wrong), but the problem still occurs: no hovering over buttons, no sound effects for over/click buttons, no transition animations working, while moving cursor (watch rain in windows):
I don't know how you are doing it, but either you are doing it wrong, or using a version of Ren'Py that have a bug. What I know is that the following code works fine with at least the 7.4.5 and 7.4.10:
Code:
default mouseX = 0
default mouseY = 0

screen myTest():
    add MousePos()

screen myHovered():

    default myX = 200
    default myY = 500

    frame:
        xpos 600 + int( mouseX / 10 )
        ypos 300 + int( mouseY / 10 )
        vbox:
            textbutton "Blabla":
                hovered [ SetScreenVariable( "myX", 500 ), SetScreenVariable( "myY", 200 ) ]
                unhovered [ SetScreenVariable( "myX", 200 ), SetScreenVariable( "myY", 500 ) ]
                action NullAction()
                tooltip "Boooh !"
            textbutton "blibli":
                hovered [ SetScreenVariable( "myX", 10 ), SetScreenVariable( "myY", 0 ) ]
                unhovered [ SetScreenVariable( "myX", 200 ), SetScreenVariable( "myY", 500 ) ]
                action NullAction()
                tooltip "My, my, my !"

    vbox:
        xpos 100 - int( mouseX / 10 )
        ypos 100 - int( mouseY / 10 )
        textbutton "Bloblo":
            hovered [ SetScreenVariable( "myX", 0 ), SetScreenVariable( "myY", 250 ) ]
            unhovered [ SetScreenVariable( "myX", 200 ), SetScreenVariable( "myY", 500 ) ]
            action NullAction()
            tooltip "Picka Picka"
        textbutton "blublu":
            hovered [ SetScreenVariable( "myX", 350 ), SetScreenVariable( "myY", 0 ) ]
            unhovered [ SetScreenVariable( "myX", 200 ), SetScreenVariable( "myY", 500 ) ]
            action NullAction()
            tooltip "Here I am, J.H"

    text "I'm Alive !" xpos myX ypos myY

    $t = GetTooltip()

    if t:
        text "[t]" xalign 0.5 yalign 0.5

label start:

    $ config.overlay_screens.append( "myTest" )
    show screen myHovered
    "END"
Globally it's the same that my last video, except that I put the textbutton position depend of the mouse position ; and obviously removed the "blablabla" that was more a visual helper that the code was working.
Once again I haven't tested with transition, mostly because I have no idea for the code, but I don't really see why it should change something.
 
  • Like
Reactions: gojira667

Sagisu

Newbie
Nov 12, 2017
30
25
Once again I haven't tested with transition, mostly because I have no idea for the code, but I don't really see why it should change something.
Ok.. now I'm speachless.. I started new, empty project, copied function `MousePos` and whole code from your last post into `script.rpy` and still.. nothing. I was testing it on version 7.4.10 and 7.4.11 (thought already that it might be the case) and got that (at the end I clicked and didn't release button for few seconds, that's why there's a pause before returning to menu):

I also build this whole project and didn't change a thing. I'm using Win10, but it still should work I suppose =/
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
This is my cut and paste code based on Anne's work.
I've changed a few things to purely give more positive feedback for my limited brain, but the core remains the same.

My only confusion is why it doesn't seem to honor the hover_color part of the style, until the button is actually pressed. But other than that, the code seems to work how I might have expected (though perhaps not how you have in mind).

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

Edit: Updated code to reflect lessons learned later in this thread... code works correctly now.


I'm not sure I'm adding anything to this conversation - except perhaps to offer an alternative copy/paste bit of code.
If it's not working, perhaps you could explain what it is you are seeing that isn't how you imagine it should be.

My only other observation was that Anne's 2nd code block didn't include the class MousePos(renpy.Displayable):. So maybe your version of that same code differs from Anne's other example? I'm reaching here.
 
Last edited:
  • Like
Reactions: gojira667

Sagisu

Newbie
Nov 12, 2017
30
25
My only confusion is why it doesn't seem to honor the hover_color part of the style, until the button is actually pressed.
And that's actually the problem I'm tackling. Anything is losing focus with this solution: there's no hover effect, no audio on hovering and so on.
I've added lacking MousePos to my test - other way it would scream with error.

What doesn't make sense is that if I have problem with hover effect, you have it too, so why anne O'nymous didn't (in video at #7 there's clearly screen going after cursor and a button that has hover effect.. I don't get it how).
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
The only extra info I can add is that if I mess around with the style within the code and use SHIFT+R to manually reload the code within an already running game... I can sometimes bug out the screen to a point where the two button blocks stop moving around with the mouse... but the hover works.

I doubt it has anything to do with this problem, but stranger things have turned out to trigger a thought process in others that ultimately ends up with a solution.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
Ok, there's definitively something weird somewhere...

Expecting that perhaps the project I used to do the test had something else, that happened to make this works, I created a blank new project using the 7.4.10 SDK. And then I get the same "don't works at all" result.

So, I tried with a different "older project", and it works, whatever the version of Ren'Py. But I'm 100% sure that there's no modification in common in all those projects.

And this is absolutely not normal :/


I don't get it how.
If it can reassure you, you aren't the only one confused here.

I can understand why it wouldn't works ; somehow Ren'Py would fail to notice that the mouse is over the button because the said button have moved. This is rational, I can live with that and even potentially find a solution if there's one.

But what I don't understand is why it works and don't works at the same time. Because as you said, it works, everyone can see it with the video I posted. But I believe you when you said it also don't works, I witnessed it with my own eyes :/

But alas I can't help you more with this. PyTom is talking more and more about the version 8.0 (the Python 3.x one), and I still have a lot of works to ports my tools to it :( I can't distract myself too much from this actually.
 
  • Like
Reactions: gojira667

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
I can confirm that recreating a new project in 7.3.5 doesn't fix things either.
(I know some of the later RenPy versions have differing code in giu.rpy for example).

Anyway, didn't help.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
I will freely admit, I'm beyond my knowledge level here and grasping at straws.

I did initially wonder if the zorder or layer were impacting things... but I don't think so.

But...

If I comment out the renpy.restart_interaction() as part of the class MousePos(renpy.Displayable):, the hover functionality works again (although the boxes stop moving smoothly and only moves when then mouse interacts with the textbuttons).

I'm left with the idea that maybe renpy.restart_interaction() is somehow interrupting the interaction process before the hover code is actioned? Perhaps there's a way of only invoke the restart_interaction if absolutely needed?

Oh... random bullshit idea time...
I tried this:

Code:
init python:

    class MousePos(renpy.Displayable):

        def event(self, ev, x, y, st):
            renpy.redraw(self, 0)
            if not -1 < x < config.screen_width or not -1 < y < config.screen_height: return None
            if x != store.mouseX or y != store.mouseY:
                store.mouseX = x
                store.mouseY = y
                renpy.restart_interaction()

        def render(self, width, height, st, at):
            #print( "rendered" )
            rv = renpy.Render(1, 1)
            return rv

It seems to work. Although the movement stuttered a bit at one point for me and felt a little sluggish and unresponsive. I'm not 100% sure why. Perhaps it was some sort of interaction when the mouse hadn't moved? (or had moved?)... I dunno.

This doesn't feel like it's the right answer... but perhaps it'll invoke a better idea in someone more knowledgeable than me about wtf an interaction is and what renpy.restart_interaction() is actually doing.

Edit: Okay, I may have an idea about the lag/stutter thing. After the "hover" has been activated - if you move the mouse away and keep it moving... then the "hover" remains triggered. If you stop the mouse, it still remains triggered. But as soon as you move the mouse again, the hover button goes back to idle state. So something seems to be stopped from running while the mouse is moving around, but as soon as it gets a moment of peace, that same something manages to finish. The same "don't do 'something' while the mouse is moving" also happens when moving between one button and another and creates this impression of stuttering/lag. I can see why my code would cause that, but can't see a solution that won't create the same problem we started with.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
If I comment out the renpy.restart_interaction() as part of the class MousePos(renpy.Displayable):, the hover functionality works again (although the boxes stop moving smoothly and only moves when then mouse interacts with the textbuttons).
Hmmm. I think I got it...


As said above, I don't really have the time to test (and my dinner is cooking), but globally my thought is that it's a question of flow order.
[With the myTest and myHovered screens from my example]:
Code:
label whatever:
    show screen myTest
    show screen myHovered
The mouse is proceeded before Ren'Py can proceed the buttons. Like an event restart the interaction, Ren'Py never proceed the buttons -> Do not works ?

Code:
label whatever:
    show screen myHovered
    show screen myTest
Ren'Py proceed the buttons before the displayable restart the interaction -> Works ?

Then it can be "automated" by either putting it in config.overlay_screens, or in its opposite, config.underlay ; the last one is where Ren'Py put the screen handling all the system key combination, and I kind of remember that you can intercept them with key in your own screens.

After, it's perhaps also possible to remove the "screen order" problem by adding a filter directly into the event method. Something that would looks like:
Code:
init python:

    import pygame_sdl2 as pygame

    class [...]

        [...]

        def event(self, ev, x, y, st):
            [...]
            if ev.type == pygame.MOUSEMOTION:
                renpy.restart_interaction()
But I guess that it wouldn't really solve the issue ; the buttons event would be triggered only if the mouse stay static.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Yup.

That seems better...

Python:
init python:

    class MousePos(renpy.Displayable):

        def event(self, ev, x, y, st):
            import pygame
            renpy.redraw(self, 0)
            if not -1 < x < config.screen_width or not -1 < y < config.screen_height: return None
            if x != store.mouseX or y != store.mouseY:
                store.mouseX = x
                store.mouseY = y
                if ev.type == pygame.MOUSEMOTION:
                    renpy.restart_interaction()

        def render(self, width, height, st, at):
            rv = renpy.Render(1, 1)
            return rv

Again... still not sure it's the right answer... but my stuttering issue went away and the hover interactions seem fluid again.

In short... it seems to be WORKING.

My checks to avoid doing anything if the mouseX and mouseY haven't changed may not be necessary any more. Though I'm so proud of them, I'm keeping them in my example. :devilish:

If I had to guess... some of those events that were triggering the def event() function were the events that updated the hover status. When the renpy.restart_interaction() was invoked, it stopped that event before it could complete. By limiting things to events that involve mouse movement, all those other events could run their natural course. Or something like that.
 
Last edited:
  • Like
Reactions: Tribe and gojira667

Radnor

Member
Game Developer
Nov 9, 2021
365
943
Not sure if i'm missing something, but for
You want the screen to move with the mouse, as the mouse moves?
-or-
You want the initial position of the screen to be relative to the mouse position, but stay in place after it appears?
The first one. Yea, sorry I didn't mention that it won't be exact mouse coordinates.
i would use something like:
Code:
init python:
  def tf_update_pos_fn(tf,st,at):
    mx,my=renpy.get_mouse_pos()
    tf.pos=(mx,my)
    return 0

transform tf_update_pos:
  function tf_update_pos_fn

screen test():
  vbox:
    pos (500,300)
    text "some buttons to test hover"
    textbutton "Button 1" action Function(print,"click 1")
    textbutton "Button 2" action Function(print,"click 2")
    textbutton "Button 3" action Function(print,"click 3")
    textbutton "Button 4" action Function(print,"click 4")
  frame:
    at tf_update_pos
    xysize (300,200)
    text "this is moving box"

label start:
  show screen test
  "test"
  return
Seems to work fine in 7.4.10, likely would work same with older versions too.