翻译:Panda3D Manual/V. Programming with Panda/G. Intervals

来源:互联网 发布:自动打电话软件 编辑:程序博客网 时间:2024/04/29 12:24
Intervals
Panda3D的Interval系统是一种复杂的脚本行为回放机制。使用Interval,我们可以建立复杂的相互关联的动画、声音效果或者其他行为,并按需执行脚本。
该系统的核心为Interval类,分为好几种不同类型,将在后面详细介绍,它们共有以下特性:每个Interval都表示在一段有限的时间内发生的一个行为(或一系列行为)。
Interval 系统的威力来自Sequence和Parallel(顺序和并行),它俩是一种特殊的Interval,可以嵌套包含任意种类的Interval(包括其 他Sequence和/或Parallel)。使用不同的Interval使我们能够用基本的行为装配出复杂的脚本。
使用Intervals
在任何Panda3D模块里使用Interval,应该首先导入interval模块:
from direct.interval.IntervalGlobal import *
所有Interval都共有很多方法。
开始运行一个Interval的代码如下:
interval.start() 
interval.start(startT, endT, playRate) 
interval.loop() 
interval.loop(startT, endT, playRate)
以上出现的三个参数都是可选的。参数startT和endT定义interval持续的时间,以秒为单位,从interval启动时计时。如果给出playRate参数,就可以使interval的运行比现实时间更快或更慢,默认值为1.0,快慢和现实时间一样。
一般来说,Interval运行到结尾就会自己停止,也可以提前终止它:
interval.finish()
这个函数将停止interval,把它的状态移到最终状态,就像已经运行到结尾一样。这一点很重要,我们可以在interval内部定义关键的清除(cleanup)行为,这样就保证清除行为在interval结束时能够执行。
可以临时暂停、继续一个interval:
interval.pause() 
interval.resume()
如果暂停interval却没有继续或完成它,余下的行为将不会被执行。
也可以在interval内部跳跃:
interval.setT(time)
这让interval跳到指定的时间,该时间从interval开头计时,以秒为单位。Interval将执行从当前时间到新的时间之间的所有行为;无法跳过中间这些行为。
把时间设为一个比当前更早的时间是合法的,interval将重新回到先前的状态。在某些情况下也是不可能的(特别是涉及函数Interval(Function Interval)时)。
interval还提供了许多查询方法:
interval.getDuration() 返回interval的持续时间,以秒为单位
interval.getT() 返回当前已经经历的时间,从interval开始时计时
interval.isPlaying() 当interval正在运行时返回ture,false表示还没开始,或已经运行完毕,或被暂停、或终止。
interval.isStopped() 当interval还没开始,或已经运行完毕,或被finish()终止时返回true,它与interval.isPlaying()不完全相同,因为在interval被暂停时不返回true。
 
插值Intervals(Lerp Intervals)
LerpInterval是Interval系统的主力军,“lerp”作为“linearly interpolate”(线性插值)的缩写,意指在一段时间内,对象的属性,如位置,从一个值到另一个值平滑地变化,可以使用LerpInterval来移动、旋转物体。
LerpInterval是所有interval中最难的一种,有很多个插值控制参数。
基于NodePath的LerpInterval纵览
大多数用于调整诸如位置、方向和缩放等NodePath变换属性的LerpInterval形式都差不多。以LerpPosInterval为例,它的作用是把模型从空间的一点平滑地移动到另一点:
i = LerpPosInterval(model, duration, pos, startPos = None, 
                    other = None, blendType = 'noBlend', 
                    bakeInStart = 1, fluid = 0, name = None)
只有模型(model)、移动的持续时间(duration)和新的位置(pos)这3个参数是必需的,其余参数都是可选的,通常可以忽略掉。
下面分别介绍各个参数的意义:

