【Python】マッチ棒クイズ開発 Part19
こんにちは、にわこまです。
今回は、kvファイルを使ったマッチ棒を作成します。kvファイルに記述出来るコードとできないコードを解説します。また、pythonファイルのみで作成したときとの違いも解説します。
誤字脱字や分からない点がございましたらご連絡お願いいたします!
メールまたはTwitterのDMまで!!
マッチ棒クイズ開発の目次はこちら
スポンサードサーチ
スポンサードサーチ
マッチ棒
ここではpythonファイルでマッチ棒の作成とkvファイルでマッチ棒の初期化と表示を作成します。
pythonファイルではマッチ棒のクラスを宣言するだけであり、今の段階では関数などは定義しません。
kvファイルではinit関数で行っていたことを記述します。sizeの指定やcanvasへの追加などを記述します。
コードの提示
kvファイルでマッチ棒を表現するクラスのソースコードを以下に示します。
# -*- coding: utf-8 -*-
#:import np numpy
<MatchStickWidget>:
size: [0, 0]
past_pos: (0, 0)
before_move_pos: (0, 0)
ms_w: 25
angle: 0
origin: (self.x + self.ms_w / 2, self.y + self.ms_w * 6.8 / 2)
# Rotate Theta
theta: np.radians(self.angle)
new_x: lambda t: (t[0] - self.origin[0]) * round(np.cos(self.theta), 3) - (t[1] - self.origin[1]) * round(np.sin(self.theta), 3) + self.origin[0]
new_y: lambda t: (t[0] - self.origin[0]) * round(np.sin(self.theta), 3) + (t[1] - self.origin[1]) * round(np.cos(self.theta), 3) + self.origin[1]
new_p: lambda t, o=(0, 0): (self.new_x(t) + o[0], self.new_y(t) + o[1])
# Stick
posStick: [self.x+self.ms_w*0.2, self.y]
sizeStick: [self.ms_w * 0.6, self.ms_w * 5.6]
pointsStick: [(self.posStick[0], self.posStick[1]), (self.posStick[0] + self.sizeStick[0], self.posStick[1]), (self.posStick[0], self.posStick[1] + self.sizeStick[1]), (self.posStick[0] + self.sizeStick[0], self.posStick[1] + self.sizeStick[1])]
rotatedPointsStick: sorted([self.new_p(point) for point in self.pointsStick], key=lambda t: t[1])
# Powder
posPowder: [self.x, self.y+self.ms_w*5.2]
sizePowder: [self.ms_w, self.ms_w*1.6]
canvas:
PushMatrix:
Rotate:
group: "rot"
angle: self.angle
origin: (self.x + self.ms_w / 2, self.y + self.ms_w * 6.8 / 2)
Color:
rgb: [0.96, 0.87, 0.7]
Rectangle:
group: "stick"
pos: [self.x+self.ms_w*0.2, self.y]
size: [self.ms_w * 0.6, self.ms_w * 5.6]
Color:
rgb: [0.8, 0.1, 0]
Ellipse:
pos: [self.x, self.y+self.ms_w*5.2]
size: [self.ms_w, self.ms_w*1.6]
PopMatrix:
<RootWidget>:
canvas:
Color:
rgb: [1, 1, 1]
Rectangle:
pos: self.pos
size: self.size
pythonファイルでマッチ棒を表現するクラスのソースコードを以下に示します。
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_file('matchstickquiz.kv')
class MatchStickWidget(Widget):
pass
class RootWidget(FloatLayout):
pass
class MatchStickQuizApp(App):
def build(self):
root = RootWidget()
root.add_widget(MatchStickWidget())
return root
if __name__ == '__main__':
MatchStickQuizApp().run()
pass
上記のコードは以下からダウンロードできます。
【Python】マッチ棒クイズ開発 Part19 ソースコード01
コードの解説 kvファイル
1行目の「# -*- coding: utf-8 -*-」は、記述している文字コードを設定しています。基本的には設定しなくても問題ありませんが、エディタによってはShift-JISを使っていることもあるため心配な方は設定しておくと安心です。
2行目の「#:import np numpy」は、numpyをインポートしています。この書き方はkv特有のもであり、「import 略称 ライブラリ名」という順番で記述します。pythonでのインポートと異なるため気を付けましょう。
詳しくは以下のリンクから確認してください。
Kivy Languageでのインポート方法はこちら
4行目から41行目の「<MatchStickWidget>:」は、マッチ棒を表現するクラスです。
5行目の「size: [0, 0]」は、マッチ棒のサイズを横幅縦幅ともに0を代入しています。
6行目の「past_pos: (0, 0)」は、過去にいた座標を保持する変数です。初期値として0を代入しています。
7行目の「before_move_pos: (0, 0)」は、移動前の座標を保持する変数です。初期値として0を代入しています。
8行目の「ms_w: 25」は、マッチ棒の横幅を保持する変数です。初期値として25を代入しています。
9行目の「angle: 0」は、マッチ棒の角度を保持する変数です。初期値として0を代入しています。
10行目の「origin」は、マッチ棒の回転の中心を保持する変数です。計算はPart7で解説しています。
12行目の「theta」は、マッチ棒の角度をラジアン単位に変換して保持する変数です。
13行目の「new_x」は、回転後のx座標を求める無名関数です。詳しくはPart8で解説しています。
14行目の「new_y」は、回転後のy座標を求める無名関数です。詳しくはPart8で解説しています。
15行目の「new_p」は、new_xとnew_yを用いて回転後の座標を求める無名関数です。詳しくはPart8で解説しています。
17行目の「posStick」は、マッチ棒の棒部分の表示座標を保持する変数です。計算はPart5で解説しています。
18行目の「sizeStick」は、マッチ棒の棒部分のサイズを保持する変数です。計算はPart5で解説しています。
19行目の「pointsStick」は、マッチ棒の棒部分の各頂点を保持する変数です。計算はPart8で解説しています。
20行目の「rotatedPointsStick」は、マッチ棒の棒部分の回転後の各頂点を保持する変数です。計算はPart8で解説しています。
22行目の「posPowder」は、マッチ棒の火薬部分の座標を保持する変数です。計算はPart5で解説しています。
23行目の「sizePowder」は、マッチ棒の火薬部分のサイズを保持する変数です。計算はPart5で解説しています。
24行目の「canvas:」は、canvasオブジェクトを宣言しています。この宣言以降でインデントを1つ下げるとcanvasへ追加するという扱いになります。
25行目の「PushMatrix:」は、回転する範囲の始まりを宣言しています。後述するPopMatrixとで囲まれたオブジェクトは一緒に回転します。
26行目の「Rotate」は、回転するために必要なオブジェクトです。角度や回転の中心を設定します。
27行目の「group: ‘rot’」は、Rotateオブジェクトにrotグループという属性を付与しています。pythonファイル側からRotateオブジェクトを呼び出す時に使用します。
28行目の「angle」は、角度を保持する変数です。
29行目の「origin」は、回転の中心を保持位する変数です。計算はPart7で解説しています。
30行目の「Color」は、後述するRectangleの色を設定するオブジェクトです。
31行目では、色をベージュに設定しています。
32行目の「Rectangle」は、マッチ棒の棒部分を表現するオブジェクトです。
33行目の「group: ‘stick’」は、Rectangleオブジェクトにstickグループという属性を付与しています。pythonファイル側からRectangleオブジェクトを呼び出す時に使用します。
34行目の「pos」は、マッチ棒の棒部分の座標を保持する変数です。35行目の「size」は、マッチ棒の棒部分のサイズを保持する変数です。
36行目の「Color」は、後述するEllipseの色を設定するオブジェクトです。
37行目では、色を赤に設定しています。
38行目の「Ellipse」は、マッチ棒の火薬部分を表現するオブジェクトです。
39行目の「pos」は、マッチ棒の火薬部分の座標を保持する変数です。
40行目の「size」は、マッチ棒の火薬部分のサイズを保持する変数です。
41行目の「PopMatrix」は、回転する範囲の終わりを宣言しています。前述したPushMatrixとで囲まれたオブジェクトは一緒に回転します。
コードの解説 pythonファイル
pythonに書かれているコードは、ほとんどこれまでのPartで解説しているため、新しく記述したコードや気を付けて欲しいコードについて解説します。
8行目の「import文」は、Builderをインポートしています。このクラスは、kvファイルを読み込むためのクラスです。
9行目の「Builder.load_file(‘matchstickquiz.kv’)」は、matchstickquiz.kvファイルを読み込むということを行っています。
MatchStickWidgetやRootWidgetはクラスの中身がほとんどkvファイル側で設定しているため、passのみとなっています。
動作確認
コードを保存したら、コマンドプロンプトを開きファイルを保存したフォルダまで移動します。移動したら、以下のコマンドを入力し実行します。
python matchstickquiz.py
以下のように表示されれば動作確認完了です。
スポンサードサーチ
スポンサードサーチ
マッチ棒
ここでは、マッチ棒クラスの関数を作成します。
具体的には、あたり判定と動かす関数と重なっているか判定する関数を作成します。
マッチ棒を表示する関数(drawMS)と回転したときの関数(rotateTheta)の2つの関数を書かなくてよくなります。
コードの提示
pythonファイルに関数を追記したソースコードを以下に示します。
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_file('matchstickquiz.kv')
import numpy as np
class MatchStickWidget(Widget):
def __init__(self, ms_w, angle, **kwargs):
super(MatchStickWidget, self).__init__(**kwargs)
self.ms_w = ms_w
self.angle = angle
self.rot = self.canvas.get_group("rot")[0]
self.stick = self.canvas.get_group("stick")[0]
def collide_point_stick(self, touch, *args):
fml = lambda a, t: a * (touch[0] - t[0]) + t[1]
xmini, xmax = min([p[0] for p in self.rotatedPointsStick]), max([p[0] for p in self.rotatedPointsStick])
ymini, ymax = min([p[1] for p in self.rotatedPointsStick]), max([p[1] for p in self.rotatedPointsStick])
if((xmini <= touch[0] and touch[0] <= xmax
and ymini <= touch[1] and touch[1] <= ymax)
and ((fml(-1, self.rotatedPointsStick[0]) <= touch[1] and fml(1, self.rotatedPointsStick[0]) <= touch[1]
and touch[1] <= fml(-1, self.rotatedPointsStick[3]) and touch[1] <= fml(1, self.rotatedPointsStick[3]))
or round(self.rotatedPointsStick[0][1]) == round(self.rotatedPointsStick[1][1]))):
return True
return False
def collide_point_powder(self, touch, *args):
origin_elli = self.new_p((self.origin[0], self.posPowder[1] + self.sizePowder[1] / 2)) # window上の座標(楕円の中心)を代入
num_x = ((touch[0] - origin_elli[0]) * np.cos(self.theta) + (touch[1] - origin_elli[1]) * np.sin(self.theta))**2
num_y = ((touch[1] - origin_elli[1]) * np.cos(self.theta) - (touch[0] - origin_elli[0]) * np.sin(self.theta))**2
value = num_x / (self.sizePowder[0]/2)**2 + num_y / (self.sizePowder[1]/2)**2
if(value <= 1):
return True
return False
def on_touch_down(self, touch, *args):
tof = (self.collide_point_stick(touch.pos) or self.collide_point_powder(touch.pos))
if(touch.is_double_tap and tof):
self.angle += 45
return True
elif(not(touch.is_double_tap) and tof):
touch.grab(self)
self.past_pos = touch.pos
return True
return False
def on_touch_up(self, touch, *args):
if(touch.grab_current is self):
touch.ungrab(self)
return True
return False
def on_touch_move(self, touch, *args):
if(touch.grab_current is self):
self.x += touch.x - self.past_pos[0]
self.y += touch.y - self.past_pos[1]
self.past_pos = touch.pos
return True
return False
def collide_center_circle(self, wid, *args):
diff_x = self.rot.origin[0] - wid.rot.origin[0]
diff_y = self.rot.origin[1] - wid.rot.origin[1]
distance = np.sqrt(diff_x**2 + diff_y**2)
if(self != wid and distance <= self.stick.size[0] and self.angle % 180 == wid.angle % 180):
return True
return False
class RootWidget(FloatLayout):
pass
class MatchStickQuizApp(App):
def build(self):
root = RootWidget()
root.add_widget(MatchStickWidget(25, 0))
return root
if __name__ == '__main__':
MatchStickQuizApp().run()
pass
上記のコードは以下からダウンロードできます。
【Python】マッチ棒クイズ開発 Part19 ソースコード02
コードの解説 pythonファイル
追記した関数の内、init関数のみが以前のinit関数と異なるため、init関数のみ解説します。
14行目から19行目の「init関数」は、MatchStickWidgetを初期化する関数です。
16行目の「self.ms_w」は、マッチ棒の横幅を保持する変数です。kvファイルにて初期値を25としていますが、こちらに代入した値が優先されます。
17行目の「self.angle」は、マッチ棒の角度を保持する変数です。kvファイルにて初期値を0としていますが、こちらに代入した値が優先されます。
18行目の「self.rot = self.canvas.get_group(“rot”)[0]」は、self.rotにkvファイルで宣言したRotateオブジェクトを代入しています。
get_group関数はcanvas上のオブジェクトでrotというグループが付与されている全てのオブジェクトをリストで取得しています。
rotというグループが付与されているオブジェクトはRotateしかないため、0番目を指定してRotateオブジェクトを取得しています。
19行目の「self.stick = self.canvas.get_group(“stick”)[0]」は、self.stickにkvファイルで宣言したRectangleオブジェクトを代入しています。
get_group関数はcanvas上のオブジェクトでstickというグループが付与されている全てのオブジェクトをリストで取得しています。
stickというグループが付与されているオブジェクトはRectangleしかないため、0番目を指定してRectangleオブジェクトを取得しています。
Kivy get_groupの詳細はこちら
動作確認
コードを保存したら、コマンドプロンプトを開きファイルを保存したフォルダまで移動します。移動したら、以下のコマンドを入力し実行します。
python matchstickquiz.py
以下のように動かすこと回転させることを確認できたら動作確認完了です。
スポンサードサーチ
まとめ
今回は、pythonファイルとkvファイルを使ってマッチ棒クラスを作成しました。
kvファイルを使うことで関数を削減することができました。pythonファイルに記述するコードの量は減ったと思います。
次回Part20は、マッチ棒枠とマッチ棒を動かす画面を作成します。
マッチ棒クイズ開発の目次はこちら
最後までお読みいただきありがとうございます。
スポンサードサーチ