Ren'Py Detecting an imagebutton position on click.

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
Ok, I'm trying to do something, and I'm still working out a reasonable way to code it. (Note: I do have a couple alternative ideas if this one doesn't pan out or isn't easily feesible, but I would like to get this one to work). First, some visual aids.

1663028651570.png


Ok, so behold my amazing MS paint skills.

Looking at this image, let's ignore 2 things - the grid, and the yellow blob. So it's just a white screen and 2 red blobs - that represents the render being used as part of the background. The grid wouldn't be present in the game, that's only there to make this example easier to understand.

The yellow blob represents an imagebutton. In theory I want do do the following:

#1: break the screen up into a 3x3 grid (so 9 zones). This would be done on the back end, not visually - the grid wouldn't actually be displayed.

#2: Create an imagebutton that moves around the screen. Simple enough with a transform. Preferrably semi-randomly, but not necessary.

#3:(optional) determine (So far I think I'd have to determine it beforehand and hardcode this bit in) the positions of the "red blobs" (Again, these are not objects in the programming sense, they're just parts of the background image). The goal is that when the imagebutton moves around, it avoids areas currently inhabited by the red blobs. There would be multiple images, with the red blobs in different positions in each image, so my only thought now would be to determine beforehand their exact positions on screen, and hardcode that into the transform somehow, so that the movement of the imagebutton avoids those exact spots.

#4: The above 3 steps I at least have some idea of how to accomplish, but so far I haven't come up with this step yet. Still working on it. Detect what "zone" the imagebutton is in, at time of click. Preform an action based on the position of the button at the time. The action itself would also be variable based on the position of the red dots, but that part is much easier, it's the positional detection of the button at time of click I haven't figured out yet.



So that's the basics of what I want to do. Have an imagebutton move around the screen semi-randomly, have it avoid specific areas that would most likely be pre-defined (But variable depending on what image is currently loaded), and have variable actions preformed based on the location of the button when it's clicked.


I'll be working on this myself on and off for a bit, but if anyone has any ideas I'd appreciate it.



Edit: so the first thing I came across when looking around was this:

Code:
default mouse_xy = (0, 0)

init python:

    def get_mouse():
        global mouse_xy
        mouse_xy = renpy.get_mouse_pos()

screen mouse_test()

    on "show" action Function(get_mouse)

    text "[mouse_xy[0]], [mouse_xy[1]]"

    timer 0.01 repeat True action Function(get_mouse)

    key "mousedown_1" action Show("spark", x=mouse_xy[0], y=mouse_xy[1])

While not exactly what I need, it's close enough that I should be able to use that as a basis to get the exact mouse position at the time the imagebutton is clicked.

If that worked, then I'd just need the imagebutton action to call a function and pass the x/y coords to it - that function would need to be capable of processing the coords and preforming the variable action depending on their positioning. Ideally I'd also want to "Mark off" zones somehow within that function, that are inhabited by the red dots.

So using the above image for an example, calling the top left zone 1, top right zone 3, bottom right zone 9, etc...

Zone 1,4,6 and 9 are currently inhabited, zone 2,3,5,6,8 are empty. If the button was clicked while in zones 2,3,5,6,8, preform 1 action. If the button was clicked while in zone 1,4,6 or 9, preform a different action. This can't just be statically defined, since in theory the position of the red dots would change every time the image changed.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,206
#2: Create an imagebutton that moves around the screen. Simple enough with a transform.
Forget about the transform, and rely on the imagebutton position, then you've your answer.


