Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Ping pong between two points?

Alexander Yeats
Registered User
Join date: 8 Sep 2005
Posts: 188
09-14-2005 17:48
I just want to do a simple script (so I thought).

Point a and point B are the targets. I would like an object to go to point A, when it gets there turn 180, and go to point B, get there 180, etc ad naseum....

Any ideas on a direction to take?

I have tried sensors, but you can't have two sensors for an object. Though I suppose I could switch sensors when the object gets close enuf to the first one?

I am also unsure how to get the full 180 rotation. Omega is great, but I dont know how to time it correctly.

I have done a loop with a time step using LLsetRot and GetRot, but that is just not working correctly.

Any help is appreciated.
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
09-14-2005 18:26
This should work...

CODE


vector pos1; //Put the first position here
vector pos2; //Put the second position here

rotation rot1; //Put the first rotation here
rotation rot2; //Put the second rotation here

integer status; //0 -- At position one, not facing right direction
//1 -- At position one, facing right direction
//2 -- At position two, not facing right direction
//3 -- At position two, facing right direction

doNext()
{
vector nextPos;
rotation nextRot;

if ( status == 0 )
nextRot = rot1;
else if ( status == 2 )
nextRot = rot2;
else
jump @doRot;

llRotTarget( nextRot, 0.05 );
llRotLookAt( nextRot, 0.5, 0.5 ); //Tweak this if it turns too fast
return;

@doRot;

if ( status == 1 )
nextPos = pos1;
else if ( status == 3 )
nextPos = pos2;
else
return;

llTarget( nextPos, 0.05 );
llMoveToTarget( nextPos, 0.5 ); //Tweak this if it moves
return;
}

default
{
state_entry()
{
llSetStatus( STATUS_PHYSICS, FALSE );

while ( llVecDist( llGetPos(), pos1 ) > .001 )
llSetPos( pos1 );
llSetRot( rot1 );
llSetPos ( pos1 );

status = 1;

llMoveToTarget( pos1, 0.1 );
llRotLookAt( rot1, .5, .5 ); //Tweak this if it turns too fast

llSetStatus( STATUS_PHYSICS | STATUS_PHANTOM, TRUE );

doNext();
}

at_target( integer num, vector targetPos, vector ourPos )
{
if ( targetPos == pos1 )
{
status = 0;
doNext();
} else if ( targetPos == pos2 )
{
status = 3;
doNext();
}
}

at_rot_target( integer num, rotation targetRot, rotation ourRot )
{
if ( targetRot == rot1 )
{
status = 1;
doNext();
} else if ( targetPos == pos2 )
{
status = 4;
doNext();
}
}
}
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
states
09-15-2005 02:36
Keknehv: wouldn't this be a bit cleaner with 4 seperate states?
Online Doesburg
absurd hero
Join date: 6 Jul 2005
Posts: 53
09-15-2005 07:19
You also might want to put comparison operators ( == ) in the 'if' statement conditions, not assignment operators ( = ).
Alexander Yeats
Registered User
Join date: 8 Sep 2005
Posts: 188
09-15-2005 07:19
4 states or not, you rock!


I had accomplished this after I posted by using a single point and a range with no physics, but the updating it takes (looped and with timer) makes the motion way too choppy.

This is a much better implementation. Thank you very much.
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
09-15-2005 16:32
From: Ben Bacon
Keknehv: wouldn't this be a bit cleaner with 4 seperate states?


I really don't see how you would separate it into 4 different states. I think it's quite clean how it is.

From: Online Doesburg
You also might want to put comparison operators ( == ) in the 'if' statement conditions, not assignment operators ( = ).


Woops, missed that XD. I've done too much VB recently. (Stupid Excel...)

Just bear in mind that I wrote this in less than 10 minutes, and I didn't test it, so don't be too hard on me... Of course, excuses aren't good...
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
09-19-2005 05:26
From: Keknehv Psaltery
Just bear in mind that I wrote this in less than 10 minutes, and I didn't test it, so don't be too hard on me...

