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
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 wrote: ↑Wed Jun 10, 2020 5:13 pmjavascript for the mouse movement that so python can follow the mouse around not in less there another way that I don't of?
Code: Select all
# original javascript
let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY);
let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
Code: Select all
# python version
rotate = math.atan2(x, y) * -180 / math.pi + 180
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)
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)
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
strayerror wrote: ↑Wed Jun 10, 2020 10:58 pmFirst, 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.
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
# original javascript let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY); let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 180;
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
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.Code: Select all
# python version rotate = math.atan2(x, y) * -180 / math.pi + 180
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.
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
class Tracker(renpy.display.layout.Window): def __init__(self, child, *args, **kwargs): child = Transform(child) super(Tracker, self).__init__(child, *args, **kwargs)
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.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 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 stillstrayerror wrote: ↑Wed Jun 10, 2020 10:58 pmFirst, 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.
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
# original javascript let radianDegrees = Math.atan2(e.pageX - mouseX, e.pageY - mouseY); let rotationDegrees = (radianDegrees * (180/ Math.PI) * -1) + 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.Code: Select all
# python version rotate = math.atan2(x, y) * -180 / math.pi + 180
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.
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
class Tracker(renpy.display.layout.Window): def __init__(self, child, *args, **kwargs): child = Transform(child) super(Tracker, self).__init__(child, *args, **kwargs)
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.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 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
Users browsing this forum: No registered users