LlSetKeyframedMotion

来自人工智能助力教育知识百科
跳转至: 导航搜索

Template:Needs Translation/


函数名
Function: llSetKeyframedMotion( list keyframes, list options );
参数:
  • list keyframes  -  表格关键帧列表:
                        向量位置(可通过KFM_TRANSLATIONKFM_DATA实现)
                        旋转方向(通过KFM_ROTATIONKFM_DATA可选)
                        浮动时间
                     每个关键帧都是相对于对象之前的变换进行解释的。例如,考虑以下关键帧列表:[<0,0,10>,ZERO_ROTATION, 5, <0, 0, 0>, ZERO_ROTATION, 5, <0, 0, -10>, ZERO_ROTATION, 5]。这将导致物体在5s的过程中向上移动10m。在接下来的5秒中,它会在这个位置停留5秒,然后向下移动10米。时间值必须是1/9。或更高版本。线速度和角速度将被限制在模拟器设置的限制(值TBD)。空列表将终止任何当前正在播放的键框动画。
 • list options - 修饰符和未来选项
返回值:指定一个对象后面要跟随的时间、位置和方向列表。对象将在模拟器的关键帧之间平滑移动。与其他非物理或关键帧对象的冲突将被忽略(不会触发任何脚本事件,也不会发生冲突处理)。与物理对象的碰撞将被计算和报告,但是关键帧对象将不受这些碰撞的影响。(不过,物理对象将受到影响。)
注意事项
Specification
 选项列表将支持以下标志:
   KFM_MODE后面跟着一个:KFM_LOOP, KFM_REVERSE, KFM_FORWARD,或KFM_PING_PONG将指定播放模式。默认为KFM_FORWARD。在提供关键帧列表时必须指定。
   KFM_DATA后面是按位组合的:KFM_TRANSLATIONKFM_ROTATION。默认情况下,旋转和平移都必须提供。如果您指定了其中一个或另一个,您应该只在关键帧列表中包含转换或旋转。必须在提供关键帧列表时指定。
   KFM_CMD_STOP, KFM_CMD_PLAY, KFM_CMD_PAUSE。STOP将暂停动画并将其重置到开始位置。PAUSE将在不重置的情况下暂停动画。播放将恢复暂停或停止的动画。
 注意,如果在选项列表中提供了KFM_COMMAND,那么它必须是列表中唯一的选项,并且不能在设置关键帧列表的同一个函数调用中指定。
 支持区域间的移动;只要指定一个目标关键帧,它将把对象放在sim边界之外,它就会跨越。这似乎提供了相同的效果,暂停动画时,它是在中间的两个区域,但观众继续插值它的运动。它会跳回sim边界,并继续以相同的速度前进,尽管路径点之间的时间会因为sim的穿越而增加。确保将这一点考虑到系统中。
 由于键框动作是一个prim属性,而不是局限于调用它的脚本,您可以使用多个脚本来控制、暂停或重新启动相同的键框动作,而不需要传递提供的原始关键帧列表。只需传递一个空的关键帧列表和KFM_COMMAND,后面跟着选项列表中的命令。

此功能在附件中不起作用。

这个函数只能在非物理对象上调用。在未来,它可能会扩展到支持物理对象,但这更加复杂,因为碰撞可能会阻止物体准时到达目标位置。

当关键帧运动处于活动状态时,您不能使用脚本来移动linkset中的任何prim。如果你必须,首先暂停和重新启动KFM_CMD_PLAY时,完成。#例子

与化身的碰撞会影响物体的角度运动,它可能无法到达最终的旋转。

这个函数只能在链接集的根prim上调用。

这个函数要求链接集使用Prim等价系统。然而,它的关键帧对象将不会受到动力学惩罚,并可以有一个物理重量高达64。

llSetKeyframedMotion是根据帧来实现的,而不是实时的。为了避免偏离预期的位置和旋转,使用1/45的整数倍数的时间,例如20.0/45.0,40.0/45.0,90.0/45.0,等等。论坛线程KFM声称增量时间必须大于0.1秒;但是在实践中,delta时间稍微超过0.1就会在DEBUG_CHANNEL上产生错误。测试显示最小的增量时间大约是6/45(.13333)秒。

 自然漂移仍然可能发生;为了获得最佳效果,使用moving_end来确定动画何时结束,并使用llSetPos来确认目标位置。或者您可以使用at_target或at_rot_target。

化身动画系统有一些缺陷,当你站在移动平台上时,可能会产生一些奇怪的动画(例如,在原地行走,脚对着骨盆)。我们希望在将来修复这些问题,但是这样做超出了该特性的范围。

与动态对象一样,使用此函数移动的对象被具有适当权限的化身(对象所有者、乘客等)选中时将暂停。当这样一个化身取消选择对象时,运动恢复,即使对象已经被KFM_CMD_PAUSE暂停。

关键帧运动在某些方面是一种prim性质。当KFM_LOOP或KFM_PING_PONG被启动时,该动作在脚本被删除后被保留。也就是说,prim将继续它的运动。它会生存下来,获取和复制。它将在服务器重启后继续存在。

区域间的流动远非完美。使用一个在弯曲路径上有大量帧的关键帧列表从sim 1交叉到sim 2会在交叉时出现明显的错误,当用[KFM_MODE, KFM_REVERSE]反转时,对象会留在sim 2中,永远不会返回到sim 1