:) considering you went to the effort just to help out some stranger for absolutely no reward, you've earned the right for us to go easy on you.
In that spirit, I am not about to attack your code, but I also want to help out other scripters, using your code as an example.
From: Keknehv Psaltery
I really don't see how you would separate it into 4 different states. I think it's quite clean how it is.
Many of us come from languages that do not inherently have Finite State Machine states. In these languages we often end up using state variables to remember what state our machine is in, and large state functions with with big switch/case blocks to handle events.
The status global variable in the above code is such a state variable with the doNext function as the state function. LSL, however, has native support for states (in fact, is built on them) and although there are exceptions (see States vs. Global Variables ) it is usually a good idea to use them.
So I was thinking something like:
CODE
// DON'T USE THIS CODE
// IT, TOO, IS BROKEN
vector Pos1; //Put the first position here
vector Pos2; //Put the second position here

rotation Position2Rotation(vector Target)
{
return llGetRot() * llRotBetween(<1,0,0> * llGetRot(), Target - llGetPos());
}

default
{
state_entry()
{
// stuff like saying hello and setting up listens or touches to start ping-pong
// or just start automatically ....
state PointToA;
}
}

state PointToA
{
state_entry()
{
rotation LookTarget = Position2Rotation(Pos1);
llRotTarget(LookTarget, 0.05);
llRotLookAt(LookTarget, 0,5, 0,5);
}

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
{
state MoveToA;
}
}

state MoveToA
{
state_entry()
{
llTarget(Pos1, 0.05);
llMoveToTarget(Pos1, 0.5);
}

at_target(integer tnum, vector targetpos, vector ourpos)
{
state PointToB;
}
}

state PointToB
{
state_entry()
{
rotation LookTarget = Position2Rotation(Pos2);
llRotTarget(LookTarget, 0.05);
llRotLookAt(LookTarget, 0,5, 0,5);
}

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
{
state MoveToB;
}
}

state MoveToB
{
state_entry()
{
llTarget(Pos2, 0.05);
llMoveToTarget(Pos2, 0.5);
}

at_target(integer tnum, vector targetpos, vector ourpos)
{
state PointToA;
}
}


That was the reason I initially suggested 4 states, and even though I have now changed my mind about that, I think it is a good illustrative example. I changed my mind when I realised it could be done just as cleanly with 2 states:
CODE
// DON"T USE THIS ONE EITHER
vector Pos1; //Put the first position here
vector Pos2; //Put the second position here

rotation Position2Rotation(vector Target)
{
return llGetRot() * llRotBetween(<1,0,0> * llGetRot(), Target - llGetPos());
}

default
{
state_entry()
{
// stuff like saying hello and setting up listens or touches to start ping-pong
// or just start automatically ....
state GoToA;
}
}

state GoToA
{
state_entry()
{
rotation LookTarget = Position2Rotation(Pos1);
llRotTarget(LookTarget, 0.05);
llRotLookAt(LookTarget, 0,5, 0,5);
}

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
{
llTarget(Pos1, 0.05);
llMoveToTarget(Pos1, 0.5);
}

at_target(integer tnum, vector targetpos, vector ourpos)
{
state GoToB;
}
}

state GoToB
{
state_entry()
{
rotation LookTarget = Position2Rotation(Pos2);
llRotTarget(LookTarget, 0.05);
llRotLookAt(LookTarget, 0,5, 0,5);
}

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
{
llTarget(Pos2, 0.05);
llMoveToTarget(Pos2, 0.5);
}

at_target(integer tnum, vector targetpos, vector ourpos)
{
state GoToA;
}
}

But I found a problem. I had been assuming that just like llListen, llTarget and llRotTarget would be removed by a state change. And I think Keknehv was under the impression that they would be removed when their at_targets were called. When I played around with this over the weekend, I found that these target functions are only removed by their corresponding remove functions, or by resetting the script. Which means that both of my attempts, as well as Keknehv's, just pile up the targets. Each time you visit point A or point B, the script adds another llRotTarget and llTarget, and none of them are ever removed. Each time you reach the target, multiple events get fired off, more and more events each time.

Does this make sense? Shouldn't these callbacks be state-bound? How many other scripts out there do not remove their targets, thinking they'll be cleaned up either when they fire the first time, or when the state is exited?
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
09-19-2005 08:30
It's not that hard to handle - the first thing you do when entering an at_target or at_rot_target is remove the target. As long as you're careful of having only one active target, you don't even need to save the handle when you created the target, just remove the handle that came in with the at_xxx event.

