Pyxel (ピクセル)は、2Dレトロゲームを作成するPythonライブラリで日本人が作者のようです。以前、記事にしたPygame zeroPython 自作シューティングゲームを作成する「Pygame Zero」 - PythonとVBAで世の中を便利にするとの違いは、キャラクターをドット絵で作成できること、しかも、GUIで「簡単操作」できることです。
とは言え、その「簡単操作」の扱いを理解するのは若干苦労しましたので、それは別途動画にしようかと思います。
本記事では、ひとまず動くコードを雛形として残します。
●Windowsでのインストール方法
pip install -U pyxel
その他のOSでのインスト方法は右記の本家のサイトを参照下さい。https://github.com/kitao/pyxel
●サンプルのダウンロード
カレントディレクトリで下記を実行します。
install_pyxel_examples
●本記事のゲームの仕様
上記サンプル「pyxel_examples」内にある「02_jump_game.py」を参考に、横スクロール風のマイティボンジャック風のゲームを作成しました。
ゲーム性はスコアで、画面左上に表示されます。スコアの稼ぎ方は3つあります。一つ目は、1ジャンプあたり200点増えます。ジャンプ回数が多い程高得点になります。二つ目には、このゲームは右側からサクランボやらのキャラクターが流れてきます。サクランボを取れば300点増え、それ以外をとると即ゲームオーバーです。3つ目に、時間と共にスコアは増えていきます。つまり、生存時間が長ければ高得点です。スコア結果は、ゲームオーバー時に自動でテキストファイルに保存します。
ゲーム操作は、キーボードでもゲームパッドも使用できます。
www.youtube.com
■本プログラム
from collections import deque, namedtuple
from random import randint
import pyxel
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")
import csv
Point = namedtuple("Point", ["w", "h"])
RIGHT = Point(16, 16)
LEFT = Point(-16, 16)
COLOR = 12
class App:
GROUND_Y = 105
def __init__(self):
pyxel.init(160, 120, caption="kosaty_bomb_jack")
pyxel.load("assets/jump_game.pyxres")
self.START = False
self.GAMEOVER = False
self.score = 0
is_active = True
self.direction = RIGHT
self.player_x = 72
self.player_y = self.GROUND_Y - 16
self.player_vy = 0
self.gravity = 0.98
self.player_is_alive = True
self.far_cloud = [(-10, 75), (40, 65), (90, 60)]
self.near_cloud = [(10, 25), (70, 35), (120, 15)]
self.fruit = [(i * 40, randint(0, 104), randint(0, 3), True) for i in range(6)]
pyxel.playm(0, loop=True)
pyxel.run(self.update, self.draw)
def update(self):
if pyxel.btnp(pyxel.KEY_Q):
pyxel.quit()
if pyxel.btn(pyxel.KEY_SPACE) or pyxel.btn(pyxel.GAMEPAD_1_START):
self.START = True
if self.GAMEOVER and (pyxel.btn(pyxel.KEY_ENTER or pyxel.btn(pyxel.GAMEPAD_1_START))) :
self.reset()
if not self.START or self.GAMEOVER:
return
self.player_vy += self.gravity
self.player_y += self.player_vy
if self.player_y > self.GROUND_Y - 16:
self.player_y = self.GROUND_Y - 16
self.update_player()
for i, v in enumerate(self.fruit):
self.fruit[i] = self.update_fruit(*v)
if not self.GAMEOVER:
self.score += 1
def update_player(self):
if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD_1_LEFT):
self.player_x = max(self.player_x - 2, 0)
self.direction = LEFT
if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD_1_RIGHT):
self.player_x = min(self.player_x + 2, pyxel.width - 16)
self.direction = RIGHT
if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.GAMEPAD_1_B):
if self.player_y == self.GROUND_Y - 16:
self.player_vy = -13
self.score += 200
if self.player_y > pyxel.height:
if self.player_is_alive:
self.player_is_alive = False
pyxel.play(3, 5)
if self.player_y > 600:
self.score = 0
self.player_x = 72
self.player_y = -16
self.player_vy = 0
self.player_is_alive = True
def update_fruit(self, x, y, kind, is_active):
if is_active and abs(x - self.player_x) < 12 and abs(y - self.player_y) < 12:
if kind == 0:
is_active = False
self.score += 300
pyxel.play(3, 4)
else:
self.GAMEOVER = True
is_active = False
pyxel.blt(self.player_x, self.player_y, 0, 16, 0, 16, 16, 0)
mylist = ['SCORE', self.score]
with open(now + '_jumping_game_by_pyxel.csv', 'a', encoding="utf-8") as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerow(mylist)
pyxel.stop()
x -= 2
if kind == 3:
y = y + randint(-2, 2)
if x < -40:
x += 240
y = randint(0, 104)
kind = randint(0, 3)
is_active = True
return (x, y, kind, is_active)
def reset(self):
self.START = True
self.GAMEOVER = False
self.score = 0
pyxel.playm(0, loop=True)
def draw(self):
if self.GAMEOVER:
MESSAGE =\
"""
GAMEOVER
PUSH ENTER
"""
pyxel.text(51, 40, MESSAGE, 1)
pyxel.text(50, 40, MESSAGE, 7)
return
pyxel.cls(COLOR)
offset = (pyxel.frame_count // 8) % 160
for i in range(2):
for x, y in self.far_cloud:
pyxel.blt(x + i * 160 - offset, y, 0, 64, 32, 32, 8, 12)
offset = (pyxel.frame_count // 4) % 160
for i in range(2):
for x, y in self.near_cloud:
pyxel.blt(x + i * 160 - offset, y, 0, 0, 32, 56, 8, 12)
offset = pyxel.frame_count % 160
for i in range(2):
pyxel.blt(i * 160 - offset, 88, 0, 0, 64, 160, 24, 12)
pyxel.rect(0, self.GROUND_Y, pyxel.width, pyxel.height, 4)
pyxel.blt(self.player_x,
self.player_y,
0,
0,
0,
self.direction[0],
self.direction[1],
COLOR)
for x, y, kind, is_active in self.fruit:
if is_active:
pyxel.blt(x, y, 0, 32 + kind * 16, 0, 16, 16, 12)
s = "SCORE {:>4}".format(self.score)
pyxel.text(5, 4, s, 1)
pyxel.text(4, 4, s, 7)
if not self.START:
MESSAGE ="PUSH SPACE KEY"
pyxel.text(61, 50, MESSAGE, 1)
pyxel.text(60, 50, MESSAGE, 7)
return
def main():
App()
if __name__ == "__main__":
main()
●参考:Windowsでexe実行ファイルひとつで保存したい場合
PyInstallerをインストールしておく必要があります。
$pip install pyinstaller
その後、下記コマンドでexeファイルを作成できます。
$pyxelpackager [pythonファイル]
しかし、もし下記のようなエラーが出た場合
AttributeError: module 'enum' has no attribute 'IntFlag'
パッケージenumが標準ライブラリenumモジュールではなく、enum34 がインストールされている可能性があり、下記のようにしてアンインストールします。
$pip uninstall enum34
以上
■日経ソフトウェア <広告>
リンク