Is this possible?

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
Vexzox
Newbie
Posts: 7
Joined: Tue May 26, 2020 2:06 pm
Contact:

Is this possible?

#1 Post by Vexzox »

Hiya! I hope you all are doing great today. :wink:
I had a question:

Is it possible to make a character's eyes follow the mouse movement? If so i would like an example code if that's not too much to ask :D If not, understandable!

Vexzox
Newbie
Posts: 7
Joined: Tue May 26, 2020 2:06 pm
Contact:

Re: Is this possible?

#2 Post by Vexzox »

Also i don't know if i posted this in the right category ^^'' Sorry, i'm new to this

rayminator
Miko-Class Veteran
Posts: 793
Joined: Fri Feb 09, 2018 12:05 am
Location: Canada
Contact:

Re: Is this possible?

#3 Post by rayminator »

it's not impossible but you need to know python and javascript to do this and that the eyes can't be connected to the characters head at all and you will have adjust the eyes every time that the character moves to a different location

this is more advance and complicated to do but it can be done I have other game do that

and you did post it in the right spot

User avatar
trooper6
Lemma-Class Veteran
Posts: 3712
Joined: Sat Jul 09, 2011 10:33 pm
Projects: A Close Shave
Location: Medford, MA
Contact:

Re: Is this possible?

#4 Post by trooper6 »

I don't think they'd need to know javascript, but python will definitely be a thing. Probably a User Defined Displayable.
A Close Shave:
*Last Thing Done (Aug 17): Finished coding emotions and camera for 4/10 main labels.
*Currently Doing: Coding of emotions and camera for the labels--On 5/10
*First Next thing to do: Code in all CG and special animation stuff
*Next Next thing to do: Set up film animation
*Other Thing to Do: Do SFX and Score (maybe think about eye blinks?)
Check out My Clock Cookbook Recipe: http://lemmasoft.renai.us/forums/viewto ... 51&t=21978

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Contact:

Re: Is this possible?

#5 Post by hell_oh_world »

I'm not sure with the with Javascript on why it ended up as an answer, since Python is the language of Ren'Py engine. Trooper's right. You should have a fair and working knowledge of python, especially classes and functions and resort to CDD (Creator-Defined Displayables).

rayminator
Miko-Class Veteran
Posts: 793
Joined: Fri Feb 09, 2018 12:05 am
Location: Canada
Contact:

Re: Is this possible?

#6 Post by rayminator »

javascript for the mouse movement that so python can follow the mouse around not in less there another way that I don't of?

User avatar
Per K Grok
Miko-Class Veteran
Posts: 882
Joined: Fri May 18, 2018 1:02 am
Completed: the Ghost Pilot, Sea of Lost Ships, Bubbles and the Pterodactyls, Defenders of Adacan Part 1-3, the Phantom Flyer
itch: per-k-grok
Location: Sverige
Contact:

Re: Is this possible?

#7 Post by Per K Grok »

rayminator wrote: Wed Jun 10, 2020 5:13 pm javascript for the mouse movement that so python can follow the mouse around not in less there another way that I don't of?
You could use renpy.get_mouse_pos() to get the cursor position, use a timer or a loop to keep getting updated positions. Use math, the cursor position and the known position of the middle of the face to calculate the angle between the two points and use that to decide which of a a number of images of eye positions to show.

rayminator
Miko-Class Veteran
Posts: 793
Joined: Fri Feb 09, 2018 12:05 am
Location: Canada
Contact:

Re: Is this possible?

#8 Post by rayminator »

here is a example what I mean but ignore the html & css I'm not talking about that it's just that java can make a lot smoother adjustments it's just that it help python to understands the mouse movement must better instead of bring rough movements

https://codepen.io/Chuloo/pen/RQYbvm

instead bitching at me maybe you should help him it's that I'm just at basic level with python I'm advance level with html/php/css/mysql and javascript I am still learn python

python only can do 4 ways of movement right,left,up and down with eye movements with java you can go all circle around

it's just that he didn't give much detail what he wanted

strayerror
Regular
Posts: 159
Joined: Fri Jan 04, 2019 3:44 pm
Contact:

Re: Is this possible?

#9 Post by strayerror »

First, seems like there's a few misconceptions to to clean up here:

1) You can't use Javascript with Ren'Py.
2) Python is definitely capable of handling the maths involved in locating the cursor relative to a point in 2D space.
3) I don't think anyone here is bashing anyone else, just trying to share knowledge and avoid confusing matters. :)

The algorithm supplied by rayminator (while in Javascript) is exactly what the doctor ordered, we just need to port it to Python so that we can put it to work in Ren'Py.

Code: Select all

# original javascript
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
This is perfect as Ren'Py transforms allow us to specify a rotation in degrees, so all we need to do is to find the input values, and apply the result to a transform.

