I'm writing a Tetris
game on Pygame
, but the pieces should crumble into sand after touching the floor or sand from other pieces. I did it through pymunk
, but did not find a single article about the interaction of pymunk
objects (sand) and sprites (falling figures).
The falling figures were made through pygame sprites, the sand was made through box2d objects.
Tell me how to track when a sprite comes into contact with a box2d
object?
Sand spawns as dynamic bodies in the shape of a circle and has type <class 'Box2D.Box2D.b2Fixture'>
So, how to track the touch of <class 'Box2D.Box2D.b2Fixture'> and the pygame
sprite
import pygame
from Box2D.b2 import world, polygonShape, circleShape, staticBody, dynamicBody
import sys, os
PPM = 4.0
TARGET_FPS = 60
TIME_STEP = 1.0 / TARGET_FPS
SCREEN_WIDTH, SCREEN_HEIGHT = 300, 600
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), 0, 32)
clock = pygame.time.Clock()
world = world(gravity=(0, -1000))
# walls
ground_body = world.CreateStaticBody(position=(0, 0), shapes=polygonShape(box=(75, 1)))
left_body = world.CreateStaticBody(position=(75, 150), shapes=polygonShape(box=(1, 175)))
right_body = world.CreateStaticBody(position=(0, 150), shapes=polygonShape(box=(1, 175)))
# prepare image for pygame.sprite
def load_image(name, colorkey=None):
fullname = os.path.join('data/images', name)
if not os.path.isfile(fullname):
print(f"Файл с изображением '{fullname}' не найден")
sys.exit()
image = pygame.image.load(fullname)
if colorkey is not None:
image = image.convert()
if colorkey == -1:
colorkey = image.get_at((0, 0))
image.set_colorkey(colorkey)
else:
image = image.convert_alpha()
return image
# sprite
class SomeFigure(pygame.sprite.Sprite):
def __init__(self, *groups):
super().__init__(*groups)
self.image = load_image("T_blue.png")
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = 100, 30
self.figures_sprites = pygame.sprite.Group()
self.figures_sprites.add(self)
def render(self):
self.rect.y += 1
self.figures_sprites.draw(screen)
figure = SomeFigure()
def create_circles(pos):
x, y = pos[0] // 4, (600 - pos[1]) // 4
for step_y in range(0, 60, 16):
step_y = -step_y // 4
for step_x in range(0, 60, 16):
print(x + step_x, y + step_y)
step_x = step_x // 4
body = world.CreateDynamicBody(position=(x + step_x, y + step_y))
circle = body.CreateCircleFixture(radius=1, density=1, friction=1)
print(type(circle))
print()
def create_rectangles(pos):
for step_y in range(0, 60, 15):
for step_x in range(0, 60, 15):
body = world.CreateDynamicBody(position=(pos[0] + step_x, pos[1] + step_y), angle=0)
box = body.CreatePolygonFixture(box=(1, 1), density=1, friction=1)
colors = {staticBody: (255, 255, 255, 255),
dynamicBody: (127, 127, 127, 127)
}
def my_draw_polygon(polygon, body, fixture):
vertices = [(body.transform * v) * PPM for v in polygon.vertices]
vertices = [(v[0], SCREEN_HEIGHT - v[1]) for v in vertices]
pygame.draw.polygon(screen, colors[body.type], vertices)
polygonShape.draw = my_draw_polygon
def my_draw_circle(circle, body, fixture):
position = body.transform * circle.pos * PPM
position = (position[0], SCREEN_HEIGHT - position[1])
pygame.draw.circle(screen, colors[body.type],
[int(x) for x in position], int(circle.radius * PPM))
circleShape.draw = my_draw_circle
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
create_circles(event.pos)
screen.fill((0, 0, 0, 0))
for body in world.bodies:
for fixture in body.fixtures:
fixture.shape.draw(body, fixture)
world.Step(TIME_STEP, 10, 10)
figure.render()
pygame.display.flip()
clock.tick(TARGET_FPS)
pygame.quit()
You can create a large box2d body with a square shape that will fit the unbroken piece. In this case, box2d will take on the job of searching for collisions between unbroken pieces and fragments of sand. All you have to do is link the sprite and this body. If you want the body not to rotate use setFixedRotation() method.
If you don't want to create a separate body for the piece, it will be quite difficult for you to implement realistic interaction between the sand and pieces (especially if the pieces move jerkily like in the original Tetris). However, you can use a simple search over all grains of sand, checking each one for intersections with the sprite. There are also more complex but more effective methods such as world queries for a given rectangular area (AABB queries)