- Joined
- Sep 20, 2018
- Messages
- 4,055
Keyframe based Mover
Intrasim mover between two arbitrary points in a sim
Setup:
rez a Mover base in one location, then some distance away rez a 2nd Mover base.
Near one of the bases, rez the mover. It should automatically position itself on top of the base.
Both bases must be rezzed before the mover is rezzed. Odd behavior may result.
Usage:
sit in the mover, and enjoy the ride to the other location. If the mover is not at your current base, click on the base to summon it.
notes:
1. The current implementation works with precisely two bases in the sim, no more, no less. This is a code sample for a functioning keyframe based mover.
EDIT: removed legacy code
Base code (the mover will rest on top of this)
kfm Mover code
script _control
Script keyframe (perform the actual keyframe actions)
Intrasim mover between two arbitrary points in a sim
Setup:
rez a Mover base in one location, then some distance away rez a 2nd Mover base.
Near one of the bases, rez the mover. It should automatically position itself on top of the base.
Both bases must be rezzed before the mover is rezzed. Odd behavior may result.
Usage:
sit in the mover, and enjoy the ride to the other location. If the mover is not at your current base, click on the base to summon it.
notes:
1. The current implementation works with precisely two bases in the sim, no more, no less. This is a code sample for a functioning keyframe based mover.
EDIT: removed legacy code
Base code (the mover will rest on top of this)
Code:
string _me = "kf mover base";
integer _ch_owner;
init()
{
_ch_owner =
(integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1));
llListen(_ch_owner,"","","");
}
default
{
state_entry()
{
init();
}
touch_end(integer num_param)
{
llRegionSay(_ch_owner, _me+"|kf mover control|go");
}
listen(integer ch, string name, key id, string msg)
{
if (ch == _ch_owner) {
list lMsg = llParseString2List(msg, ["|"], []);
string src = llList2String(lMsg,0);
string tgt = llList2String(lMsg,1);
string cmd = llList2String(lMsg,2);
string arg = llList2String(lMsg,3);
if ((tgt == _me) || (tgt == "all")) {
if (cmd == "request") {
if (arg == "pos") {
llRegionSay(_ch_owner,
_me+"|"+src+"|response|"+arg+"|"
+(string)llGetPos()
);
}
}
}
}
}
}
script _control
Code:
string _me = "kf mover control";
integer _ch_owner; // unique ch is created based on the owner's UUID
//list links = [];
list bases = [];
string timer_context;
vector vStart;
vector vDest;
key kUser;
integer tries;
integer timeout = FALSE;
// ask the two bases for the kf mover to transmit their positions
// this is picked up again in the listen event for _ch_owner
request_base_pos()
{
bases = [];
timer_context == "polling";
if (!timeout) {
llSetTimerEvent(0.5);
llRegionSay(
_ch_owner,
_me+"|kf mover base|request|pos"
);
}
}
init()
{
// set channel number
_ch_owner =
(integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1));
llListen(_ch_owner,"","","");
// set pos
llSitTarget(<0,0,0.8>,llEuler2Rot(<0,0,0>*DEG_TO_RAD));
kUser = NULL_KEY;
// keyframe requres PHYSICS_SHAPE_CONVEX
llSetLinkPrimitiveParamsFast(
LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]
);
timer_context = "polling";
llSetTimerEvent(0.5);
request_base_pos();
}
lookAt(vector vLookAt)
{
vector dir;
rotation r;
// ... and calculate rotation for prim/object
dir = llVecNorm(vLookAt - llGetPos());
r = clamp_to_Z(llRotBetween(<1.0, 0.0, 0.0>, dir));
llSetRot(r);
}
// clamp rotation to Z axis only
rotation clamp_to_Z(rotation r)
{
vector v = llRot2Euler(r)*RAD_TO_DEG;
v.x = 0;
v.y = 0;
return(llEuler2Rot(v*DEG_TO_RAD));
}
// move the keyframe object
go()
{
lookAt(vDest);
llMessageLinked(LINK_SET, 0, _me+"|kf mover keyframe|go",NULL_KEY);
}
default
{
on_rez(integer start_param)
{
init();
}
state_entry()
{
init();
}
changed(integer change) {
if (llAvatarOnSitTarget() != NULL_KEY) {
kUser = llAvatarOnSitTarget();
llRequestPermissions(
llAvatarOnSitTarget(), PERMISSION_TRIGGER_ANIMATION
);
}
}
run_time_permissions(integer perm) {
string anim = llGetInventoryName(INVENTORY_ANIMATION, 0);
if (anim != "") {
llStopAnimation("sit");
llStopAnimation("sit_generic");
llStopAnimation("sit_female");
llStartAnimation(anim);
go();
}
}
moving_end()
{
// refresh base info so a new start & dest can be determined.
llSetPos(vDest);
llUnSit(kUser);
request_base_pos();
}
listen(integer ch, string name, key id, string msg)
{
// tokenize and process link message parameters
if (ch == _ch_owner) {
list lMsg = llParseString2List(msg, ["|"], []);
string src = llList2String(lMsg,0);
string tgt = llList2String(lMsg,1);
string cmd = llList2String(lMsg,2);
string arg = llList2String(lMsg,3);
if ((tgt == _me) || (tgt == "all")) {
if (cmd == "go") {
go();
}
if (cmd == "response") {
if (arg == "pos") {
vector v = (vector)llList2String(lMsg,4);
bases += [v];
}
}
}
}
}
timer()
{
vector pos = llGetPos();
integer i;
vector v;
llSetTimerEvent(0.0);
timer_context = "";
// After a half second delay to receive and store base pos,
// record which base is closest, and move to sit on that base.
if (llGetListLength(bases) != 2) {
llOwnerSay("** Not enough bases: 2 are requred **");
}
for (i=0; i<llGetListLength(bases); i++) {
v=llList2Vector(bases, i);
if (i == 0) vStart = v;
else {
if (llVecDist(v, pos) < llVecDist(vStart, pos)) {
vStart = v;
}
}
}
llSetPos(vStart);
// now determine the other base (assuming it is > 1 meter away)
for (i=0; i<llGetListLength(bases); i++) {
v = llList2Vector(bases, i);
if (llVecDist(v, pos) > 1) vDest = v;
}
// set start and dest keyframes
llMessageLinked(LINK_SET, 0, _me+"|kf mover keyframe|setStart|"
+(string)vStart, NULL_KEY
);
llMessageLinked(LINK_SET, 0, _me+"|kf mover keyframe|setDest|"
+(string)(vDest), NULL_KEY
);
}
}
Code:
string _me = "kf mover keyframe";
integer status=0;
vector vStart;
vector vDest;
vector vMove;
float fTransit;
list options=[
KFM_DATA, KFM_TRANSLATION
];
init()
{
// clear & stop previous keyframe movement
// mention of these steps are not clearly stated in documentation
llSetKeyframedMotion([], []);
llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
}
default {
state_entry() {
init();
}
touch_start(integer total_number) {
}
link_message(integer source, integer num, string msg, key id)
{
//tokenize and process link messages
list lMsg = llParseString2List(msg, ["|"], []);
string src = llList2String(lMsg,0);
string tgt = llList2String(lMsg,1);
string cmd = llList2String(lMsg,2);
string arg = llList2String(lMsg,3);
if ((tgt == _me) || (tgt == "all")) {
if (cmd == "setStart") {
vStart = (vector)arg;
}
else if (cmd == "setDest") {
vDest = (vector)arg;
vMove = vDest-vStart;
fTransit = llVecDist(vStart, vMove)/100;
}
else if (cmd == "go") {
llSetKeyframedMotion([], []);
llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_STOP]);
llSetKeyframedMotion(
[vMove, 5], [KFM_DATA, KFM_TRANSLATION]
);
}
}
}
}
Last edited:
