【Kivy】Pythonで落ちもの系ゲーム開発(テトリス風)Part8

【Kivy】Pythonで落ちもの系ゲーム開発(テトリス風)ぱーとはちを表すサムネイル

こんにちは、にわこまです。

今回は、落ちてくるミノ(currentMino)の操作について作成します。具体的には、左移動、右移動、下移動、左回転、右回転です。さらに、それらのボタンも作成します。ボタンはどれも似たような構造をしているため1つ理解出来れば他のボタンも理解できると思います。

 

誤字脱字や何かございましたらご連絡お願いいたします。

 

 

スポンサードサーチ


落ちもの系ゲーム開発Part8

落ちもの系ゲーム開発ぱーとはちを表す画像

Part1で紹介した、今回作成するゲームの概要を改めて説明します。

 

今回開発するゲームは、テトリスに似ています。テトリスのルールは、以下のような7つのミノと呼ばれるブロックを積み重ねていき、横一列をミノで満たしたらその行が消され得点します。

7種類のブロックの形

 

 

作るゲームの概要

テトリスではミノが消される条件は、「横一列にミノがいっぱいになったら」ですが、今回開発するゲームでは隣り合うブロックが10になったら消されるというものです。

ゆえに、ブロック1つひとつには1~9までの数字が代入されます。

以下のような場合、オレンジ色の線で囲まれたブロックが消されます。

削除されるブロックを示した画像

 

さらに、ブロックが消されたら、その列上で消されたブロックの座標より高い位置にあるブロックは消されたブロックの数だけ下がります。(左図から右図のようになります。)

ブロックが削除された後、どのように上のブロックが動作するかを示した画像

 

 

開発環境

OS : Windows10

Python Version : 3.7

Kivy Version : 1.11.1

Numpy Version : 1.18.1

 

 

開発手順

1.全てのミノに共通する関数を作成します。(共通の初期値、動作、関数)

2.7種類のミノ(Iミノ、JミノLミノ、Oミノ、Sミノ、Tミノ、Zミノ)を作成します。

3.ブロックの削除関係

4.kivyを使ってアプリケーションを開発します。

 

1、2、3は4のための準備であるため、簡単です。4は1番ボリュームがあります。

 

 

今回は、

左移動、右移動、下移動、左回転、右回転のミノの操作を行く関数の作成とkvファイルでのボタンのレイアウトを作成します。

kvファイルでのレイアウトが少し複雑になっています。

 

 

ミノを操作する関数

5種のボタンを表す画像

左移動、右移動、下移動、左回転、右回転の5つの関数を作成します。tenplusapp.pyからtenplus.pyで作成したCommonFuncクラスの関数を実行します。

 

以下は、5つの操作を実行する関数のソースコードです。tenplusapp.pyに追記します。

# 上記省略

class FieldWidget(Widget):
    nextMino = ObjectProperty(None)
    score = NumericProperty(0)
    def __init__(self, **kwargs):
        # 省略
    def updateGrid(self, *args):
        # 省略
    def updateWalls(self, *args):
        # 省略
    def updateBlocks(self, *args):
        # 省略
    def updateCurrentMino(self, *args):
        # 省略
    def update(self, *args):
        # 省略
    def rollLeft(self, *args):
        tof = self.currentMino.rollLeft(tenplus.field)
        if(tof):
            self.updateCurrentMino()
    def rollRight(self, *args):
        tof = self.currentMino.rollRight(tenplus.field)
        if(tof):
            self.updateCurrentMino()
    def moveLeft(self, *args):
        tof = self.currentMino.moveLeft(tenplus.field)
        if(tof):
            self.updateCurrentMino()
    def moveRight(self, *args):
        tof = self.currentMino.moveRight(tenplus.field)
        if(tof):
            self.updateCurrentMino()
    def moveUnder(self, *args):
        tof = self.currentMino.moveUnder(tenplus.field)
        if(tof):
            self.updateCurrentMino()

class PlayWidget(FloatLayout):
      pass

# 下記省略

 

 

コードの解説

18行目から21行目の「rollLeft関数」は、左回転の操作を行う関数です。

19行目の「tof」は、TureまたはFalseが代入されます。tenplus.pyのCommonFuncのrollLeftを実行し、回転できたらTureが返却され、回転できなければFalseが返却されます。

20行目の「IF文」は、操作が実行されたか、されてないかを判定する条件です。

21行目の「self.updateCurrentMino()」は、落ちてくるミノ(currentMino)を再描画しています。つまり、変化した状態を盤面上に反映しています。

 

22行目から25行目の「rollRight関数」は、右回転の操作を行う関数です。

