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

【Kivy】Pythonで落ちもの系ゲーム開発ぱーときゅうを表すサムネイル

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

今回は、「次落ちてくるミノ」の表示とスコアの表示を行います。前回Part8で作成したボタン群のレイアウトで、上から2行目にあたる部分を作成します。スコアは、実際に動かしてみないと動作確認ができないため、スコアを表示する場所を作成します。

 

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

 

 

スポンサードサーチ


落ちもの系ゲーム開発Part9

落ちもの系ゲーム開発ぱーときゅう

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番ボリュームがあります。

 

 

今回は、

「次に落ちてくるミノ」の表示(NextWidget)とスコアを表示する部分(ScoreLabel)を作成します。

 

 

次に落ちてくるミノ

次落ちてくるミノを表示するクラス

「次に落ちてくるミノ」を表示するクラス(NextWidget)を作成します。具体的には、4×4のマス目を作り、そこに次に落ちてくるミノを表示します。

次に落ちてくるミノは、FieldWidgetから取得します。つまり、FieldWidgetのnextMinoとNextWidgetのnextMinoは常に同じミノを保持しています。

 

以下は、NextWidgetクラスのソースコードです。tenplusapp.pyに追記します。

# 上記省略

class NextWidget(Widget):
    nextMino = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(NextWidget, self).__init__(**kwargs)
        self.wsize = 0     # FieldWidgetのwsizeと同じ, kvファイルで指定
        self.hsize = 0     # FieldWidgetのhsizeと同じ, kvファイルで指定
        self.blocksList = []
        self.bind(pos=self.on_nextMino)
        self.bind(pos=self.update)
        self.update()
    def on_nextMino(self, *args):
        for cw in self.blocksList:
            self.remove_widget(cw)
        posList = list(zip(*np.where((0 < self.nextMino.mino) & (self.nextMino.mino < 10))))
        for pos in posList:
            num = self.nextMino.mino[pos]
            cw = ColorWidget(pos=[self.x+int(self.wsize*pos[1]), self.y+int(self.hsize*(3-pos[0]))],
                             size=[self.wsize, self.hsize],
                             color=[0.86, 0.08, 0.23, 0.5],
                             number=num)
            self.blocksList.append(cw)
            self.add_widget(cw)
        pass
    def update(self, *args):
        self.canvas.clear()
        # background color
        self.canvas.add(Color(rgba=[1, 1, 1, 1]))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        # drawGrid
        self.canvas.add(Color(rgba=[0.55, 0.55, 0.55, 1]))
        for i in range(1, 4):
            rpos = self.y + self.hsize*i
            cpos = self.x + self.wsize*i
            self.canvas.add(Line(points=[self.x, rpos, self.x+self.width, rpos], width=1))    # row line : 横線
            self.canvas.add(Line(points=[cpos, self.y, cpos, self.y+self.height], width=1))    # col line : 縦線
        # draw outline
        self.canvas.add(Color(rgba=[0, 0, 0, 1]))
        self.canvas.add(Line(rectangle=[self.x, self.y, self.width, self.height], width=3))
    pass

class ColorWidget(Widget):
    # 省略

def getRandomMino():
    # 省略

class FieldWidget(Widget):
    # 省略

class PlayWidget(FloatLayout):
    pass

class TitleWidget(FloatLayout):
    pass

class RootWidget(FloatLayout):
    def gotoTitle(self):
        self.clear_widgets()
        self.add_widget(TitleWidget())
    def gotoPlayDisplay(self):
        self.clear_widgets()
        pw = PlayWidget()
        pw.ids.fw.update()
        pw.ids.nw.nextMino = pw.ids.fw.nextMino = getRandomMino()  # 追加部
        self.add_widget(pw)
    pass

# 下記省略

 

以下は、FieldWidgetのnextMinoをNextWidgetのnextMinoに代入するコードです。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
                on_nextMino: root.ids.nw.nextMino=self.nextMino
        BoxLayout:
            orientation: 'vertical'
            size_hint_x: 4
            # 省略

# 下記省略

 

 

コードの解説(Pythonファイル)

3行目から41行目の「NextWidget」は、「次落ちてくるミノ」を表示するためのクラスです。

 

4行目の「nextMino」は、「次に落ちてくるミノ」を代入する変数です。ObjectPropertyによって、nextMinoが変化したときにイベントが発生するようにしてあります。

 

5行目から12行目の「__init__関数」は、NextWidgetkルアスの初期値を設定する関数です。

7行目の「self.wsize」と8行目の「self.hsize」は、マス目の一辺を表す変数です。このクラスでは0と設定していますが、FieldWidgetのwsize、hsizeを同じにするため、0と設定しています。具体的な数字は、kvファイルで設定します

9行目の「self.blocksList」は、「次に落ちてくるミノ」のブロックを代入するリストです。

10行目の「self.bind(pos=self.on_nextMino)」は、NextWidgetの表示位置が変化したときに、後述するon_nextMino関数を実行するように設定しています。

11行目の「self.bind(pos=self.update)」は、NextWidgetの表示位置が変化したときに、後述するupdate関数を実行するように設定しています。

12行目の「self.update()」は、後述するupdate関数を実行しています。

 

13行目から25行目の「on_nextMino関数」は、nextMinoが変化したときに実行する関数です。

14、15行目の「for文」は、blocksListに代入されているブロックに対して操作を行うコードです。

15行目の「self.remove_widget(cw)」は、NextWidgetから桃区鄭のウィジェットを削除しています。

16行目の「posList」は、nextMinoの配列から1~9の数字が代入されている座標を取得しています。

17行目から24行目の「for文」は、posListに代入されている座標に対して操作を行うコードです。