示例
//如果你的客户不是网格感知,请使用以下代码:
llSetLinkPrimitiveParamsFast(LINK_THIS,
    [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
llSetKeyframedMotion(
    [<0.0, 0.0, 10.0>,  5,
        <0.0, 0.0, -10.0>,  5],
    [KFM_DATA, KFM_TRANSLATION, KFM_MODE, KFM_PING_PONG]);
llSetKeyframedMotion(
    [<0.0, 0.0, 10.0>, llEuler2Rot(<90, 45, 180> * DEG_TO_RAD), 5,
        <0.0, 0.0, -10.0>, llEuler2Rot(<270, 225, 360> * DEG_TO_RAD), 5],
    [KFM_MODE, KFM_REVERSE]);

Pause-Play

llSetKeyframedMotion([],[KFM_COMMAND, KFM_CMD_PAUSE]);
//修改这里的prim位置
llSetKeyframedMotion([],[KFM_COMMAND, KFM_CMD_PLAY]);

Sample Script - Key Framed Follower

在某些情况下,旋转会产生运行时错误,除非它们被归一化。这个脚本演示了使用规范化目标旋转使用llKeyframedMotion创建追随者的方法——例如,考虑一下一辆车后面的一辆车。

integer isSensorRepeatOn;
 
rotation NormRot(rotation Q)
{
    float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s);
 
    return
        <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>;
}
 
default
{
    state_entry()
    {
        llSetLinkPrimitiveParamsFast(LINK_ROOT,
                [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX,
            PRIM_LINK_TARGET, LINK_ALL_CHILDREN,
                PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
    }
 
    touch_start(integer total_number)
    {
        isSensorRepeatOn = !isSensorRepeatOn;
 
        if (isSensorRepeatOn)
        {
        //  PUBLIC_CHANNEL has the integer value 0
            llSay(PUBLIC_CHANNEL, "Sensor repeat switched on!");
 
            llSensorRepeat("", "91b39b5b-13b1-2517-273a-67360b842c02", SCRIPTED, 10.0, PI, 0.1);
        }
        else
//      {
            llSensorRemove();
//      }
    }
 
    sensor(integer num_detected)
    {
        vector ownPosition = llGetPos();
        rotation ownRotation = llGetRot();
        vector detectedPosition = llDetectedPos(0);
        rotation detectedRotation = llDetectedRot(0);
 
        llSetKeyframedMotion(
            [(detectedPosition - ownPosition) + <-1.0, 0.0, 0.2>*detectedRotation,
            NormRot(detectedRotation/ownRotation), 0.12],
            []);
    }
}

Universal Hinged Motion in 8 Key Frames

该脚本将把一个盒子沿平行于该盒子y轴的一条边旋转

该脚本适用于任何prim方向

注意,每帧接受的最小时间是1/9S=0.11111111S,而不是0.1S

float angleEnd=PI_BY_TWO;
float speed=0.2; // m/S
float steps=8.0; // number of Key Frames
float step=0.0;
list KFMlist=[];
vector V;
integer open=TRUE;
vector basePos;
rotation baseRot;
 
float motion_time( float mt)
{
    mt = llRound(45.0*mt)/45.0;
    if ( mt > 0.11111111 ) return mt;
    else return 0.11111111;
}
 
default
{
    state_entry()
    {
        llSetMemoryLimit(0x2000);
        llSetPrimitiveParams([PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
        basePos = llGetPos();
        baseRot = llGetRot();
        vector v1 = 0.5*llGetScale()*llGetRot();
        rotation deltaRot = llEuler2Rot(< 0.0, angleEnd/steps, 0.0>);
        while ( step < steps )
        {
            V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleEnd*step/steps);
            V = v1*llAxisAngle2Rot(llRot2Left(llGetRot()), angleEnd*(step+1.0)/steps) - V;
            KFMlist += [V, deltaRot, motion_time(llVecMag(V)/speed)];
            step += 1.0;
        }
    }
    touch_end( integer n)
    {
        llSetKeyframedMotion( [], []);
        if ( open )
        {
            llSetPrimitiveParams([PRIM_POSITION, basePos, PRIM_ROTATION, baseRot]);
            llSetKeyframedMotion( KFMlist, []);
        }
        else
        {
            llSetKeyframedMotion( KFMlist, [KFM_MODE, KFM_REVERSE]);
        }
        open = !open;
    }
    on_rez( integer n) { llResetScript(); }
}

在编辑了prim位置,旋转和/或大小脚本应该重置,以更新运动

Continuous Spin

下面的例子所做的事情与使用llTargetOmega(<0.0,0.0,1.0>,PI,1.0)使一个prim围绕z轴连续旋转相同,假设prim被设置为凸壳并且是非物理的。

integer gON;
 
default
{
    touch_start(integer total_number)
    {
        gON = !gON;
        if (gON)
        {
            // Make repeated rotations of PI/2 radians, each taking 0.5 seconds
            llSetKeyframedMotion([llEuler2Rot(<0.0,0.0,PI/2>), 0.5],[KFM_DATA,KFM_ROTATION, KFM_MODE,KFM_LOOP]);
        }
        else
        {
            llSetKeyframedMotion([],[]);
        }
    }
}
相关函数
相关事件

附录:(表格)

options flags Additional Parameters Description
[ KFM_COMMAND ] 0 [ integer command ] 设置命令
KFM_CMD_PLAY 0 ?
KFM_CMD_STOP 1 ?
KFM_CMD_PAUSE 2 ?
[ KFM_MODE ] 1 [ integer mode ] 设置回放模式 默认为KFM_FORWARD
KFM_FORWARD 0 ?
KFM_LOOP 1 ?
KFM_PING_PONG 2 ?
KFM_REVERSE 3 ?
[ KFM_DATA ] 2 [ integer fields ] 设置关键帧包含什么数据 (默认为(KFM_ROTATION | KFM_TRANSLATION)
KFM_ROTATION 0x1 ?
KFM_TRANSLATION 0x2 ?