How to get an object's own position?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
Vitalis
Newbie
Posts: 8
Joined: Wed Nov 24, 2021 5:05 pm
Contact:

How to get an object's own position?

#1 Post by Vitalis »

This seems like a trivial question but I've been combing through the documentation and googling it and can't find an answer even though it seems like a very basic problem.

Suppose I have an imagebutton which I want to show a screen and pass its own position coordinates to it. Fake code:

Code: Select all

imagebutton idle "button" action ToggleScreen ("screen", x=imagebutton.xpos, y=imagebutton.ypos)
How do I achieve this?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: How to get an object's own position?

#2 Post by Remix »

You might want to alter your logic and just try to read the active focus coordinates when needed:

Code: Select all


renpy.focus_coordinates()

    This attempts to find the coordinates of the currently-focused displayable. 
    If it can, it will return them as a (x, y, w, h) tuple. 
    If not, it will return a (None, None, None, None) tuple.
Frameworks & Scriptlets:

Vitalis
Newbie
Posts: 8
Joined: Wed Nov 24, 2021 5:05 pm
Contact:

Re: How to get an object's own position?

#3 Post by Vitalis »

Thanks for your suggestion. :)

Unfortunately this is of limited use because it only gives the position of the focused displayables and is not universal. The button example was just one use case scenario. Does RenPy *seriously* not have the simple function of allowing an object (button, displayable or otherwise) to figure out its own absolute coordinates on the screen? It seems like it should be a no-brainer, instead it seems you have to hack around it. :/

I tried your idea out anyway because an imagebutton with a pop-up menu is the case I'm now interested in:

Code: Select all

label start:
 call screen testMenu
 jump start

default testVariable = "test pending"
 
screen testMenu:
 frame:
  pos (0.5, 0.5)
  
  $ buttons = ("button1", "button2", "button3")
  $ buttonPos = 0
  
  for button in buttons:
   imagebutton:
    idle "button"
    ypos buttonPos
    action ToggleScreen("testMenu2")
   $ buttonPos += 120
   
screen testMenu2:
 modal True
 python:
  x = int(renpy.focus_coordinates()[0])
  y = int(renpy.focus_coordinates()[1])
  
  buttons2 = ("buttonA", "buttonB", "buttonC")
  
 imagebutton:
  idle "transparent"
  action (
   ToggleScreen("testMenu2")
   )
 
 frame:
  pos(x+200,y)
  xsize 200
  ysize 200
  $ buttonPos2 = 0
  for button in buttons2:
   imagebutton:
    idle "button"
    ypos buttonPos2
    action (
     SetVariable("testVariable", "test complete"),
     print(testVariable),
    )
   $ buttonPos2 += 120

The issue is that when buttons A, B, C are pressed, testMenu2 shifts position, as it takes the new focus coordinates. (Renpy also throws an error if I open the game menu and come back when testMenu2 is opened.)

I tried passing the coordinates from the button itself as an argument for testMenu2(coordinates), like this:

Code: Select all

ToggleScreen("test2", renpy.focus_coordinates()),
but RenPy just throws "TypeError: 'tuple' object is not callable".

Any ideas? Perhaps there is another way to pass the coordinates or maybe there's something I don't understand here about how screens are called?

User avatar
m_from_space
Miko-Class Veteran
Posts: 974
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: How to get an object's own position?

#4 Post by m_from_space »

As I understand it you want to create a popup menu. So when you press button1, another list of buttons right next to it should appear? Like the Windows startmenu or something like that?

I just tried a basic version myself for figuring stuff out for you. You should let renpy position stuff and not worry about where things are (but just place them somewhere you want). Now you just have to create the list of actions that will trigger when the subbuttons get pressed.

P.S. If you "call" your screen instead of using "show", don't forget to "Return()" via the screen, otherwise you will be stuck.

Code: Select all

define buttons = ["button1", "button2", "button3"]
define subbuttons = [
    # subbuttons for button1
    ["buttonA"],
    # subbuttons for button2
    ["buttonB", "buttonC"],
    # subbuttons for button3
    ["buttonD", "buttonE", "buttonF"]
]

