Get any objects (Rect, Surfaces…) at position / coordinates X, Y

Question:

So the question is simple:

Given a Surface, let’s call it screen and x,y coordinates, can I get anything that lays at that coordinates on that Surface?

For example, let’s say we have typical, Player attack, and if the attack reach the Enemy position x,y then enemy dies.

So given this simple app (is an example only not a real app)

import pygame as pg
from pygame.math import Vector2

# pygame constants
CLOCK = pg.time.Clock()
WIN_SIZE = (1280, 640)
# pygame setup
pg.init()
# screen
window = pg.display.set_mode(WIN_SIZE, 0, 32)
background = pg.Surface(WIN_SIZE)

player = pg.Surface(Vector2(12, 64))
player_rect = player.get_rect(topleft=Vector2(150, 150))
player_attack = False
player.fill((102, 255, 178))
player_attack_range = 20 # player can hit at min 20 pixel from target

enemy = pg.Surface(Vector2(12, 64))
enemy_rect = player.get_rect(topleft=Vector2(175, 150))
enemy.fill(pg.Color("green"))

while True:
    background.fill((0, 0, 0))  # screen clear

    # Render enemy
    attacked = False
    if player_attack:
        # !!!!! HERE !!!!!
        # Now we check if the playuer is close enough to the enemy, so we MUST know the enemy pos
        distance_x = abs(player_rect.x - enemy_rect.x)
        if distance_x > player_attack_range:
            attacked = True
            enemy.fill(pg.Color("red"))
    if not attacked:
        enemy.fill(pg.Color("green"))

    background.blit(enemy, enemy_rect.topleft)
    # Render player
    background.blit(player, player_rect.topleft)


    # Events
    for event in pg.event.get():
        if event.type == pg.QUIT or (
                event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):  # x button and esc terminates the game!
            exit(1)
        # ............. Mouse ............. #
        if event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                player_attack = True
        if event.type == pg.MOUSEBUTTONUP:
            if event.button == 1:
                player_attack = False

    pg.display.update()  # 2) Update the game
    window.blit(background, (0, 0))  # 3) Repaint the screen
    CLOCK.tick(60)  # 4) Wait 60 Frames

enter image description here

When is attacked

enter image description here

Now I always seen it done this way more or less:

distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
    attacked = True
    enemy.fill(pg.Color("red"))

With this example, I’m not pointing out the code implementation but the fact that, the player must know the target position and then check whether or not the target is hit

But what I want to know, let’s say I don’t know the enemy position, and the player just attacks, is there a way that we can get what’s currently on the surface at the attack range?

So do something like

attacked_area_x = abs(player_rect.x +  player_attack_range) # only care of x coords
rects_or_surfaces_in_area = background.what_have_we_got_here(Vector(attacked_area, 0))
for r in rects_or_surfaces_in_area:
    print("Hit!")
Asked By: Federico Baù

||

Answers:

Update

So By checking MDN documentation of Game Development MDN I actually find a game algorithm / Technique that is similar (but concept is the same) of my solution.

Is called the Broad Phase

From the documentation:

road phase should give you a list of entities that could be colliding. This can be implemented with a spacial data structure that will give you a rough idea of where the entity exists and what exist around it. Some examples of spacial data structures are Quad Trees, R-Trees or a Spacial Hashmap.

So yes, it seems one of many good approach to solve this problem.


So, after some research and thanks to Rabbid76 and his answer here How do I detect collision in pygame? which covers in details the most common collisions in Pygame, it seems that what I was looking for natively is just not possible.

Maybe is normal, I’m also new to game development and maybe what I want to do just doesn’t make any sense, but I bet it does.

The scenario I’m facing is, just one player with a sword hitting, so I asked my self, why should I need to know prior what objects lie on the sword path and check if is hit, instead, do the hit and request to the parent screen "what object are in the sword path"? , which, is for sure faster because we don’t need to know what object that have collision are (and avoid a for loop and check for each react/surface).

So, let’s say there are many many object that have collision and a player may hit it, it would be way faster do don’t know what’ there but request it instead to the surface, so basically the surface should be aware of its children / objects.

I tried a bit the Surface.subsurface() and the Surface.get_parent() but couldn’t make it work, anyway still, in a surface area we may have many thinks like:

  • draws, Rect, Surfaces, Sprites, Imgs etc…

I have only 2 solutions in my mind:

Map the objects coordinates

This only really works if, the entities are static, if so, then we could create a dict with key x:y coordinates and then check if the sword is in within a certain x:y and exist in the dict, then the entity is hit.

But with entity then moves by themself, is a nigtmare and will have a worst problem than before, you would need to update the keys at each frame and so on..

Make it ‘distance’ sensible

So, this could apply to each entity that is moving and may or may not hit something that has a collision. But staying on the example context, let’s say we are iterating thourgh entities / object that at each frame are updating their position.

We could check how distant are from the player, let’s say 2 chunks away (or the sword lenght) and collect them in a list

Then we that list, we check if, once the sword is updated, is hitting anything close by.

Still, pretty sure there are better ways (without changing or extending pygame its self).

And maybe, by extending pygame, there may be a way to implement a ‘surface aware’ class that when we request a specific Rect area, it tells us what’s there.

Answered By: Federico Baù