はじめに
Panda3Dは、Pythonで3Dゲームや視覚化アプリケーションを作成するための強力なオープンソースエンジンです。このエンジンは、初心者にも使いやすく、同時に高度な機能も備えています。この記事では、Panda3Dの基本から応用まで、15の章に分けて詳しく解説します。各章では、概念の説明とともに、実際のコード例を提供します。
第1章: Panda3Dの基本
Panda3Dは、3D空間を扱うためのシーングラフという概念を使用します。シーングラフは、3D世界の要素を階層的に管理する木構造です。
from direct.showbase.ShowBase import ShowBase
class MyGame(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# 3Dモデルをロード
self.model = self.loader.loadModel("models/panda")
# シーンに追加
self.model.reparentTo(self.render)
game = MyGame()
game.run()
このコードは、Panda3Dの基本的な構造を示しています。ShowBaseクラスを継承してゲームを作成し、3Dモデルをロードしてシーンに追加しています。
第2章: カメラの制御
3D空間を見るためには、カメラの制御が重要です。Panda3Dでは、カメラの位置や向きを簡単に変更できます。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Point3
class CameraControl(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.model = self.loader.loadModel("models/panda")
self.model.reparentTo(self.render)
# カメラの位置を設定
self.camera.setPos(0, -10, 5)
# カメラの向きを設定
self.camera.lookAt(Point3(0, 0, 0))
game = CameraControl()
game.run()
このコードでは、カメラの位置を(0, -10, 5)に設定し、原点(0, 0, 0)を見るように向きを調整しています。
第3章: ライティング
3D空間の雰囲気を作り出すには、適切なライティングが欠かせません。Panda3Dでは、様々な種類のライトを使用できます。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import AmbientLight, DirectionalLight, Vec4
class Lighting(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.model = self.loader.loadModel("models/panda")
self.model.reparentTo(self.render)
# 環境光を追加
ambientLight = AmbientLight("ambient light")
ambientLight.setColor(Vec4(0.2, 0.2, 0.2, 1))
self.render.setLight(self.render.attachNewNode(ambientLight))
# 指向性光源を追加
directionalLight = DirectionalLight("directional light")
directionalLight.setDirection(Vec4(-1, -1, -1, 0))
directionalLight.setColor(Vec4(0.8, 0.8, 0.8, 1))
self.render.setLight(self.render.attachNewNode(directionalLight))
game = Lighting()
game.run()
このコードでは、環境光と指向性光源を追加しています。環境光は全体的な明るさを、指向性光源は影の付き方を制御します。
第4章: テクスチャとマテリアル
3Dモデルの見た目を向上させるには、テクスチャとマテリアルの適用が重要です。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Texture, TextureStage
class TextureAndMaterial(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.model = self.loader.loadModel("models/panda")
self.model.reparentTo(self.render)
# テクスチャをロード
texture = self.loader.loadTexture("textures/panda_texture.png")
# テクスチャをモデルに適用
self.model.setTexture(texture)
# マテリアルを設定
self.model.setMaterial(self.loader.loadMaterial("materials/panda_material.mat"))
game = TextureAndMaterial()
game.run()
このコードでは、テクスチャをロードしてモデルに適用し、さらにマテリアルを設定しています。
第5章: アニメーション
3Dモデルにアニメーションを適用することで、シーンに動きを加えることができます。
from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor
class Animation(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# アニメーション付きモデルをロード
self.actor = Actor("models/panda-model", {"walk": "models/panda-walk"})
self.actor.reparentTo(self.render)
# アニメーションを再生
self.actor.loop("walk")
game = Animation()
game.run()
このコードでは、アニメーション付きのモデルをロードし、「歩く」アニメーションをループ再生しています。
第6章: 物理シミュレーション
Panda3Dには物理エンジンが組み込まれており、リアルな物理シミュレーションを実現できます。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Vec3
from panda3d.bullet import BulletWorld, BulletPlaneShape, BulletRigidBodyNode, BulletBoxShape
class Physics(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# 物理世界を作成
self.world = BulletWorld()
self.world.setGravity(Vec3(0, 0, -9.81))
# 地面を作成
shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
node = BulletRigidBodyNode('Ground')
node.addShape(shape)
np = self.render.attachNewNode(node)
np.setPos(0, 0, 0)
self.world.attachRigidBody(node)
# 箱を作成
shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
node = BulletRigidBodyNode('Box')
node.setMass(1.0)
node.addShape(shape)
np = self.render.attachNewNode(node)
np.setPos(0, 0, 2)
self.world.attachRigidBody(node)
# 更新タスクを追加
self.taskMgr.add(self.update, 'update')
def update(self, task):
dt = globalClock.getDt()
self.world.doPhysics(dt)
return task.cont
game = Physics()
game.run()
このコードでは、物理世界を作成し、地面と箱を配置しています。箱は重力の影響を受けて落下します。
第7章: 衝突検出
ゲーム内のオブジェクト間の衝突を検出することは、多くのゲームメカニクスの基礎となります。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionNode, CollisionSphere
from panda3d.core import CollisionHandlerQueue
class Collision(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# 衝突検出システムを設定
self.cTrav = CollisionTraverser()
self.cHandler = CollisionHandlerQueue()
# プレイヤーオブジェクトを作成
self.player = self.loader.loadModel("models/panda")
self.player.reparentTo(self.render)
# プレイヤーに衝突球を追加
cNode = CollisionNode('player')
cNode.addSolid(CollisionSphere(0, 0, 0, 1))
cNodePath = self.player.attachNewNode(cNode)
self.cTrav.addCollider(cNodePath, self.cHandler)
# 障害物を作成
self.obstacle = self.loader.loadModel("models/box")
self.obstacle.reparentTo(self.render)
self.obstacle.setPos(5, 0, 0)
# 障害物に衝突ノードを追加
cNode = CollisionNode('obstacle')
cNode.addSolid(CollisionSphere(0, 0, 0, 1))
self.obstacle.attachNewNode(cNode)
# 更新タスクを追加
self.taskMgr.add(self.update, 'update')
def update(self, task):
self.cTrav.traverse(self.render)
for i in range(self.cHandler.getNumEntries()):
entry = self.cHandler.getEntry(i)
print(f"Collision detected: {entry.getFromNodePath()} hit {entry.getIntoNodePath()}")
return task.cont
game = Collision()
game.run()
このコードでは、プレイヤーと障害物に衝突球を設定し、衝突が発生したときにコンソールに出力します。
第8章: ユーザー入力の処理
ゲームを対話的にするには、キーボードやマウスからの入力を処理する必要があります。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Vec3
class UserInput(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.player = self.loader.loadModel("models/panda")
self.player.reparentTo(self.render)
# キー入力のイベントを設定
self.accept("arrow_left", self.moveLeft)
self.accept("arrow_right", self.moveRight)
self.accept("arrow_up", self.moveForward)
self.accept("arrow_down", self.moveBackward)
# マウス操作を有効化
self.disableMouse()
self.taskMgr.add(self.spinCameraTask, "SpinCameraTask")
def moveLeft(self):
self.player.setPos(self.player.getPos() + Vec3(-1, 0, 0))
def moveRight(self):
self.player.setPos(self.player.getPos() + Vec3(1, 0, 0))
def moveForward(self):
self.player.setPos(self.player.getPos() + Vec3(0, 1, 0))
def moveBackward(self):
self.player.setPos(self.player.getPos() + Vec3(0, -1, 0))
def spinCameraTask(self, task):
angleDegrees = task.time * 6.0
angleRadians = angleDegrees * (pi / 180.0)
self.camera.setPos(20 * sin(angleRadians), -20 * cos(angleRadians), 3)
self.camera.lookAt(0, 0, 0)
return task.cont
game = UserInput()
game.run()
このコードでは、矢印キーでプレイヤーを移動させ、マウスでカメラを回転させることができます。
第9章: サウンドとBGM
ゲームに音楽や効果音を追加することで、プレイヤーの没入感を高めることができます。
from direct.showbase.ShowBase import ShowBase
from direct.showbase import Audio3DManager
class Sound(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# 3Dオーディオマネージャーを作成
self.audio3d = Audio3DManager.Audio3DManager(self.sfxManagerList[0], camera)
# BGMを再生
self.bgm = self.loader.loadMusic("music/background.ogg")
self.bgm.setLoop(True)
self.bgm.play()
# 効果音をロード
self.effect = self.audio3d.loadSfx("sounds/effect.wav")
# 効果音を再生するオブジェクトを作成
self.soundObject = self.loader.loadModel("models/panda")
self.soundObject.reparentTo(self.render)
self.soundObject.setPos(5, 0, 0)
# 効果音を3D空間に配置
self.audio3d.attachSoundToObject(self.effect, self.soundObject)
# スペースキーで効果音を再生
self.accept("space", self.playEffect)
def playEffect(self):
if self.effect.status() != self.effect.PLAYING:
self.effect.play()
game = Sound()
game.run()
このコードでは、BGMをループ再生し、スペースキーを押すと3D効果音が再生されます。
第10章: パーティクルシステム
パーティクルシステムを使用することで、火や煙、雨などの複雑な視覚効果を作成できます。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Filename
from direct.particles.ParticleEffect import ParticleEffect
class Particles(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# パーティクル効果を作成
self.effect = ParticleEffect()
self.effect.loadConfig(Filename("particles/fire.ptf"))
# パーティクルをシーンに追加
self.effect.start(parent=self.render, renderParent=self.render)
self.effect.setPos(0, 0, 0)
# カメラの位置を調整
self.camera.setPos(0, -10, 5)
self.camera.lookAt(0, 0, 0)
game = Particles()
game.run()
このコードでは、火のパーティクル効果をロードし、シーンに追加しています。パーティクルファイル(fire.ptf)は事前に作成しておく必要があります。
第11章: シェーダーの使用
シェーダーを使用することで、高度なグラフィック効果を実現できます。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader
class ShaderExample(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# モデルをロード
self.model = self.loader.loadModel("models/panda")
self.model.reparentTo(self.render)
# シェーダーをロード
shader = Shader.load(Shader.SL_GLSL,
vertex="shaders/vertex.glsl",
fragment="shaders/fragment.glsl")
# シェーダーをモデルに適用
self.model.setShader(shader)
# シェーダー入力を設定
self.model.setShaderInput("time", 0)
# 更新タスクを追加
self.taskMgr.add(self.update, "update")
def update(self, task):
# 時間を更新
self.model.setShaderInput("time", task.time)
return task.cont
game = ShaderExample()
game.run()
このコードでは、カスタムシェーダーをモデルに適用し、時間に基づいて変化するエフェクトを作成しています。vertex.glslとfragment.glslファイルは別途作成する必要があります。
第12章: GUIの作成
ゲーム内のメニューやHUD(ヘッドアップディスプレイ)を作成するには、GUIシステムを使用します。
from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectGui import DirectButton, DirectFrame, DirectLabel
from panda3d.core import TextNode
class GUIExample(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# フレームを作成
self.frame = DirectFrame(frameColor=(0, 0, 0, 0.5),
frameSize=(-0.5, 0.5, -0.5, 0.5),
pos=(0, 0, 0))
# ラベルを作成
self.label = DirectLabel(text="スコア: 0",
parent=self.frame,
scale=0.07,
pos=(-0.4, 0, 0.4),
text_align=TextNode.ALeft)
# ボタンを作成
self.button = DirectButton(text="クリック",
parent=self.frame,
scale=0.1,
pos=(0, 0, -0.4),
command=self.onButtonClick)
self.score = 0
def onButtonClick(self):
self.score += 1
self.label["text"] = f"スコア: {self.score}"
game = GUIExample()
game.run()
このコードでは、フレーム、ラベル、ボタンを含む簡単なGUIを作成しています。ボタンをクリックするとスコアが増加します。
第13章: マルチプレイヤーネットワーキング
ネットワークを介して複数のプレイヤーが参加できるゲームを作成するには、ネットワーキング機能を実装する必要があります。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import QueuedConnectionManager, QueuedConnectionListener
from panda3d.core import QueuedConnectionReader, ConnectionWriter
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
class NetworkingExample(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# ネットワークコンポーネントを初期化
self.cManager = QueuedConnectionManager()
self.cListener = QueuedConnectionListener(self.cManager, 0)
self.cReader = QueuedConnectionReader(self.cManager, 0)
self.cWriter = ConnectionWriter(self.cManager, 0)
# サーバーを開始
self.port = 9099
self.socket = self.cManager.openTCPServerRendezvous(self.port, 1000)
self.cListener.addConnection(self.socket)
# タスクを追加
self.taskMgr.add(self.tskListenerPolling, "Poll the connection listener", -39)
self.taskMgr.add(self.tskReaderPolling, "Poll the connection reader", -40)
def tskListenerPolling(self, task):
if self.cListener.newConnectionAvailable():
rendezvous = PointerToConnection()
netAddress = NetAddress()
newConnection = PointerToConnection()
if self.cListener.getNewConnection(rendezvous, netAddress, newConnection):
newConnection = newConnection.p()
self.cReader.addConnection(newConnection)
print(f"New connection from {netAddress.getIpString()}")
return task.cont
def tskReaderPolling(self, task):
if self.cReader.dataAvailable():
datagram = NetDatagram()
if self.cReader.getData(datagram):
self.processData(datagram)
return task.cont
def processData(self, datagram):
iterator = PyDatagramIterator(datagram)
message = iterator.getString()
print(f"Received message: {message}")
game = NetworkingExample()
game.run()
このコードは、基本的なTCPサーバーを実装しています。クライアントからの接続を受け付け、メッセージを処理します。実際のゲームでは、このコードをさらに拡張して、ゲーム状態の同期などを行う必要があります。
第14章: AIと経路探索
ゲーム内のNPC(ノンプレイヤーキャラクター)にインテリジェントな行動をさせるには、AIと経路探索アルゴリズムを実装する必要があります。
from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath, Vec3
from direct.task import Task
import random
class AIExample(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# NPCを作成
self.npc = self.loader.loadModel("models/panda")
self.npc.reparentTo(self.render)
self.npc.setScale(0.5)
# 目標地点のリスト
self.waypoints = [
Vec3(5, 5, 0),
Vec3(-5, 5, 0),
Vec3(-5, -5, 0),
Vec3(5, -5, 0)
]
self.current_waypoint = 0
# 更新タスクを追加
self.taskMgr.add(self.updateNPC, "UpdateNPC")
def updateNPC(self, task):
# 現在の目標地点
target = self.waypoints[self.current_waypoint]
# NPCの現在位置
current_pos = self.npc.getPos()
# 目標への方向ベクトル
direction = target - current_pos
# 一定の速度で移動
speed = 0.1
if direction.length() > speed:
direction.normalize()
new_pos = current_pos + direction * speed
self.npc.setPos(new_pos)
else:
# 目標に到達したら次の目標へ
self.current_waypoint = (self.current_waypoint + 1) % len(self.waypoints)
# NPCの向きを進行方向に合わせる
self.npc.lookAt(target)
return Task.cont
game = AIExample()
game.run()
このコードでは、NPCが定義されたウェイポイント(目標地点)を順番に巡回します。実際のゲームでは、より複雑な経路探索アルゴリズム(例:A*アルゴリズム)を実装することで、障害物を避けながら目的地に到達するようなAIを作成できます。
第15章: ゲームステートとシーン管理
大規模なゲームでは、異なるゲーム状態(メニュー、ゲームプレイ、ゲームオーバーなど)を管理し、シーンを切り替える必要があります。
from direct.showbase.ShowBase import ShowBase
from direct.fsm.FSM import FSM
from direct.gui.DirectGui import DirectButton
class GameState(FSM):
def __init__(self, game):
FSM.__init__(self, "GameState")
self.game = game
def enterMenu(self):
self.game.menu_scene.show()
self.game.play_scene.hide()
self.game.gameover_scene.hide()
def exitMenu(self):
self.game.menu_scene.hide()
def enterPlay(self):
self.game.play_scene.show()
def exitPlay(self):
self.game.play_scene.hide()
def enterGameOver(self):
self.game.gameover_scene.show()
def exitGameOver(self):
self.game.gameover_scene.hide()
class GameExample(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# シーンを作成
self.menu_scene = self.loader.loadModel("models/menu")
self.play_scene = self.loader.loadModel("models/level")
self.gameover_scene = self.loader.loadModel("models/gameover")
self.menu_scene.reparentTo(self.render)
self.play_scene.reparentTo(self.render)
self.gameover_scene.reparentTo(self.render)
# ゲームステートを初期化
self.game_state = GameState(self)
# メニューボタンを作成
self.start_button = DirectButton(text="ゲーム開始", scale=0.1, pos=(0, 0, 0.2),
command=self.startGame)
self.quit_button = DirectButton(text="終了", scale=0.1, pos=(0, 0, -0.2),
command=self.quit)
# 初期状態をメニューに設定
self.game_state.request("Menu")
def startGame(self):
self.game_state.request("Play")
def gameOver(self):
self.game_state.request("GameOver")
def quit(self):
self.userExit()
game = GameExample()
game.run()
このコードでは、FSM(有限状態機械)を使用してゲームの状態を管理しています。メニュー、プレイ、ゲームオーバーの3つの状態があり、それぞれの状態に応じて適切なシーンを表示します。
以上で、Panda3Dを使用したPythonゲーム開発の15章にわたる詳細な解説が完了しました。これらの章を通じて、3Dゲーム開発の基本から応用まで幅広くカバーしています。実際のゲーム開発では、これらの要素を組み合わせて使用することになります。Panda3Dの豊富な機能を活用し、創造的なゲームを作成してください。