Breakout

Raspberry Piにロータリーエンコーダを接続して値を読み取る事はできたので、これを何かに応用したいと考えた。真っ先に思い付いたのは「ブロック崩し」のパドルコントローラとしての応用で、そもそも秋月でロータリーエンコーダを購入したのは、Arduinoのゲームコントローラに使えないか?と考えての事。

結局Arduinoで使うことは無かったが、Raspberry Piならゲーム作成用のライブラリも存在しているので、試しに作ってみる事にした。実を言うと前回の「PyGameでサウンド再生」はその過程で引っかかった事なのだ。

まずはロータリーエンコーダを使わずに、キーボードで操作できるバージョンを作ってみた。プログラムは以下の通り。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import pygame
from pygame.locals import *

screen_rect = Rect(0, 0, 480, 640)
white = 255, 255, 255
black = 0, 0, 0
flip = ((10, 10, 10, 10,  8,  8,  8,  8,
          8,  8,  8,  8,  8,  8,  8,  8,
          8,  8,  8,  8,  8,  8,  6,  6,
          6,  6,  6,  6,  4,  4,  4,  4),
        ( 4,  4,  4,  4,  8,  8,  8,  8,
          8,  8,  8,  8,  8,  8,  8,  8,
          8,  8,  8,  8,  8,  8,  8,  8,
          8,  8,  8,  8, 10, 10, 10, 10))