23行目の「tof」は、TureまたはFalseが代入されます。tenplus.pyのCommonFuncのrollRightを実行し、回転できたらTureが返却され、回転できなければFalseが返却されます。

24行目の「IF文」は、操作が実行されたか、されてないかを判定する条件です。

25行目の「self.updateCurrentMino()」は、落ちてくるミノ(currentMino)を再描画しています。つまり、変化した状態を盤面上に反映しています。

 

26行目から29行目の「moveLeft関数」は、左移動の操作を行う関数です。

27行目の「tof」は、TureまたはFalseが代入されます。tenplus.pyのCommonFuncのmoveLeftを実行し、移動できたらTureが返却され、移動できなければFalseが返却されます。

28行目の「IF文」は、操作が実行されたか、されてないかを判定する条件です。

29行目の「self.updateCurrentMino()」は、落ちてくるミノ(currentMino)を再描画しています。つまり、変化した状態を盤面上に反映しています。

 

30行目から33行目の「moveRight関数」は、右移動の操作を行う関数です。

31行目の「tof」は、TureまたはFalseが代入されます。tenplus.pyのCommonFuncのmoveRightを実行し、移動できたらTureが返却され、移動できなければFalseが返却されます。

32行目の「IF文」は、操作が実行されたか、されてないかを判定する条件です。

33行目の「self.updateCurrentMino()」は、落ちてくるミノ(currentMino)を再描画しています。つまり、変化した状態を盤面上に反映しています。

 

34行目から37行目の「moveUnder関数」は、下移動の操作を行う関数です。

35行目の「tof」は、TureまたはFalseが代入されます。tenplus.pyのCommonFuncのmoveUnderを実行し、移動できたらTureが返却され、移動できなければFalseが返却されます。

36行目の「IF文」は、操作が実行されたか、されてないかを判定する条件です。

37行目の「self.updateCurrentMino()」は、落ちてくるミノ(currentMino)を再描画しています。つまり、変化した状態を盤面上に反映しています。

 

tof = True or False

 

 

動作確認

ボタンを作成したときに動作確認を行います。

 

 

スポンサードサーチ


ボタンクラス

ボタンのクラスを表す画像

5つの操作を実行するためのボタンクラスを作成します。具体的には、サイズや背景色などを設定します。また、タイトル画面にもどるボタンも作成します。

ボタンには画像を使って操作を表します。以下のファイルをダウンロードしてください。

Private File - Access Forbidden

 

以下は、操作ボタンのクラスをソースコードです。tenplusapp.kvファイルに追記します。

# -*- coding: utf-8 -*-
# Button Group
<BtnGroup@Button>:
    font_size: 36
    text: ''
    canvas.after:
        Color:
            rgba: [0, 0, 0, 1]
        Line:
            rectangle: [self.x, self.y, self.width, self.height]
            width: 3
<Btn@BtnGroup>:             # BtnStart and BtnStop
    background_normal: ''
    background_color: [0.2, 0.7, 0.2, 1]
<BtnOperation@BtnGroup>:    # BtnLt, BtnRollLt, BtnRollRt, BtnRt
    canvas.before:
        Color:
            rgba: [0.2, 0.85, 0.2, 1]
        Rectangle:
            pos: self.pos
            size: self.size

<BtnStart@Btn>:
    text: 'Start'
<BtnStop@Btn>:
    text: 'Stop'
<BtnLt@BtnOperation>:
    background_normal: './LeftArrow.png'
<BtnRt@BtnOperation>:
    background_normal: './RightArrow.png'
<BtnRollLt@BtnOperation>:
    background_normal: './lra.png'
<BtnRollRt@BtnOperation>:
    background_normal: './rra.png'
<BtnUnder@BtnOperation>:
    background_normal: './UnderArrow.png'

<FieldWidget>:

<PlayWidget>:
    # 省略

# 下記省略

 

 

コードの解説

3行目から11行目の「<BtnGroup@Button>:」は、ボタン全体に共通する要素を設定しています。kivyライブラリのButtonクラスを継承しています。

5、6行目の「Color」は、後述するLineの色を黒色に指定しています。

7、8、9行目の「Line」は、ボタンの縁を線で囲むために指定しています。囲む範囲は、[self.x, self.y, self.width, self.height]によってボタンのサイズちょうどにしてあります。線の太さは3に設定しています。

 

10行目から13行目の「<Btn@BtnGroup>:」は、スタートボタンとストップボタンに共通する要素を設定しています。

11行目の「font_size: 36」は、フォントサイズを36に設定しています。

13行目の「background_color」は、ボタンの背景色を緑色に設定しています。また、背景色を設定するためには、「background_normal」は空値を設定すr必要があります。

 

