【问题标题】:Testing if two sprite overlap while using matrices在使用矩阵时测试两个精灵是否重叠
【发布时间】:2020-02-25 16:28:59
【问题描述】:

现在,我有一个球在屏幕上沿随机对角线方向移动,当它与它碰撞时会从墙上反弹。

我想做的事: 如果球越过网格中的单元格/框,则让单元格/框变为红色,然后继续更改颜色,直到该特定点的次数为 10。如果该值等于 10,则程序结束。

让我给你一个场景:假设我有一个 3x3 网格,该网格中的每个单元格的值都是 0。每次球越过一个单元格时,该值就会增加 1,直到达到 3。每个当值上升时,该单元格的颜色会发生变化。 0=白色,1=红色,2=绿色,3=蓝色。这就是我想要的输出。

我目前所拥有的: 整个屏幕变为红色,而不仅仅是单个单元格。 我现在只是测试一种颜色,但后来想添加多种颜色。

for row in range(GRIDY):
    for column in range(GRIDX):
        rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
        color = WHITE
        if GRIDY or GRIDX == 1:
            color = RED
        pg.draw.rect(screen, color, rect)

以下是其余代码供参考:

import sys
import math
from random import randrange
import pygame as pg

# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

# define measurements
WIDTH, HEIGHT, MARGIN = 10, 10, 1
GRIDX, GRIDY = 91, 36

class GridObject(pg.sprite.Sprite):
    def __init__(self, pos, grid, *groups):
        super().__init__(groups)

        # create image from grid
        self.grid = grid
        self.gridsize = (len(grid[0]), len(grid))
        imgsize = self.gridsize[0]*(WIDTH+MARGIN), self.gridsize[1]*(HEIGHT+MARGIN)
        self.image = pg.Surface(imgsize, flags=pg.SRCALPHA)
        self.image.fill((0, 0, 0, 0))
        col = (235, 175, 76)
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                if self.grid[r][c] == 1:
                    rect = [(MARGIN + WIDTH) * c + MARGIN, (MARGIN + HEIGHT) * r + MARGIN, WIDTH, HEIGHT]
                    pg.draw.rect(self.image, col, rect)

        self.rect = self.image.get_rect(center=pos)
        self.vel = pg.math.Vector2(8, 0).rotate(randrange(360))
        self.pos = pg.math.Vector2(pos)


    def update(self, boundrect):
        self.pos += self.vel
        self.rect.center = self.pos
        if self.rect.left <= boundrect.left or self.rect.right >= boundrect.right:
            self.vel.x *= -1                            
        if self.rect.top <= boundrect.top or self.rect.bottom >= boundrect.bottom:
            self.vel.y *= -1     
        # align rect to grid
        gridpos = round(self.rect.x / (WIDTH+MARGIN)), round(self.rect.y / (HEIGHT+MARGIN))
        self.rect.topleft = gridpos[0] * (WIDTH+MARGIN), gridpos[1] * (HEIGHT+MARGIN)

ballGrid = [[0, 1, 1, 1, 0],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [0, 1, 1, 1, 0]]

def main():
    #overlap = False
    screen = pg.display.set_mode((GRIDX * (WIDTH+MARGIN) + MARGIN, GRIDY * (HEIGHT+MARGIN)))
    # Set title of screen
    pg.display.set_caption("Ball With Grid")
    clock = pg.time.Clock()
    sprite_group = pg.sprite.Group()
    ball = GridObject((495, 193), ballGrid, sprite_group)
    done = False
    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
                # Used to track the grid coordinates
                if event.type == pg.MOUSEBUTTONDOWN:
                    # Get the position is screen is clicked
                    pos = pg.mouse.get_pos()
                    # Change the x/y screen coordinates to grid coordinates
                    column = pos[0] // (WIDTH + MARGIN)
                    row = pos[1] // (HEIGHT + MARGIN)
                    # Set that location to one
                    grid[row][column] = 1
                    print("Click ", pos, "Grid coordinates: ", row, column)


        screen.fill((0, 0, 0))

        # Draw the grid and add values to the cells
        for row in range(GRIDY):
            for column in range(GRIDX):
                rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
                color = WHITE
                if GRIDY or GRIDX == 1:
                    color = RED
                pg.draw.rect(screen, color, rect)

        sprite_group.update(screen.get_rect())
        sprite_group.draw(screen)

        pg.display.flip()
        clock.tick(30)

if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
    sys.exit()

顺便说一句,如果这看起来相似,我的其他帐户发布了这个问题: Using a matrix as a sprite and testing if two sprites overlap

图片链接: https://imgur.com/gallery/UicBs6q

【问题讨论】:

  • 我一定是不小心取消了检查。现在检查了

标签: python matrix pygame collision-detection overlap


【解决方案1】:

假设我有一个 3x3 网格,该网格中的每个单元格的值为 0。每当球越过一个单元格时,该值就会增加 1,直到达到 3。每次值增加时,该单元格的颜色发生变化。 0=白色,1=红色,2=绿色,3=蓝色。这就是我想要的输出。

对于每个单元格,您可以知道球在一个单元格上的频率。由于球覆盖超过 1 个单元格(球的大小大于 1 个单元格),因此当球滚过单元格时,单元格计数器将在连续帧中递增多次。
所以你必须确保一个球已经离开了一个单元格,然后它的计数器才被允许再次递增。

创建一个整数值的二维数组(嵌套列表),用字段的大小初始化为 0。该文件使单元格的命中计数器感到痛苦。此外,创建一个列表 (hitList),用于存储帧中已命中的单元格的索引。

hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)]
hitList = []

必须将网格 (hitGrid) 和列表 (hitList) 传递给 GridObject 类的 update 方法。如果球接触了一个区域并且该区域在前一帧中没有被触及,那么网格中的相应条目必须增加。此外,该函数还可以设置一个全局变量max_hit,以当前帧中的最高拟合计数:

class GridObject(pg.sprite.Sprite):
    # [...]

    def update(self, boundrect, gridHit):
        # [...]

        # increment touched filed
        global max_hit
        max_hit = 0
        oldHitList = hitList[:]
        hitList.clear()
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                p = gridpos[1] + r, gridpos[0] + c
                if p in oldHitList:
                    hitList.append(p)
                elif self.grid[r][c] == 1:
                    if p[0] < len(hitGrid) and p[1] < len(hitGrid[p[0]]):
                        hitList.append(p)
                        if p not in oldHitList:
                            hitGrid[p[0]][p[1]] +=1
                            max_hit = max(max_hit, hitGrid[p[0]][p[1]])

在将update 调用到GridObject 对象后评估max_hit

sprite_group.update(screen.get_rect(), hitGrid, hitList)
if max_hit >= 4:
    print("game over")
    done = True

根据gridHit 中的值为字段着色:

for row in range(GRIDY):
    for column in range(GRIDX):
        rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
        colorlist = [WHITE, RED, GREEN, BLUE]
        color = colorlist[min(len(colorlist)-1, hitGrid[row][column])]
        pg.draw.rect(screen, color, rect)

看例子:

from random import randrange
import pygame as pg

# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# define measurements
WIDTH, HEIGHT, MARGIN = 10, 10, 1
GRIDX, GRIDY = 36, 36

class GridObject(pg.sprite.Sprite):
    def __init__(self, pos, grid, *groups):
        super().__init__(groups)

        # create image from grid
        self.grid = grid
        self.gridsize = (len(grid[0]), len(grid))
        imgsize = self.gridsize[0]*(WIDTH+MARGIN), self.gridsize[1]*(HEIGHT+MARGIN)
        self.image = pg.Surface(imgsize, flags=pg.SRCALPHA)
        self.image.fill((0, 0, 0, 0))
        col = (235, 175, 76)
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                if self.grid[r][c] == 1:
                    rect = [(MARGIN + WIDTH) * c + MARGIN, (MARGIN + HEIGHT) * r + MARGIN, WIDTH, HEIGHT]
                    pg.draw.rect(self.image, col, rect)

        self.rect = self.image.get_rect(center=pos)
        self.vel = pg.math.Vector2(8, 0).rotate(randrange(360))
        self.pos = pg.math.Vector2(pos)


    def update(self, boundrect, hitGrid, hitList):
        self.pos += self.vel
        self.rect.center = self.pos
        if self.rect.left <= boundrect.left or self.rect.right >= boundrect.right:
            self.vel.x *= -1                            
        if self.rect.top <= boundrect.top or self.rect.bottom >= boundrect.bottom:
            self.vel.y *= -1     
        # align rect to grid
        gridpos = round(self.rect.x / (WIDTH+MARGIN)), round(self.rect.y / (HEIGHT+MARGIN))
        self.rect.topleft = gridpos[0] * (WIDTH+MARGIN), gridpos[1] * (HEIGHT+MARGIN)

        # increment touched filed
        global max_hit
        max_hit = 0
        oldHitList = hitList[:]
        hitList.clear()
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                p = gridpos[1] + r, gridpos[0] + c
                if p in oldHitList:
                    hitList.append(p)
                elif self.grid[r][c] == 1:
                    if p[0] < len(hitGrid) and p[1] < len(hitGrid[p[0]]):
                        hitList.append(p)
                        if p not in oldHitList:
                            hitGrid[p[0]][p[1]] +=1
                            max_hit = max(max_hit, hitGrid[p[0]][p[1]])

ballGrid = [[0, 1, 1, 1, 0],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [0, 1, 1, 1, 0]]

def main():
    #overlap = False
    screen = pg.display.set_mode((GRIDX * (WIDTH+MARGIN) + MARGIN, GRIDY * (HEIGHT+MARGIN)))
    # Set title of screen
    pg.display.set_caption("Ball With Grid")
    clock = pg.time.Clock()
    sprite_group = pg.sprite.Group()
    ball = GridObject((screen.get_width()//2, screen.get_height()//2), ballGrid, sprite_group)
    hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)]
    hitList = []
    done = False
    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.KEYDOWN and event.key == pg.K_SPACE:
                hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)] 

        screen.fill((0, 0, 0))

        # Draw the grid and add values to the cells
        for row in range(GRIDY):
            for column in range(GRIDX):
                rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
                colorlist = [WHITE, RED, GREEN, BLUE]
                color = colorlist[min(len(colorlist)-1, hitGrid[row][column])]
                pg.draw.rect(screen, color, rect)

        sprite_group.update(screen.get_rect(), hitGrid, hitList)
        if max_hit >= 4:
            print("game over")
            done = True
        sprite_group.draw(screen)

        pg.display.flip()
        clock.tick(30)

if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
    sys.exit()

【讨论】:

  • 有点。检查我对问题的修订
  • 我喜欢目前的输出,但我需要实现的另一件事是,如果颜色变化变为 10,或者在这种情况下,如果球越过蓝色,则程序结束(因此值为4 因为 3 = 蓝色)。
  • 是的。非常感谢你。这非常有用且内容丰富。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-13
  • 1970-01-01
相关资源
最近更新 更多