#3: [...] The goal is that when the imagebutton moves around, it avoids areas currently inhabited by the red blobs.
Python:
init python:

    def moveIt():
        #  Endless loop. There should be always at least 5 moves
        # (out of 8) that will be valid, so it shouldn't loops too
        # much, even taking count of the randomization.
        while True:
            # Randomize the next position of the yellow button
            direction = renpy.random.randint( 0, 7 )

            if direction == 0:    # Up
                # Return if the new position is a valid one.
                if isValid( yellowX, yellowY - 1 ):
                    return
            elif direction == 1:  # Up & Right
                if isValid( yellowX + 1, yellowY - 1 ):
                    return
            elif direction == 2:  # Right
                if isValid( yellowX + 1, yellowY ):
                    return
            elif direction == 3:  # Down & Right
                if isValid( yellowX + 1, yellowY + 1 ):
                    return
            elif direction == 4:  # Down
                if isValid( yellowX, yellowY + 1 ):
                    return
            elif direction == 5:  # Down & Left
                if isValid( yellowX - 1, yellowY + 1 ):
                    return
            elif direction == 6:  # Left
                if isValid( yellowX - 1, yellowY ):
                    return
            elif direction == 7:  # Up & Left
                if isValid( yellowX - 1, yellowY - 1 ):
                    return

    # /!\ WARNING /!\
    #  I wrote this on the fly, I can have messed up a bit, and there's
    # probably a better way to do all this.
    def isValid( x, y ):
        # Out of the screen limit.
        if not ( 0 < x < config.screen_width and  0 < y < config.screen_height ):
            return False
        # Collision with first red button.
        # If a part of the yellow button is on the same row than the first red button
        if ( red1[0] < x < red1[0] + redWidth or red1[0] < x + yellowWidth < red1[0] + redWidth ):
            # And a part of the yellow button is on the same line than the first red button
            if ( red1[1] < y < red1[1] + redHeight or red1[1] < x + yellowHeight < red1[1] + redHeight ):
                # The position is not valid
                return False
        # Collision with the second red button.
        if ( red2[0] < x < red2[0] + redWidth or red2[0] < x + yellowWidth < red2[0] + redWidth ):
            if ( red2[1] < y < red2[1] + redHeight or red2[1] < x + yellowHeight < red2[1] + redHeight ):
                return False

        # The position is valid, update the effective position of the yellow button.
        store.yellowX = x
        store.yellowY = y
        return True


# Position of the yellow button
default yellowX = 0
default yellowY = 0
default yellowWidth = 20  # Width of the yellow button
default yellowHeight = 20 # Height of the yellow button

default red1 = ( 200, 50 )    # X,Y for the first red button
default red2 = ( 100, 100 )  # X,Y for the second red button
default redWidth = 20    # width of a red button
default redHeight = 20   # height of a red button


screen myScreen():

    # Every 0.1 second, update the position of the yellow button.
    timer 0.1 repeat True action Function( moveIt )

    # Display the yellow button,
    imagebutton:
        idle "yellowButton.png"
        # at its actual position.
        xpos yellowX
        ypos yellowY
        action NullAction()

#4: [...] Detect what "zone" the imagebutton is in, at time of click.
yellowX and yellowY give you that information.


The rest is just an adaptation of the code above. Just keep in mind that, as I said, I wrote it on the fly. So it's untested and I can have made some typos, or messed a bit the algorithm. But at least it give you the spirit of the solution.
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
That's a solid base to work from, but there's a couple of issues.

#1 - red1 and red2 would update (change position) every image. Because of the way I'm trying to build this, I can't know beforehand which one it'll update to. (There is no way to know. On one playthrough it might do image 1, then 7, then 6, on another it might do image 3, then 4, then 2, on a 3rd it might do 5 then 5 then 5 then 5. it's just not going to be predictable as it will vary depending on user input).

If I use your code as a base, I'd have to create over a dozen "red" variables, and set the proper one each time the image is updated. (DO-able, but a lot longer code).

2nd, if I'm reading your code right, it basically would teleport the button to seperate grids on every update, which isn't what I want.

My reasoning for using a transform is because I want smooth movement, so I was thinking of using some adaptation of a linear morph so it would slide across the screen smoothly.

That is also the reason I was looking into using exact mouse position, since I want it to essentially "glide" across the screen, and I don't want it just popping up in pre-defined positions.

This way, if I have the exact mouse position, I can throw that into a function with predefined actions based on which "zone" that particular mouse position is in. One of the things I hadn't figured out with that method, was how to "avoid" the red areas. I also haven't actually worked out the function itself at all.
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
To be clear, when I say "smooth movement" I'm looking for something along the lines of this:



Not exactly like that, and definitely faster, but that's the closest example I can think of.
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
To be clear, when I say "smooth movement" I'm looking for something along the lines of this:



Not exactly like that, and definitely faster, but that's the closest example I can think of.
I watched that gif for WAY too long waiting for it to hit the corner. Thankfully, it did!
 
Last edited:
  • Like
Reactions: KingArthur101

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,206
#1 - red1 and red2 would update (change position) every image.
And where's the issue exactly ?
The code is clearly designed to take in count this possibility, using variables in place of hardcoded values.