screen testMenu():
    default submenu = None
    frame:
        background None
        padding (0,0)
        xpos 0.5 ypos 0.5
        hbox:
            spacing 5
            vbox:
                spacing 5
                for i, b in enumerate(buttons):
                    imagebutton idle Solid("#f005") action [Notify("You pressed "+b), If(submenu == i, SetLocalVariable("submenu", None), SetLocalVariable("submenu", i))] xsize 200 ysize 50
            if submenu is not None:
                use subMenu(submenu)

screen subMenu(sub):
    vbox:
        spacing 5
        for i, b in enumerate(subbuttons):
            if i == sub:
                for sb in b:
                    imagebutton idle Solid("#0f05") action Notify("This is button "+sb) xsize 200 ysize 50
            else:
                null width 200 height 50

Vitalis
Newbie
Posts: 8
Joined: Wed Nov 24, 2021 5:05 pm
Contact:

Re: How to get an object's own position?

#5 Post by Vitalis »

m_from_space: Thank you for replying. :) I learned a bit from your code (didn't know about enumerate, seems handy) but your solution, while clever, kind of ignores my initial question. From what I understand it just creates empty vbox slots to achieve the illusion of position change.

I have a long list menu created through iteration and some of the items have a pop up sub-menu button attached to them. Here's the simplified code of what I'm doing:

Code: Select all

default itemlist = [
 {"name": "itemType1", "items": ("item1", "item2", "item3")},
 {"name": "itemType2", "items": ("item4", "item5")},
 {"name": "itemType3", "items": ("item6", "item7")},
 ]
 
default optionlist = ["option1", "option2", "option3", "option4", "option5"]
 
screen listMenu():
 frame:
  xalign 1.0
  vbox:
   spacing 20
   for list in itemlist:
    text list["name"]
    imagebutton idle "button" action ToggleScreen("subMenu")
    
    for item in list["items"]:
     textbutton item action Notify(item+" equpped")
    
screen subMenu():
 imagebutton idle "transparent" action ToggleScreen("subMenu")
    
 vpgrid:
  cols 3
  for option in optionlist:
   imagebutton idle "button2" action Notify(option+" picked")
I need the new submenu to open to the left of the button's position and over the listMenu. I also want the subMenu to close when the user clicks outside of its bounds. Everything else works as expected but the positioning seems to be the final wrinkle.

Like I mentioned before, with the help of renpy.focus_coordinates(), I'm able to make the subMenu appear where I want but then every subsequent action gives it new coordinates, shifting it around or resulting in errors when it gets a None.

It's extremely frustrating because it seems like such a trivial problem - passing the pos of imagebutton in listMenu to subMenu and adding an offset would solve the issue immediately. I just discovered and tried experimenting with renpy.get_image_bounds and renpy. get_placement but with no luck so far. For an engine aimed at amateur programmers RenPy seems incredibly opaque sometimes. :|

Vitalis
Newbie
Posts: 8
Joined: Wed Nov 24, 2021 5:05 pm
Contact:

Re: How to get an object's own position?

#6 Post by Vitalis »

Ok I managed to get it to work with a global Boolean telling subMenu if it should update its coordinates. Still feels like a huge hack to me and something that must surely be achievable by simpler means. :?

I'd still love to know if there's a more elegant way to this and just get the coordinates for an object directly. :)

My solution:

Code: Select all

default menuActive = False

screen listMenu():
(...)
 for list in itemlist:
    text list["name"]
    imagebutton idle "button" action (SetVariable("menuActive", True), ToggleScreen("subMenu"))
(...)

screen subMenu():
 imagebutton idle "transparent" action ToggleScreen("subMenu")
 
 python:
  if menuActive:
   x = int(renpy.focus_coordinates()[0])
   y = int(renpy.focus_coordinates()[1])
   
   menuActive = False

 vpgrid:
  xalign 1.0
  xpos x
  ypos y
  (...)
 

philat
Eileen-Class Veteran
Posts: 1910
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: How to get an object's own position?

#7 Post by philat »

