ゲームエンジン Panda3D の基本機能
【概要】 ゲームエンジンPanda3Dの主な機能として,シーングラフ,カメラ,ライティング,シャドウマッピングがある.シーングラフはツリー構造のデータ構造で,3D空間内のオブジェクトを階層的に管理し,親ノードの変形が子ノードに継承される特性がある.カメラは視点を表現し,位置・向き・視野角の設定により,3D空間からスクリーン座標への変換を制御する.ライティングは環境光・平行光・点光源などの光源を組み合わせて,リアルな光の表現を可能にする.シャドウマッピングは光源からの深度情報を利用して影を生成する技術である.これらの技術要素を組み合わせることで,高品質で臨場感のある3D表現を実現できる.
【目次】
概要
- 3次元空間での基本操作:XYZ軸による座標指定,Point3による位置表現,Vec3による方向表現
- 3次元モデルファイルの読み込み:loadModel
- 3次元モデルの配置:位置 setPos(),拡大縮小 setScale(),回転 setHpr()/setQuat()
- シーングラフ構造:NodePath(シーングラフ内のノードを参照するためのオブジェクト),reparentTo,attachNewNode
- カメラの制御と操作:camera.setPos(),camera.lookAt(),base.camLens.setFov()
- 光源(ライティング)の設定:環境光(AmbientLight),平行光(DirectionalLight),PointLight
- イベントハンドラの登録:accept(キーコード例:"A","space","enter","arrow_left","arrow_up","arrow_down","arrow_right" など)
- タスク管理:taskMgr.add による定期実行
- シャドウシステム:shadowMap,setShadowCaster
- 既定のマウス操作:右ボタン,左ボタンを押しながらマウスを動かすと,シーン全体が動く
用語
3次元空間の基本概念
3次元座標系
互いに垂直な3つの座標軸であるXYZ軸による3次元直交座標系で,3D空間内の位置を表現する基本的な仕組みである.X軸は左右,Y軸は前後,Z軸は上下を表し,点(2,3,1)は右に2,前に3,上に1の位置を示す.空間全体の基準となるワールド座標系とオブジェクトごとの基準となるローカル座標系があり,オブジェクトの配置と移動の基礎となる.
Point3と Vec3
Point3は3D空間内の位置を表し,Vec3は方向と大きさを持つ量であるベクトルを表現する.位置と方向の違いを明確に区別することで,object.setPos(Point3(0, 0, 0))のように位置設定などの3D空間での操作を行える.点の減算でベクトルを得られ,点にベクトルを加えて新しい点を得られる.Vec3(1,0,0)はX方向への単位ベクトルを表す.
シーン構造
シーングラフ (Scene Graph)
3D空間内のオブジェクトの階層構造を管理するツリー構造のデータ構造である.シーングラフ内のノードを参照するためのオブジェクトであるNodePathの集合として表現され,座標変換や属性の継承が可能である.親の移動が子に影響し,子の移動は親に影響しない特性を持つ.render.attachNewNode("parent")で親ノードを作成するなど,3D空間の管理と操作のための仕組みである.
NodePath
シーングラフ内のノードを参照するためのオブジェクトである.階層構造の中でノードへのパスを表し,attachNewNode()で階層構造を構築し,reparentTo()でノードの親子関係を設定する.3Dオブジェクトの配置や変形を制御するために有用である.renderは最上位ノードとなる.
3Dモデルの構成要素
メッシュ (Mesh)
3Dモデルを構成する頂点,辺,面の集合である.3つ以上の頂点で構成される面であるポリゴンの集合によって形状を表現する。表面画像であるテクスチャや,物体の材質を定義するマテリアルなどの表面属性を持つ.3Dオブジェクトの見た目を決定する最も重要な要素の一つであり,複雑な形状の表現を可能にする.
ジオメトリ (Geometry)
3D空間内の形状データを表現する基本要素である.点,線,面などで構成され,頂点座標,面や頂点の向きを示す法線ベクトル,テクスチャ座標などの属性を持つ.GeomVertexDataやGeomTrianglesクラスで形状定義が行われ,3D描画の処理であるレンダリングパイプラインで処理される.
頂点 (Vertex)
3D空間内の点を表す要素であり,位置座標(x,y,z)を持つ.法線ベクトル,色,テクスチャ座標などの付加的な属性を持ち,GeomVertexWriterで頂点データを生成できる.
ポリゴン (Polygon)
3つ以上の頂点で構成される面であり,3Dモデルの表面を形成するための要素である.最も基本的なポリゴンは三角形である.3D形状の表現の基礎であり,画像を面に貼り付けるテクスチャマッピングにおいても使用される.
法線ベクトル (Normal Vector)
面や頂点の向きを示す長さが1の単位ベクトルである.光の反射計算に利用される.法線ベクトルの向きは,面の表裏を判定する基準となる.滑らかな陰影表現であるスムーズシェーディングや凹凸表現のバンプマッピングなどの表現にも使用され,3Dオブジェクトの光沢や陰影表現に役立つ.normal.addData3()で法線を設定する.
テクスチャ (Texture)
3Dモデルの表面に貼り付ける画像データである.色や凹凸などの詳細な表現を可能にし,拡散反射,鏡面反射,法線マップなど複数のテクスチャを組み合わせることでより豊かな表現が可能となる.リアルな表面表現を実現する技術である.
カメラと視点
カメラ (Camera)
3D空間内の視点を表現する.3D空間からスクリーン座標への変換を制御するために利用される.位置,向き,視野角などのパラメータを持ち,透視投影または平行投影の投影方式を指定できる.camera.setPos(0, -10, 3)のようにsetPos()で位置を設定し,lookAt()で注視点を設定する.
視野角 (Field of View, FOV)
カメラが捉える視界の広さを角度で表現するパラメータである.値が大きいほど広い範囲が見えるが画面の歪みも大きくなる.一般的なゲームでは60度から90度の範囲で設定され,base.camLens.setFov(80)で80度に設定するなど,3D表現の重要な要素となる.
オイラー角
ヨー(水平回転),ピッチ(縦回転),ロール(傾き)という3つの角度で物体の向きを表現する回転表現方法である.object.setHpr(0, 0, 0)のようにsetHpr()メソッドで向きをリセットする.
照明と影
ライティング (Lighting)
3D空間内の光源と照明効果を制御するシステムである.環境光(AmbientLight),平行光(DirectionalLight),点光源,スポットライトなどの異なる種類の光源を組み合わせ,render.setLight(render.attachNewNode(alight))で光源を適用するなど,リアルな光の表現が可能となる.3D表現の質を決定する重要な要素である.
光源の種類
3D空間を照らす光源には複数の種類がある.alight.setColor((0.2, 0.2, 0.2, 1))で暗めに設定できる環境光(AmbientLight)は空間全体を均一に照らし,平行光(DirectionalLight)は太陽光のような平行光線を生成する.また,PointLightは点から全方向に広がる光,SpotLightは円錐状の光を表現する.
シャドウマッピング (Shadow Mapping)
光源から見たシーンの深度情報を記録し,3D空間内のオブジェクトに影を付与する技術である.光源からの距離を計算し,オブジェクトによって光が遮られる部分を特定することで,リアルな影を表現する.dlight.setShadowCaster(True, 2048, 2048)で2048×2048の高解像度影を生成する.影は,3D表現に重要である.
デプスバッファ (Depth Buffer)
各ピクセルの奥行き情報を保持するメモリ領域である.オブジェクトの前後関係を表現するために使用され,Zバッファとも呼ばれる.見えない部分を除去する隠面消去の基本的なアルゴリズムを実現し,props.setDepthBits(24)で24ビット精度の深度情報を設定する.シャドウマッピングなどの高度な表現技術の基礎となる.
レンダリングと入力処理
レンダリング (Rendering)
3D空間のデータを2D画像として描画する一連の処理過程である.モデリングデータの変換,光源計算,テクスチャマッピング,ピクセル処理など,複数の段階を経て最終的な画像を生成する.render.setShaderAuto()で自動シェーディング設定を行うなど,3Dグラフィックスの最終段階を構成する.
キーボード/マウス入力
ユーザーからの操作を検出し処理する.キーの押下,保持,解放を区別し,マウスは位置と移動量を検出する.self.accept("space", self.jump)でスペースキーの処理を登録するなど,accept()メソッドでイベントを登録し,イベント発生時に実行されるコールバック関数で対応する処理を実行する.ユーザーとのインタラクションを実現する基盤である.
タスク管理
定期実行や遅延実行の管理を行う.taskMgr.add(self.update, "UpdateTask")のようにtaskMgr.add()で定期実行タスクを登録し,dt = globalClock.getDt()でフレーム間の経過時間を取得する.入力処理,状態更新,描画の繰り返しであるゲームループの実装や時間に基づく処理の制御など,動的な3D環境の実現に必要不可欠な機能である.
Panda3Dによる3次元ゲーム開発の基礎
1. 座標系の基本操作
3次元空間での位置指定
XYZ軸による3次元直交座標系(互いに垂直な3つの座標軸)で位置を表現する.X軸は左右,Y軸は前後,Z軸は上下を表す.点(2,3,1)は右に2,前に3,上に1の位置を示す.ワールド座標系(空間全体の基準となる座標系)とローカル座標系(オブジェクトごとの基準となる座標系)の2種類がある.
右手座標系
親指(X),人差し指(Y),中指(Z)を直角に交差させた時の右手の形で表現される座標系である.3Dゲームエンジンの標準的な座標系である.回転方向や法線(面に垂直な方向)の判定では,右手座標系の場合と左手座標系の場合で処理が変わる.
ベクトルと点の違い
点は位置(2,3,1),ベクトルは方向と大きさを表す.点の減算でベクトルを得られ,点にベクトルを加えて新しい点を得られる.ベクトルの加減算や内積・外積の演算が3D空間の計算で重要である.
以下のコードでは,3D空間内でのオブジェクトの配置と移動を制御する.Point3クラスによる位置指定,setPos()による配置,setHpr()による向きの設定,そしてsetScale()によるサイズ調整を示している.
オブジェクトの配置と移動
object.setPos(Point3(0, 0, 0)) # 位置の設定
object.setHpr(0, 0, 0) # 向きの設定(heading, pitch, roll)
object.setScale(1.0) # スケールの設定
相対移動
object.setPos(object, Point3(1, 0, 0)) # ローカル座標でx方向に1単位移動
座標系のポイント
- Point3は位置,Vec3は方向を表現する
- setPos()で絶対位置,相対位置を指定できる
- HPR(heading(水平回転),pitch(縦回転),roll(傾き))で回転を制御する
2. シーン管理の基本
木構造によるデータ構造
親ノードと子ノード(階層関係を持つデータ要素)の階層関係を表現する構造である.3D空間では親の移動が子に影響し,子の移動は親に影響しない特徴を持つ.アニメーション(動画表現)やオブジェクトのグループ化に利用する.キャラクターの骨格構造も木構造で表現する.
階層構造の概念
オブジェクト間の親子関係を管理する仕組みである.例えば,車体(親)とタイヤ(子),腕(親)と手(子)の関係がある.
3Dモデルのファイル形式
OBJ, FBX(これらは3次元モデルデータの標準形式である)などの形式で3Dモデルデータを保存する.形状,材質,テクスチャ(表面画像),アニメーションなどの情報を含む.
以下のコードでは,シーングラフの構成要素として,2つのノード parent と child の階層構造を設定し,3Dモデルをシーンに配置している.attachNewNode()を用いて設定している.
ノードの階層構造
parent = render.attachNewNode("parent")
child = parent.attachNewNode("child")
child.setPos(1, 0, 0) # 親からの相対位置
モデルの読み込みと配置
model = loader.loadModel("models/box")
model.reparentTo(render)
シーン管理のポイント
- attachNewNode()で階層構造を構築する
- reparentTo()でノードの親子関係を設定する
- renderは最上位ノードである
3. カメラ制御の基本
視点と注視点の概念
視点は観察者(カメラ)の位置,注視点は見つめる対象の位置を表す.この2点で視線方向が決定する.カメラワーク(視点移動)の基本である.TPS(三人称視点ゲーム)やFPS(一人称視点ゲーム)などの視点制御に利用される.
視野角(Field of View)とクリッピング面
視野角(Field of View)は,カメラが捉える視界の広さを角度で表現するパラメータである.クリッピング面(表示範囲を定める面)は表示距離の範囲を定義し,近接面と遠方面で設定する.
オイラー角による回転表現
3つの角度(ヨー(水平回転),ピッチ(縦回転),ロール(傾き))で物体の向きを表現する.特定の角度での回転制限に注意が必要である.
以下のコードでは,カメラの初期設定と視点回転を設定している.setPos()による位置設定,lookAt()による注視点指定,setFov()による視野角設定,そして視点回転の制限を実装している.
カメラの初期設定
camera.setPos(0, -10, 3)
camera.lookAt(0, 0, 0) # 注視点の設定
base.camLens.setFov(80) # 視野角の設定
マウスによる視点回転
heading -= (x - 100) * sensitivity # 水平回転
pitch = max(-90, min(90, pitch)) # 垂直回転制限
カメラ制御のポイント
- setPos()とlookAt()で視点と注視点を設定する
- setFov()で視野角を調整する
- ピッチ角は-90度から90度に制限する
4. ライティングの基本
光の基本特性
光の直進,反射,屈折,減衰の物理特性を考慮する.光源からの距離による減衰や物体表面での反射特性を再現する.色はRGB値(赤緑青の三原色)で表現し,各成分は0から1の範囲である.
拡散反射と鏡面反射
拡散反射(全方向への均等な反射)は光を全方向に均等に反射する.鏡面反射(特定方向への強い反射)は特定方向への強い反射である.マテリアル(材質)のプロパティでこれらを制御する.物理ベースレンダリング(PBR)では金属度と粗さで質感を表現する.
環境光(AmbientLight)と平行光(DirectionalLight)
環境光(AmbientLight)は空間全体を均一に照らす光である.平行光(DirectionalLight)は太陽光のような平行光線である.スポットライト(円錐状の光)は円錐状に広がる光である.それぞれ色と強度を設定する.
以下のコードでは,基本的なライティング(光源)を設定する.環境光(AmbientLight)による環境光の設定,平行光(DirectionalLight)による指向性光源の追加,光の色と強度の制御を実装している.
光源(ライティング)の設定
alight = AmbientLight("ambient")
alight.setColor((0.2, 0.2, 0.2, 1))
render.setLight(render.attachNewNode(alight))
dlight = DirectionalLight("sun")
dlight.setColor((1, 1, 1, 1))
dlnp = render.attachNewNode(dlight)
render.setLight(dlnp) # シーンに指向性光源を追加
ライティングのポイント
- 環境光(AmbientLight)で全体的な明るさを設定する
- 平行光(DirectionalLight)で方向性のある光を設定する
- setColor()で光の色と強度を調整する
5. 入力処理の基本
イベント駆動プログラミング
キー入力やマウス操作をイベント(プログラム上の事象)として検出する.コールバック関数(イベント発生時に実行される処理の関数)で対応する処理を実行する.イベントキュー(処理待ち行列)で入力を管理する.
キーボード/マウス入力の仕組み
キーの押下,保持,解放を区別する.マウスは位置と移動量を検出する.移動での相対モード(カーソル位置を固定する方式)ではカーソルを中心に固定し移動量のみ使用する.ゲームパッドなど他の入力デバイスも同様の仕組みである.
入力状態管理
現在の入力状態をフラグや数値で保持する.キーの同時押しにも対応できる.入力履歴の管理で連続技なども実現できる.
以下のコードでは,基本的な入力処理を実現している.キーイベントの登録,コールバック関数の設定,キー状態の管理を実装している.
キー入力の設定
self.accept("escape", sys.exit)
self.accept("space", self.jump)
キー状態の管理
self.keys = {"w": False, "a": False}
self.accept("w", lambda: self.updateKey("w", True))
self.accept("w-up", lambda: self.updateKey("w", False))
入力処理のポイント
- accept()でキーイベントを登録する
- キーの状態をdict形式で管理する
- -upサフィックスでキーを離した時の処理を実装する
6. タスク管理の基本
ゲームループ
入力処理,状態更新,描画を繰り返す.フレームレート(画面更新頻度)の制御,時間管理が重要である.固定時間ステップ(一定間隔の更新)と可変時間ステップ(処理時間に応じた更新)の使い分けが必要である.
フレームレートと時間管理
フレームレートは1秒あたりの画面更新回数である.60FPS(1秒間に60回の更新)が標準的である.処理負荷による変動を考慮し,前フレームからの経過時間に基づいてで動きを調整することが重要である.垂直同期(モニタの更新と同期)でティアリング(画面の横分割現象)を防止する.
タスクスケジューリング
定期実行や遅延実行の管理である.優先度による実行順序の制御を行う.
以下のコードでは,基本的なタスク管理を実現している.taskMgr.add()による定期実行タスクの登録,経過時間の取得,ゲーム状態の更新処理を実装している.
定期実行タスクの登録
taskMgr.add(self.update, "UpdateTask")
def update(self, task):
dt = globalClock.getDt() # 経過時間
self.updateGame(dt) # ゲーム状態の更新
return task.cont
タスク管理のポイント
- taskMgr.add()で定期実行タスクを登録する
- getDt()でフレーム間の経過時間を取得する
- task.contで処理の継続を指示する
7. シャドウマッピングの基本
シャドウマッピングの原理
シャドウマッピングは,光源から見たシーンの深度情報をもとに,3D空間内のオブジェクトに影を付与する技術である.2段階レンダリング(二段階の描画処理)で影を表現する.1段階目で深度マップ作成,2段階目で影の判定を行う.画像の平滑化処理で品質を調整する.
深度バッファ(デプスバッファ)
各ピクセルまでの距離を記録するバッファである.Z-バッファとも呼ばれる.隠面消去(見えない部分の除去)と影の計算に使用される.精度は16-32ビット.
アーティファクトと対策
シャドウアクネ(斑点状の影の不具合),エイリアシング(ジャギー,輪郭の段差)などの問題がある.バイアス値調整,PCF(影の境界を滑らかにする処理手法),カスケードシャドウ(視点からの距離に応じた解像度調整)などで対策する.
以下のコードでは,基本的なシャドウマッピングを実現する.光源からのシャドウマップの生成,解像度の設定,シャドウマッピングの有効化を実装している.
シャドウマップの設定
dlight = DirectionalLight("shadow")
dlight.setShadowCaster(True, 2048, 2048) # 解像度2048x2048
dlnp = render.attachNewNode(dlight)
render.setLight(dlnp) # シーンに光源を追加
シャドウ有効化
render.setShaderAuto() # 自動シェーダー設定
シャドウマッピングのポイント
- setShadowCaster()でシャドウマップを有効化する
- setShaderAuto()で自動シェーディング設定を行う
Panda3Dを使用したファーストパーソンビューのオープンワールドゲームの基本実装
概要
このプログラムは,Panda3D(3次元ゲームエンジン)を使用した3D空間でのインタラクティブな操作を実現する.2048x2048の高解像度シャドウマッピングによる影の表現,WASDキーによる移動とマウスによる視点回転,スペースキーによる球体の生成・消去,エスケープキーによるプログラム終了などの機能を実装する.
プログラムの基本機能
カメラ
- 初期位置:(0, -10, 3)
- 注視点:(0, 0, 0)
- 視野角:80度
self.camera.setPos(0, -10, 3)
self.camera.lookAt(0, 0, 0)
self.camLens.setFov(80)
地形
- 位置:(0, 0, 0)
- サイズ:100x100の平面(-50から+50)
- 色:緑色(0.2, 0.5, 0.2)
cm.setFrame(-50, 50, -50, 50) # サイズ設定
ground.setPos(0, 0, 0) # 位置
ground.setColor(0.2, 0.5, 0.2, 1) # 色
オブジェクト
- 固定オブジェクト:4つの立方体
- (-3, 3, 0):赤色
- (3, 3, 0):緑色
- (-3, -3, 0):青色
- (3, -3, 0):黄色
cube_positions = [
(Point3(-3, 3, 0), Vec4(1, 0, 0, 1)), # 赤
(Point3(3, 3, 0), Vec4(0, 1, 0, 1)), # 緑
(Point3(-3, -3, 0), Vec4(0, 0, 1, 1)), # 青
(Point3(3, -3, 0), Vec4(1, 1, 0, 1)) # 黄
]
- 動的オブジェクト:球体(半径0.3,16x16分割)
- カメラの前方2単位の位置に生成
- 白色(1, 1, 1, 1)
def toggle_sphere(self):
if self.sphere is None:
self.create_sphere()
else:
self.sphere.removeNode()
self.sphere = None
- 球体の生成
- 半径: 0.3
- 16リングx16セグメントの分割数
sphere_node = self.create_sphere_mesh(radius=0.3)
# 16x16の分割(デフォルトパラメータ)
rings=16, segments=16
照明(ライティング)
- 環境光(AmbientLight):暗めの白色(0.2, 0.2, 0.2, 1)
- 平行光(DirectionalLight):白色(1, 1, 1, 1)
- 位置:(20, -20, 40)
- 注視点:(0, 0, 0)
ambient_light.setColor(Vec4(0.2, 0.2, 0.2, 1))
directional_light.setColor(Vec4(1, 1, 1, 1))
light_np.setPos(20, -20, 40)
プログラムで実現されている特徴的な手法、技術
1. シャドウマッピング(Shadow Mapping)
シャドウマッピングは,光源から見たシーンの深度情報を記録し,3D空間内のオブジェクトに影を付与する技術である.この技術により,より現実感のある映像表現を実現することができる.
高解像度の影生成:2048×2048ピクセルの高解像度で影を生成し,オブジェクトの輪郭に沿って正確で滑らかな影を表現する.
深度情報の詳細な管理:24ビットの深度バッファ(デプスバッファ)により,細かな奥行きの違いを表現し,近接から遠方までの正確な影の位置と形状を計算する.
カラー情報とアルファ値の処理:RGBAカラー形式により,色情報と透明度を処理し,半透明オブジェクトの影をリアルに表現する.
実装のコード例:シャドウマッピングの初期設定 シャドウバッファを2048×2048ピクセルの高解像度で作成し,24ビットの深度バッファ(デプスバッファ)とRGBAカラー形式を設定する.この設定により,オブジェクトの輪郭に沿った正確な影の表現が可能になる.
def init_shadow_mapping(self):
"""シャドウマッピングの初期設定を行う"""
try:
self.shadowSize = 2048
# シャドウバッファの作成
winprops = WindowProperties.size(self.shadowSize, self.shadowSize)
props = FrameBufferProperties()
props.setRgbColor(1)
props.setAlphaBits(1)
props.setDepthBits(24)
効果
- 視覚的なリアリティの向上
- 光源位置に応じた自然な影の表現
- 影の濃淡による立体感の強化
- オブジェクト間の位置関係の明確化
2. メッシュ生成
メッシュ生成は,頂点データの生成と管理によって3Dモデルの形状を構築する技術である.球体などの基本的な3Dオブジェクトを,パラメータを変更しながら作成することができる.
頂点データの生成と管理:オブジェクトのメッシュを構成する頂点のX,Y,Z座標値を計算する.
法線ベクトルの計算:各頂点における面の向きを表す法線ベクトルを計算し,光の反射計算に使用する.
実装のコード例:球体メッシュの生成 3次元空間内に球体を生成する.半径,リング数,セグメント数をパラメータとして受け取り,頂点データ,法線ベクトル,色情報を計算する.これにより,光の反射を考慮した自然な見た目の球体が生成される.
def create_sphere_mesh(self, radius=1, rings=16, segments=16):
try:
# 頂点フォーマットの設定
format = GeomVertexFormat.getV3n3c4()
vdata = GeomVertexData('sphere', format, Geom.UHStatic)
# 頂点書き込み用のオブジェクトを作成
vertex = GeomVertexWriter(vdata, 'vertex')
normal = GeomVertexWriter(vdata, 'normal')
color = GeomVertexWriter(vdata, 'color')
# 球の頂点を生成
for ring in range(rings + 1):
phi = ring * pi / rings
for segment in range(segments + 1):
theta = segment * 2.0 * pi / segments
x = radius * sin(phi) * cos(theta)
y = radius * sin(phi) * sin(theta)
z = radius * cos(phi)
vertex.addData3(x, y, z)
normal.addData3(x/radius, y/radius, z/radius)
color.addData4(1, 1, 1, 1)
3. 入力処理
ユーザーの操作を受け付け,3D空間内での動作に変換する.マウスとキーボードからの入力を処理する.
実装のコード例:カメラ制御の初期設定 カメラの向きを制御するためのヘッディング角とピッチ角,マウス感度を初期化する.カメラの更新タスクをタスクマネージャに登録することで,継続的なカメラの制御を実現する.
def init_camera_control(self):
"""カメラ制御の初期設定を行う"""
self.heading = 0
self.pitch = 0
self.mouse_sensitivity = 0.2
self.taskMgr.add(self.update_camera, "UpdateCameraTask")
実装のコード例:キーボード入力の処理(WASDキーによる移動) WASDキーによる移動制御と,ESCキーによる終了,スペースキーによる操作を設定する.キーの押下と解放を検知し,移動状態を管理する仕組みを実装する.
def setup_keys(self):
"""キー設定の初期化を行う"""
try:
# 移動制御の初期化
self.move_speed = 0.2
self.keys = {"w": False, "a": False, "s": False, "d": False}
# キーバインドの設定
self.accept("escape", sys.exit)
self.accept("space", self.toggle_sphere)
# 移動キーのバインド
key_bindings = [
("w", True), ("a", True), ("s", True), ("d", True),
("w-up", False), ("a-up", False), ("s-up", False), ("d-up", False)
]
for key, value in key_bindings:
self.accept(key if value else key, self.update_key, [key.split("-")[0], value])
意義
- 直感的な操作性の実現
- マウスによる視点制御
- WASDキーによる移動
4. シーン管理
シーン管理(3D空間内のオブジェクトを管理)により,3D空間内のオブジェクトを管理する.
実装のコード例:シーンオブジェクトの生成 3D空間内に4つの立方体を配置する.それぞれの立方体は床面(z=0)に接地し,異なる位置と色を持つ.これにより,空間内のオブジェクトの配置と見た目を制御する.
def create_scene_objects(self):
"""シーン内のオブジェクトを生成する"""
try:
# 4つの立方体を配置(z=0で床に接地)
cube_positions = [
(Point3(-3, 3, 0), Vec4(1, 0, 0, 1)),
(Point3(3, 3, 0), Vec4(0, 1, 0, 1)),
(Point3(-3, -3, 0), Vec4(0, 0, 1, 1)),
(Point3(3, -3, 0), Vec4(1, 1, 0, 1))
]
for pos, color in cube_positions:
self.create_cube(pos, size=1.0, color=color)
実装のコード例:個別オブジェクトの生成(立方体オブジェクトを生成するプログラム) 指定された位置,サイズ,色で立方体を生成する.基本モデルをロードし,パラメータに従って変形と着色を行い,シーンに追加する.シェーダーを自動設定することで,光源に応じた陰影を表現する.
def create_cube(self, pos, size=1.0, color=Vec4(1, 1, 1, 1)):
"""立方体オブジェクトを生成する
Args:
pos (Point3): 立方体の位置
size (float): 立方体のサイズ
color (Vec4): 立方体の色
"""
try:
cube = self.loader.loadModel("models/box")
cube.setPos(pos)
cube.setScale(size)
cube.setColor(color)
cube.setShaderAuto()
cube.reparentTo(self.render)
5. カメラ制御
カメラ制御により,ユーザーの視点であるカメラを制御する.
実装のコード例:カメラ移動の制御 WASDキーの入力状態に応じてカメラを移動する.各キーの押下状態を確認し,対応する方向にカメラを移動させることで,3D空間内での視点移動を実現する.
def move_camera(self, task):
"""カメラの移動を更新する
Args:
task: タスクオブジェクト
Returns:
Task.cont: タスクを継続することを示す値
"""
try:
if self.keys["w"]:
self.camera.setPos(self.camera, Point3(0, self.move_speed, 0))
if self.keys["s"]:
self.camera.setPos(self.camera, Point3(0, -self.move_speed, 0))
if self.keys["a"]:
self.camera.setPos(self.camera, Point3(-self.move_speed, 0, 0))
if self.keys["d"]:
self.camera.setPos(self.camera, Point3(self.move_speed, 0, 0))
return task.cont
実装のコード例:カメラ回転の制御 マウスの移動に応じてカメラの向きを変更する.マウスポインタの位置から回転角を計算し,視点の上下角度を制限しながらカメラの向きを更新する.これにより,自然な視点操作を実現する.
def update_camera(self, task):
"""カメラの視点を更新する
Args:
task: タスクオブジェクト
Returns:
Task.cont: タスクを継続することを示す値
"""
try:
md = self.win.getPointer(0)
x = md.getX()
y = md.getY()
if self.win.movePointer(0, 100, 100):
self.heading -= (x - 100) * self.mouse_sensitivity
self.pitch -= (y - 100) * self.mouse_sensitivity
# 視点の上下角度を制限
self.pitch = max(-90, min(90, self.pitch))
self.camera.setHpr(self.heading, self.pitch, 0)
return task.cont