If I use your code as a base, I'd have to create over a dozen "red" variables, and set the proper one each time the image is updated. (DO-able, but a lot longer code).
Not to be harsh, but if it's really the only solution you can think about, you aren't ready for the kind of feature you want to implement.
I mean, why create dozen of "red" variables ? The obvious solution is to update the values of the two existing variables. After all, it's what variables are made for, to have a value that vary.


2nd, if I'm reading your code right, it basically would teleport the button to seperate grids on every update, which isn't what I want.
The code move the imagebutton by one fucking pixel at time ! Well, technically sometimes by two pixels, but it's still only one, diagonally.
Up to you to add grids limits or whatever you want in isValid function. I'm here to help you solve your issue, not to write the whole code for you.


My reasoning for using a transform is because I want smooth movement,
I used 0.1 second for the timer, but you can obviously change this value.
As Ren'Py is actually designed, you can goes up to a movement each 0.015 seconds, what is far more than what is needed to have smooth movements.


This way, if I have the exact mouse position, I can throw that into a function with predefined actions based on which "zone" that particular mouse position is in.
What is perfectly possible by having the exact position of the imagebutton the instant the player will click on it... And it's precisely what you have with yellowX and yellowY.


One of the things I hadn't figured out with that method, was how to "avoid" the red areas. I also haven't actually worked out the function itself at all.
Well, you have everything you want on the code I gave. But since you know that it will not works, while clearly having no idea regarding what the code effectively do, here's another answer:

To know the exact position of a displayable, you need a combination between and . Good luck to implement it, and to make it works with the "avoid red button" parts.
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
Damn you're an angry person. I'm fully appreciative of the help, but damn son.

I responded when I had just woken up. I guess I should have edited it? But I realized later on that it does the 1 pixel movement thing - at first I misread it and thought it was teleporting. I misread it entirely and was looking at it wrong, that's all. Once I woke up, I figured out what it was actually doing.


But, all of that said, I still see a few problems. First, the randomness is.... a bit too random. I'd have to enable the code to see what it looks like, but just looking at it, it looks like it would provide quite jarring (and back and forth) movement at times. I want it semi-random, but still smooth. Notice that gif I posted? It smoothly goes from left to right and up to down before going back in a different direction, there's no chance for it to suddenly reverse.

