在pygame中旋转命中框矩形

问题描述 投票:0回答:2

我目前正在构建一个自上而下的赛车,但是我正在尝试与静态方形物体进行准确的碰撞检测。

最好的方法是什么?

这是我的代码的一部分:

class VehicleSprite(Entity):
    MAX_FORWARD_SPEED = 18
    MAX_REVERSE_SPEED = 1

    def __init__(self, images, position):
        Entity.__init__(self)
        self.src_images = images
        self.images = images
        self.rect = self.images.get_rect(center=position)
        self.position = pygame.math.Vector2(position)
        self.velocity = pygame.math.Vector2(0, 0)
        self.speed = self.direction = 0
        self.k_left = self.k_right = self.k_down = self.k_up = 0
        self.width = 28
        self.height = 64
        self.numImages = 8
        self.cImage = 0

    def update(self, time):
        self.speed += self.k_up + self.k_down
        # To clamp the speed.
        self.speed = max(-self.MAX_REVERSE_SPEED,
                         min(self.speed, self.MAX_FORWARD_SPEED))

        # Degrees sprite is facing (direction)
        self.direction += (self.k_right + self.k_left)
        rad = math.radians(self.direction)
        self.velocity.x = -self.speed*math.sin(rad)
        self.velocity.y = -self.speed*math.cos(rad)
        self.position += self.velocity
        if (self.cImage >= self.numImages - 1):
            self.cImage = 0
        else:
            self.cImage += 1
        self.images = pygame.transform.rotate(self.src_images, self.direction)
        self.rect = self.images.get_rect(center=self.position)

    def render(self, screen, camera):
        screen.blit(self.images, (self.rect.topleft+camera), (self.cImage*self.width, 0, self.width, self.height))

这是碰撞检测位:

for sprite in all_sprites:
    for crate in crates:
        crate.update(time)
    for crate in crates:
        screen.blit(power_img, crate.position+camera)
        # Collision with crate
        if ((purple_bike.position.x >= crate.x) and purple_bike.position.x < (crate.x + crate.width) or (purple_bike.position.x + purple_bike.width) > crate.x and purple_bike.position.x + purple_bike.width < crate.x + crate.width) and ((purple_bike.position.y > crate.y) and purple_bike.position.y < (crate.y + crate.height) or (purple_bike.position.y + purple_bike.height) > crate.y and (purple_bike.position.y + purple_bike.height) < (crate.y + crate.height)):
            print("Hit")
            purple_bike.speed = 0
python rotation pygame collision-detection image-rotation
2个回答
2
投票

这里有一个示例,向您展示如何将 Pymunk 与 pygame 结合使用来获得旋转的 hitboxes/碰撞矩形。绿色轮廓是 Pymunk 形状的边缘,蓝色矩形是 pygame 矩形,其唯一目的是存储精灵的 blit 位置。熟悉 Pymunk 需要一些时间,但它是在 pygame 中实现旋转、任意形状的 hitbox 的最佳方法之一。

import sys
import math

import pygame as pg
import pymunk as pm
from pymunk import Vec2d


def flipy(p):
    """Convert chipmunk coordinates to pygame coordinates."""
    return Vec2d(p[0], -p[1]+600)


class Player(pg.sprite.Sprite):

    def __init__(self, pos, space, mass=0.3):
        super().__init__()
        self.image = pg.Surface((52, 72), pg.SRCALPHA)
        pg.draw.polygon(self.image, pg.Color('steelblue2'),
                        [(1, 72), (26, 1), (51, 72)])
        self.rect = self.image.get_rect(center=pos)
        self.orig_image = self.image
        # The verts for the Pymunk shape in relation
        # to the sprite's center.
        vertices = [(0, 36), (26, -36), (-26, -36)]
        # Create the physics body and shape of this object.
        moment = pm.moment_for_poly(mass, vertices)
        self.body = pm.Body(mass, moment)
        self.shape = pm.Poly(self.body, vertices, radius=3)
        self.shape.friction = .8
        self.shape.elasticity = .2
        self.body.position = pos
        # Add them to the Pymunk space.
        self.space = space
        self.space.add(self.body, self.shape)

        self.accel_forw = False
        self.accel_back = False
        self.turn_left = False
        self.turn_right = False
        self.topspeed = 1790
        self.angle = 0

    def handle_event(self, event):
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_w:
                self.accel_forw = True
            if event.key == pg.K_a:
                self.turn_left = True
            if event.key == pg.K_d:
                self.turn_right = True
            if event.key == pg.K_s:
                self.accel_back = True
        if event.type == pg.KEYUP:
            if event.key == pg.K_w:
                self.accel_forw = False
            if event.key == pg.K_a:
                self.turn_left = False
            if event.key == pg.K_d:
                self.turn_right = False
            if event.key == pg.K_s:
                self.accel_back = False

    def update(self, dt):
        # Accelerate the pymunk body of this sprite.
        if self.accel_forw and self.body.velocity.length < self.topspeed:
            self.body.apply_force_at_local_point(Vec2d(0, 624), Vec2d(0, 0))
        if self.accel_back and self.body.velocity.length < self.topspeed:
            self.body.apply_force_at_local_point(Vec2d(0, -514), Vec2d(0, 0))
        if self.turn_left and self.body.velocity.length < self.topspeed:
            self.body.angle += .1
            self.body.angular_velocity = 0
        if self.turn_right and self.body.velocity.length < self.topspeed:
            self.body.angle -= .1
            self.body.angular_velocity = 0
        # Rotate the image of the sprite.
        self.angle = self.body.angle
        self.rect.center = flipy(self.body.position)
        self.image = pg.transform.rotozoom(
            self.orig_image, math.degrees(self.body.angle), 1)
        self.rect = self.image.get_rect(center=self.rect.center)


