Automatically align imagebuttons around a coordinate?

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.
Message
Author
Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Automatically align imagebuttons around a coordinate?

#1 Post by Georgine »

Let's say I have twelve imagebuttons that I want to display in a circle on my screen: do I need to set coords for each button manually, or is there an easy way for me to set a center point, give it a list of the imagebuttons I have, and tell it to space and align in a circle around my center point?

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#2 Post by Asceai »

Use a for loop in the screen to specify the imagebuttons. Import math and you can use math.sin/cos to specify the coordinates.

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#3 Post by Georgine »

I'm afraid my math is pretty bad, could you help me understand exactly how I can use math.sin/cos?

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#4 Post by Asceai »

In the screen you want the buttons to appear in:

Code: Select all

    $import math
    $center_x = 400
    $center_y = 300
    $radius = 100
    $button_info = [("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction()),
                    ("idle.png", "hover.png", NullAction())]
    $button_idx = 0
    for btn in button_info:
      $angle = button_idx * math.pi * 2 / len(button_info)
      $x = math.cos(angle) * radius + center_x
      $y = math.sin(angle) * radius + center_y
      imagebutton idle btn[0] hover btn[1] action btn[2] xanchor 0.5 yanchor 0.5 xpos int(x) ypos int(y)
      $button_idx += 1
The graphics and actions for each of the buttons (I've only specified idle and hover images, but you can add more) are specified in button_info, which is a list of tuples.

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#5 Post by Georgine »

Holy cow, that is exactly what I've been trying to do for 2 weeks... adapted it to my project and it works perfectly! Thank you very much ^^

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#6 Post by Georgine »

Quick follow-up to the above- if I want to change the idle and hover images of one of the imagebuttons in response to player input, how do I specify which imagebutton I want to change? I know I can use selected_idle and selected_hover as the attributes to change, but I'm not sure how to target a specific imagebutton within the group.

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#7 Post by Asceai »

That's a good question! It's somewhat complex and, as far as I can tell, undocumented.

Basically, an imagebutton (or anything else) is 'selected' if the action associated tells it that it is selected. If there are multiple actions (for example, because a list or tuple of actions was supplied) if any of them returns True when its get_selected() method is called, the button is selected.

Hence, one way of making a button selected is to give it an action that does nothing but returns True when get_selected() is called (in addition to the action that actually does what you want the button to do). For example, SetVariable will return true to get_selected if the variable is already the value you are trying to set it to. So you could do this:

Code: Select all

init python:
  dummy_true_value = True
and then have a button with this:

Code: Select all

action [NullAction(), SetVariable("dummy_true_value", True)]
(where NullAction() is the actual action the button is designed to perform)
This should ensure that button is always marked as selected. You can control this by using If(). For example, if you want the value to be selected only if the 'button_selected' variable is True, you can do this:

Code: Select all

action [NullAction(), If(button_selected, true=SetVariable("dummy_true_value", True), false=NullAction())]
Alternatively, you could create a custom action class that takes a param that tells it whether to return True for get_selected() or not, and this is probably the best way if you're going to be doing this a lot.

EDIT: Of course, you can't make an imagebutton not selected if there's some other action that's causing it to be listed as being selected. You can probably throw in a custom python function in place of an action for certain purposes, as these will never count as being selected.


EDIT 2: Here's a potential Selected action to make this cleaner:

Code: Select all

init python:
  class Selected(Action):
    def __init__(self, is_selected):
      self.is_selected = is_selected
    def get_selected(self):
      return self.is_selected
    def __call__(self):
      pass
With this, you could replace this:

Code: Select all

action [NullAction(), If(button_selected, true=SetVariable("dummy_true_value", True), false=NullAction())]
with this:

Code: Select all

action [NullAction(), Selected(button_selected)]

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#8 Post by Georgine »

Ooh this is really helpful, thank you! I understand the concept, but bear with me as I work out the syntax- if I adapt the example you gave in your second edit and created a Selected action, am I correct that to implement it, in every imagebutton within the $button_info = [] block, I would want to replace

Code: Select all

("idleimage.png", "hoverimage.png", NullAction())
with

Code: Select all

("idleimage.png", "hoverimage.png",action [NullAction(), Selected(button_selected)])
, and then replace the NullAction() within that action block with whatever I wanted the button to do in addition to returning True for get_selected()?

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#9 Post by Asceai »

Yes, minus the text 'action'

Code: Select all

("idleimage.png", "hoverimage.png", [NullAction(), Selected(button_selected)])
In addition, you probably want selected_idle and selected_hover images, so you'd want to expand the contents of the tuple:

Code: Select all

("idleimage.png", "hoverimage.png", "selectedidleimage.png", "selectedhoverimage.png", [NullAction(), Selected(button_selected)])
and

Code: Select all

imagebutton idle btn[0] hover btn[1] selected_idle btn[2] selected_hover btn[3] action btn[4] xanchor 0.5 yanchor 0.5 xpos int(x) ypos int(y)
or something like that.

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#10 Post by Georgine »

Excellent, thank you! This is the very final thing, but there's a logical leap I'm not making with what I'm using this for: after the player has made whatever changes they wish to the images, I basically want to evaluate each of the imagebuttons in order, and depending on which image has been assigned, jump to a specific label (all of which end with a return that kicks the player back to this routine). What I can't figure out is how to efficiently set the right destination- the best solution I've come upon is to keep a variable for each imagebutton, set that variable equal to the name of the correct label when the player assigns a new image to the button, and adding

Code: Select all

jump expression button1_destination
.

Do you think that's a reasonable way to approach the problem, or am I being too roundabout in my solution?

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#11 Post by Asceai »

I don't exactly understand what you're doing, but it sounds reasonable enough.

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#12 Post by Georgine »

Outstanding, thank you so much for your help- in a few posts you've helped with stuff I've been slamming my head into for a good while ^^

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#13 Post by Georgine »

I hope you don't mind a quick follow-up Asceai: I implemented everything we discussed and everything runs fine, but when I adjusted the tuples for five images instead of just idle/hover, I started getting TypeError: 'str' object is not callable whenever I clicked on an imagebutton. I know that usually means something is in quotes that shouldn't be, but I've been combing through my syntax and can't figure out where the problem is. Icons switch between idle/hover fine, but whenever I click on one to select it, the line that calls interface_screen generates the error:

Code: Select all

screen interface_screen:
    $import math
    $center_x = 400
    $center_y = 300
    $radius = 250
    $button_info =  [("iconidle.png", "iconhover.png", "iconselectedidle.png", "iconselectedhover.png", "iconaction.png", [ui.callsinnewcontext ("selected_action_screen_label"), Selected(button_selected)]),
                    ("icon2idle.png", "icon2hover.png", "icon2selectedidle.png", "icon2selectedhover.png", "icon2action.png", [ui.callsinnewcontext ("selected_action_screen_label"), Selected(button_selected)]),
                    ("icon3idle.png", "icon3hover.png", "icon3selectedidle.png", "icon3selectedhover.png", "icon3action.png", [ui.callsinnewcontext ("selected_action_screen_label"), Selected(button_selected)])]
    $button_idx = 0
    for btn in button_info:
        $angle = button_idx * math.pi * 2 / len(button_info)
        $x = math.cos(angle) * radius + center_x
        $y = math.sin(angle) * radius + center_y
        imagebutton idle btn[0] hover btn[1] selected_idle btn[2] selected_hover btn[3] action btn[4] xanchor 0.5 yanchor 0.5 xpos int(x) ypos int(y)
        $button_idx += 1
    textbutton "Activate" xpos 0.5 ypos 0.5 action ui.callsinnewcontext ("activation_code_label") 

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: Automatically align imagebuttons around a coordinate?

#14 Post by Asceai »

It's because action btn[4] needs to be an action, not an image, and your btn[4] is "iconaction.png", "icon2action.png" etc.
I'm not sure if you can specify, with screen language, an activate image, but for now the easiest fix is to just remove the action image from the tuples

Code: Select all

    $button_info =  [("iconidle.png", "iconhover.png", "iconselectedidle.png", "iconselectedhover.png", [ui.callsinnewcontext ("selected_action_screen_label"), Selected(button_selected)]),
etc.

Georgine
Regular
Posts: 55
Joined: Fri Jan 24, 2014 7:47 am
Contact:

Re: Automatically align imagebuttons around a coordinate?

#15 Post by Georgine »

And it works perfectly, thank you! Goes to show what happens when you forget to comment half-written code and then leave it for a few days. XD

Post Reply

Who is online

Users browsing this forum: apocolocyntose, simple_human