That said, taking what you wrote and changing it to do that isn't hard. Just adding a simple if clause in the function to not activate unless it's hit a certain position works just fine. (So up doesn't activate until it hits 1080, down doesnt activate until it hits 0, just for example.) Just thinking off the top of my head, I think the better option is to define a start position and trajectory, randomize the movement to the point of randomizing the angle but not the direction, and clause it like I mentioned above so it stays on trajectory until it hits a boundary.

But, this is the one part I have an actual issue with:


Not to be harsh, but if it's really the only solution you can think about, you aren't ready for the kind of feature you want to implement.
I mean, why create dozen of "red" variables ? The obvious solution is to update the values of the two existing variables. After all, it's what variables are made for, to have a value that vary.
And where's the issue exactly ?
The code is clearly designed to take in count this possibility, using variables in place of hardcoded values.
It's not really. You default 2 variables (you used random numbers, obviously I'd use specific ones but that's not the point). You use those for the "Collision detection" which works just fine.... but how are you updating the values of those variables?

Where are you getting the information from? Like I said, the actual values are static, pulled from the images. I *could* do some ridiculous and overly complicated nonsense to try and read the image and pull the values directly from it, (using pixel detection would by my first guess?) or I could create overlays and get the values from that....


Or I could just code in a couple dozen variables that have those static values already set in them. Sure in your example the "red1,red2" would be constantly updated, but from where? Where is the information coming from? The easiest solution, BY FAR, is to have multiple static variables with that information already in them, and send it to red1/red2.

When I said you'd need dozens of "red" variables this is what I meant - the information needs to come from somewhere, and there was nothing in your code to actually get that information, so the easiest way is just to store it beforehand.


Look dude. I honestly do appreciate the help, I'm probably going to adapt the code you provided to help do what I want, and I wanna say thanks for your assistance. However it's a little silly that you got grumpy and sarcastic because I questioned anything you said instead of instantly sucking your dick. I mean.... chill?
 

guest1492

Member
Apr 28, 2018
350
288
First, the randomness is.... a bit too random. I'd have to enable the code to see what it looks like, but just looking at it, it looks like it would provide quite jarring (and back and forth) movement at times. I want it semi-random, but still smooth.
You just have to set direction at the end of the loop (after the if-elif block). That way, it will only change direction if the current direction will lead to somewhere invalid.

You default 2 variables (you used random numbers, obviously I'd use specific ones but that's not the point). You use those for the "Collision detection" which works just fine.... but how are you updating the values of those variables?

[...]

Or I could just code in a couple dozen variables that have those static values already set in them. Sure in your example the "red1,red2" would be constantly updated, but from where? Where is the information coming from?
You would change the values of red1 and red2 whenever you change the image shown. Since presumably the images were created beforehand, you already know the coordinates of the red blobs and can code them in.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,206
Damn you're an angry person.
I'm grumpy, and it's not at 51yo that I'll change.



But, all of that said, I still see a few problems. First, the randomness is.... a bit too random.
Everything is possible, by making small change to the algorithm. Sometime there's possibly a better algorithm to choose for a given variation, but I keep the original one because it will be easier to understand the changes, and also less works for me.

For this particular case, just split the direction choice in two parts. Firstly decide if the direction will change, and then decide of the new direction only if it's expected to change.

Python:
# By default, randomly pick-up a new direction
default prevDirection = None

    def moveIt():
        while True:
            # No direction decided yet.
            direction = None

            # A direction is already defined
            if not prevDirection is None:
                # 30% chance that the direction change
                if renpy.random.randint( 0, 10 ) < 3:
                    direction = None
                # 70% chance to keep the previous direction
                else:
                    direction = prevDirection

            # No direction defined, pick a new one.
            if direction is None:
                direction = renpy.random.randint( 0, 7 )

            if direction == 0:    # Up
                # /!\ Change of paradigm /!\
                # If the position is not valid, perform a new loop
                if not isValid( yellowX, yellowY - 1 ):
                    continue
            elif direction == 1:  # Up & Right
                if not isValid( yellowX + 1, yellowY - 1 ):
                    continue
            elif direction == 2:  # Right
                if not isValid( yellowX + 1, yellowY ):
                    continue
            elif direction == 3:  # Down & Right
                if not isValid( yellowX + 1, yellowY + 1 ):
                    continue
            elif direction == 4:  # Down
                if not isValid( yellowX, yellowY + 1 ):
                    continue
            elif direction == 5:  # Down & Left
                if not isValid( yellowX - 1, yellowY + 1 ):
                    continue
            elif direction == 6:  # Left
                if not isValid( yellowX - 1, yellowY ):
                    continue
            elif direction == 7:  # Up & Left
                if not isValid( yellowX - 1, yellowY - 1 ):
                    continue

            # The new position is valid, keep a copy of the actual direction
            store.prevDirection = direction
            # and quit.
            return

But you can also decide that the direction will be kept for X iteration, at least as long as it don't lead to a collision.

/!\ WARNING /!\
This one is partly broken. "moveCount" is not redefined if "prevDirection" lead to an invalid movement.
Python:
# By default, randomly pick-up a new direction
default prevDirection = None
# By default, finished the previous move.
default moveCount = 0

    def moveIt():
        while True:
            # No direction decided yet.
            direction = None

            # A direction is already defined
            if not prevDirection is None:
                # The yellow button have finished its previous move
                if moveCount == 0:
                    direction = None
                else:
                    direction = prevDirection

            # No direction defined, pick a new one.
            if direction is None:
                direction = renpy.random.randint( 0, 7 )

            if direction == 0:    # Up
                # /!\ Change of paradigm /!\
                # If the position is not valid, perform a new loop
                if not isValid( yellowX, yellowY - 1 ):
                    continue
            elif direction == 1:  # Up & Right
                if not isValid( yellowX + 1, yellowY - 1 ):
                    continue
            elif direction == 2:  # Right
                if not isValid( yellowX + 1, yellowY ):
                    continue
            elif direction == 3:  # Down & Right
                if not isValid( yellowX + 1, yellowY + 1 ):
                    continue
            elif direction == 4:  # Down
                if not isValid( yellowX, yellowY + 1 ):
                    continue
            elif direction == 5:  # Down & Left
                if not isValid( yellowX - 1, yellowY + 1 ):
                    continue
            elif direction == 6:  # Left
                if not isValid( yellowX - 1, yellowY ):
                    continue
            elif direction == 7:  # Up & Left
                if not isValid( yellowX - 1, yellowY - 1 ):
                    continue

            # The new position is valid, keep a copy of the actual direction
            store.prevDirection = direction
            # One less move to do
            if moveCount > 0:
                store.moveCount -= 1
            # Else decide how long it will move on the current direction
            else:
                store.moveCount = 100 + renpy.random.randint( 10, 30 )
            # and quit.
            return

Or that the movement change only in case of collision.

/!\ WARNING /!\
This one is partly broken.
1) After the first move, the yellow button will only move in diagonal, what physically realistic, but only half pleasant. This said, make it only move in diagonal and you can reduce the direction to 4.
2) There's a slowdown effect since the move will only occur during the next frame ; add one more frame if the collision happen in a corner of the screen.
Python:
# By default, move to the left
default direction = 6

   def moveIt():
        if direction == 0:    # Up
            if isValid( yellowX, yellowY - 1 ):
                return
        elif direction == 1:  # Up & Right
            if isValid( yellowX + 1, yellowY - 1 ):
                return
        elif direction == 2:  # Right
            if isValid( yellowX + 1, yellowY ):
                return
        elif direction == 3:  # Down & Right
            if isValid( yellowX + 1, yellowY + 1 ):
                return
        elif direction == 4:  # Down
            if isValid( yellowX, yellowY + 1 ):
                return
        elif direction == 5:  # Down & Left
            if isValid( yellowX - 1, yellowY + 1 ):
                return
        elif direction == 6:  # Left
            if isValid( yellowX - 1, yellowY ):
                return
        elif direction == 7:  # Up & Left
            if isValid( yellowX - 1, yellowY - 1 ):
                return

        # A collision occurred, change the direction
        if direction == 0:    # Up
            # Move down now, but will it be to the left, or to the right ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 3 # Down & Right
            else:
                store.direction = 5 # Down & Left
        elif direction == 1:  # Up & Right
            # Move Down & Right
            store.direction = 3
        elif direction == 2:  # Right
            # Move left now, but will it be up, or down ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 5 # Down & Left
            else:
                store.direction = 7 # Up & Left
        elif direction == 3:  # Down & Right
            # Move Up & Right
            store.direction = 1
        elif direction == 4:  # Down
            # Move up now, but will it be to the left, or to the righ ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 1 # Up & Right
            else:
                store.direction = 7 # Up & Left
        elif direction == 5:  # Down & Left
            # Move Up & Left
            store.direction = 7
        elif direction == 6:  # Left
            # Move right now, but will it be up, or down ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 1 # Up & Right
            else:
                store.direction = 3 # Down & Right
        elif direction == 7:  # Up & Left
            # Move Down & Left
            store.direction = 5