Vitalis wrote: Sun May 15, 2022 7:59 am I tried passing the coordinates from the button itself as an argument for testMenu2(coordinates), like this:

Code: Select all

ToggleScreen("test2", renpy.focus_coordinates()),
but RenPy just throws "TypeError: 'tuple' object is not callable".
The second argument is transition. ToggleScreen("test2", transition=None, renpy.focus_coordinates())

User avatar
m_from_space
Miko-Class Veteran
Posts: 974
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: How to get an object's own position?

#8 Post by m_from_space »

Vitalis wrote: Sun May 15, 2022 12:26 pmFor an engine aimed at amateur programmers RenPy seems incredibly opaque sometimes. :|
Well, Renpy is for making visual novels, not for creating an operating system. When Renpy is not able to do what you want, just do it with python and classes. You can create your own Displayables.

It never seemed interesting to me to get an objects position, since I usually know the position to begin with (because I placed it).

And I still don't think you need the position to make a simple menu overlapping the other one. If you place your main menu, you know where it will be, right? You don't have to use relative coordinates, just place it on a fixed position on the screen. Your game resolution is what your frame is, right?

What I mean is:

You don't have to create a menu that builds itself out of lists. Just create the menu how you want it.

Code: Select all

screen testMenu:
    vbox:
        imagebutton "button1" action ToggleScreen("submenu_button1")
        imagebutton "button2" action ToggleScreen("submenu_button2")
        
screen submenu_button1:
    tag submenu
    vbox:
        xpos 0 ypos 0
        imagebutton "buttonA":
            ...
        
screen submenu_button2:
    tag submenu
    vbox:
        xpos 0 ypos 50
        imagebutton "buttonB":
        ...

Vitalis
Newbie
Posts: 8
Joined: Wed Nov 24, 2021 5:05 pm
Contact:

Re: How to get an object's own position?

#9 Post by Vitalis »

philat wrote: Sun May 15, 2022 10:27 pm The second argument is transition. ToggleScreen("test2", transition=None, renpy.focus_coordinates())
Oooh ok, thanks.

@m_from space

My menu is scrollable so the button summoning the submenu can be at almost any y coordinate. The menu also contains multiple lists of items that will change often as I add more options in subsequent builds. It just makes no sense to me to create the menu and input the positions manually.

I do use Python a lot in my project but my knowledge of it is fragmentary and I'm learning on the fly, so I'm not sure how to achieve that particular task with it, that's why I asked here. :) I haven't looked into creating my own Displayabel Classes yet, maybe I'll read up on it.

User avatar
m_from_space
Miko-Class Veteran
Posts: 974
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: How to get an object's own position?

#10 Post by m_from_space »

Vitalis wrote: Thu May 19, 2022 4:43 pmMy menu is scrollable so the button summoning the submenu can be at almost any y coordinate. The menu also contains multiple lists of items that will change often as I add more options in subsequent builds. It just makes no sense to me to create the menu and input the positions manually.
This actually still doesn't sound like you need a function that dynamically creates things out of lists (and I say this to make your life easier and also because manually crafting menus will not stress renpy execution as much as calculating everything everytime the screen is drawn).

If your menu is inside a scrolling viewport, it doesn't matter where the submenus are placed, because renpy automatically places them relative to the parent screen/window. Example:

Code: Select all

screen testMenu:
    textbutton "button1" action NullAction() xpos 0 ypos 0
    textbutton "button2" action NullAction() xpos 0 ypos 20
    
screen centerscreen:
    frame:
        background None
        xpos 0.5 ypos 0.5
        use testMenu

label start:
    # will be placed on the top left of the screen
    show screen testMenu
    # will show testMenu in the middle, but all buttons are shifted accordingly
    show screen centerscreen
But maybe you can also elaborate on what you're actually trying to do with that menu. What purpose does it have inside the game? Will the player expand the menu by unlocking stuff or does he literally give input that will expand the menus. In other words: Is it determined what items will actually be part of the menu once you are finished with the game? If so, then craft them manually! That's also way more flexible.

Post Reply

Who is online

Users browsing this forum: Google [Bot], Majestic-12 [Bot]