如何使用Pygame的sprite_collide_mask方法检测碰撞方向?

2024-09-22 16:31:28 发布

您现在位置:Python中文网/ 问答频道 /正文

所以我要做的是用Pygame创建一个台球桌游戏,球会像这样从台球桌的边缘反弹。在

我有这样一个台球桌的png图像:

enter image description here

白色的部分是透明的,因此当制作一个遮罩,并将球放在透明部分内时,它使用Pygame的sprite.collide_mask(ballsprite, tablesprite)函数进行碰撞。背景是深绿色。在

但是,sprite.collide_mask函数返回一个碰撞点,我不知道如何计算球应该朝哪个方向移动,因为我只有球的起点(自上次碰撞以来)和碰撞点。在

我知道我应该计算一个曲面的法向量,但是当Pygame的mask collision没有提供任何函数来做这个的时候,我该怎么做呢?在

我想到的一个解决方案是创建一个近似白色(透明)区域的矩形边界框。但是,如果我将矩形设置为透明,那么就不会发生任何冲突(如果我使用mask方法),如果我将矩形设置为与背景相同的颜色,那么它将始终注册一个碰撞,因为球位于矩形的“rect”中。在

我该如何处理碰撞检测,以及如何处理游戏的方向变化?在

(如果需要,我可以提供我的代码)


Tags: 函数图像游戏pngmask方向pygame边缘
1条回答
网友
1楼 · 发布于 2024-09-22 16:31:28

我认为最好借助物理库Pymunk创建一个台球游戏。开始可能有点困难,但是你不必自己去实现物理。在

我这里有一个例子,只是为了演示代码的外观。你必须熟悉图书馆才能了解一切。(用WASD键控制球。)

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 Ball(pg.sprite.Sprite):

    def __init__(self, pos, space):
        super().__init__()
        self.image = pg.Surface((60, 60), pg.SRCALPHA)
        pg.draw.circle(self.image, pg.Color('steelblue2'), (30, 30), 29)
        pg.draw.circle(self.image, pg.Color('black'), (30, 10), 5)
        self.rect = self.image.get_rect(center=pos)
        self.orig_image = self.image
        # Create the physics body and shape of this object.
        self.body = pm.Body()
        self.shape = pm.Circle(self.body, radius=30)
        self.shape.density = .0001
        self.shape.friction = .1
        self.shape.elasticity = .99
        self.body.position = pos
        # Add them to the Pymunk space.
        self.space = space
        self.space.add(self.body, self.shape)
        print(self.body.mass)

        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 = 0.1
        self.shape.elasticity = .92
        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.ball = Ball((300, 300), self.space)
        self.ball2 = Ball((400, 300), self.space)
        self.all_sprites.add(self.ball, self.ball2)
        # Position and vertices tuples for the walls.
        vertices = [
            ([10, 80], ((0, 0), (200, 0), (90, 500), (0, 500))),
            ([400, 250], ((40, 80), (200, 0), (170, 90), (10, 170))),
            ([600, 450], ((20, 40), (300, 0), (300, 120), (10, 100))),
            ([760, 10], ((0, 0), (30, 0), (30, 420), (0, 400))),
            ([10, 10], ((0, 0), (760, 0), (700, 60), (0, 60))),
            ([10, 580], ((0, 0), (760, 0), (700, 60), (0, 60))),
            ]

        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.ball.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)
        pg.display.flip()


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

相关问题 更多 >