But, this is the one part I have an actual issue with:
[...] but how are you updating the values of those variables?
Hmm... By assigning a new value to "red1" and "red2" ?

Python:
label whatever:
    # Reset yellow button
    $ yellowX = 200
    $ yellowY = 100
    # possibly reset the direction and move count

    # then change the location of the red buttons.
    $ red1 = (500, 200)
    $ red2 = (100, 800)
    # Make the move starts

Where are you getting the information from? Like I said, the actual values are static, pulled from the images.
Well, in the same way that you intended to get it at first.
I mean, you asked for a way to know the position of the yellow button, so it will avoid the red ones. But at no time you asked for a way to know the position of the red buttons, what mean that either you know it, or you've already a way to get it.


Where is the information coming from? The easiest solution, BY FAR, is to have multiple static variables with that information already in them, and send it to red1/red2.
*sigh*

Python:
define redPositions = { 
        "image1.jpg": ( ( 100, 100 ), ( 200, 100 ) ),
        [...]
        "imageX.jpg": ( ( 800, 500 ), ( 300, 1200 ) ),
        [...]
        "imageN.jpg": ( ( 200, 700 ), ( 100, 500 ) ) }

label whatever:
    $ red1 = redPositions["image1.jpg"][0]
    $ red2 = redPositions["image1.jpg"][1]
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,715
2,721
I'm grumpy, and it's not at 51yo that I'll change.