Code: Select all

# python version
rotate = math.atan2(x, y) * -180 / math.pi + 180
One way to access mouse data when it changes (rather than on a fixed interval) is by using a Creator Defined Displayable, this will give us visibility on events, allowing us to update how the eyes will be rotated.

As our new displayable will need to hold a child (so you can put the image of an eye inside), rather than re-invent the wheel, we can start from Ren'Py's Window class. We could have used the Container class, however the advantage of starting with Window will become apparent in a moment.

Code: Select all

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)
We now have a pretty standard Window displayable, however when we create it we wrap the child in a Transform, this is what will allow us to manipulate it later. Next we need to start thinking about event handling. Thankfully the magic that is Ren'Py automatically adjusts event data to make it more relevant to our displayable, and the mouse position will be given to us relative to the top left corner of our displayable. As we want to rotate about the centre of our displayble and not the top left corner, it's necessary for us to do a little maths to adjust the values provided to us to be relative to the centre instead. This is where we see the benefit of having chosen to subclass Window, as a Window keeps track of it's size so we don't have to, inside the window_size instance variable.

Code: Select all

        def event(self, ev, x, y, st):
            width, height = self.window_size
            cx = x - width / 2.
            cy = y - height / 2.
            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180
            renpy.redraw(self.child, 0)
            return super(Tracker, self).event(ev, x, y, st)
The code above reads the window x and y sizes, cuts them in half to find the middle and substracts them from the input mouse position that was relative to the top left corner. This leaves x and y relative to the centre of the displayable. We then feed those adjusted values into the algorithm we ported over to Python earlier to yield the angle which describes the cursor position relative to the displayable (where a value of 0 describes a cursor directly above the displayable). Then we simply apply that value to the transform we wrapped around the child displayable. In order for this change to be seen on screen we must inform renpy that a redraw operation is required by the child, that is the purpose of the renpy.redraw call; without this the values will still be adjusted but the visual representation on screen will not reflect them. Finally we forward the event to the parent class so that it may be resolved normally.

The resulting CDD will actively rotate its children to orient toward the cursor so long as it is within the bounds of the game window. This is a basic example and does not check things like event type or that a redraw is actually required (if the rotate value was the same, then not calling redraw would help performance.) Such improvements are left as an exercise for the reader and would be highly recommended before attempting to use in production. A complete example of the solution outlined may be found below for reference, playing around with and improving.

Getting more specific about the OP's eye problem, each iris/eyeball would need to be wrapped in an instance of the CDD and layered behind a face with transparent eye-sockets. One limitation with this approach is that the eyes would always be on the edge of the eye-socket, there is no expectation they could ever stare straight out of the screen, however hopefully this is still useful in the scenario outlined.

Some things to consider if hoping to build on this for use in production:
- Only redraw if the rotation value has changed.
- Only calculate rotation for mouse events.
- Are there times when the eyes shouldn't track the cursor?
- Will importing math globally conflict with any variables already in use.

Hope you found this helpful, any feedback is appreciated. :)

---

Code: Select all

# The complete Tracker CDD example.
init python:
    '''
    This is sample code intended to help understanding and to provide
    a basic example with which to experiment. It is NOT intended for
    production use!
    '''

    import math

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)

        def event(self, ev, x, y, st):
            width, height = self.window_size

            cx = x - width / 2.
            cy = y - height / 2.

            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180

            renpy.redraw(self.child, 0)

            return super(Tracker, self).event(ev, x, y, st)

# This is just making an arrow shape so the example doesn't need an image.
image line = Solid('000', xysize=(20, 87), anchor=(.5, 0.))
image shape = Fixed(
    Solid('000', xysize=(20, 200), xalign=(.5)),
    Transform('line', pos=(.25, -.1), rotate=45),
    Transform('line', pos=(.75, -.1), rotate=-45),
    xysize=(100, 200))
image arrow = Tracker('shape')

# Little example script.
label start:
    scene
    show arrow at truecenter
    "Your mouse cursor is in this direction!"
    return

rayminator
Miko-Class Veteran
Posts: 793
Joined: Fri Feb 09, 2018 12:05 am
Location: Canada
Contact:

Re: Is this possible?

#10 Post by rayminator »

strayerror wrote: Wed Jun 10, 2020 10:58 pm First, seems like there's a few misconceptions to to clean up here:

1) You can't use Javascript with Ren'Py.
2) Python is definitely capable of handling the maths involved in locating the cursor relative to a point in 2D space.
3) I don't think anyone here is bashing anyone else, just trying to share knowledge and avoid confusing matters. :)

The algorithm supplied by rayminator (while in Javascript) is exactly what the doctor ordered, we just need to port it to Python so that we can put it to work in Ren'Py.

Code: Select all

