Phaser3の練習にちょっとしたゲームを作ってみた。
リファクタリングはまた今度する(しない)。
以下からプレイ可能
スライム君を会社に戻そうと追ってくるタコから逃げるゲーム。
方向キーで移動、スペースキーでゲームのカードを落とせる。
PC専用で、IEだと動かない。
Phaser3の概要はかなり掴めたかな。
シーン遷移周りのベストプラクティスなんかはちょっと考えたい。
今のやり方だとメモリリーク起こしてないかちょっと心配。
Phaser3の練習にちょっとしたゲームを作ってみた。
リファクタリングはまた今度する(しない)。
以下からプレイ可能
スライム君を会社に戻そうと追ってくるタコから逃げるゲーム。
方向キーで移動、スペースキーでゲームのカードを落とせる。
PC専用で、IEだと動かない。
Phaser3の概要はかなり掴めたかな。
シーン遷移周りのベストプラクティスなんかはちょっと考えたい。
今のやり方だとメモリリーク起こしてないかちょっと心配。
JavaScriptのゲームライブラリについて少し調べてみたが、Phaserが中々便利そう。
ゲーム制作に必要なものは一通りそろってて、コードも読みやすかった。
2018/10/16現在だと、Phaser3系と、2系でドキュメントが混在してる感じなのがネックかな。
公式のチュートリアルが短い上に分かりやすいので、これで概要が掴めた。
書き始めた時は、公式チュートリアルのcreateとかupdate関数のthisが何を指しているのかいまいち把握できなくて混乱した。
どうもこれはPhaser.Sceneオブジェクトを参照しているようで、PhaserではこのSceneオブジェクトに対する操作を中心にプログラムを書いていくっぽい。
なのでリファレンスのSceneドロップダウンリスト以下を参照すれば、必要な情報は一通り手に入る。
公式の作例も充実しているので、リファレンスと作例見れば大体のことが解決するので助かる。
こんな感じでモダンな書き方にも対応してるようだ。
Eelで新規ウィンドウを開く際に、単純にhtmlやjavascriptから開くとアプリケーションモードが解除されて、タブやらメニューバーやら表示されてしまう。
別に問題があるわけじゃないんだけど、何となく格好が悪い。
ということで、Pythonから新規ウィンドウを開けないか試してみた。
単純にeel.start()をもう一度呼べばいいのではないかと試してみる。
フォルダ構成main.py
web
└─ main.html
main.pyimport eel
def main():
eel.init("web")
eel.start("main.html")
"""新しいウィンドウを開く"""
@eel.expose
def open_window():
eel.start("main.html")
if __name__ == '__main__':
main()
main.html<html>
<head>
<meta charset="UTF-8">
<title>eel_exsample</title>
<script type="text/javascript" src="/eel.js"></script>
<script type="text/javascript">
// 新しいWindowを開くPythonスクリプトを実行する
function openWindow() {
eel.open_window();
}
</script>
</head>
<body>
<a href="#" onclick="openWindow()">openWindow</a>
</body>
</html>
openWindowのリンクを押下すると新しいウィンドウが開いた。
問題なく動作しているようだが、コンソールに以下のエラーが出ている。
OSError: [WinError 10048] 通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。: ('localhost', 8000)
2018-10-04T01:58:57Z failed with OSError
同じポートでもう一つWebサーバーを立てようとして怒られてるようだ。
1回目のeel.start()ですでにサーバーは立ってるから問題ないんだけどちょっと気持ちが悪い。
以下のように立てるwebサーバーのポートに0を指定してやって、空いている任意のポートを取得するようにしてやればエラーは発生しなくなる。
main.pyimport eel
def main():
eel.init("web")
eel.start("main.html", options={"port" : 0,})
"""新しいウィンドウを開く"""
@eel.expose
def open_window():
eel.start("main.html", options={"port" : 0,})
if __name__ == '__main__':
main()
ただ、新しいウィンドウを開くたびにサーバーが立つのでこれは無いかな。
Eelのソースを読んだところ、eel.browsers.open()にてよしなにchromiumのウィンドウを開いてくれているようだ。。。たぶん。
なので、以下のように直接eel.browsers.open()を呼び出してやれば、エラーメッセージも出てこず、サーバプロセスは一つのみで動作させることができる。
注意点として、1回目にeel.start()に渡したオプションを使いまわすようにすること (eel.start()にて必要なオプションを詰めてくれるので、それを使いまわす)。
main.pyimport eel
web_app_options = {
'port': 0,
}
def main():
eel.init("web")
# eel.start()では、引数に渡したオプションに未指定のものがあった場合、デフォルトの値を詰めてくれる。
eel.start("main.html", options=web_app_options)
"""新しいウィンドウを開く"""
@eel.expose
def open_window():
# 第一引数は開きたいURLのリスト、第二引数はeel.start()に渡したオプション
eel.browsers.open(["main.html",], web_app_options)
if __name__ == '__main__':
main()
ちょっと無理やり感があるかな。
その他の注意点として、開いたばかりのウィンドウのjavascriptの関数をPythonから呼び出せないことに注意する。
eel.sleep()などで開き終わるまで待ってやれば、新規ウィンドウのjavascriptの関数を呼び出せるようになる。
また、同じhtmlを複数ウィンドウ開いている場合、そのhtmlに含まれるjavascriptの関数をPythonから呼び出したら、すべてのウィンドウでその関数が実行される。
一つのウィンドウのみ対象にしたい場合は、javascriptに変数として任意のIDを持たせるなどの工夫が必要になりそう。
と色々はまりどころがあること考えると、Eelで複数ウィンドウ開くのは悪手くさいな。
少なくとも複数ウィンドウに同じ名称のjavascriptの関数が含まれる状況は避けた方がいい(Pythonから呼ばれないなら問題ない)。
複数ウィンドウが必要な場合は、javascriptのモーダルダイアログ扱うようなライブラリ使って、あんまりポカスカ新規ウィンドウが作られないようにした方がいいかな。
大体の機能はモーダルダイアログで済みそうだし、それならアプリケーションモードの見た目との相性も悪くないはず。
自分用にちょっとしたGUIをPythonで作りたいときのライブラリにしっくりくるものが無かったんだけど、最近Eelというライブラリを試してみたらこれが中々良い感じだった。
Electronを軽くした感じのライブラリで、GUIをhtmlで作成できる。
内部的にはbottleでサーバーを立てて、それにChromiumからリクエストを送るような作りになっているようだ。
例えば、リンクをクリックした際にPythonのスクリプトを起動したい場合は以下のようにする。
フォルダ構成main.py
web
└─ main.html
main.pyimport eel
def main():
eel.init("web")
eel.start("main.html")
"""link1が押下された際に呼び出すスクリプト"""
@eel.expose
def link1_click():
print("link1_clicked")
"""link2が押下された際に呼び出すスクリプト"""
@eel.expose
def link2_click(args):
print(args)
return "link2_clicked"
if __name__ == '__main__':
main()
main.html<html>
<head>
<meta charset="UTF-8">
<title>eel_exsample</title>
<script type="text/javascript" src="/eel.js"></script>
<script type="text/javascript">
// 非同期でPythonスクリプトを実行する
function link1Click() {
eel.link1_click();
}
// Pythonスクリプトに引数を渡し、結果の帰り値を待つ例
async function link2Click() {
let val = await eel.link2_click("args-test")();
alert(val);
}
</script>
</head>
<body>
<a href="#" onclick="link1Click()">Link1</a>
<br>
<a href="#" onclick="link2Click()">Link2</a>
</body>
</html>
Link1は単純にPythonスクリプトを実行する例、Link2はPythonスクリプトに引数を渡し、その帰り値を待つ実行例。
非常にシンプルである。
自分用に作ったスクリプトにちょっとしたGUIを付けたいときなんかは非常に重宝しそう。
作ったソフト公開するには作りが素朴すぎるかな。少し使い込んでみてここら辺の感覚はつかみたい。
これとPygletぐらいあれば、自分のやりたいことは一通り簡単に実装できそうだ。