Everything is possible, by making small change to the algorithm. Sometime there's possibly a better algorithm to choose for a given variation, but I keep the original one because it will be easier to understand the changes, and also less works for me.

For this particular case, just split the direction choice in two parts. Firstly decide if the direction will change, and then decide of the new direction only if it's expected to change.

Python:
# By default, randomly pick-up a new direction
default prevDirection = None

    def moveIt():
        while True:
            # No direction decided yet.
            direction = None

            # A direction is already defined
            if not prevDirection is None:
                # 30% chance that the direction change
                if renpy.random.randint( 0, 10 ) < 3:
                    direction = None
                # 70% chance to keep the previous direction
                else:
                    direction = prevDirection

            # No direction defined, pick a new one.
            if direction is None:
                direction = renpy.random.randint( 0, 7 )

            if direction == 0:    # Up
                # /!\ Change of paradigm /!\
                # If the position is not valid, perform a new loop
                if not isValid( yellowX, yellowY - 1 ):
                    continue
            elif direction == 1:  # Up & Right
                if not isValid( yellowX + 1, yellowY - 1 ):
                    continue
            elif direction == 2:  # Right
                if not isValid( yellowX + 1, yellowY ):
                    continue
            elif direction == 3:  # Down & Right
                if not isValid( yellowX + 1, yellowY + 1 ):
                    continue
            elif direction == 4:  # Down
                if not isValid( yellowX, yellowY + 1 ):
                    continue
            elif direction == 5:  # Down & Left
                if not isValid( yellowX - 1, yellowY + 1 ):
                    continue
            elif direction == 6:  # Left
                if not isValid( yellowX - 1, yellowY ):
                    continue
            elif direction == 7:  # Up & Left
                if not isValid( yellowX - 1, yellowY - 1 ):
                    continue

            # The new position is valid, keep a copy of the actual direction
            store.prevDirection = direction
            # and quit.
            return

But you can also decide that the direction will be kept for X iteration, at least as long as it don't lead to a collision.

/!\ WARNING /!\
This one is partly broken. "moveCount" is not redefined if "prevDirection" lead to an invalid movement.
Python:
# By default, randomly pick-up a new direction
default prevDirection = None
# By default, finished the previous move.
default moveCount = 0

    def moveIt():
        while True:
            # No direction decided yet.
            direction = None

            # A direction is already defined
            if not prevDirection is None:
                # The yellow button have finished its previous move
                if moveCount == 0:
                    direction = None
                else:
                    direction = prevDirection

            # No direction defined, pick a new one.
            if direction is None:
                direction = renpy.random.randint( 0, 7 )

            if direction == 0:    # Up
                # /!\ Change of paradigm /!\
                # If the position is not valid, perform a new loop
                if not isValid( yellowX, yellowY - 1 ):
                    continue
            elif direction == 1:  # Up & Right
                if not isValid( yellowX + 1, yellowY - 1 ):
                    continue
            elif direction == 2:  # Right
                if not isValid( yellowX + 1, yellowY ):
                    continue
            elif direction == 3:  # Down & Right
                if not isValid( yellowX + 1, yellowY + 1 ):
                    continue
            elif direction == 4:  # Down
                if not isValid( yellowX, yellowY + 1 ):
                    continue
            elif direction == 5:  # Down & Left
                if not isValid( yellowX - 1, yellowY + 1 ):
                    continue
            elif direction == 6:  # Left
                if not isValid( yellowX - 1, yellowY ):
                    continue
            elif direction == 7:  # Up & Left
                if not isValid( yellowX - 1, yellowY - 1 ):
                    continue

            # The new position is valid, keep a copy of the actual direction
            store.prevDirection = direction
            # One less move to do
            if moveCount > 0:
                store.moveCount -= 1
            # Else decide how long it will move on the current direction
            else:
                store.moveCount = 100 + renpy.random.randint( 10, 30 )
            # and quit.
            return

Or that the movement change only in case of collision.