Actually, that's the second thing you do - the first is to remove/apply forces to make sure you stopped moving :) I've seen one instance where my object kept moving after I removed the target - I'm assuming the removeTarget call happened before the physics engine could clamp my object down to its target. Or it may have been server lag glitching something. Either way, I play it safe now.

From: someone
Shouldn't these callbacks be state-bound?


Depends on the effect you're trying to achieve. I can see a low-damped object that can be moved out of its target by external forces, that returns slowly back to the target. That would be a situation where you'd want the event notification to do stuff when you were at the target, but would also want the target to persist.
Pol Tabla
synthpop saint
Join date: 18 Dec 2003
Posts: 1,041
09-19-2005 10:03
Could this code be adapted to create a "vehicle" that always followed a predetermined course?
_____________________
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
09-19-2005 10:12
I built an automated 'vehicle' that does exactly that :) Mine is non-physics, so the actual movement code is different. But the basic logic is the same - find the next waypoint, turn to face it, move towards it, once you've reached it, find the next waypoint, repeat. My 'vehicle' is intended to be a shuttle, so it has some other gravy - it can stop at certain waypoints (where it'll turn to line up with the marker), announce "You have arrived at...", and so on... but that's all frills, the core mechanism works very similarly to what's here.
Pol Tabla
synthpop saint
Join date: 18 Dec 2003
Posts: 1,041
09-19-2005 10:17
Ziggy, that sounds very much like something I've been looking for. May I contact you in-world about this sometime?
_____________________
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
09-19-2005 10:37
Sure. This isn't really the forum for talking about *cough* advertising *cough* one's projects anyway :)
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
09-19-2005 15:20
From: Keknehv Psaltery
This should work...

I still prefer my way, even though I see how it could be done using separate states. The state_entry event would be fired on each iteration, unless you had another variable. I'm probably just being stubborn, though.

From: Ben Bacon

But I found a problem. I had been assuming that just like llListen, llTarget and llRotTarget would be removed by a state change. And I think Keknehv was under the impression that they would be removed when their at_targets were called. When I played around with this over the weekend, I found that these target functions are only removed by their corresponding remove functions, or by resetting the script. Which means that both of my attempts, as well as Keknehv's, just pile up the targets. Each time you visit point A or point B, the script adds another llRotTarget and llTarget, and none of them are ever removed. Each time you reach the target, multiple events get fired off, more and more events each time.

Does this make sense? Shouldn't these callbacks be state-bound? How many other scripts out there do not remove their targets, thinking they'll be cleaned up either when they fire the first time, or when the state is exited?


Ah, I couldn't remember that bit. Well, it's not terribly difficult to add code to make them remove targets...

CODE

vector pos1; //Put the first position here
vector pos2; //Put the second position here

rotation rot1; //Put the first rotation here
rotation rot2; //Put the second rotation here

integer currentTarget; //The handle of the current target or rot_target

integer status; //0 -- At position one, not facing right direction
//1 -- At position one, facing right direction
//2 -- At position two, not facing right direction
//3 -- At position two, facing right direction

doNext()
{
vector nextPos;
rotation nextRot;

if ( status == 0 )
nextRot = rot1;
else if ( status == 2 )
nextRot = rot2;
else
jump @doRot;

currentTarget = llRotTarget( nextRot, 0.05 );
llRotLookAt( nextRot, 0.5, 0.5 ); //Tweak this if it turns too fast
return;

@doRot;

if ( status == 1 )
nextPos = pos1;
else if ( status == 3 )
nextPos = pos2;
else
return;

currentTarget = llTarget( nextPos, 0.05 );
llMoveToTarget( nextPos, 0.5 ); //Tweak this if it moves
return;
}

default
{
state_entry()
{
llSetStatus( STATUS_PHYSICS, FALSE );

while ( llVecDist( llGetPos(), pos1 ) > .001 )
llSetPos( pos1 );
llSetRot( rot1 );
llSetPos ( pos1 );

status = 1;

llMoveToTarget( pos1, 0.1 );
llRotLookAt( rot1, .5, .5 ); //Tweak this if it turns too fast

llSetStatus( STATUS_PHYSICS | STATUS_PHANTOM, TRUE );

doNext();
}

at_target( integer num, vector targetPos, vector ourPos )
{
llTargetRemove( currentTarget );
if ( targetPos == pos1 )
{
status = 0;
doNext();
} else if ( targetPos == pos2 )
{
status = 3;
doNext();
}
}

at_rot_target( integer num, rotation targetRot, rotation ourRot )
{
llRotTargetRemove( currentTarget );
if ( targetRot == rot1 )
{
status = 1;
doNext();
} else if ( targetPos == pos2 )
{
status = 4;
doNext();
}
}
}


