简体   繁体   English

Pygame:Sprite 侧面碰撞

[英]Pygame: Collision by Sides of Sprite

Is there a way in pygame to look for a collision between the a particular side of a sprite and a particular side of another sprite in pygame? pygame 中有没有办法在 pygame 中寻找精灵的特定侧面和另一个精灵的特定侧面之间的碰撞? For example, if the top of sprite A collides with the bottom of Sprite B, return True.例如,如果精灵 A 的顶部与精灵 B 的底部碰撞,则返回 True。

I am certain there is a way to do this, but I can't find any particular method in the documentation.我确信有一种方法可以做到这一点,但我在文档中找不到任何特定的方法。

Thanks!谢谢!

There is no function to get sides collision in PyGame. PyGame 中没有获得侧面碰撞的功能。

But you could try to use pygame.Rect.collidepoint to test if A.rect.midleft , A.rect.midright , A.rect.midtop , A.rect.midbottom , A.rect.topleft , A.rect.bottomleft , A.rect.topright , A.rect.bottomright are inside B.rect ( pygame.Rect ).但是你可以尝试使用pygame.Rect.collidepoint来测试A.rect.midleft , A.rect.midright , A.rect.midtop , A.rect.midbottom , A.rect.topleft , A.rect.bottomleft , A.rect.topright , A.rect.bottomrightB.rect ( pygame.Rect ) 内。


EDIT:编辑:

Example code.示例代码。 Use arrows to move player and touch enemy.使用箭头移动玩家并接触敌人。

( probably it is not optimal solution ) 可能不是最佳解决方案

import pygame

WHITE = (255,255,255)
BLACK = (0  ,0  ,0  )
RED   = (255,0  ,0  )
GREEN = (0  ,255,0  )
BLUE  = (0  ,0  ,255)

