はじめに
今回は、セーブ機能付きのゲームを実行ファイル化する方法を紹介します。といっても、pyからexeへの変換の手順は変わりません。セーブ機能を実装するためのファイル操作をするときに、注意すべき点があります。この記事はその点について詳しく解説したものです。
この記事の内容はWindowsを想定しています。筆者はMacを持っていないので、確認できないのですが、似たような問題が発生する場合は、おそらく同様に解決できると思います。
注意すべき点
今回のテーマで注意すべき点は、ディレクトリ構造です。ゲームを作るときは、プロジェクトフォルダを作って、その中にsrc
など、目的別のファイルを置くのが普通だと思います。そして、セーブデータもプロジェクトフォルダの内部に生成すると思います。自分で実行する分には、これで事足りるのですが、いざ動作確認を終えて実行ファイル化すると、セーブデータが残らないという問題が発生します。(筆者は発生しました)
原因について
セーブデータが残らない原因として、実行ファイルを置いている位置と、プログラムの実行パスが異なる点が挙げられます。どこから実行したかに関わらず、実行パスがC:\Users\ユーザー名\AppData\Local\Temp\.pyxel\play\ランダムな数字\実行ファイルの名前
となるようです。
たとえばgame.exe
という名前のファイルを起動した場合、C:\Users\ユーザー名\AppData\Local\Temp\.pyxel\play\ランダムな数字\game
になります。game
の中身は、実行ファイル化するときに読み込んだプロジェクトフォルダとなっています。
これだけなら問題はなさそうですが、パスに書いたランダムな数字というのが厄介なポイントです。実行ファイルからプログラムを実行したタイミングで、それ以下のディレクトリ構造が再生成されているようなので、セーブデータを残しても、次にプログラムを起動した際に初期化されてしまいます。
つまり、プロジェクトフォルダ内に、いくらセーブデータを残そうが意味はないと考えていいでしょう。
解決策
解決策は単純な話で、再生成されない部分にセーブデータを残せばいいです。
具体的には、上位の階層にセーブデータ用のファイルを生成し、初回以外はそこからセーブデータを読み込むようにするのがおすすめです
バージョン2.2.6で追加されたuser_data_dir
というものを使うと、安全なデータ保存先が取得可能だと、Xの方でコメントをいただきました。
path = pyxel.user_data_dir('開発者名', 'アプリ名')
print(path) # C:\Users\ユーザー名\.pyxel\開発者名\アプリ名
これを踏まえて、以下のようなコードで、セーブ機能を実装することができました。
import pyxel, os, pickle
#保存先のパスを取得
directory_path = pyxel.user_data_dir('開発者名', 'アプリ名')
save_path = os.path.join(directory_path, "save.pkl")
try:
with open (save_path, "rb") as f:
save_data = pickle.load(f)
# 二回目以降の処理
except FileNotFoundError:
# 初回時の処理
with open(save_path, "wb") as f:
pickle.dump(save_data, f)
pyxel.user_data_dir
を使ってセーブデータ格納用フォルダを生成します
後は普通のファイル操作と同様に、初回実行時のみセーブ用ファイルの生成を行い、二回目以降は読み込みを行うなど、各自必要な処理をしてください。
プログラム内で書き換えないようなファイル(フォントファイルなど)は相対パスのままでもいいのですが、セーブデータ以外にも書き換えるファイルがある場合は、セーブデータ用のファイルと同じところにおいてあげると良いでしょう。
修正前の記事の内容(一応残してますが見なくていいです)
今回はプロジェクトフォルダの直下にメインファイルを置いている想定で書いています。import os, pickle
current_path = os.path.abspath(__file__)
# 4階層上のディレクトリに作成
directory_path = os.path.abspath(os.path.join(current_path, "..", "..", "..", "..", "game"))
os.makedirs(directory_path, exist_ok=True)
save_path = os.path.join(directory_path, "save.pkl")
try:
with open (save_path, "rb") as f:
save_data = pickle.load(f)
# 二回目以降の処理
except FileNotFoundError:
# 初回時の処理
with open(save_path, "wb") as f:
pickle.dump(save_data, f)
#----あとは各自の処理----
osモジュールを使ってメインファイルの絶対パスを取得した後、その4つ上(.pyxel)を指定し、プロジェクトフォルダの名前でセーブデータ格納用フォルダを生成します(初回のみ)
まとめ
セーブ機能付きのゲームを実行ファイル化するときは、実行時に初期化されない位置へセーブ用ファイルを生成する必要がある!
その際に、user_data_dir
というメソッドが提供されているので、これを使うと便利!
最後に
ここまで読んで下さりありがとうございました。この記事では実行ファイル化する手順については触れなかったので、そちらについても確認したい方は、以下の記事も併せてご覧ください。
参考