1

I am working on a Pygame project and there is water on the map. I want to slow down the player if player goes into water. Area where water is has an elliptic shape. I can slow down player if the shape was a rectangle but i don't want the non-water area to slow down the player. So how can i get the area where player is supposed to be slowed down? How to control if character's coordinates are in the ellipse?

Edit: I checked the link in the comments and it worked for me.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    Did you try searching for any existing algorithms? I came across [this one](https://www.geeksforgeeks.org/check-if-a-point-is-inside-outside-or-on-the-ellipse/) which solves your problem – byxor Jan 29 '20 at 16:26
  • I checked the link and implemented it in my code. Changed few things and it worked. I looked for it on google but i didn't encounter that site, my bad. – confusedProgrammer Jan 29 '20 at 16:54
  • 1
    Does this answer your question? [How to detect if an ellipse intersects(collides with) a circle](https://stackoverflow.com/questions/2945337/how-to-detect-if-an-ellipse-intersectscollides-with-a-circle) – Hoog Jan 29 '20 at 20:37
  • 1
    The pygame solution is to use a [Sprite](https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite) with a mask. In that case the "water" can have any shape. See [How can I made a collision mask?](https://stackoverflow.com/questions/56043600/how-can-i-made-a-collision-mask/56045037#56045037) – Rabbid76 Jan 30 '20 at 05:47

2 Answers2

4

The collision of an ellipse and a point can be reduced to the collision of a circle and a point by scaling the ellipse to appear as a circle and scaling the distance vector of the point to the center of the ellipse in the same way. Since the ellipses are axis-aligned in PyGame, this can easily be achieved by scaling one of the coordinates by the ratio of the ellipse axis length.

Define the bounding rectangle (pygame.Rect) of the ellipse (ellipse_rect) and get the semi-axis (a, b):

a = ellipse_rect.width // 2
b = ellipse_rect.height // 2

Compute the ratio of the semi-axis

scale_y = a / b

Define an point (test_x, test_y) and calculate the vector of the point to the center of the ellipse (cpt_x, cpt_y). Scale the y-coordinate of the vector with the ratio of semi-x-axis and semi-y-axis:

cpt_x, cpt_y = ellipse_rect.center
dx = test_x - cpt_x
dy = (test_y - cpt_y) * scale_y

The point lies in the ellipse if the square of the Euclidean distance (dx*dx + dy*dy) is smaller than the square of the semi-x axis (a*a):

collide = dx*dx + dy*dy <= a*a  

See also Collision and Intersection - Point and Ellipse


Minimal example:

import pygame

pygame.init()

width, height = 400, 400
window = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

ellipse_rect = pygame.Rect(0, 0, 200, 100)
ellipse_rect.center = window.get_rect().center

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    a = ellipse_rect.width // 2
    b = ellipse_rect.height // 2
    scale_y = a / b
    cpt_x, cpt_y = ellipse_rect.center
    test_x, test_y = pygame.mouse.get_pos()
    dx = test_x - cpt_x
    dy = (test_y - cpt_y) * scale_y
    collide = dx*dx + dy*dy <= a*a  
            
    window.fill(0)
    
    color = (127, 0, 0) if collide else (0, 127, 0)
    pygame.draw.ellipse(window, color, ellipse_rect)
    if collide:
        pygame.draw.ellipse(window, (255, 255, 255), ellipse_rect, 3)

    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • i think i was my bad to not mark it as solved. I didn't knew how to do that since i didn't use stackoverflow that much at the time. My question came out to be more related to math than pygame but i really appreciate your solution. – confusedProgrammer Jan 12 '21 at 15:12
0

Simple code to check the points let ellipse is the object and x,y be the player's location

Path = ellipse.get_path()
if [x,y ] in Path:
       print('Yes')

For Example

Path=np.array([[ 0.        , -1.        ],
   [ 0.2652031 , -1.        ],
   [ 0.51957987, -0.89463369],
   [ 0.70710678, -0.70710678],
   [ 0.89463369, -0.51957987],
   [ 1.        , -0.2652031 ],
   [ 1.        ,  0.        ],
   [ 1.        ,  0.2652031 ],
   [ 0.89463369,  0.51957987],
   [ 0.70710678,  0.70710678],
   [ 0.51957987,  0.89463369],
   [ 0.2652031 ,  1.        ],
   [ 0.        ,  1.        ],
   [-0.2652031 ,  1.        ],
   [-0.51957987,  0.89463369],
   [-0.70710678,  0.70710678],
   [-0.89463369,  0.51957987],
   [-1.        ,  0.2652031 ],
   [-1.        ,  0.        ],
   [-1.        , -0.2652031 ],
   [-0.89463369, -0.51957987],
   [-0.70710678, -0.70710678],
   [-0.51957987, -0.89463369],
   [-0.2652031 , -1.        ],
   [ 0.        , -1.        ],
   [ 0.        , -1.        ]])

be the path

if [ 0.89463369,  0.51957987] in Path:
   print('Yes')