class Block(pygame.sprite.Sprite):
    def __init__(self, filename, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(filename).convert()
        ckey = self.image.get_at((0,0))
        self.image.set_colorkey(ckey, RLEACCEL)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(x * 48 + 1, y * 16 + 64, w, h)
    def update(self, ball):
        pass
    def draw(self):
        screen.blit(self.image, self.rect)

class Ball(pygame.sprite.Sprite):
    def __init__(self, filename):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(filename).convert()
        ckey = self.image.get_at((0,0))
        self.image.set_colorkey(ckey, RLEACCEL)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(10, 200, w, h)
        self.speed = [8, 8]
    def update(self):
        self.rect.move_ip(self.speed)
        if self.rect.left <= 0 or self.rect.right > screen_rect.width:
            self.speed[0] = -self.speed[0]
        if self.rect.top <= 0:
            self.speed[1] = -self.speed[1]
        self.rect = self.rect.clamp(screen_rect)
    def draw(self):
        screen.blit(self.image, self.rect)
    def reset(self):
        self.rect = Rect(10, 200, self.rect.width, self.rect.height)
        self.speed = [8, 8]

class Paddle(pygame.sprite.Sprite):
    def __init__(self, filename):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(filename).convert()
        ckey = self.image.get_at((0,0))
        self.image.set_colorkey(ckey, RLEACCEL)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(208, 592, w, h)
        self.speed = [0, 0]
    def update(self):
        self.rect.move_ip(self.speed)
        self.rect = self.rect.clamp(screen_rect)
    def draw(self):
        screen.blit(self.image, self.rect)

def set_block(blocks):
    for y in range(0, 8):
        for x in range(0, 10):
            if y < 2:
                block = Block("block_red.png", x, y)
            elif y < 4:
                block = Block("block_orange.png", x, y)
            elif y < 6:
                block = Block("block_green.png", x, y)
            elif y < 8:
                block = Block("block_yellow.png", x, y)
            blocks.add(block)

if __name__ == '__main__':
    pygame.mixer.pre_init(frequency = 44100, size = -16, channels = 2, buffer = 1024)
    pygame.init()
    screen = pygame.display.set_mode(screen_rect.size)
    #bg = pygame.image.load("breakout_bg.png").convert()
    pygame.event.set_allowed([QUIT, KEYDOWN, KEYUP])
    font = pygame.font.Font(None, 40)
    ball = Ball("ball.png")
    paddle = Paddle("paddle.png")
    sound01 = pygame.mixer.Sound('se01.wav')
    sound02 = pygame.mixer.Sound('se02.wav')
    blocks = pygame.sprite.RenderUpdates()
    set_block(blocks)
    clock = pygame.time.Clock()
    pmove = [0, 0]
    state = 0
    bleft = 3
    tick = pygame.time.get_ticks()
    while True:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.mixer.quit()
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN:
                if event.key == K_LEFT:
                    pmove[0] = -8
                elif event.key == K_RIGHT:
                    pmove[1] = 8
            elif event.type == KEYUP:
                if event.key == K_LEFT:
                    pmove[0] = 0
                elif event.key == K_RIGHT:
                    pmove[1] = 0
        paddle.speed = [pmove[0] + pmove[1], 0]
        screen.fill(black)
        #screen.blit(bg, screen_rect)
        paddle.update()
        if state == 1:
            ball.update()
            if pygame.sprite.spritecollideany(ball, blocks):    # Hit Block?
                sound01.play()
                for block in blocks:
                    if pygame.sprite.collide_rect(ball, block):
                        clip = block.rect.clip(ball.rect)
                        if(clip.height > clip.width):
                            ball.speed[0] = -ball.speed[0]
                        else:
                            ball.speed[1] = -ball.speed[1]
                        blocks.remove(block)
            if pygame.sprite.collide_rect(ball, paddle):        # Hit Paddle?
                sound02.play()
                ball.speed[1] = -ball.speed[1]
                clip = paddle.rect.clip(ball.rect)
                p = clip.left - paddle.rect.left + clip.width / 2
                if p >= 32:
                    p = 63 - p
                vx = flip[0][p]
                if ball.speed[0] < 0:
                    vx = -vx
                ball.speed[0] = vx
                ball.speed[1] = -flip[1][p]
            ball.draw()
        blocks.draw(screen)
        paddle.draw()
        if state == 0:
            text = font.render('Ready!', True, (255, 255, 255))
            screen.blit(text, (190, 300))
            if tick + 2000 < pygame.time.get_ticks():
                state = 1
        elif state == 1:
            if ball.rect.bottom >= screen_rect.height:
                ball.reset()
                bleft = bleft - 1
                if bleft > 0:
                    state = 2
                else:
                    state = 3
                tick = pygame.time.get_ticks()
        elif state == 2:
            msg = "Ball Left : " + str(bleft)
            text = font.render(msg, True, (255, 255, 255))
            screen.blit(text, (175, 300))
            if tick + 2000 < pygame.time.get_ticks():
                state = 1
        elif state == 3:
            text = font.render('GAME OVER', True, (255, 255, 255))
            screen.blit(text, (160, 300))
            if tick + 2000 < pygame.time.get_ticks():
                pygame.mixer.quit()
                pygame.quit()
                sys.exit()
        if not blocks and ball.rect.top > 320:
            set_block(blocks)   # All Blocks Clear
        pygame.display.update()

動かす為には画像ファイルとサウンドファイルも必要なので、ソースコードと必要なファイルはまとめてGoogle Driveへ置いた。ここにアクセスして、画面左上の「ファイル」をクリックしてメニューを開き、「ダウンロード」を選択。

GoogleDrive

解凍してRaspberry Piにすべてのファイルを転送したら

$ python Breakout.py

で起動する。
操作はカーソルキーの左右のみ。
Breakout_cap

PyGameでゲームを作るのは初めてなので、あちこち無駄があったり良くない使い方があるような気がするが、大目に見て欲しい。Python2.7とPyGameがインストールされていれば、Windowsでも動作するはず(MacOSは未確認)。
最初は背景画像も表示するようにしていたのだが、ボールが見えにくくなるだけだったのでコメントアウトして外してある。良さそうな画像があったら差し替えてみても面白いかも。ブロックとボール、パドルも単なる画像なので差し替え可能。

次はこれを元に、ロータリーエンコーダーでパドルを操作できるバージョンを作ってみる予定。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中