18行目の「num」は、取り出した座標に代入されている数字を代入する変数です。

19行目から22行目の「ColorWidget」は、ブロックを表現するクラスです。

23行目の「self.blocksList.append(cw)」は、新しく生成したColorWidgetクラスをblocksListに代入しています。

24行目の「self.add_widget(cw)」は、cwをNextWidgetに追加し、ブロックを表示しています。

 

26行目から40行目の「update関数」は、NextWidgetの背景色や縦横の線、アウトラインを表示する関数です。

27行目の「self.canvas.clear()」は、NextWidgetクラスのcanvasに追加されているウィジェットを全て削除しています。

29行目の「Color」は、後述するRectangleの色を白色に指定しています。

30行目の「Rectangle」は、背景の色を設定しています。

32行目の「Color」は、後述するLineの色を灰色に設定しています。

33行目から37行目の「for文」は、縦横の線を表示するためのコードです。

34行目の「rpos」と35行目の「cpos」は、それぞれ縦横の表示位置を計算しています。

36、37行目の「Line」は、縦横の線です。線の太さは1に設定しています。

 

66行目は、NextWidgetのnextMinoとFieldWidgetのnextMinoに同じミノを代入しています。

 

 

コードの解説(kvファイル)

19行目の「on_nextMino」は、FieldWidgetのnextMinoが変化したとき、そのnextMinoをNextWidgetのnextMinoに代入する関数です。

NextWidgetのidやレイアウトは後述します。

 

 

動作確認

スコアを表示する部分とレアイアウトを作成し終わったら、動作確認を行います。

 

 

スポンサードサーチ


スコアを表現するクラス

スコアを表示するクラス

スコアを表示する場所を作成します。kvファイルにてScoreLabelというスコアを表示するラベルクラスを作成します。

 

以下は、ScoreLabelクラスのソースコードです。tenplusapp.kvに追記します。

# -*- coding: utf-8 -*-
<ScoreLabel@Label>:
    color: [0.39, 0.58, 0.93, 1]
    font_size: 36
    size_hint: [None, None]
    text: 'Score'
    canvas.before:
        Color:
            rgba: [1, 1, 1, 1]
        Rectangle:
            pos: self.pos
            size: self.size
    canvas.after:
        Color:
            rgba: [0, 0, 0, 1]
        Line:
            rectangle: [self.x, self.y, self.width, self.height]
            width: 3

# Button Group
<BtnGroup@Button>:
    # 省略

# 下記省略

 

 

コードの解説

2行目から17行目の「<ScoreLabel@Label>:」は、スコアを表示場所です。kivyライブラリのLabelクラスを継承しています。

 

3行目の「color」は、スコアの文字の色を薄い青色に設定しています。

4行目の「font_size」は、文字の大きさを36に設定しています。

5行目の「text」は、スコアの初期文字列を「Score」に設定しています。

 

6行目から11行目は、ラベルの背景色を設定しています。色を白色に、長方形の大きさと表示位置をラベルの大きさ、表示位置と同じに設定しています。

 

12行目から17行目は、アウトラインの設定をしています。色を黒色に、線の太さを3にし、ラベルん周りを囲むように設定しています。

 

 

動作確認

NextWidgetとScoreLabelのレイアウトを作成し終わったら、動作確認を行います。

 

 

NextWidgetとScoreLabelのレイアウト

落ちもの系ゲーム開発のレイアウトを表す画像

「次に落ちてくるミノ」を表示するクラスNextWidgetとスコアを表示するScoreLabelのレイアウトを作成します。前回Part8で作成したレイアウトの上から2行目に作成します。

 

以下は、NextWidgetとScoreLabelのレイアウトのソースコードです。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
                on_nextMino: root.ids.nw.nextMino=self.nextMino
        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]
                        NextWidget:
                            id: nw
                            wsize: fw.wsize
                            hsize: fw.hsize
                            size_hint: [0.25, 1]
                        ScoreLabel:
                            id: slb
                            size_hint: [0.75, 1]
                    BoxLayout:
                        # 省略
                    BtnUnder:
                        # 省略
            Button:
                # 省略

<TitleWidget>:
    # 省略

# 下記省略

 

 

コードの解説

38行目から45行目の「BoxLayout」は、NextWidgetとScoreLabelのレイアウトを設定しています。NextWidgetとScoreLabelを横並びに設置します。

 

38行目から42行目の「NextWidget」は、「次に落ちてくるミノ」を表示するレイアウトです。

39行目の「id」は、このクラスに「nw」というIDを設定しています。

40行目と41行目は、それぞれFieldWidgetのself.wsizeとself.hsizeを設定しています。

42行目の「size_hint」は、ウィジェットを相対サイズを使って設定しています。横幅が25%、縦幅が100%です。

 

43行目から45行目の「ScoreLabel」は、スコアを表示するレイアウトです。

44行目の「id」は、このクラスに「slb」というIDを設定しています。

45行目の「size_hint」は、サイズを比率によって設定しています。横幅が75%で、縦幅が100%です。

 

 

動作確認

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

python tenplusapp.py

 

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

次に落ちてくるミノ表示とスコアを表示

 

「次に落ちてくるミノ」とスコアを確認したら、操作ボタンを押しても反応しないことを確認します。ゲーム開始前は、ミノを動かせないように設定しています。

確認できたら完了です。

 

 

スポンサードサーチ


まとめ

まとめの画像

今回は、「次に落ちてくるミノ」を表示するクラスとスコアを表示するクラスを作成しました。また、レイアウトも作成しました。

 

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

 

次回Part10は、スタートストップボタンと実際にブロックを削除する関数、いて位間隔でcurrentMinoを下方向に移動させる関数を作成します。

 

 

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


スポンサードサーチ