torinaブログ

DjangoとBootstrap4で作成したブログ
Python, Django, Kivy, Bootstrap, Apache等のメモです
ソースコード

Kivyで、シンプルなシューティングゲーム①

Python Kivy Kivy
2016年11月5日3:35
Python3.4
Kivy1.9.1
で、シンプルなシューティングゲームを作ります。
今回は自機キャラの移動と弾の発射処理です。

画面内をクリックすると、弾が出ます。


画面ないをクリックしながら左右に動かすと、自機(蛇)が移動します。


当然、弾は画面外に行くと消えます。



今回は、Kivy公式のチュートリアルを参考に作っています。
PongGameですね。
https://kivy.org/docs/tutorials/pong.html

素敵な日本語訳
https://pyky.github.io/kivy-doc-ja/tutorials/pong.html


main.py
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import (
    ListProperty,
    NumericProperty,
    ObjectProperty,
    ReferenceListProperty,
)
from kivy.uix.widget import Widget
from kivy.vector import Vector


class Player(Widget):
    """プレイヤー"""

    pass


class Shot(Widget):
    """プレイヤーの弾"""

    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(10)
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self):
        """弾の移動処理"""

        self.pos = Vector(*self.velocity) + self.pos


class ShootingGame(Widget):
    """ルートウィジェット。シューティングゲーム全体の管理"""

    player = ObjectProperty(None)
    shots = ListProperty()

    def on_touch_move(self, touch):
        """タッチしたまま移動でプレイヤー移動"""

        if 0 < touch.x < self.width:
            self.player.center_x = touch.x

    def on_touch_down(self, touch):
        """画面タッチで弾発射"""

        shot = Shot(pos=self.player.center)
        self.shots.append(shot)
        self.add_widget(shot)

    def update(self, dt):
        """1/60秒毎に呼ばれる、ゲーム更新処理

        各ショットの移動処理や、敵キャラの移動、それぞれの衝突判定を行う
        """

        for ball in self.shots:
            ball.move()
            if (ball.y < self.y) or (ball.top > self.top):
                self.remove_widget(ball)
                self.shots.remove(ball)


class ShootingApp(App):

    def build(self):
        game = ShootingGame()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

ShootingApp().run()


shooting.kv
<Player>:
    size: 100, 100
	canvas:
	    Rectangle:
	        pos: self.pos
	        size: self.size
	        source: 'player.png'

<Shot>:
    size: 10, 30 
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size 

<ShootingGame>:
	player: player

	canvas:
	    Rectangle:
	        pos: self.pos
	        size: self.size
	        source: 'background.jpg'

    Player:
        id: player
        pos: root.center_x - self.width/2, 0


まずはシンプルなkvファイルを見てみます。
これはプレイヤーで、100x100、画像を指定しています。
<Player>:
    size: 100, 100
	canvas:
	    Rectangle:
	        pos: self.pos
	        size: self.size
	        source: 'player.png'


これは弾です。10x30で、Ellipseは円を描画します。Rectangleは短形です。
<Shot>:
    size: 10, 30 
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size 



そして、ルートウィジェットのShootingGameです。
背景画像を指定して、初期配置ということでプレイヤーの配置ですね。
posは何してるかというと、このウィジェットの中央 ― キャラの幅の半分 で、真ん中に配置したい、ということです。
<ShootingGame>:
	player: player

	canvas:
	    Rectangle:
	        pos: self.pos
	        size: self.size
	        source: 'background.jpg'

    Player:
        id: player
        pos: root.center_x - self.width/2, 0



main.pyです。
お約束の処理ですが、Clock.schedule_intervalを呼んでいることに注意です。
1秒間に60回呼ばれることになります。リアルタイムなゲームでは一般的な処理ですね。
class ShootingApp(App):

    def build(self):
        game = ShootingGame()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

ShootingApp().run()



今のところ、Playerクラスに特別な指定はしていません。
class Player(Widget):
    """プレイヤー"""

    pass


これは弾になります。moveは移動処理ですね。
チュートリアルのPongBallの処理をそのまま持ってきていますが、中々便利です。
velocityは速さなんて意味の単語ですね。今回は縦シューティングなので、弾は上に飛びます。
なのでvelocity_xに0、velocity_yに正の10です。
class Shot(Widget):
    """プレイヤーの弾"""

    velocity_x = NumericProperty(0)
    velocity_y = NumericProperty(10)
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self):
        """弾の移動処理"""

        self.pos = Vector(*self.velocity) + self.pos



弾は複数発射されるので、ListPropertyです。
class ShootingGame(Widget):
    """ルートウィジェット。シューティングゲーム全体の管理"""

    player = ObjectProperty(None)
    shots = ListProperty()



これは見たままの処理ですね。プレイヤーの移動です。
on_touch_moveは、Widgetに元から用意されたイベントです。
    def on_touch_move(self, touch):
        """タッチしたまま移動でプレイヤー移動"""

        if 0 < touch.x < self.width:
            self.player.center_x = touch.x



弾の発射です。pos=self.player.centerは、キャラの真ん中から弾が出るということですね。
画面に弾を表示させ、かつリストにも格納しています。
on_touch_downもまた、Widgetに元から用意されたイベントです。
    def on_touch_down(self, touch):
        """画面タッチで弾発射"""

        shot = Shot(pos=self.player.center)
        self.shots.append(shot)
        self.add_widget(shot)


これが更新処理です。
現在の弾を全て移動させ、画面外に出てたらremove_widgetで消し、リストからも消去するだけです。
    def update(self, dt):
        """1/60秒毎に呼ばれる、ゲーム更新処理

        各ショットの移動処理や、敵キャラの移動、それぞれの衝突判定を行う
        """

        for ball in self.shots:
            ball.move()
            if (ball.y < self.y) or (ball.top > self.top):
                self.remove_widget(ball)
                self.shots.remove(ball)


元のPong Game Tutorialよりもシンプルになりました。
次回は敵キャラを作り、やっつけてみます。