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

【Kivy】Pythonで落ちもの系ゲーム開発Part11を表すサムネイル

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

今回は、削除するブロックを目立たせる関数と落ちてくるスピードを変化させる関数を作成します。また、この落ちもの系ゲームを完成させます。

 

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

 

 

スポンサードサーチ


落ちもの系ゲーム開発Part11

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

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

 

 

今回は、

削除するブロックを目立たせる関数(drawDelBlocks)と落ちてくるスピードを変化させる関数(calcIntervalTime)を作成します。

 

 

FrameWidget

FrameWdiget&delBlocksGen

ブロックを目立たせるウィジェット(FrameWidget)を作成します。具体的には、ブロックの周りをオレンジ色で囲んで目立たせます。pythonファイルではクラスを宣言だけします。具体的なコードはkvファイルに記述します。

また、処理と処理の間を任意の時間だけ待つ関数(delBlocksGen)を作成します。

 

以下は、FrameWidgetとdelBlocksGenのソースコードです。tenplusapp.pyに追記します。

import kivy
kivy.require('1.11.1')

import tenplus
from tenplus import Imino, Jmino, Lmino, Omino, Smino, Tmino, Zmino
import random
import numpy as np

from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.graphics import Color, Line, Rectangle
from kivy.properties import ObjectProperty, NumericProperty

from kivy.core.window import Window
Window.size = (910, 720)

from kivy.lang import Builder
Builder.load_file('tenplusapp_part11.kv')

class FrameWidget(Widget):
    pass

def delBlocksGen(gen):
    def delBlocksStep(dt):
        try:
            sec = gen.__next__()
            Clock.schedule_once(delBlocksStep, sec)
        except StopIteration:
            pass
    delBlocksStep(None)

# 下記省略

 

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

# -*- coding: utf-8 -*-
<FrameWidget>:
    size_hint: [None, None]
    canvas.before:
        Color:
            rgba: [1, 0.55, 0, 0.8]    # darkorange #ff8c00
        Line:
            rectangle: [self.x, self.y, self.width, self.height]
            width: 2

# 下記省略

 

 

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

23、24行目は、FrameWidgetのクラスを宣言だけしています。

 

26行目から33行目の「delBlocksGen関数」は、処理と処理の間を任意の時間待つ関数です。引数はgenです。genにはyieldが設定された関数が代入されます。

28行目から32行目は、「try~except…文」を使って、エラー時の処理を設定しています。

29行目の「sec = gen.__next__()」は、genを実行し、返却値をsecに代入しています。

30行目は、Clockオブジェクトを使用し実際に処理と処理の間をsec秒待ちます。正確には、sec秒後に自分自身を呼び出し、gen関数をもう一度実行します。

32行目は、genが実行できなかった場合passを実行します。つまり、エラーが発生しても無視するということです。

33行目は、delBlocksStep関数を実行しています。

 

 

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

2行目から9行目の「<FrameWidget>:」は、ブロックを目立たせるウィジェットのデザインです。

3行目の「size_hint: [None, None]」は、相対サイズを無効にしています。FrameWidgetのサイズには絶対サイズを使用します。

5、6行目の「Color」は、後述するLineをオレンジ色にしています。

7、8、9行目の「Line」は、線を引く範囲と太さを指定しています。線の範囲はサイズを同じ大きさで、太さは2に設定しています。

 

 

動作確認

本記事の最後で動作確認を行います。

 

 

スポンサードサーチ


削除するブロックを目立たせる

削除するブロックを目立たせる関数

先ほど作ったFrameWidgetを使って、削除するブロックを目立たせる関数(drawDelBlocks)を作成します。また、前回作ったdelBlocksTask関数を少し変化させ、処理と処理の間を任意の時間待てるようにします。

 

以下は、drawDelBlocksのソースコードです。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 gameStart(self, *args):
        # 省略
    def gameStop(self, *args):
        # 省略
    def drawDelBlocks(self, delPosList, *args):
        for pos in delPosList:
            num = tenplus.field[pos]
            frw = FrameWidget(pos=[self.x + int(self.wsize*pos[1]), self.y + int(self.hsize*(25-pos[0]))],
                              size=[self.wsize, self.hsize])
            self.blocksList.append(frw)
            self.add_widget(frw)
    def delBlocksTask(self, *args):
        delPosList = tenplus.getdelPosList(tenplus.field)
        while(len(delPosList) != 0):
            nob = len(delPosList)   # nob = Number of Blocks
            self.score = self.score + nob * 100
            self.drawDelBlocks(delPosList)
            tenplus.downNum(tenplus.field, delPosList)
            delPosList = tenplus.getdelPosList(tenplus.field)
            yield 1
            self.updateBlocks()
        tof45 = tenplus.checkExitCond(tenplus.field, (4, 5))    # 終了していなければ、Falseが返される
        tof46 = tenplus.checkExitCond(tenplus.field, (4, 6))    # 終了していなければ、Falseが返される
        if(not(tof45) and not(tof46)):
            # 最初のミノ生成以外はここから生成
            self.currentMino = self.nextMino
            self.nextMino = getRandomMino()
            self.updateCurrentMino()
            self.calcIntervalTime()
            self.evt = Clock.schedule_interval(self.func, 1)
        else:
            print("Game over!")
    def func(self, *args):
        tof = self.currentMino.moveUnder(tenplus.field)
        if(tof):
            self.updateCurrentMino()
        else:
            self.evt.cancel()
            self.evt = None
            self.currentMino.updateField(tenplus.field)     # 実フィールドに現ミノを固定
            self.currentMino = None
            self.updateCurrentMino()
            self.updateBlocks()
            # 条件を満たしたminoを削除
            delBlocksGen(self.delBlocksTask())
    def rollLeft(self, *args):
        # 省略
    def rollRight(self, *args):
        # 省略
    def moveLeft(self, *args):
        # 省略
    def moveRight(self, *args):
        # 省略
    def moveUnder(self, *args):
        # 省略

