3

I am aware that on the net and also here the question has already been asked, but unfortunately not in the Python environment. Looking on the net, I found this (Link) and from there I started working on it. Since I'm using Pyglet, I wrote the function as a thread. But first, I show you what I thought and wanted to accomplish:

enter image description here

P = Sprite Player Position

M = Mouse Position

C = An imaginary circle, having as its radius the distance between P and M.

0, 1, 2, 3, 4, 5, 6, 7 = Directions that the sprite can have

a = Angle between one direction and another = 45°

S = Section of the circle corresponding to the sprite direction. In simple words, if M is present in S, the direction is equal to 1

start, end = Start Angle and End Angle

So, in the function, I inserted a while loop. Later, I had to calculate when the radius was:

while mpc_thread:
    radius = math.hypot(mpx - cpx, mpy - cpy) + 20

mpx, mpy = Mouse position (X, Y)

cpx, cpy = Sprite Player Position (X, Y)

I used math.hypot thanks to this (Link). I added 20, so that the radius slightly exceeded the position of the mouse.

Then I added a for loop to check the circle section for each direction:

while mpc_thread:
    radius = math.hypot(mpx - cpx, mpy - cpy) + 20
    for ang_obj in range(0, fchar):
        reference_angle = 360 // fchar * ang_obj
        s_angle = reference_angle - (360 / (fchar / 2))
        e_angle = reference_angle + (360 / (fchar / 2))

fchar = Amount of Sprite directions, in this case 8

To find out the starting and ending angle for each direction, I divided the lap angle by twice the number of directions. Then I subtracted / added the result to the reference angle.

From here on the problems started. Writing in the way I posted the first link, the if function didn't detect anything and if I went in negative (, I got an error. I then searched for a solution and found this (Link) from the answer of user7048690. Modified the function, I got a new problem (math domain error). So I changed math.sqrt with cmath.sqrt, and it worked. But a new problem had arisen. That is, always following that answer, the if function drastically reduced FPS to 0/1. Now I don't know where to head. Can you help me with this problem? How should I build the function correctly and work properly? I hope I understood what I meant by my question.

BlackFenix06
  • 577
  • 1
  • 6
  • 22

1 Answers1

3

The easiest way to find the best direction is to calculate the cosine of the angle between the line from the player position to the mouse position and the line from the player position to the 8 points.
It has to be found that direction vector which has the smallest angle to the "mouse" direction. The cosine of 0 degrees is 1 and the cosine of 180° is -1. So the direction with the greatest cosine is the direction to be found.

The easiest way to calculate the cosine is the dot product.

In general the dot product of 2 vectors is equal the cosine of the angle between the 2 vectors multiplied by the magnitude (length) of both vectors.

dot( A, B ) == | A | * | B | * cos( angle_A_B ) 

This follows, that the dot product of 2 unit vectors is equal the cosine of the angle between the 2 vectors, because the length of a unit vector is 1.

uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )

The dot product of 2-dimensional vectors A and B can be calculated by 2 multiplications and 1 addition:

dotAB = Ax * Bx + Ay * By  

Set up a list with the 8 normalized directions:

dir = [(0, 1), (0.707, 0.707), (1, 0), (0.707, -0.707),
       (0, -1), (-0.707, -0.707), (-1, 0), (-0.707, 0.707)]

Find the "best" direction, this is the direction with the closest angle or the greatest cosine of the angle:

dx, dy = mpx - cpx, mpy - cpy
max_i = max([i for i in range(len(dir))], key = lambda i: dx*dir[i][0] + dy*dir[i][1])

Finally max_i contains the searched direction.

Note the algorithm doesn't calculate and compare the cosine of the angles, it compares product of the cosine and the radius. dx*dir[i][0] + dy*dir[i][1] is eqault to radius * cos(alpha).

The searched point finally is:

radius = math.hypot(mdir_x, mdir_y) + 20
X = (dir[max_i][0] * radius, dir[max_i][1] * radius)

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you for your answer. I didn't understand only the fact of the 8 normalized directions. More than anything, I didn't understand how you got those results. – BlackFenix06 Apr 03 '19 at 20:21
  • Moreover, unfortunately, perhaps I have explained myself wrongly, in case you tell me that I correct the question in this way. The eight directions are simply directions in which the player is turned and the circle section is as if it were his field of view. Therefore, the mouse must be in its visible field and depending on where it is around the player, the direction of the sprite changes to be oriented towards the mouse. – BlackFenix06 Apr 03 '19 at 20:24
  • I'm not sure how to explain it, since English is not really my language. I have (in this case) 8 images of the player that correspond to the 8 directions in the image. – BlackFenix06 Apr 03 '19 at 20:42
  • My intention was to change between the different images based on where the mouse was compared to the position of the player. In this way, the illusion was given that the player turned on itself and followed the mouse. That's why I thought about what I designed above. For example, as long as the mouse is in S, the direction of the Player is number 1. If the mouse leaves S, it means that it will go to another section and the direction of the Player will then change accordingly – BlackFenix06 Apr 03 '19 at 20:42
  • It is as if the imaginary circle drawn before was divided into 8 sections (which hypothetically represents the visual field of the player). If the mouse is present in one of these sections, then the player changes its direction (one of the 8 in the drawing) to the one in which the mouse is present. – BlackFenix06 Apr 03 '19 at 20:42
  • In fact I didn't say it was wrong, in fact I also thanked you for your help =D When I wrote the second answer, it was just to ask if I had explained my question about what I was looking for. I apologize, maybe I caused more confusion than anything else. – BlackFenix06 Apr 03 '19 at 20:47
  • However, removing the mess I made in the comments, I return to my first comment on the 8 normalized directions. – BlackFenix06 Apr 03 '19 at 20:50
  • @BlackFenix06 The key is that the direction which is closest to the mouse is identified by that `i` which gives the maximum for `dx*dir[i][0] + dy*dir[i][1]`. Because `dir[i]` is the direction vector and (`dx`, `dy`) is the vector from `P` to the mouse. – Rabbid76 Apr 03 '19 at 20:52
  • Ah ok, now I understand. Thank you very much again for your help =D – BlackFenix06 Apr 03 '19 at 20:58
  • @BlackFenix06 The direction with the closest angle is that direction with the greatest cosine of the angle, because the cosine of 0° is 1 and the cosine of 180° is -1. The cosine is equivalent to `dx*dir[i][0] + dy*dir[i][1]`. – Rabbid76 Apr 03 '19 at 21:00
  • I noticed yesterday, but I couldn't write you an answer, which in the radius calculation you put `radius = math.hypot(mdir_x, mdir_y) + 20`. `mdir_x` and `mdir_y` what it refers to? – BlackFenix06 Apr 04 '19 at 16:51
  • @BlackFenix06 Now it is unnecessary. It is related to the cod of your question `radius = math.hypot(mpx - cpx, mpy - cpy) + 20`. First , I thought that you wanted to calculate a point with a certain distance. – Rabbid76 Apr 04 '19 at 16:53
  • Ah ok, so it was just as an example if I understood. I replace it with mine and end, right? – BlackFenix06 Apr 04 '19 at 17:23