Through some trial and error, I have decided to draw each of the four Tetris blocks for each shape as their own sprites on separate surfaces. There is a main pivot block in which the other 3 blocks get their y position. Currently working on getting the shape to stop at bottom of screen OR if collision with other blocks at bottom of screen. The movement is controlled in update_shape(). If there is a collison on the rect.bottom of the bottom facing blocks, the movement must stop. Any ideas? Sorry in advance, my OOP is newbie level at best!
import pdb
import random
import sys
import pygame
from pygame.locals import (
K_ESCAPE, K_SPACE,
K_c, K_x, K_z,
K_UP, K_DOWN, K_RIGHT, K_LEFT, K_F1,
K_RSHIFT, K_LSHIFT, K_RCTRL, K_LCTRL,
QUIT, KEYDOWN, KEYUP
)
VERSION = 0.25
# Colors
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
RED = ( 255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
CYAN = ( 0, 255, 255)
YELLOW = ( 255, 255, 0)
PURPLE = ( 128, 0, 128)
ORANGE = ( 255, 165, 0)
PINK = ( 255, 0, 255)
# Screen dimensions
SCREEN_WIDTH, SCREEN_HEIGHT = 432, 720 # 18 x 30
# Frames Per Second
#
FPS = 20
# Gravity
# Gravity = G
#1G = 1 cell per frame
# 0.1G = 1 cell per 10 frames
# TODO - how does this work?
GRAVITY = 2
# tilesize for blocks and grid for playfield and game screen
TILESIZE = 24
###############################################################################
class SingleBlock(pygame.sprite.Sprite):
""" base class for block sprite """
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.tile = TILESIZE
# initial start for block
self.pivot_x = SCREEN_WIDTH / 2
self.pivot_y = 0
# 7 block types (this will be order for all block data)
self.block_types = ['t', 'j', 'z', 'o', 's', 'l', 'i']
self.block_colors = [PURPLE, BLUE, RED, YELLOW, GREEN, ORANGE, CYAN]
# coordinates for each shape
# (excludes pivot block which is auto drawn by function)
self.block_coords = [
[[ 0, -1], [-1, 0], [ 1, 0]], # 0 tu
[[ 0, -1], [ 1, 0], [ 0, 1]], # 1 tr
[[-1, 0], [ 1, 0], [ 0, 1]], # 2 td spawn
[[ 0, -1], [-1, 0], [ 0, 1]], # tl 3
[[ 0, -1], [-1, 1], [ 0, 1]], # jl 4
[[-1, -1], [-1, 0], [ 1, 0]], # ju 5
[[ 0, -1], [ 1, -1], [ 0, 1]], # jr 6
[[-1, 0], [ 1, 0], [ 1, 1]], # jd 7 spawn
[[-1, 0], [ 0, 1], [ 1, 1]], # zh 8 spawn
[[ 1, -1], [ 1, 0], [ 0, 1]], # zv 9
[[-1, 0], [-1, 1], [ 0, 1]], # o 10
[[ 1, 0], [-1, 1], [ 0, 1]], # sh 11 spawn
[[ 0, -1], [ 1, 0], [ 1, 1]], # sv 12
[[ 0, -1], [ 0, 1], [ 1, 1]], # lr 13
[[-1, 0], [ 1, 0], [-1, 1]], # ld 14 spawn
[[-1, -1], [ 0, -1], [ 0, 1]], # ll 15
[[-1, 0], [ 1, 0], [ 1, -1]], # lu 16
[[ 0, -2], [ 0, -1], [ 0, 1]], # iv 17
[[-2, 0], [-1, 0], [ 1, 0]] # ih 18 spawn
]
def draw_single_block(self, color, x, y):
self.surf = pygame.Surface((self.tile, self.tile))
self.surf.fill(color)
self.rect = self.surf.get_rect(left=x, top=y)
outline_rect = pygame.Rect(0, 0, self.tile, self.tile)
pygame.draw.rect(self.surf, BLACK, outline_rect, 2)
def make_shape(self, shape_index, color_index):
self.shape_index = shape_index
# draw Pivot block and add to groups
self.P = PivotBlock(color_index)
self.all_sprites.add(self.P)
self.moving.add(self.P)
# Create three blocks and add to groups
self.three_blocks = []
for block in range(3):
block = CoupledBlocks(self.block_coords[self.shape_index][block][0],
self.block_coords[self.shape_index][block][1],
color_index)
self.all_sprites.add(block)
self.moving.add(block)
self.three_blocks.append(block)
def handle_events(self, pressed_keys):
if pressed_keys[K_UP] or pressed_keys[K_x]:
self.rotate_clockwise()
if pressed_keys[K_LCTRL] or pressed_keys[K_RCTRL] or pressed_keys[K_z]:
self.rotate_counter_clockwise()
if pressed_keys[K_LEFT]:
self.left_shift()
if pressed_keys[K_RIGHT]:
self.right_shift()
if pressed_keys[K_DOWN]:
self.non_locking_soft_drop()
if pressed_keys[K_SPACE]:
self.hard_drop()
if pressed_keys[K_LSHIFT] or pressed_keys[K_RSHIFT] or pressed_keys[K_c]:
self.hold()
if pressed_keys[K_F1]:
self.pause()
if self.rect.centery > SCREEN_HEIGHT - (self.tile * 1.5):
self.rect.centery = SCREEN_HEIGHT - (self.tile * 1.5)
else:
self.block_fall()
def block_fall(self):
""" changes y by Gravity """
def rotate_clockwise(self):
""" Up arrow or X """
pass
def rotate_counter_clockwise(self):
""" Ctrl or Z """
pass
def left_shift(self):
""" Left arrow """
pass
def right_shift(self):
"""" Right arrow """
pass
def non_locking_soft_drop(self):
""" Down arrow """
pass
def hard_drop(self):
""" space bar """
pass
def hold(self):
""" Shift or C """
pass
def pause(self):
""" Esc or F1 """
pass
def update_shape(self):
#print(self.moving)
for block in self.moving:
print(block)
print(block.rect)
#for hits in pygame.sprite.groupcollide(self.moving, self.stopped, False, False):
if block.rect.bottom >= SCREEN_HEIGHT:
self.stopped.add(block)
self.moving.remove(block)
block.rect.bottom = SCREEN_HEIGHT
else:
self.P.rect.y += GRAVITY
print(self.P.rect.y)
for block in range(3):
self.three_blocks[block].rect.x = (self.three_blocks[block].block_coords[self.shape_index][block][0] * self.tile) + self.P.rect.x
self.three_blocks[block].rect.y = (self.three_blocks[block].block_coords[self.shape_index][block][1] * self.tile) + self.P.rect.y
def update(self):
pass
###############################################################################
class PivotBlock(SingleBlock):
""" """
def __init__(self, color):
super(PivotBlock, self).__init__()
# draw pivot block
self.draw_single_block(self.block_colors[color], self.pivot_x, self.pivot_y)
def update(self):
pass
###############################################################################
class CoupledBlocks(SingleBlock):
""" """
def __init__(self, x_offset, y_offset, color):
super(CoupledBlocks, self).__init__()
self.x_offset = x_offset
self.y_offset = y_offset
self.my_x = (self.x_offset * self.tile) + self.pivot_x
self.my_y = (self.y_offset * self.tile) + self.pivot_y
self.draw_single_block(self.block_colors[color], self.my_x, self.my_y)
def update(self):
pass
################################################################### Game Class
class Game(SingleBlock):
def __init__(self, width=SCREEN_WIDTH, height=SCREEN_HEIGHT, fps=FPS):
""" Initialize pygame, window, background, font, ... """
super(Game, self).__init__()
pygame.init()
self.width = width
self.height = height
self.tile = TILESIZE
# screen, caption and background
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Block Stack Extravaganza! v' + str(VERSION))
self.background = pygame.Surface(self.screen.get_size()).convert()
self.background.fill(BLACK)
# Mange how fast screen updates
self.fps = fps
self.clock = pygame.time.Clock()
self.playtime = 0.0
# Fonts
self.font = pygame.font.SysFont('mono', 15, bold=True)
# Create groups
self.all_sprites = pygame.sprite.Group()
self.moving = pygame.sprite.Group()
self.stopped = pygame.sprite.Group()
def run(self):
""" MAIN LOOP """
# Create block type and add to groups
self.make_shape(10,0)
### MAIN LOOP #########################################################
# Variable to keep main loop running
running = True
while running:
# Events
for event in pygame.event.get():
if event.type == QUIT :
running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == KEYUP:
pass
# Handle key presses
#pressed_keys = pygame.key.get_pressed()
#B.handle_events(pressed_keys)
'''
if not self.moving:
self.make_shape(2,0)
'''
self.update_shape()
# Update sprite groups
for ent in self.all_sprites:
ent.update()
# Blit background map
self.screen.blit(self.background, (0,0))
# Text draw
milliseconds = self.clock.tick(self.fps)
self.playtime += milliseconds / 1000.0
self.draw_text("FPS: {:6.3}{}PLAYTIME: {:6.3}SECONDS".format(self.clock.get_fps(), " "*5, self.playtime))
# Draw all sprites
for ent in self.all_sprites:
self.screen.blit(ent.surf, ent.rect)
#ent.handle_events(event)
#BB.update()
#self.update_shape()
#if pygame.sprite.spritecollideany(player, characters):
# Flip to display current frame
pygame.display.flip()
pygame.quit()
print("This game was played for {0:.2f} seconds".format(self.playtime))
sys.exit()
def draw_text(self, text):
""" Center text in window """
fw, fh = self.font.size(text) # fw fontwidth, fh fontheight
surface = self.font.render(text, True, (GREEN))
self.screen.blit(surface, ((self.width - fw) // 2, (self.height - fh) // 2))
######################################################################### main
if __name__ == "__main__":
Game().run()