class Player():

    def __init__(self, x=0, y=0, width=150, height=150):

        self.rect = pygame.Rect(x, y, width, height)

        self.speed_x = 5
        self.speed_y = 5

        self.move_x = 0
        self.move_y = 0

        self.collision = [False] * 9

        self.font = pygame.font.SysFont("", 32)
        self.text = "";

    def set_center(self, screen):
        self.rect.center = screen.get_rect().center

    def event_handler(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                self.move_x -= self.speed_x
            elif event.key == pygame.K_RIGHT:
                self.move_x += self.speed_x
            elif event.key == pygame.K_UP:
                self.move_y -= self.speed_y
            elif event.key == pygame.K_DOWN:
                self.move_y += self.speed_y

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                self.move_x += self.speed_x
            elif event.key == pygame.K_RIGHT:
                self.move_x -= self.speed_x
            elif event.key == pygame.K_UP:
                self.move_y += self.speed_y
            elif event.key == pygame.K_DOWN:
                self.move_y -= self.speed_y

    def update(self):
        self.rect.x += self.move_x
        self.rect.y += self.move_y

    def draw(self, screen):

        pygame.draw.rect(screen, WHITE, self.rect, 2)
        self.draw_point(screen, self.rect.topleft, self.collision[0])
        self.draw_point(screen, self.rect.topright, self.collision[1])
        self.draw_point(screen, self.rect.bottomleft, self.collision[2])
        self.draw_point(screen, self.rect.bottomright, self.collision[3])

        self.draw_point(screen, self.rect.midleft, self.collision[4])
        self.draw_point(screen, self.rect.midright, self.collision[5])
        self.draw_point(screen, self.rect.midtop, self.collision[6])
        self.draw_point(screen, self.rect.midbottom, self.collision[7])

        self.draw_point(screen, self.rect.center, self.collision[8])

    def draw_point(self, screen, pos, collision):
        if not collision:
            pygame.draw.circle(screen, GREEN, pos, 5)
        else:
            pygame.draw.circle(screen, RED, pos, 5)

    def check_collision(self, rect):
        self.collision[0] = rect.collidepoint(self.rect.topleft)
        self.collision[1] = rect.collidepoint(self.rect.topright)
        self.collision[2] = rect.collidepoint(self.rect.bottomleft)
        self.collision[3] = rect.collidepoint(self.rect.bottomright)

        self.collision[4] = rect.collidepoint(self.rect.midleft)
        self.collision[5] = rect.collidepoint(self.rect.midright)
        self.collision[6] = rect.collidepoint(self.rect.midtop)
        self.collision[7] = rect.collidepoint(self.rect.midbottom)

        self.collision[8] = rect.collidepoint(self.rect.center)

    def render_collision_info(self):

        text = "collision: "
        print "collision:",

        if self.collision[0] or self.collision[2] or self.collision[4]:
            text += "left "
            print "left",

        if self.collision[1] or self.collision[3] or self.collision[5]:
            text += "right "
            print "right",

        if self.collision[0] or self.collision[1] or self.collision[6]:
            text += "top "
            print "top",

        if self.collision[2] or self.collision[3] or self.collision[7]:
            text += "bottom "
            print "bottom",

        if self.collision[8]:
            text += "center "
            print "center",

        print

        self.text = self.font.render(text, 1, WHITE)

    def draw_collision_info(self, screen, pos):
        screen.blit(self.text, pos)

#----------------------------------------------------------------------

class Game():

    def __init__(self):

        pygame.init()

        self.screen = pygame.display.set_mode( (800,600) )
        pygame.display.set_caption("Side Collision")

        self.player = Player()
        self.enemy  = Player()
        self.enemy.set_center(self.screen)


    def run(self):
        clock = pygame.time.Clock()

        RUNNING = True

        while RUNNING:

            # --- events ----

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    RUNNING = False

                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        RUNNING = False

                self.player.event_handler(event)

            # --- updates ---

            self.player.update()
            self.enemy.update()

            self.player.check_collision(self.enemy.rect)
            self.enemy.check_collision(self.player.rect)
            self.player.render_collision_info()
            self.enemy.render_collision_info()

            # --- draws ----

            self.screen.fill(BLACK)

            self.player.draw(self.screen)
            self.enemy.draw(self.screen)

            self.player.draw_collision_info(self.screen, (0,0))
            self.enemy.draw_collision_info(self.screen, (0,32))

            pygame.display.update()

            # --- FPS ---

            clock.tick(30)

        pygame.quit()

#----------------------------------------------------------------------

Game().run()

在此处输入图片说明


EDIT (08.2016): version with colllisions rect , rect_ratio , circle编辑(08.2016):带有碰撞rectrect_ratiocircle

GitHub: furas/python-examples/pygame/collisions GitHub: furas/python-examples/pygame/collisions

在此处输入图片说明

Like Furas has said, no, there is not way to get side collisions in Pygame past the point system he set up.就像 Furas 所说的那样,不,没有办法在 Pygame 中通过他设置的点系统来获得侧面碰撞。 And even that one wont give you what you want, because you can never be sure which direction the collision happened when dealing with rows, columns or corners of Rectangles.甚至那个也不会给你你想要的东西,因为在处理矩形的行、列或角时,你永远无法确定碰撞发生在哪个方向。

This is why most tutorials recommend saving your sprites initial direction.这就是为什么大多数教程建议保存您的精灵初始方向。 then moving in the opposite direction in case of a collision.然后在发生碰撞时向相反的方向移动。

I solved this by creating multiple collide boxes.我通过创建多个碰撞盒解决了这个问题。 This should help a lot of people.这应该可以帮助很多人。

https://youtu.be/W-Vz6le1YUg https://youtu.be/W-Vz6le1YUg

Code:代码:

if tanner.axe.colliderect(oak.red) and. 
      tanner.playerHitBox.colliderect(oak.leftHit) and. 
      keys_pressed[pygame.K_SPACE]:
      Number_of_Hits_Left += 1
      print(Number_of_Hits_Left)

 if tanner.axe.colliderect(oak.red) and. 
       tanner.playerHitBox.colliderect(oak.rightHit) and. 
       keys_pressed[pygame.K_SPACE]:
       Number_of_Hits_Right += 1
       print(Number_of_Hits_Right)

So I have a total of 5 hit boxes to accomplish said mission.所以我总共有 5 个命中盒来完成上述任务。 And really all you would have to do is create your main hit box, then create 2 side boxes on the left and right side of main hit box, so that they barely overlap.实际上,您所要做的就是创建主命中框,然后在主命中框的左侧和右侧创建 2 个侧框,以便它们几乎不重叠。 So let's say you shoot a bullet your code would be something like above.因此,假设您发射子弹,您的代码将类似于上面的内容。 "When bullet collides with side box AND when bullet collides with main box, do something." “当子弹与侧框碰撞以及子弹与主框碰撞时,做点什么。”

The logic behind collision is like that:碰撞背后的逻辑是这样的:

#Assume two surfaces
objectA
objectB

if objectA.right > objectB.left and objectA.left < objectB.right and objectA.top < objectB.bottom and objectA.bottom > objectB.top
    #Collision happens

if you want to detect side collision (as if objectA hitted objectB on the side) you can do the following:如果您想检测侧面碰撞(就像 objectA 在侧面击中 objectB),您可以执行以下操作:

#Here is the code where objectA moves
objectA.X += 10
#Here check Collision, if collision happens objectA hitted objectB from the side
objectA.Y += 10
#Here check Collision again, if collision is true then objectA hitted object B from top/bottom

For objectA give the object this method:对于 objectA 给对象这个方法:

def is_collided_with(self, sprite): return self.rect.colliderect(sprite.rect)

This return statement returns either True or False此 return 语句返回 True 或 False

then in the main loop for collisions just do: if objectA.is_collided_with(ObjectB): Collision happened!然后在碰撞的主循环中只做: if objectA.is_collided_with(ObjectB): Collision happened!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM