Panda3D场景安排
Panda3D将其对象存储在场景图中,从本质上讲,这是一个对象的层次结构,称为节点。
给定节点的状态(位置与旋转等状态)与其父级的状态是相对的,例如”0“的表示值表示与其父级位于同一位置。因此,每个节点都会影响其儿童节点及其子女,依次影响儿童节点。
但通常情况下我们不直接操作节点,而是操作”NodePath“,"NodePath"包含对其节点的引用,描述了从根部到有关节点的通过场景图的路线。
模型加载
loader用于加载多种不同类型的对象,包括非动画模型。要加载文件,只需将以下代码添加到__init__
中:
loader.loadModel("Models/Misc/environment")
Panda3D 允许不加入后缀名加载模型,加载模型后还需要将其连接到场景图中,通过使其称为NodePath中的一个节点来实现。
通过下列代码,可以将模型简单连接到场景图根部,NodePath由Panda3D自动提供,可在”render“中进行访问
self.environment.reparentTo(render)
在Panda3D中动画模型被称为”Actors“,不通过loader进行加载,而是直接通过实例化进行创建。构造中通过字典传递动画名称和动画文件。act_p3d_chan和a_p3d_chan_run同样为.egg文件。
from direct.actor.Actor import Actor
self.tempActor = Actor("Models/PandaChan/act_p3d_chan", {"walk" : "Models/PandaChan/a_p3d_chan_run"})
self.tempActor.reparentTo(render)
渲染属性
总的来说,对象的所有属性都可以算是对象的渲染状态。
属性的分配
nodePath.node().setAttrib(attributeObject)
任务
Tasks:应用程序执行时每个帧调用一次的特殊功能。
| 变量 | 目的 |
| ------------ | ---------------------------------------- |
| Task.done
| 指定任务已完成并将其从任务管理器中删除。 |
| Task.cont
| 下一帧再次执行任务。 |
| Task.again
| 再次执行任务,使用与最初指定相同的延迟。 |
**TaskMgr:**Panda3D中所有任务都通过该对象进行处理
添加任务:taskMgr.add(exampleTask, 'MyTaskName')
若要指定参数,可以:taskMgr.add(exampleTask, 'MyTaskName', extraArgs=[a,b,c], appendTask=True)
删除任务:taskMgr.remove('MyTaskName')
任务链
创建
taskMgr.setupTaskChain('chain_name', numThreads = None, tickClock = None,
threadPriority = None, frameBudget = None,
frameSync = None, timeslicePriority = None)
任务链由其唯一名称识别。重复呼叫设置具有相同任务链名称的TaskChain()将重新配置同一任务链。
numThreads:指定为此任务链提供服务的线程数。默认值为零,这意味着任务链将由主线程处理。如果将此设置为 1,则将生成一个线程,以正常顺序一次处理链中的所有任务。如果将此设置为高于 1 的某个数字,则将生成多个线程来处理链上的任务。在这种情况下,有些任务可能同时运行,任务排序难以保证。
threadPriority:可选择TP_low,TP_normal,TP_high,TP_urgent。指定了分配给此任务链上的线程的优先级。
frameBudget:允许此任务链每帧运行的最大时间(秒内),设置为 -1 表示无限制(默认值)。
frameSync:设置为True则使任务链与时钟同步。
timeslicePriority:时间优先性,将其设置为True更改优先级的含义,以便某些任务的运行频率降低,与其使用的时间与其优先值成正比。
事件处理
接收事件
from direct.showbase import DirectObject
class myDirectObject(DirectObject.DirectObject):
#无限接收
myDirectObject.accept('Event Name', myDirectObjectMethod)
#接收一次
myDirectObject.acceptOnce('Event Name', myDirectObjectMethod)
忽略事件
#忽略某个事件
myDirectObject.ignore('Event Name')
#忽略所有事件
myDirectObject.ignoreAll()
发送事件
messenger.send('Event Name')
主循环
Panda3D中的主循环通常由run()实现
视角
相机
在Panda3D中,我们所看到的界面视角是以一个"camera"的模块决定的,Panda3D提供了对相机的访问和设置权限,包括一个引用节点路径的变量,即为"camera"。
示例
self.camera.setPos(0, 0, 32)
self.camera.setP(-90)
阴影
在Panda3D中,光也是一种节点,其包括多种类型:方向灯、环境灯……
环境灯
环境灯用恒定的光线填充场景,有助于确保未照明区域不会完全变黑、
示例
from panda3d.core import AmbientLight
from panda3d.core import Vec4
ambientLight = AmbientLight("ambient light")
ambientLight.setColor(Vec4(0.2, 0.2, 0.2, 1))
# 连接到场景中
self.ambientLightNodePath = render.attachNewNode(ambientLight)
# 设置光的影响节点
render.setLight(self.ambientLightNodePath)
方向灯
示例
from panda3d.core import DirectionalLight
mainLight = DirectionalLight("main light")
self.mainLightNodePath = render.attachNewNode(mainLight)
self.mainLightNodePath.setHpr(45, -45, 0)
render.setLight(self.mainLightNodePath)
控制
key-events
Panda3D中处理关键事件
# 存储关键信息
self.keyMap = {
"up" : False,
"down" : False,
"left" : False,
"right" : False,
"shoot" : False
}
def updateKeyMap(self, controlName, controlState):
self.keyMap[controlName] = controlState
self.accept("w", self.updateKeyMap, ["up", True])
DirectGui
DirectGui 系统用于在程序中创建按钮、标签、文本条目和帧。所有这些项目都可以用文本、图像和 3D 图形进行装饰。
Panda3D的坐标系中,x轴指示左右方向,y轴指示屏幕内外方向,z轴指示上下方向。
碰撞检测
Panda3D中提供了处理物理碰撞的两个系统:内置碰撞系统和Bullet物理系统
内置碰撞系统有三个主要元素:Traversers, Handlers, and Solids.
Traversers:用于检测各种物理物体的碰撞
Handlers:处理程序当物体碰撞时会发生的事件
Solids:实际的碰撞物体本身
通常我们只能使用一个traverser, 并让它检查每次更新的碰撞情况,ShowBase 类提供了一个名为"cTrav"的默认变量:如果您为此变量分配了一个新的traverser,Panda 将自动为您更新它。
示例
from panda3d.core import CollisionTraverser
self.cTrav = CollisionTraverser()
Handlers的使用
CollisionHandlerQueue:将碰撞事件存储在队列中,并允许您根据需要访问它们。您还可以对队列进行排序,以便轻松获得第一次碰撞。
CollisionHandlerEvent:每当指定的碰撞发生时,就会引发调用事件
CollisionHandlerPusher:防止指定的固体物体与其他固体物体相交
示例
from panda3d.core import CollisionHandlerPusher
from panda3d.core import CollisionSphere, CollisionNode
self.pusher = CollisionHandlerPusher()
colliderNode = CollisionNode("player")
colliderNode.addSolid(CollisionSphere(0, 0, 0, 0.3))
collider = self.tempActor.attachNewNode(colliderNode)
# 显示碰撞物体
collider.show()
# 添加为活跃对象
base.pusher.addCollider(collider, self.tempActor)
base.cTrav.addCollider(collider, self.pusher)
Panda3D 的内置系统不会检查每个对象与所有其他对象,只有程序指定的对象才会进行碰撞检测,其他的对象视为非活跃对象。只有活跃对象能够与其他物体碰撞,非活跃对象仅能被碰撞。
在碰撞中我们还可以指定碰撞响应的维度,否则默认为三维的碰撞响应。
#指定碰撞响应维度为水平
self.pusher.setHorizontal(True)
Panda3D就是通过这样的机制创建具有障碍性质的对象。
后续
喜欢的话可以关注一下我的公众号技术开发小圈,尤其是对深度学习以及计算机视觉有兴趣的朋友,我会把相关的源码以及更多资料发在上面,希望可以帮助到新入门的大家!