14行目から21行目の「<BtnOperation@BtnGroup>:」は、操作を行うボタンに共通する要素を設定しています。

15行目の「text: ”」は、操作ボタンには文字列を使わないため、ボタンのテキストに空値を設定しています。これは無くても問題ありませんが、一応設定しています。

17、18行目の「Color」は、後述するRectangleの色を緑色に設定しています。

19、20行目の「Rectangle」は、操作ボタンの背景色をウィジェットを使って設定しています。

 

22行目の「<BtnStart@Btn>:」と24行目の「<BtnStop@Btn>:」は、それぞれスタートボタンとストップボタンを表すクラスです。Btnクラスを継承しています。

 

26行目から35行目のボタンクラスは、操作ボタンのクラスです。BtnOperationクラスを継承しています。また、それぞれ「background_normal:」に、対応する画像を指定しています。

 

 

動作確認

ボタンのレイアウトが完成したときに確認します。

 

 

ボタンのレイアウト

ボタンのレイアウトを表す画像

操作を実行する関数とボタンのクラスを作成することができたため、ボタンのレイアウトを作成します。具体的には、盤面の右側に作成していきます。

 

以下は、ボタン群のソースコードです。tenplusapp.kvに追記します。

# 上記省略

<FieldWidget>:

<PlayWidget>:
    canvas.before:
        Color:
            rgba: [1, 1, 1, 1]
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        AnchorLayout:
            anchor_x: 'center'
            anchor_y: 'center'
            size_hint_x: 3
            FieldWidget:
                id: fw
        BoxLayout:
            orientation: 'vertical'
            size_hint_x: 4
            AnchorLayout:
                anchor_x: 'center'
                anchor_y: 'center'
                size_hint_y: 4
                BoxLayout:
                    orientation: 'vertical'
                    size_hint: [None, None]
                    size: [fw.wsize*16, fw.hsize*16]
                    BoxLayout:
                        size_hint: [1, 0.25]
                        # startボタンとstopボタンの動作は次回以降
                        BtnStart:
                        BtnStop:
                    BoxLayout:
                        size_hint: [1, 0.25]
                        # scoreと次のミノを表示する予定
                    BoxLayout:
                        size_hint: [1, 0.25]
                        BtnLt:
                            size_hint: [0.25, 1]
                            on_press: if(fw.currentMino != None): fw.moveLeft()
                        BtnRollLt:
                            size_hint: [0.25, 1]
                            on_press: if(fw.currentMino != None): fw.rollLeft()
                        BtnRollRt:
                            size_hint: [0.25, 1]
                            on_press: if(fw.currentMino != None): fw.rollRight()
                        BtnRt:
                            size_hint: [0.25, 1]
                            on_press: if(fw.currentMino != None): fw.moveRight()
                    BtnUnder:
                        size_hint: [1, 0.25]
                        on_press: if(fw.currentMino != None): fw.moveUnder()
            Button:
                background_normal: ''
                background_color: [1, 1, 1, 1]
                color: [0.8, 0.8, 0.8, 1]
                font_size: 36
                pos_hint: {'right':1, 'y':0}
                size_hint: [None, None]
                size: [200, 100]
                text: 'Title -->'
                on_press: app.root.gotoTitle()

<TitleWidget>:
    # 省略

# 下記省略

 

 

コードの解説

22行目から64行目が新たに追加したコードです。

 

22行目から25行目の「AnchorLayout」は、ボタン群をなるべく真ん中に表示させるためのレイアウトです。ゆえに、「anchor_x」と「anchor_y」は、centerと設定しています。さらに、縦のサイズ「size_hint_y」は4としています。後述するボタンと4対1でレイアウトを分けています。

FieldWidgetの右側のレイアウト

 

26行目から29行目の「BoxLayout」は、「タイトル画面へ戻るボタン」以外のボタンを設置するためのレイアウトです。

27行目の「orientation: ‘vertical’」は、ボックスのレイアウトを縦方向と設定しています。

28行目の「size_hint: [None, None]」は、レイアウトのサイズは絶対サイズを使用するため相対サイズはNoneに設定しています。

29行目の「size: [fw.wsize*16, fw.hsize*16]」は、縦横のサイズをそれぞれブロックの16倍に設定しています。これは、「次に落ちてくるミノ」もこのレイアウトに表示するためブロックのサイズの整数倍にしています。

アンカーレイアウトの中のレイアウト

 

30行目から34行目の「BoxLayout」は、スタートストップボタンを横並びに表示するためのレイアウトです。先ほどの図でBoxLayout1にあたる部分です。