model要移动的模型,应该是一个NodePath。duration插值的持续时间(秒)。pos模型的目的地(移动到的新位置)。通常是一个Point3(x, y, z),但作为高级特性,也可以是返回一个Point3的一个Python函数,如果是一个函数,它将在真正开始插值时调用。startPos插值开始时模型的初始位置。如果省略这个参数,模型将从当前位置开始移动。和上一个pos参数一样,它也可以是一个Python函数,在插值开始时调用。
注 意,如果想从当前位置移动物体,最好省略这个参数而不必显式调用startPos=object.getPos(),因为object.getPos() 在interval创建时取值而不是在interval启动时。在一个Sequence里植入一连串LerpInterval时,不这样做是有道理的。
other这 个参数一般设为None,声明一个普通插值。如果传进来一个NodePath,将声明这个一个相对插值,将相对那个NodePath计算pos和 startPos。每一帧都计算一次相对变换,所以如果那个NodePath在插值期间动画,那么变化将在插值中体现。因此,不要让模型相对自己插值。blendType
这个参数指定插值开始和结束时的缓急程度。可以是下面几个值之一:
'easeIn'开始缓慢,逐渐加快到全速,然后突然停止。'easeOut'开始全速,之后减缓到一个平缓的结尾。'easeInOut'开始缓慢,逐渐加快到全速,然后减慢,平缓地停止。'noBlend'突然开始突然结束。bakeInStart该 参数属于一个高级特性。一般值为1,意为在interval开始运行时确定模型的起始位置,并在interval运行期间保留该位置。大多数情况满足我们 的需求。但如果给它0值,每一帧都将根据模型的当前位置和插值已经经历的时间重新计算起始位置,这样就使程序在模型插值时也能移动它,插值也随之适应性调 整。当startPos参数有值时,这个参数将不起作用。fluid如果等于1,插值将使用setFluidPos()代替setPos()来进行模型动画。参考“快速移动物体”一节。该参数只有开启模型的碰撞系统时才有意义。因为通常情况下,当模型直接由程序控制时,我们没有理由打开碰撞系统。因此fluid参数很少使用。

其他基于NodePath的LerpIntervals
除了位置,NodePath的许多属性都可以通过插值来控制。下面列出几种不同的LerpIntervals:
LerpPosInterval(model, duration, pos, startPos) 
LerpHprInterval(model, duration, hpr, startHpr) 
LerpQuatInterval(model, duration, quat, startQuat) 
LerpScaleInterval(model, duration, scale, startScale) 
LerpShearInterval(model, duration, shear, startShear) 
LerpColorInterval(model, duration, color, startColor) 
LerpColorScaleInterval(model, duration, colorScale, startColorScale)
以上函数的参数都跟LerpPosInterval的参数差不多;它们也有类似的捷径(例如model.hprInterval())。
还有很多组合式LerpInterval,可以同时进行几项插值。(把几个LerpInterval合并在一个Parallel里也能达到同样效果,只是合成式interval更方便,执行也更快。)
LerpPosHprInterval(model, duration, pos, hpr, startPos, startHpr) 
LerpPosQuatInterval(model, duration, pos, quat, startPos, startQuat) 
LerpHprScaleInterval(model, duration, hpr, scale, startHpr, startScale) 
LerpQuatScaleInterval(model, duration, quat, scale, startQuat, startScale) 
LerpPosHprScaleInterval(model, duration, pos, hpr, scale, startPos, startHpr, startScale) 
LerpPosQuatScaleInterval(model, duration, pos, quat, scale, startPos, startQuat, startScale) 
LerpPosHprScaleShearInterval(model, duration, pos, hpr, scale, shear, startPos, startHpr, startScale, startShear) 
LerpPosQuatScaleShearInterval(model, duration, pos, quat, scale, shear, startPos, startQuat, startScale, startShear)
其他类型的LerpInterval
除了对NodePath进行动画,你还可以用LerpInterval对物体的任何参数进行blend。用一个LerpFunctionInterval就能实现:
def myFunction(t): 
  ... do something based on t ... 

i = LerpFunc(myFunction, fromData = 0, toData = 1, duration = 0.0, 
  blendType = 'noBlend', extraArgs = [], name = None)
