【问题标题】:Dealing with Sprites and Collisions Using Pygame使用 Pygame 处理精灵和碰撞
【发布时间】:2015-04-25 12:50:57
【问题描述】:

我正在使用 pygame 学习 python,我正在研究一些涉及精灵和碰撞的东西。我看过一些例子,但我还是不太明白。我试图做的是能够在用户按下“=”键时添加精灵(一个球),并且还能够在按下“-”时删除最后添加的精灵。我不能只删除最后一个,我只能删除所有这些。

到目前为止,我已经能够将球添加到窗口中,并让它们从墙壁上反弹回来(有点)。当 2 个球相撞时,它们尚未完全接触,它们会弹开。有时球会被卡住并且不会移动,有时球会从他们不应该的框架反弹。

这是我第一次与精灵组合作,希望能提供任何帮助/指导以使这项工作顺利进行。谢谢。

代码:

球.py

import pygame
from pygame.locals import *


class Ball(pygame.sprite.Sprite):
    def __init__(self, x, y, vx, vy):

        super().__init__();
        self.image = pygame.image.load("ball.png").convert()


        self.image.set_colorkey(pygame.Color(0, 0, 0))

        self.rect = self.image.get_rect()

        self.rect.x = x
        self.rect.y = y
        self.vx = vx
        self.vy = vy

    def draw(self, SCREEN):
        SCREEN.blit(self.image, (self.rect.x, self.rect.y))

    def move(self, SCREEN, balls):
        l_collide = self.rect.x + self.image.get_width() + self.vx > SCREEN.get_width()
        r_collide = self.rect.x + self.vx < 0
        t_collide = self.rect.y + self.vy < 0
        b_collide = self.rect.y + self.image.get_height() + self.vy > SCREEN.get_height()

        a = pygame.sprite.spritecollide(self, balls, False, False)

        if len(a) > 1:
            self.vx *= -1
            self.vy *= -1



        if l_collide or r_collide:
            self.vx *= -1


        if t_collide or b_collide:
            self.vy *= -1

        self.rect.x += self.vx
        self.rect.y += self.vy

ball_animation.py

import pygame
import sys
import random
import math
from pygame.locals import *
from ball.ball import Ball
from random import randint

def ball_list(num):
    ball_list = pygame.sprite.Group()

    for x in range(num):
        rand_x = random.randint(0,400)
        rand_y = random.randint(0,400)
        vx = 4
        vy = 5

        ball_list.add(Ball(rand_x, rand_y, vx, vy))

    return ball_list

def main():
    pygame.init()

    FPS = 30
    FPS_CLOCK = pygame.time.Clock()

    # COLOR LIST
    BLACK = pygame.Color(0, 0, 0)

    # Code to create the initial window
    window_size = (500, 500)
    SCREEN = pygame.display.set_mode(window_size)

    # set the title of the window
    pygame.display.set_caption("Bouncing Ball Animation")

    # change the initial background color to white
    SCREEN.fill(BLACK)

    balls = ball_list(0)

    while True:  # <--- main game loop
        for event in pygame.event.get():
            if event.type == QUIT:  # QUIT event to exit the game
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_EQUALS:
                    balls.add(Ball(randint(0,400),randint(0,400), 4,5))
                if event.key == K_MINUS:
                    try:
                        balls.remove()
                    except IndexError:
                        print('There is no balls to take!')




        SCREEN.fill(BLACK)
        for x in balls:
            x.move(SCREEN,balls)
            x.draw(SCREEN)

        pygame.display.update()  # Update the display when all events have been processed
        FPS_CLOCK.tick(FPS)

if __name__ == "__main__":
    main()

【问题讨论】:

  • @Johan 是对的,你这里有两个问题,这就是为什么我回过头来把我的答案命名为第一个问题,因为我没有回答所有问题。

标签: python pygame sprite


【解决方案1】:

按时移除精灵

问题是sprite.Group.remove(sprites) 要你指定它应该删除哪些精灵。 sprites 这里应该是您想要从组中删除的精灵/精灵列表。这意味着要删除按键上添加的最后一个球,您需要保留一个球精灵列表和pop() 最近添加的项目,然后使用pop() 的结果作为要从团体。 sprite.Group 有一个 .sprites() 方法,它返回组中所有精灵的列表,按添加顺序排列。这个列表是从组中生成的,实际上并不是与它的接口,所以对这个列表做的事情不会影响到组。然而,我们仍然可以使用它来获取最后添加的精灵。这是它的样子:

elif event.key == K_0:
    try:
        sprite_list = balls.sprites()
        to_remove = sprite_list[-1] # Get last element of list
        balls.remove(to_remove)
    except IndexError:
        print('There is no balls to take!')

碰撞