31行目の「size_hint: [1, 0.25]」は、このレイアウトのサイズを比率によって設定しています。横100%、縦25%です。縦のサイズが25%である理由は、これと似たようなレイアウトを合計4つ作成するからです。

 

35、36、37行目の「BoxLayout」は、「次に落ちてくるミノ」とスコアを横並びに表示するためのレイアウトです。先ほどの図でBoxLayout2にあたるレイアウトです。

36行目の「size_hint: [1, 0.25]」は、このレイアウトのサイズを比率によって設定しています。横100%、縦25%です。

 

38行目から51行目の「BoxLayout」は、左移動、左回転、右回転、右移動の操作を横並びに表示するためのレイアウトです。先ほどの図でBoxLayout3にあたるレイアウトです。

39行目の「size_hint: [1, 0.25]」は、このレイアウトのサイズを比率によって設定しています。横100%、縦25%です。

 

40行目から42行目の「BtnLt」は、左移動を実行するボタンです。

41行目の「size_hint: [0.25, 1]」は、ボタンのサイズを比率によって設定しています。横25%、縦100%です。

42行目の「on_press」は、ボタンが押されたときの処理を設定しています。具体的には、currentMinoがNoneでないときmoveLeft関数を実行します。

 

43行目から45行目の「BtnRollLt」は、左回転を実行するボタンです。

43行目の「size_hint: [0.25, 1]」は、ボタンのサイズを比率によって設定しています。横25%、縦100%です。

45行目の「on_press」は、ボタンが押されたときの処理を設定しています。具体的には、currentMinoがNoneでないときrollLeft関数を実行します。

 

46行目から48行目の「BtnRollRt」は、右回転を実行するボタンです。

47行目の「size_hint: [0.25, 1]」は、ボタンのサイズを比率によって設定しています。横25%、縦100%です。

48行目の「on_press」は、ボタンが押されたときの処理を設定しています。具体的には、currentMinoがNoneでないときrollRight関数を実行します。

 

49行目から51行目の「BtnRt」は、右移動を実行するボタンです。

50行目の「size_hint: [0.25, 1]」は、ボタンのサイズを比率によって設定しています。横25%、縦100%です。

51行目の「on_press」は、ボタンが押されたときの処理を設定しています。具体的には、currentMinoがNoneでないときmoveRight関数を実行します。

ボックスレイアウトさんのレイアウト。左から左移動ボタン、左回転ボタン、右回転ボタン、右移動ボタン。

 

52行目から54行目の「BtnUnder」は、下移動を実行するボタンです。先ほどの図でButton4にあたります。

53行目の「size_hint: [1, 0.25]」は、ボタンのサイズを比率によって設定しています。横100%、縦25%です。

54行目の「on_press」は、ボタンが押されたときの処理を設定しています。具体的には、currentMinoがNoneでないときmoveUnder関数を実行します。

 

55行目から64行目の「Button」は、「タイトル画面へもどるボタン」です。

57行目の「background_color: [1, 1, 1, 1]」は、背景色を白色に設定しています。また、背景色を設定するために「background_nomal」には、空値を指定しています。

58行目の「color:」は、文字の色を灰色に設定しています。

59行目の「font_size: 36」は、フォントサイズを36に設定しています。

60行目の「pos_hint:」は、ボタンがウィンドウの右下に来るように表示位置を設定しています。

62行目の「size: [200, 100]」は、ボタンのサイズを横200、縦100で絶対サイズ指定しています。絶対サイズを使用するため、相対サイズの「size_hint」はNoneにしています。

63行目の「text: ‘Title –>’」は、文字列を指定しています。

64行目の「on_press」は、ボタンが押されたときの処理を設定しています。

 

 

動作確認

コマンドプロンプトを開き、ファイルが保存してあるフォルダまで移動します。移動したら以下のコマンドを入力し、実行します。

python tenplusapp.py

 

以下のように表示されます。

tenplusapp.py実行時の画像

 

5種の操作ボタンを押してみて、ミノの動きを確認します。

5種のボタンの動作確認を行った画像

 

ボタンと操作が対応していることを確認できたら、動作確認は完了です。

 

 

スポンサードサーチ


まとめ

まとめの画像

今回は、落ちてくるミノ操作を実行する関数を作成しました。具体的には、左回転、右回転、左移動、右移動、下移動の関数を作成しました。さらに、ボタンのレイアウトも作成しました。

 

本記事で作成したコードを以下に示します。

本記事で使用したボタンの画像を以下に示します。

Private File - Access Forbidden

 

次回Part9は、「次に落ちてくるミノ」の表示とスコアの表示の作成を行います。

 

 

最後までお読みいただきありがとうございます。


スポンサードサーチ