class Wall(pg.sprite.Sprite):

    def __init__(self, pos, verts, space, mass, *sprite_groups):
        super().__init__(*sprite_groups)
        # Determine the width and height of the surface.
        width = max(v[0] for v in verts)
        height = max(v[1] for v in verts)
        self.image = pg.Surface((width, height), pg.SRCALPHA)
        pg.draw.polygon(self.image, pg.Color('sienna1'), verts)
        self.rect = self.image.get_rect(topleft=pos)

        moment = pm.moment_for_poly(mass, verts)
        self.body = pm.Body(mass, moment, pm.Body.STATIC)
        # Need to transform the vertices for the pymunk poly shape,
        # so that they fit to the image vertices.
        verts2 = [(x, -y) for x, y in verts]
        self.shape = pm.Poly(self.body, verts2, radius=2)
        self.shape.friction = 1.0
        self.shape.elasticity = .52
        self.body.position = flipy(pos)
        self.space = space
        self.space.add(self.shape)


class Game:

    def __init__(self):
        self.done = False
        self.screen = pg.display.set_mode((800, 600))
        self.clock = pg.time.Clock()
        self.bg_color = pg.Color(60, 60, 60)

        self.space = pm.Space()
        self.space.gravity = Vec2d(0.0, 0.0)
        self.space.damping = .4

        self.all_sprites = pg.sprite.Group()

        self.player = Player((100, 300), self.space)
        self.all_sprites.add(self.player)
        # Position-vertices tuples for the walls.
        vertices = [
            ([80, 120], ((0, 0), (100, 0), (70, 100), (0, 100))),
            ([400, 250], ((20, 40), (100, 0), (80, 80), (10, 100))),
            ([200, 450], ((20, 40), (300, 0), (300, 120), (10, 100))),
            ([760, 10], ((0, 0), (30, 0), (30, 420), (0, 400))),
            ]

        for pos, verts in vertices:
            Wall(pos, verts, self.space, 1, self.all_sprites)

    def run(self):
        while not self.done:
            self.dt = self.clock.tick(30) / 1000
            self.handle_events()
            self.run_logic()
            self.draw()

    def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True

            self.player.handle_event(event)

    def run_logic(self):
        self.space.step(1/60)
        self.all_sprites.update(self.dt)

    def draw(self):
        self.screen.fill(self.bg_color)
        self.all_sprites.draw(self.screen)
        # Debug draw - Pymunk shapes are green, pygame rects are blue.
        for obj in self.all_sprites:
            shape = obj.shape
            ps = [flipy(pos.rotated(shape.body.angle) + shape.body.position)
                  for pos in shape.get_vertices()]
            ps.append(ps[0])
            pg.draw.rect(self.screen, pg.Color('blue'), obj.rect, 2)
            pg.draw.lines(self.screen, (90, 200, 50), False, ps, 2)

        pg.display.flip()


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

0
投票

由于它们是精灵,您可以使用

pygame.sprite.collide_rect(sprite1, sprite2)
检查碰撞。如果两个精灵发生碰撞(更具体地说,是它们的
rect
),则语句为
True
。所以:

if pygame.sprite.collide_rect(sprite1, sprite2):
    print("Hit")
    purple_bike.speed = 0

如果他们在同一组,您可以使用

pygame.sprite.spritecollide()
。在文档中,它的参数是:

spritecollide(sprite, group, dokill, collided = None)

如果要删除

sprite
,请将第三个参数 (
doKill
) 设置为
True
。否则,将其设置为
False

© www.soinside.com 2019 - 2024. All rights reserved.