因此,这涉及更多内容,并且在您的代码中修复起来并不那么简单。要了解问题所在,请查看您的碰撞速度调整对屏幕边框的实际作用。

l_collide = self.rect.x + self.image.get_width() + self.vx > SCREEN.get_width()
r_collide = self.rect.x + self.vx < 0
t_collide = self.rect.y + self.vy < 0
b_collide = self.rect.y + self.image.get_height() + self.vy > SCREEN.get_height()

#################

if l_collide or r_collide:
    self.vx *= -1

if t_collide or b_collide:
    self.vy *= -1

考虑代码中的单个时间步长。我们检查精灵是否位于边界边缘任意数量。如果它悬在上面,我们反转速度。在某些情况下,您的边缘检查会给您带来麻烦。如果您的self.vx 小于您当前位置X 与x 维度边界之间的差值,您将反转您的速度,向边界移动self.vx 返回,但不要使它过去了。在下一个时间步中,您将看到您仍在边界之外,您的程序将再次反转self.vx,实际上将您从边界离开。在这种情况下,您将通过self.vx 来回绑定每个时间步长。通常这不会在您的代码中发生,除非您在边界上生成一个新的 ball 精灵,该精灵比该球的 self.vxself.vy 更远。这可以通过确保您不会从边缘生成球来解决,或者更好的是,仅在需要时才反转您的速度。

if (l_collide and self.vx>0) or (r_collide and self.vx<0):
    self.vx *= -1


if (t_collide and self.vy<0) or (b_collide and self.vy>0):
    self.vy *= -1

请注意,如果我们越过边缘,我们只会反转速度并且速度会朝着那个方向更深。现在对于您的精灵,您有两个选项,就像边界一样:

  • 只能在不会发生碰撞的空白空间中启动新的ball
  • 实施某种方法来计算正确的速度调整,并且仅在速度朝相反方向移动时应用它。

从我在文档中读到的内容来看,sprite.Group 看起来像是用于检查精灵是否重叠,而 不是 用于物理模拟。我建议对 2d 物理模拟进行一些研究,以便很好地概念化您应该希望在对象之间交流哪些信息。我敢肯定那里有一些不错的教程。

最后,要解决您的另一个问题,即为什么它们似乎没有接触时会发生碰撞。 sprite.spritecollide 正在返回哪些精灵与 rectangles 相交。如果您的ball.png 为透明度设置了颜色键,这不会影响精灵的rect。 Pygame 似乎在sprite.spritecollidecollided 关键字中实现了旨在处理此问题的功能:

pygame.sprite.spritecollide()

  • 在组中查找与另一个精灵相交的精灵。

spritecollide(sprite, group, dokill, collided = None) -> Sprite_list

collided 参数是一个回调函数,用于计算两个精灵是否发生碰撞。它应该将两个精灵作为值,并返回一个布尔值 > 指示它们是否正在碰撞。如果没有通过碰撞,所有精灵必须>有一个“rect”值,它是精灵区域的一个矩形,将>用于计算碰撞。 碰撞的可调用对象:

  • collide_rect
  • collide_rect_ratio
  • collide_circle
  • collide_circle_ratio
  • collide_mask

来自pygame 文档。 collide_circle 函数的文档指出,您的精灵应该有一个 radius 属性,否则将计算出一个以适应圆形内的整个矩形。因此,我建议在您的 Ball.__init__ 函数中添加:

self.radius = self.rect.width/2 

这将使collide_circle 使用近似于您的ball 图像的半径,假设它是居中的圆形并占据整个图像。接下来,您必须通过更改将碰撞规范添加到碰撞检查中:

a = pygame.sprite.spritecollide(self, balls, False, False)

a = pygame.sprite.spritecollide(self, balls, False, pygame.sprite.collide_circle)

如果你解决了在彼此内部不产生新的ball 对象的问题,这一切都应该很好。如果您无法让它们在彼此内部产生,请考虑使用不同的数据结构或不同的碰撞检查方式来获得您想要的结果。祝你好运!

【讨论】:

  • 谢谢,我确实阅读了您所写的内容并且很有意义。我现在想看看如何检查球在哪里产卵。同样仍在处理碰撞,有时球会将球推出框架,这是不应该的。
【解决方案2】:

我可以在你的文字中看到两个问题

  1. 您只想删除一个精灵,而不是精灵组中的所有精灵

如果您查看 pygame 文档,您会发现 spritegroup.remove 有一个可选参数。您可以通过将所需的精灵作为参数来删除单个精灵,例如 myspritegroup.remove(mysprite)。

  1. 您遇到了碰撞问题

只要球在创建时不会相互重叠,您的碰撞对我有效,您可以简单地检查一下。祝你好运:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-25
    • 2017-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多