# original javascript
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
This is perfect as Ren'Py transforms allow us to specify a rotation in degrees, so all we need to do is to find the input values, and apply the result to a transform.

thank you for explain that I try to say for bests that I can say there are a lot people the that don't under stand javascript and that but there thing I have to learn about python still

Code: Select all

# python version
rotate = math.atan2(x, y) * -180 / math.pi + 180
One way to access mouse data when it changes (rather than on a fixed interval) is by using a Creator Defined Displayable, this will give us visibility on events, allowing us to update how the eyes will be rotated.

As our new displayable will need to hold a child (so you can put the image of an eye inside), rather than re-invent the wheel, we can start from Ren'Py's Window class. We could have used the Container class, however the advantage of starting with Window will become apparent in a moment.

Code: Select all

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)
We now have a pretty standard Window displayable, however when we create it we wrap the child in a Transform, this is what will allow us to manipulate it later. Next we need to start thinking about event handling. Thankfully the magic that is Ren'Py automatically adjusts event data to make it more relevant to our displayable, and the mouse position will be given to us relative to the top left corner of our displayable. As we want to rotate about the centre of our displayble and not the top left corner, it's necessary for us to do a little maths to adjust the values provided to us to be relative to the centre instead. This is where we see the benefit of having chosen to subclass Window, as a Window keeps track of it's size so we don't have to, inside the window_size instance variable.

Code: Select all

        def event(self, ev, x, y, st):
            width, height = self.window_size
            cx = x - width / 2.
            cy = y - height / 2.
            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180
            renpy.redraw(self.child, 0)
            return super(Tracker, self).event(ev, x, y, st)
The code above reads the window x and y sizes, cuts them in half to find the middle and substracts them from the input mouse position that was relative to the top left corner. This leaves x and y relative to the centre of the displayable. We then feed those adjusted values into the algorithm we ported over to Python earlier to yield the angle which describes the cursor position relative to the displayable (where a value of 0 describes a cursor directly above the displayable). Then we simply apply that value to the transform we wrapped around the child displayable. In order for this change to be seen on screen we must inform renpy that a redraw operation is required by the child, that is the purpose of the renpy.redraw call; without this the values will still be adjusted but the visual representation on screen will not reflect them. Finally we forward the event to the parent class so that it may be resolved normally.

The resulting CDD will actively rotate its children to orient toward the cursor so long as it is within the bounds of the game window. This is a basic example and does not check things like event type or that a redraw is actually required (if the rotate value was the same, then not calling redraw would help performance.) Such improvements are left as an exercise for the reader and would be highly recommended before attempting to use in production. A complete example of the solution outlined may be found below for reference, playing around with and improving.

Getting more specific about the OP's eye problem, each iris/eyeball would need to be wrapped in an instance of the CDD and layered behind a face with transparent eye-sockets. One limitation with this approach is that the eyes would always be on the edge of the eye-socket, there is no expectation they could ever stare straight out of the screen, however hopefully this is still useful in the scenario outlined.

Some things to consider if hoping to build on this for use in production:
- Only redraw if the rotation value has changed.
- Only calculate rotation for mouse events.
- Are there times when the eyes shouldn't track the cursor?
- Will importing math globally conflict with any variables already in use.

Hope you found this helpful, any feedback is appreciated. :)

---

Code: Select all

# The complete Tracker CDD example.
init python:
    '''
    This is sample code intended to help understanding and to provide
    a basic example with which to experiment. It is NOT intended for
    production use!
    '''

    import math

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)

        def event(self, ev, x, y, st):
            width, height = self.window_size

            cx = x - width / 2.
            cy = y - height / 2.

            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180

            renpy.redraw(self.child, 0)

            return super(Tracker, self).event(ev, x, y, st)

# This is just making an arrow shape so the example doesn't need an image.
image line = Solid('000', xysize=(20, 87), anchor=(.5, 0.))
image shape = Fixed(
    Solid('000', xysize=(20, 200), xalign=(.5)),
    Transform('line', pos=(.25, -.1), rotate=45),
    Transform('line', pos=(.75, -.1), rotate=-45),
    xysize=(100, 200))
image arrow = Tracker('shape')

# Little example script.
label start:
    scene
    show arrow at truecenter
    "Your mouse cursor is in this direction!"
    return

rayminator
Miko-Class Veteran
Posts: 793
Joined: Fri Feb 09, 2018 12:05 am
Location: Canada
Contact:

Re: Is this possible?

#11 Post by rayminator »

strayerror wrote: Wed Jun 10, 2020 10:58 pm First, seems like there's a few misconceptions to to clean up here:

1) You can't use Javascript with Ren'Py.
2) Python is definitely capable of handling the maths involved in locating the cursor relative to a point in 2D space.
3) I don't think anyone here is bashing anyone else, just trying to share knowledge and avoid confusing matters. :)