这个高级interval和前面的LerpInterval有很多共同之处,区别在它不直接变化一个值,而是调用一个指定的函数,该函数传入一个浮点参数t,从fromData到toData改变。接下来由你的函数根据当前t的值来设定物体的属性。
 
函数Intervals(Function Intervals)
函数interval和插值interval不同,后者在一段时间内把数据传给一个函数,前者只是调用一个函数。在与sequences和 parallels组合时经常使用函数interval,它的格式很简单。
intervalName = Func(myFunction)
传递函数时不用包含括号(例如传入函数指针Func)。如果myFunction有参数,将它们传给Func,如下所示:
def myFunction(arg1,arg2): 
   blah 

intervalName = Func(myFunction, arg1, arg2)
在sequences和parallels里,函数不能自己调用自己,必须把它们封装进interval里进行调用。因为函数interval没有duration参数,所以它在调用时立刻执行完。
 
Actor Intervals
Actor interval使actor动画作为一个interval播放,并通过sequences和parallels与其他interval结合在一起。
我 们通过指定帧(startFrame和endFrame)或秒(startTime和endTime)来播放动画的其中一段,也可以只指定 startFrame或startTime,然后给出时间长度duration参数。如果所有这些参数都没有指定,默认将播放整段动画。
假如endFrame在startFrame之前,或者播放速度playRate为负数,动画将被倒着播放。
你指定的动画范围可以超出实际的动画长度,但如果这样做的话,你要指定参数loop = 1或constrainedLoop = 1。下面解释一下原因。
loop 参数接受一个布尔值。如果为真,当interval超出动画最后一帧时,动画将重新开始播放。如果为假,当interval超出动画最后一帧时,动画将停 在最后的位置。注意,无论该参数的值是真还是假,所有的interval都会给定一个有限的duration参数。ActorInterval持续的时间 由duration参数,或者startTime/endTime参数,或者startFrame/endFrame参数控制。设置loop=1并不影响 ActorInterval的持续时间,它只控制动画超出范围的情况。
constrainedLoop 参数的功能与loop类似,loop = 1表示整段动画进行循环,而constrainedLoop=1表示只在startFrame和endFrame间循环。即,如果指定loop = 1,动画越过endFrame时,下一帧将从第0帧开始播放。如果指定constrainedLoop=1,endFrame的下一帧将回到 startFrame。
除了动画的名字不可省略,其他参数都是可选的。
from direct.interval.ActorInterval import ActorInterval 
myInterval = myactor.actorInterval( 
    "Animation Name", 
    loop= <0 or 1>, 
    contrainedLoop= <0 or 1>, 
    duration= D, 
    startTime= T1, 
    endTime= T2, 
    startFrame= N1, 
    endFrame= N2, 
    playRate = R 
    partName = PN, 
    lodName = LN, 
)
 
声音Intervals(Sound Intervals)
关于如何加载、播放声音请查阅“载入和播放声音和音乐”一章。
声音interval在interval内播放声音。和actor interval一样,声音interval拥有一个loop循环参数,可以暂停声音,以及volume(音量)和starTime参数。
mySound=loader.loadSfx("mySound.wav") 

myInterval = SoundInterval( 
    mySound, 
    loop = 0 or 1, 
    duration = myDuration, 
    volume = myVolume, 
    startTime = myStartTime 
)
声音interval提供的循环不是很干脆。每次循环间总有1/10秒的停顿。更好的循环播放方法请查阅“载入和播放声音和音乐”一章。
 
运动路径与粒子Intervals(Motion Path and Particle Intervals)
运动路径是Panda3D的一个高级特性,我们将在以后介绍。运动路径也有自己的interval,它的参数和“函数interval”非常相似,只要求运动路径和它作用的那个NodePath两个参数。
intervalName = MopathInterval(<Motion Path Name>,NodePath, <Name>)
粒子效果也可以在interval里运行:
intervalName = ParticleInterval( 
    <Particle Effect Name>, 
    <Parent>, 
    worldRelative = 1, 
    loop = 0 or 1, 
    duration = myDuration 
)
 