# 下記省略

 

 

コードの解説

22行目から27行目の「drawDelBlocks関数」は、FrameWidgetを使って削除するブロックを目立たせる関数です。引数はdelPosListで、削除するブロックの座標のリストを与えます。

23行目から27行目の「for文」は、削除するブロックに対して切り返し操作を行います。

24、25行目は、変数frwに作成したFrameWidgetを代入しています。

26行目の「self.blocksList.append(frw)」は、blocksListにFrameWidgetを追加しています。blocksListに追加していることで、updateBlocks関数を実行するとFrameWidgetは削除されます。

27行目の「self.add_widget(frw)」は、実際にFrameWidgetを表示しています。

 

33行目の「self.drawDelBlocks(delPosList)」は、実際にdrawDelBlocksを実行しています。

 

36行目の「yield 1」は、1を返却しています。この返却値はdelBlocksGen、delBlocksStepで処理と処理の間を任意の時間待つために使われます。この場合は、1秒待つために使われます。

 

61行目の「delBlocksGen(self.delBlocksTask())」は、delBlocksTaskを引数とし、delBlocksGenを実行しています。つまり、delBlocksTaskにおいて処理と処理の間で任意の時間待ちたい処理があるということです。

 

 

動作確認

本記事の最後で動作確認を行います。

 

 

スピードを変化させる関数

スピードを変化させる関数

スコアによって落ちてくるスピードを変化さえる関数(calsIntervalTime)を作成します。落ちもの系ゲームは大体、時間やスコアによって、落ちてくるスピードが変化します。そんな変化をこのゲームでも実現します。

 

以下は、calcIntervalTimeのソースコードです。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 gameStart(self, *args):
        # 省略
    def gameStop(self, *args):
        # 省略
    def calcIntervalTime(self, *args):
        if(2000 <= self.score and self.score < 6000 and self.interval_time > 0.9):
            self.interval_time = 0.9
        elif(6000 <= self.score and self.score < 10000 and self.interval_time > 0.7):
            self.interval_time = 0.7
        elif(10000 <= self.score and self.score < 14000 and self.interval_time > 0.5):
            self.interval_time = 0.5
        elif(14000 <= self.score and self.interval_time > 0.3):
            self.interval_time = 0.3
    def drawDelBlocks(self, delPosList, *args):
        # 省略
    def delBlocksTask(self, *args):
        # 省略
    def func(self, *args):
        tof = self.currentMino.moveUnder(tenplus.field)
        if(tof):
            self.updateCurrentMino()
        else:
            self.evt.cancel()
            self.evt = None
            self.currentMino.updateField(tenplus.field)     # 実フィールドに現ミノを固定
            self.currentMino = None
            self.updateCurrentMino()
            self.updateBlocks()
            self.calcIntervalTime()
            # 条件を満たしたminoを削除
            delBlocksGen(self.delBlocksTask())
    def rollLeft(self, *args):
        # 省略
    def rollRight(self, *args):
        # 省略
    def moveLeft(self, *args):
        # 省略
    def moveRight(self, *args):
        # 省略
    def moveUnder(self, *args):
        # 省略

# 下記省略

 

 

コードの解説

22行目から30行目の「calcIntervalTime」は、スコアによって落下スピードを変化させる関数です。

「2000以上6000未満」の場合は、0.9秒間隔で落下します。

「6000以上10000未満」の場合は、0.7秒間隔で落下します。

「10000以上14000未満」の場合は、0.5秒間隔で落下します。

「14000以上」の場合は、0.3秒間隔で落下します。

条件は完全に任意であるため、お好みで変えてみてください。

 

46行目の「self.calcIntervalTime()」は、落下スピードを変化させる関数を実行しています。

 

 

動作確認(完成)

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

python tenplusapp.py

 

以下のように、削除するブロックの周りがオレンジ色の線で囲まれていることを確認します。また、ある程度スコアを取り続けると落下スピードが変化することも確認します。

削除するブロックが目立っている場面

 

確認できたら完成です。

 

 

スポンサードサーチ


まとめ

まとめの画像

今回は、落ちもの系ゲームを完成させました。テトリスに似ているゲームを開発したため、テトリスくらいなら、ブロックの削除条件を変えるだけで作ることが出来ると思います。

 

作ってほしいゲームやアプリケーションがあったら、メールかtwitterにてリクエストください。

 

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

 

 

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


スポンサードサーチ