PyScriptが面白そうって話を聞いたので PyScriptでぷよぷよ作ってみる。
内容は Elmで作ったものと同じものを目指す。
PyScript
AnacondaがリリースしたWebブラウザ上でpythonが実行できるフレームワークらしい。
公式からスクリプト読み込むだけで実行できるのでめちゃめちゃお手軽
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
※ CORSエラーが出る時があったのでローカルにbuildしておくのもいいかも。
使い方
大体は↓に書いてある。
pythonのコードを <py-script>
タグに入れるだけでいいらしい。
<py-script>
実行したいコード
</py-script>
ここではゲーム作成の肝になるテンプレートタグ
とイベントリスナー
、イベントループ
だけ紹介。
※ ドキュメントがあまり無いので、サンプルみて作ることになりそう。
テンプレートタグ
まずはpython側からhtmlの要素を取得する話。
id指定の場合は↓で要素を取得できる
...
<divid="grid-container">
</div>
...
<py-script>
grid_container = Element("grid-container")
</py-script>
そして、要素の操作や追加は↓のようにjavascriptみたいに可能
# 追加
grid_container.element.appendChild(puyo_html.element)
# 変更 (イメージの変更)
puyo_html = Element("puyo-blue")
puyo_html_img = puyo_html.select("img")
puyo_html_img.element.src = "hoge.img"
そしてtemplateタグを使う場合はcloneを利用する。
...
<template id="puyo-template">
<div class="view-cell" style="position: static">
<img src="" class="puyo">
</div>
</template>
...
<py-script>
# スタイルとイメージを変更し、要素を追加
puyo_html = puyo_template.clone('puyo-template', to=grid_container)
puyo_html.element.style = "position: absolute;"
puyo_html_img = puyo_html.select("img")
puyo_html_img.element.src = c.img
grid_container.element.appendChild(puyo_html.element)
</py-script>
これでViewの更新、変更はなんとかなりそう。
イベントリスナー
ゲーム作るならキーボード入力がないとってことでイベントリスナー。
この辺はpyodideを見ないとダメッぽい。
結論から、↓でキーイベントを取得できる。
from pyodide import create_proxy
async def key_down(event):
# ...なにかやりたい処理
pass
game_panel = document.querySelector("body")
game_panel.addEventListener("keydown", create_proxy(key_down))
ほぼjavascriptの書き方だが、addEventListenerで特定のイベントを取得し、create_proxyでpythonの関数にproxyする。
↑の場合はevent.key
とかでキーコードが取得できる。
イベントループ
asyncio
のイベントループが使えるみたい。
async def tick():
while True:
try:
# ...なにかの処理
await asyncio.sleep(1)
print(tock)
except Exception as e:
break
pyscript.run_until_complete(tick())
run_until_complete
以外は試してない。使えるのだろうか...
ぷよぷよを作る
あとは作るだけ。
処理内容は以前elmで作ったものと変わらず
テンプレートでぷよを定義して、それをひたすら動かしていく。
空のボード・操作するぷよ・次のぷよ・ゲームのステータスを初期状態として定義し、イベントループ/イベントリスナーで盤面を更新する。
更新毎にupdate_view()
で画面に盤面を反映。
def __init__(self):
self.board = [[Puyo.Empty for i in range(COL)] for i in range(ROW)]
self.gripped_puyo = [
{ 'position': {'row': 0, 'col': 1}, 'color': Puyo.random_choice()},
{ 'position': {'row': 1, 'col': 1}, 'color': Puyo.random_choice()}
]
for p in self.gripped_puyo:
self.board[p['position']['row']][p['position']['col']] = p['color']
self.next_puyo = [
[Puyo.random_choice(), Puyo.random_choice()],
[Puyo.random_choice(), Puyo.random_choice()],
]
self._status = Status.Normal
self.init_view()
def update_view(self):
count = 0
for r in self.board[1:]:
for c in r:
puyo_html = Element("puyo-" + str(count))
puyo_html_img = puyo_html.select("img")
puyo_html_img.element.src = c.img
count += 1
def init_view(self):
pos = [0,0]
count = 0
for r in self.board:
if pos[0] == 0:
pos[0] = pos[0] + 1
continue
pos[1] = 0
for c in r:
puyo_html = puyo_template.clone('puyo-'+str(count), to=grid_container)
puyo_html.element.style = "position: absolute; top: " + str(25 * (pos[0]-1)) + "px; left: " + str(25 * (pos[1]+1)) + "px;"
puyo_html_img = puyo_html.select("img")
puyo_html_img.element.src = c.img
grid_container.element.appendChild(puyo_html.element)
pos[1] = pos[1] + 1
count += 1
pos[0] = pos[0] + 1
self.update_next_view()
def update_next_view(self):
count = 0
for pair in self.next_puyo:
for c in pair:
puyo_html = Element('next-puyo-'+str(count))
puyo_html_img = puyo_html.select("img")
puyo_html_img.element.src = c.img
count += 1
出来上がり。
pyscript-gamesみたいなプロジェクトもできるのかな