At least, I *think* that's what you were talking about.
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
09-20-2005 02:30
From: Keknehv Psaltery
At least, I *think* that's what you were talking about.
Yeah, that's it.

As far as making it state-bound is concerned (when that behaviour is appropriate), I guess the good old state_exit event makes a good "destructor" to state_entry's "constructor" (Resource Aquisition is Initialisation, anyone?)
Owner Maltese
Registered User
Join date: 13 Sep 2005
Posts: 65
How about something far simpler?
09-20-2005 07:00
I just want something that moves back and forth within 0.5 - 1 meters. No rotation, but able to adjust the speed. This item is a child in an object.

I've tried My own code but it just sits there, doesn't move and stares at Me, I've also tried to fiddle with the above code but I'm completely confused about that. (Which is not hard to do, admittingly...), Also, the damn touch button greys out on Me again whenever I add a code, what's up with that?

This is the code I tried on My own before looking at this tread.

CODE

vector whereamI;
vector Imgoingwhere;
vector Imnowat;
integer attarget;

default
{
state_entry()
{
attarget = 0;
whereamI = llGetLocalPos();
Imgoingwhere = whereamI + <0,0,0.4>;
}

touch_start(integer total_number)
{
llSay(0, (string)Imnowat);
if (attarget == 0)
{
llMoveToTarget(Imgoingwhere,0.1);
attarget = 1;
Imnowat = llGetLocalPos();
}
else
{
llMoveToTarget(whereamI,0.1);
attarget = 0;
Imnowat = llGetLocalPos();
}
}
}


I know this is real primative, eventually it will move on it's own back and forth, but I'm just trying to get the bloody thing to move in the first place.

Thanks.
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
09-20-2005 07:38
Owner: Is your prim physical? I believe llMoveToTarget only works on phys prims. Have you tried llSetPos instead?
Consider:
CODE

vector PositionA;
vector PositionB;

state default
{
state_entry()
{
PositionA = llGetLocalPos();
PositionB = PositionA+ <0, 0, 0.4>;
state GotoA;
}
}

state GotoA
{
state_entry()
{
llSetPos(PositionA);
}

touch_start(integer total_number)
{
state GotoB;
}
}

state GotoB
{
state_entry()
{
llSetPos(PositionB);
}

touch_start(integer total_number)
{
state GotoA;
}
}
Alexander Yeats
Registered User
Join date: 8 Sep 2005
Posts: 188
09-20-2005 08:51
From: Ben Bacon
I guess the good old state_exit event makes a good "destructor" to state_entry's "constructor" (Resource Aquisition is Initialisation, anyone?)


Ahh its like the joys of coding in a non garbage collected language...

I never thought I would have to also garbage collect states ! LOL
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
09-20-2005 09:32
From: Alexander Yeats
Ahh its like the joys of coding in a non garbage collected language...

I never thought I would have to also garbage collect states ! LOL

LSL# ??? LSL.NET ??? :D
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
09-20-2005 16:38
From: Ben Bacon
LSL# ??? LSL.NET ??? :D


The answer to all these problems is Assembly! You won't need garbage collection after you have been enlightened with this...
Alexander Yeats
Registered User
Join date: 8 Sep 2005
Posts: 188
09-21-2005 08:52
From: Keknehv Psaltery
The answer to all these problems is Assembly! You won't need garbage collection after you have been enlightened with this...


No doubt. But even then memory corruption is not hard to do with the random pointer in the wrong place :o
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
09-21-2005 19:18
This is why we work in protected mode systems that bind and gag anything that oversteps its boundaries (not Windows). With windoze, all sorts of interesting effects can happen with buggy assembly-- such as a locked up computer. Linux does not experience this problem.
Kala Bijoux
Material Squirrel
Join date: 16 Nov 2004
Posts: 112
09-21-2005 19:51
Unless you're writing device drivers or other kernel stuff, and then you can have all sorts of fun again.