The algorithm supplied by rayminator (while in Javascript) is exactly what the doctor ordered, we just need to port it to Python so that we can put it to work in Ren'Py.

Code: Select all

# original javascript
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
This is perfect as Ren'Py transforms allow us to specify a rotation in degrees, so all we need to do is to find the input values, and apply the result to a transform.

Code: Select all

# python version
rotate = math.atan2(x, y) * -180 / math.pi + 180
One way to access mouse data when it changes (rather than on a fixed interval) is by using a Creator Defined Displayable, this will give us visibility on events, allowing us to update how the eyes will be rotated.

As our new displayable will need to hold a child (so you can put the image of an eye inside), rather than re-invent the wheel, we can start from Ren'Py's Window class. We could have used the Container class, however the advantage of starting with Window will become apparent in a moment.

Code: Select all

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)
We now have a pretty standard Window displayable, however when we create it we wrap the child in a Transform, this is what will allow us to manipulate it later. Next we need to start thinking about event handling. Thankfully the magic that is Ren'Py automatically adjusts event data to make it more relevant to our displayable, and the mouse position will be given to us relative to the top left corner of our displayable. As we want to rotate about the centre of our displayble and not the top left corner, it's necessary for us to do a little maths to adjust the values provided to us to be relative to the centre instead. This is where we see the benefit of having chosen to subclass Window, as a Window keeps track of it's size so we don't have to, inside the window_size instance variable.

Code: Select all

        def event(self, ev, x, y, st):
            width, height = self.window_size
            cx = x - width / 2.
            cy = y - height / 2.
            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180
            renpy.redraw(self.child, 0)
            return super(Tracker, self).event(ev, x, y, st)
The code above reads the window x and y sizes, cuts them in half to find the middle and substracts them from the input mouse position that was relative to the top left corner. This leaves x and y relative to the centre of the displayable. We then feed those adjusted values into the algorithm we ported over to Python earlier to yield the angle which describes the cursor position relative to the displayable (where a value of 0 describes a cursor directly above the displayable). Then we simply apply that value to the transform we wrapped around the child displayable. In order for this change to be seen on screen we must inform renpy that a redraw operation is required by the child, that is the purpose of the renpy.redraw call; without this the values will still be adjusted but the visual representation on screen will not reflect them. Finally we forward the event to the parent class so that it may be resolved normally.

The resulting CDD will actively rotate its children to orient toward the cursor so long as it is within the bounds of the game window. This is a basic example and does not check things like event type or that a redraw is actually required (if the rotate value was the same, then not calling redraw would help performance.) Such improvements are left as an exercise for the reader and would be highly recommended before attempting to use in production. A complete example of the solution outlined may be found below for reference, playing around with and improving.

Getting more specific about the OP's eye problem, each iris/eyeball would need to be wrapped in an instance of the CDD and layered behind a face with transparent eye-sockets. One limitation with this approach is that the eyes would always be on the edge of the eye-socket, there is no expectation they could ever stare straight out of the screen, however hopefully this is still useful in the scenario outlined.

Some things to consider if hoping to build on this for use in production:
- Only redraw if the rotation value has changed.
- Only calculate rotation for mouse events.
- Are there times when the eyes shouldn't track the cursor?
- Will importing math globally conflict with any variables already in use.

Hope you found this helpful, any feedback is appreciated. :)

---

Code: Select all

# The complete Tracker CDD example.
init python:
    '''
    This is sample code intended to help understanding and to provide
    a basic example with which to experiment. It is NOT intended for
    production use!
    '''

    import math

    class Tracker(renpy.display.layout.Window):
        def __init__(self, child, *args, **kwargs):
            child = Transform(child)
            super(Tracker, self).__init__(child, *args, **kwargs)

        def event(self, ev, x, y, st):
            width, height = self.window_size

            cx = x - width / 2.
            cy = y - height / 2.

            self.child.rotate = math.atan2(cx, cy) * -180 / math.pi + 180

            renpy.redraw(self.child, 0)

            return super(Tracker, self).event(ev, x, y, st)

# This is just making an arrow shape so the example doesn't need an image.
image line = Solid('000', xysize=(20, 87), anchor=(.5, 0.))
image shape = Fixed(
    Solid('000', xysize=(20, 200), xalign=(.5)),
    Transform('line', pos=(.25, -.1), rotate=45),
    Transform('line', pos=(.75, -.1), rotate=-45),
    xysize=(100, 200))
image arrow = Tracker('shape')

# Little example script.
label start:
    scene
    show arrow at truecenter
    "Your mouse cursor is in this direction!"
    return
thank you for explain that I try to say for bests that I can say there are a lot people the that don't under stand javascript and that but there thing I have to learn about python still

Post Reply

Who is online

Users browsing this forum: No registered users