2

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()

  • To be more specific, if ANY of the 4 blocks collides with the bottom of the playfield (SCREEN_HEIGHT for now), all 4 blocks need to stop there. – stephenmjefferson Nov 16 '19 at 16:15

0 Answers0