【问题标题】:Pygame, efficient collision handling for rpg game?Pygame,RPG游戏的有效碰撞处理?
【发布时间】:2013-07-25 06:12:42
【问题描述】:

我目前正在使用 pygame 编写一个类似 Snes 的小角色扮演游戏。
在我的地图上,我有 4 层:
0:地形,无碰撞(在字符下)
1:在地形上没有碰撞(在字符下)
2:碰撞对象(char下)
3:超过 char 对象

我想为我的第二层中的每个图块创建一个矩形,以防止角色越过这些对象。有没有一种有效的方法可以在每次角色移动时不检查每个矩形?谢谢。

【问题讨论】:

    标签: python python-2.7 pygame


    【解决方案1】:

    pygame 有很多为此优化的函数,比如Rect.collidelist:

    collidelist()
    测试列表中的一个矩形是否相交

    collidelist(list) -> 索引

    测试矩形是否与矩形序列中的任何一个碰撞。返回找到的第一个碰撞的索引。如果没有发现冲突,则返回 -1 的索引。

    如果您仍然遇到性能问题,您可以使用QuadTree

    Here's an implementation in python using pygame.

    from pygame import Rect
    
    class QuadTree(object):
        """An implementation of a quad-tree.
    
        This QuadTree started life as a version of [1] but found a life of its own
        when I realised it wasn't doing what I needed. It is intended for static
        geometry, ie, items such as the landscape that don't move.
    
        This implementation inserts items at the current level if they overlap all
        4 sub-quadrants, otherwise it inserts them recursively into the one or two
        sub-quadrants that they overlap.
    
        Items being stored in the tree must be a pygame.Rect or have have a
        .rect (pygame.Rect) attribute that is a pygame.Rect
            ...and they must be hashable.
    
        Acknowledgements:
        [1] http://mu.arete.cc/pcr/syntax/quadtree/1/quadtree.py
        """
    
        def __init__(self, items, depth=8, bounding_rect=None):
            """Creates a quad-tree.
    
            @param items:
                A sequence of items to store in the quad-tree. Note that these
                items must be a pygame.Rect or have a .rect attribute.
    
            @param depth:
                The maximum recursion depth.
    
            @param bounding_rect:
                The bounding rectangle of all of the items in the quad-tree. For
                internal use only.
            """
    
            # The sub-quadrants are empty to start with.
            self.nw = self.ne = self.se = self.sw = None
    
            # If we've reached the maximum depth then insert all items into this
            # quadrant.
            depth -= 1
            if depth == 0 or not items:
                self.items = items
                return
    
            # Find this quadrant's centre.
            if bounding_rect:
                bounding_rect = Rect( bounding_rect )
            else:
                # If there isn't a bounding rect, then calculate it from the items.
                bounding_rect = Rect( items[0] )
                for item in items[1:]:
                    bounding_rect.union_ip( item )
            cx = self.cx = bounding_rect.centerx
            cy = self.cy = bounding_rect.centery
    
            self.items = []
            nw_items = []
            ne_items = []
            se_items = []
            sw_items = []
    
            for item in items:
                # Which of the sub-quadrants does the item overlap?
                in_nw = item.rect.left <= cx and item.rect.top <= cy
                in_sw = item.rect.left <= cx and item.rect.bottom >= cy
                in_ne = item.rect.right >= cx and item.rect.top <= cy
                in_se = item.rect.right >= cx and item.rect.bottom >= cy
    
                # If it overlaps all 4 quadrants then insert it at the current
                # depth, otherwise append it to a list to be inserted under every
                # quadrant that it overlaps.
                if in_nw and in_ne and in_se and in_sw:
                    self.items.append(item)
                else:
                    if in_nw: nw_items.append(item)
                    if in_ne: ne_items.append(item)
                    if in_se: se_items.append(item)
                    if in_sw: sw_items.append(item)
    
            # Create the sub-quadrants, recursively.
            if nw_items:
                self.nw = QuadTree(nw_items, depth, (bounding_rect.left, bounding_rect.top, cx, cy))
            if ne_items:
                self.ne = QuadTree(ne_items, depth, (cx, bounding_rect.top, bounding_rect.right, cy))
            if se_items:
                self.se = QuadTree(se_items, depth, (cx, cy, bounding_rect.right, bounding_rect.bottom))
            if sw_items:
                self.sw = QuadTree(sw_items, depth, (bounding_rect.left, cy, cx, bounding_rect.bottom))
    
    
        def hit(self, rect):
            """Returns the items that overlap a bounding rectangle.
    
            Returns the set of all items in the quad-tree that overlap with a
            bounding rectangle.
    
            @param rect:
                The bounding rectangle being tested against the quad-tree. This
                must possess left, top, right and bottom attributes.
            """
    
            # Find the hits at the current level.
            hits = set( [ self.items[n] for n in rect.collidelistall( self.items ) ] )
    
            # Recursively check the lower quadrants.
            if self.nw and rect.left <= self.cx and rect.top <= self.cy:
                hits |= self.nw.hit(rect)
            if self.sw and rect.left <= self.cx and rect.bottom >= self.cy:
                hits |= self.sw.hit(rect)
            if self.ne and rect.right >= self.cx and rect.top <= self.cy:
                hits |= self.ne.hit(rect)
            if self.se and rect.right >= self.cx and rect.bottom >= self.cy:
                hits |= self.se.hit(rect)
    
            return hits
    

    与在简单循环中检查每个图块相比,这将大大提高性能。

    【讨论】:

    • 我喜欢 Rect.collidelist 函数。看看它是否能完成这项工作:D
    【解决方案2】:

    您可以使用recs 并检查它们是否像 Dominic Kexel 所说的那样发生碰撞,但是如果您的图形不完全是正方形并且碰撞检测需要完美,我建议使用带有 pygame 掩码的 pygame sprite 对象.可以根据 alpha 值(透明度)设置遮罩,并且非常容易处理:

    class Entity(pygame.sprite.Sprite):
        def __init__(self, image): #where image is a pygame image with that has had the convert_alpha() function used on it
            self.image = image
            self.rect = image.get_rect() #you can use a rect and a mask
            self.mask = pygame.mask.from_surface(self.image) #creates a mask from the image ans assigns it to the sprite object
    

    然后您可以使用rect 属性轻松检查碰撞(您可以使用矩形处理各种不同类型的碰撞),或者您可以使用遮罩检查像素完美碰撞。

    任何这些都可以使用 pygames sprite 碰撞函数来完成,例如 pygame.sprite.spritecollide()pygame.sprite.collide_mask() + 更多!

    阅读:

    http://www.pygame.org/docs/ref/sprite.html

    http://www.pygame.org/docs/ref/mask.html

    http://www.pygame.org/docs/ref/rect.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多