概要
私がPanda3Dを使ってきてつまづいたりした所の解決策を解説します。この記事は今後アップデートされるかもしれません。
この記事で使用しているツールのバージョン: Windows 10, Panda3D: 1.10.14, Python: 3.12.3
Panda3Dの基本的な使い方は、以下などを参照。
Programming with Panda3D — Panda3D
Manual
Python+Panda3Dによるゲームプログラミング入門 Panda3Dゲームエンジンのテキストブック | 多田 憲孝 |本 | 通販 | Amazon
※アソシエイトリンクです。
基本
base とは ShowBase のインスタンスのこと。
ShowBase を継承したクラスでは self がそのまま base と同じ役割を持つ。
from direct.showbase.ShowBase import ShowBase
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
#以下アプリの準備
#例 selfをbaseに変更しても可
self.setFrameRateMeter(True)
base.setFrameRateMeter(True)
model = self.loader.loadModel(f"model.egg", noCache=True)
model = base.loader.loadModel(f"model.egg", noCache=True)
if __name__ == "__main__":
app = MyApp()
app.run()
ファイルの読み込み
絶対パス
Panda3Dでは絶対パスをvfs形式(Linux形式)で書かないと失敗することがあります。
Windowsでは "D:/path/to/file.gltf" のように書くのが一般的ですが、
Panda3Dでは "/d/path/to/file.gltf" といったように、"D:"を"/d"にします。
model = self.loader.loadModel("D:/resource/model.gltf") #エラー
model = self.loader.loadModel("/d/resource/model.gltf") #OK
相対パス
Panda3D の相対パスは「Python の カレントディレクトリ」を基準に解決されます。
しかし VSCode や PyCharm の実行設定によっては カレントディレクトリ が自動的に main.py の場所になる場合があります。
Python組み込み関数(open()など)とPanda3Dの基準ディレクトリがずれることがあります。
例えばファイル階層が以下の時、
root/
└ resource/
└ image.png
└ src/
└ project-name/
└ main.py
image.pngを読み込むには、以下のように書きます。
image = self.loader.loadTexture("resource/image.png") #rootが基準の場合
image = self.loader.loadTexture("../../resource/image.png") #main.pyが基準の場合
絶対パス、相対パスの仕組みはややこしいので、以下のような、どこからでも絶対パスを返す、PathManagerを作ると良いでしょう。
root/
└ resource/
└ image.png
└ src/
└ project-name/
└ main.py
└ shared/
└ util/
└ path_manager.py
from pathlib import Path
import re
class PathManager:
PROJECT_ROOT = Path(__file__).resolve().parents[4] #path_manager.pyから5つ上の階層
RESOURCE_DIR = PROJECT_ROOT / "resources"
@staticmethod
def to_vfs(path: Path) -> str:
"""
Windows の絶対パスを Panda3D VFS 形式に変換する
例: D:/path/to/file → /d/path/to/file
"""
posix = path.as_posix()
# Windows ドライブレターを検出
match = re.match(r"([A-Za-z]):/(.*)", posix)
if match:
drive = match.group(1).lower()
rest = match.group(2)
return f"/{drive}/{rest}"
return posix # もともと UNIX 形式ならそのまま
@classmethod
def resource(cls, *paths: str):
p = cls.RESOURCE_DIR.joinpath(*paths)
return p
@classmethod
def resource_vfs(cls, *paths: str):
p = cls.RESOURCE_DIR.joinpath(*paths)
return cls.to_vfs(p)
使い方
path = PathManager.resource_vfs("image.png")
self.loader.loadTexture(path)
キャッシュ
モデルやテクスチャなどを変更しても反映されない場合、キャッシュが残っているからかもしれない。
windowsの場合、キャッシュは以下に保存されているので、必要があれば削除する。
C:\Users\You\AppData\Local\Panda3D-1.10\cache
また、読み込むときにnoCache引数をTrueにすると、キャッシュを読み込まなくなる。(キャッシュが削除されるわけではない)
model = self.loader.loadModel(f"model.egg", noCache=True)
参考: Clearing Cached Models - Pipeline - Panda3D
フレームレート制限
from ctypes import windll
windll.winmm.timeBeginPeriod(1) #精度を上げる
from panda3d.core import ClockObject
globalClock = ClockObject.getGlobalClock()
globalClock.setMode(ClockObject.M_limited)
globalClock.setFrameRate(24) #24fpsに制限
windllについて
デフォルトのWindowsのタイマーの精度は、細かい計算をするのには悪い。
BiginPeriodで精度を上げる。
参考: 【Solved】Limited framerate is not stable - Scripting Issues - Panda3D
レンダリング
座標系
Panda3Dは「右手座標」「Z-up」です。
オイラー角は node_path.setHpr(h, p, r) で指定します(H=Z軸、P=X軸、R=Y軸)。
カメラとサンライトのデフォルトの方向
回転がない時、+Y軸の方向を向いています。
透明
node_path.setTransparency(TransparencyAttrib.MAlpha)
node_path.setAlphaScale(0.5)
透明オブジェクトの中にさらに透明を描画したい場合は、MDualにする?
node_path.setTransparency(TransparencyAttrib.MDual)
参考: TransparencyAttrib — Panda3D Manual
デバッグ
フレームレートの表示
self.setFrameRateMeter(True)
アナライザーの表示
現在のオブジェクトの数、頂点数などが分かる
self.setSceneGraphAnalyzerMeter(True)
その他(宣伝)
私の作品を見て頂けるとうれしいです。
YouTube
【Multiply or Release】やさしくかがやく色塗りバトル - YouTube
ニコニコ動画
黒白の縄張りバトル - Multiply or Release 3D (Python / Panda3D)
検索用キーワード
Python Panda3D 自作ゲーム