顺序和并行(Sequences and Parallels)
使用Sequences和Parallels必须包含下面这行:
from direct.interval.IntervalGlobal import *
Sequences 和Parallels用于控制interval何时发生。Sequence一个接一个地运行interval,效果就像下达一个“按顺序来”的命令。 Parallel则是一个“一起来”的命令,它同时运行所有interval。它们的格式都非常简单,都可以使用任何类型的interval。
mySequence = Sequence(<Interval>, ¦,<Interval>, name = "Sequence Name") 
myParallel = Parallel(<Interval>, ¦,<Interval>, name = "Parallel Name")
Sequence和Parallel也可以结合起来获得更复杂的控制。可以给Sequence的delay(延迟)参数传递一个等待interval,这个等待interval可以预先定义,但不一定非要这么做。
delay = Wait(2.5) 
pandaWalkSeq = Sequence(Parallel(pandaWalk,pandaWalkAnim),delay, 
    Parallel(pandaWalkBack,pandaWalkAnim), Wait(1.0),Func(myFunction,arg1))
上 例首先得到一个等待interval。然后,生成一个Sequence,它使用Parallel、等待interval,另一个Parallel和另一个 等待interval,并调用在Sequence中生成的myFunction函数。这样一个Sequence可能很长很快,因此在创建主 Sequence时我们应该谨慎地定义它内部的Parallel和Sequence。
 
位置、旋转及缩放Intervals(Position, Rotation and Scale Intervals)
把物体移动到某个点或者旋转某个Hpr值,Panda3D会自动生成interval。可以对物体调用posInterval和hprInterval。
myInterval1=myActor.posInterval(1.0,Point3(10,10,10)) #移动actor到点(10,10,10)
myInterval2=myActor.posInterval(2.0,Point3(8,-5,10)) #速度加快一倍
myInterval3=myActor.posInterval(1.0,Point3(2,-3,8),startPos=Point3(2,4,1)) #也可以指定一个起点
myInterval4=myActor.hprInterval(1.0,Vec3(180,90,0)) #旋转
有了以上这些interval,就可以轻松创建SequenceParallel
mySequence=Sequence(myInterval2,myInterval4) 
mySequence.start() 
myParallel=Parallel(myInterval3,myInterval1) 
myParallel.loop()
scaleInterval、posHprInterval、posScaleInterval、hprScaleInterval和posHprScaleInterval的工作方式都相同。
注意:物理引擎对用posInterval()移动的节点不起作用。
 
抛射Intervals(Projectile Intervals)
抛射interval使NodePath在重力的作用下沿抛物线运动。
myInterval = ProjectileInterval(<Node Path>, startPos = Point3(X,Y,Z), endPos = Point3(X,Y,Z), 
  duration = <Time in seconds>, startVel = Point3(X,Y,Z), endZ = Point3(X,Y,Z), 
  gravityMult = <multiplier>, name = <Name>)
所有的参数都不是必须的。下面是几个创建抛射interval的参数组合。(假如不提供startPos,将以interval启动时的节点位置作为起始位置。注意,那样的话你必须提供duration。)
  • startPos, endPos, duration——在duration指定的时间内(秒)从startPos移动到endPos
  • startPos, startVel, duration——给定一个起始速度,在一定时间内运动
  • startPos, startVel, endZ——给定一个起始速度,直到碰到给定的Z平面才停止运动
此外,你还可以通过‘gravityMult’参数提供一个因子来改变重力的大小和方向,‘2’将使重力增大为原来的2倍,‘.5’使重力减半,‘-1’ 倒转重力的方向。
下面用一小段代码说明抛射interval:
camera.setPos(0,-45,0) 

# 载入球模型
self.ball = loader.loadModel("smiley") 
self.ball.reparentTo(render) 
self.ball.setPos(-15,0,0) 

#设置抛射interval 

self.trajectory = ProjectileInterval(self.ball, startPos = Point3(-15,0,0), 
  endPos = Point3(15,0, 0), duration = 1) 
self.trajectory.loop()
 
原创粉丝点击