/!\ WARNING /!\
This one is partly broken.
1) After the first move, the yellow button will only move in diagonal, what physically realistic, but only half pleasant. This said, make it only move in diagonal and you can reduce the direction to 4.
2) There's a slowdown effect since the move will only occur during the next frame ; add one more frame if the collision happen in a corner of the screen.
Python:
# By default, move to the left
default direction = 6

   def moveIt():
        if direction == 0:    # Up
            if isValid( yellowX, yellowY - 1 ):
                return
        elif direction == 1:  # Up & Right
            if isValid( yellowX + 1, yellowY - 1 ):
                return
        elif direction == 2:  # Right
            if isValid( yellowX + 1, yellowY ):
                return
        elif direction == 3:  # Down & Right
            if isValid( yellowX + 1, yellowY + 1 ):
                return
        elif direction == 4:  # Down
            if isValid( yellowX, yellowY + 1 ):
                return
        elif direction == 5:  # Down & Left
            if isValid( yellowX - 1, yellowY + 1 ):
                return
        elif direction == 6:  # Left
            if isValid( yellowX - 1, yellowY ):
                return
        elif direction == 7:  # Up & Left
            if isValid( yellowX - 1, yellowY - 1 ):
                return

        # A collision occurred, change the direction
        if direction == 0:    # Up
            # Move down now, but will it be to the left, or to the right ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 3 # Down & Right
            else:
                store.direction = 5 # Down & Left
        elif direction == 1:  # Up & Right
            # Move Down & Right
            store.direction = 3
        elif direction == 2:  # Right
            # Move left now, but will it be up, or down ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 5 # Down & Left
            else:
                store.direction = 7 # Up & Left
        elif direction == 3:  # Down & Right
            # Move Up & Right
            store.direction = 1
        elif direction == 4:  # Down
            # Move up now, but will it be to the left, or to the righ ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 1 # Up & Right
            else:
                store.direction = 7 # Up & Left
        elif direction == 5:  # Down & Left
            # Move Up & Left
            store.direction = 7
        elif direction == 6:  # Left
            # Move right now, but will it be up, or down ?
            if renpy.random.randint( 0, 1 ) == 0:
                store.direction = 1 # Up & Right
            else:
                store.direction = 3 # Down & Right
        elif direction == 7:  # Up & Left
            # Move Down & Left
            store.direction = 5



Hmm... By assigning a new value to "red1" and "red2" ?

Python:
label whatever:
    # Reset yellow button
    $ yellowX = 200
    $ yellowY = 100
    # possibly reset the direction and move count

    # then change the location of the red buttons.
    $ red1 = (500, 200)
    $ red2 = (100, 800)
    # Make the move starts



Well, in the same way that you intended to get it at first.
I mean, you asked for a way to know the position of the yellow button, so it will avoid the red ones. But at no time you asked for a way to know the position of the red buttons, what mean that either you know it, or you've already a way to get it.




*sigh*

Python:
define redPositions = {
        "image1.jpg": ( ( 100, 100 ), ( 200, 100 ) ),
        [...]
        "imageX.jpg": ( ( 800, 500 ), ( 300, 1200 ) ),
        [...]
        "imageN.jpg": ( ( 200, 700 ), ( 100, 500 ) ) }

label whatever:
    $ red1 = redPositions["image1.jpg"][0]
    $ red2 = redPositions["image1.jpg"][1]

Heh.

I took the base you wrote (so thank for that), and made it work. I edited it a bunch and created a priority system. Basically it always first tries to maintain direction, then changes if it can't. (ex: it's going down/left and collides - it'll try to go up/left, then left, if it can't then it'll try to go 3 different directions right, and finally if it can't, it'll go up/down for a bit and then try to go left/right again. If it's headed right, reverse that).

Then I created a function for grid detection based on coords, and edited the numbers a bit so it's significantly faster. Mostly it's working properly, it does need to be fixed a little bit to be smoother - but I'm 90% sure that's just a matter of fixing the priority system I set up. I did have 2 questions though:

#1, later on in the script (related to all of this) I'm doing something like this:

$partnerhit = renpy.random.choice(['y','y','y','n'])

basically just creating a 75% chance of something happening. Do you know of a better way to do this? I looked into it briefly and I couldn't find anything in renpy that handled weighted choices, outside of creating a weighting function, but doing it the above way just seemed easier than that.

Second:

for example, you have this:

if ( red1[0] < x < red1[0] + redWidth or red1[0] < x + yellowWidth < red1[0] + redWidth ):

So that's what's "detecting" the collision. Do you know what part of the image is being used? I'm working under the assumption it's the top left corner of the image (Or rather the coords correspond to the top left corner), but I just wanted to double check that.