Python Forum
[PyGame] Breaking Raycasting loop
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Breaking Raycasting loop
#1
I have this raycasting code I've been working on. It mostly functions except I can't figure out how to break out of all the loops when the ray hits something. As it is I can see blocks that are supposed to be invisible behind he first block that the ray hits. The points in the ray are in order from the player. If I can break out further when contact is made then further blocks will not be illuminated.

    def cast_rays(self):
        rays = []
        for b in self.game.level.block_dict.values():
            b.illuminated = False
        for ray in range(0, 360, 20):
            ray_points = []
            for depth in range(20, 100, 5):
                x = self.rect.centerx - math.sin(ray) * depth
                y = self.rect.centery + math.cos(ray) * depth
                ray_points.append((x,y))
            rays.append(ray_points)

            for block in self.game.level.block_dict.items():
                if abs(block[0][0] - self.rect.centerx) < 200:
                    if abs(block[0][1] - self.rect.centery) < 200:
                        for ray in rays:
                            for point in ray:
                                if block[1].rect.collidepoint(point):
                                    block[1].illuminated = True
                                    break
                                else:
                                    continue
                                break
                                
        
Reply
#2
When a ray hits a block, what should happen to the ray? Do you want to stop looping when any ray hits a block?
Reply
#3
(Aug-07-2022, 09:41 PM)deanhystad Wrote: When a ray hits a block, what should happen to the ray? Do you want to stop looping when any ray hits a block?

No. The plan was to have a list of points for each ray, check the points in order, and if it hits something, stop, set the tile to "illuminated" and start on the next ray. As it is, it continues through and illuminates any block it hits.
Reply
#4
I don't think you need to compute all the ray points. I would check each point as it is computed and stop when a collision is detected.
def illuminate_block(self, blocks, ray_point):
    """ Find first block that collides with ray_point,  Returns block or None """
    for block in blocks:
        if block[1].rect.collidepoint(ray_point):
            block[1].illuminated = True
            return block
    return None
   
def cast_rays(self):
    cx = self.rect.centerx
    cy = self.rect.centery
    blocks = []
    for b in self.game.level.block_dict.values():
        b.illuminated = False
        # Cull the block list here so it doesn't have to happen for each ray point.
        if abs(b[0][0] - cx) < 200 and abs(b[0][1] - cy) < 200:
            blocks.append[b]
    for ray in range(0, 360, 20):
        # Do expensive sin and cos once per ray
        sin_ray = math.sin(ray)
        cos_ray = math.cos(ray)
        for depth in range(20, 100, 5):
            ray_point = (cx - sin_ray * depth, cy + cos_ray * depth)
            if self.illuminate_block(blocks, ray_point) is not None:
                break
If you don't like using an additional funciton you can use a while loop.
def cast_rays(self):
    cx = self.rect.centerx
    cy = self.rect.centery
    blocks = []
    for b in self.game.level.block_dict.values():
        b.illuminated = False
        if abs(b[0][0] - cx) < 200 and abs(b[0][1] - cy) < 200:
            blocks.append[b[1]]
    for ray in range(0, 360, 20):
        sin_ray = math.sin(ray)
        cos_ray = math.cos(ray)
        depth = 20
        while depth <= 100:
            for b in blocks:
                if b.rect.collidepoint((cx - sin_ray * depth, cy + cos_ray * depth)):
                    b.illuminated = True
                    depth = 100
                    break
            depth += 5
What are the blocks[0]? If they are sprites there are pygame functions that will do the work for you.
Reply
#5
block[0] is a tuple for x,y coordinates. I didn't use blocks[0]. I used for block in self.game.level.block_dict.items(). So I get a list of tuples for the key/value pairs. block[0] is a tuple for x,y coordinates which are dictionary keys. block[1] is the sprite.

I did initially checked each point as made, but changed it in my futile struggle to get things to work.

And for no real reason, I thought that pygame.rect.collidepoint() would be the fastest way to check.
Reply
#6
If it collides with an object, then mark the tile as "illuminated" and go on to the next one. In its current form, it penetrates and illuminates whatever obstruction it encounters.
phrazle
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Raycasting(again) walls with different heights robie972003 0 2,472 Mar-23-2019, 01:18 AM
Last Post: robie972003
  raycasting error robie972003 4 3,034 Mar-14-2019, 12:46 AM
Last Post: robie972003

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020