Jump to content

You are moonwalking. Everyone is...


CaithLynnSayes
 Share

You are about to reply to a thread that has been inactive for 678 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

So i have made this script for my own avatar that makes heel clicking sounds. I know i know, there are about 53 billion of those around. Mine is a tad different. It uses llCastRay to know if i'm walking on grass (aka Linden Land) and in that case it's silent. Why would heels click on grass, right?:) It also has a volume control. So just a tiny hud with 2 sliders. Sound volume and speed of the clicks. I put it up for next to nothing on MP a couple of years ago and to my surprise it sells like hot cakes. Still does to this day.

 

Now, I am using a seperate script on myself that i wrote to slow me down. When you analyze the default avatar walk (AO off) you'll have what is known as the "duck walk". Everyone hates it, but i find it interesting in how it works. I've been chasing it and trying to understand it. It seems to match your avatar speed. In other words, you're not moonwalking with it. So the script i wrote is fairly easy and goes a little something like this: I take the controls of the avatar and i have a vector called V that has the value vector V = llGetPos() + <-targetspd,0,0> * llGetRot() * llEuler2Rot(<0,0,0.0>*DEG_TO_RAD); After that i simply llMoveToTarget(V,tau);

 

This method works fine for me personally. I've also added a small hud to it with a slider, so i can fine tune the speed of the avatar with a selected third party walk animation from an AO so my avatar will walk the speed the animation was mocap'ped at or made. the only real issue with this is that llMoveToTarget slowed down isn't really happy on elevations, or uphill slopes i should say. I can't do stairs all that well using this script, my avatar and its camera will bounce and the avatar will jump and eventually get in it"s running animation because (i assume) the viewer isn't sure what's going on.

 

Now, Open Sim has a better solution for this and an actual function called osSetSpeed. It works well enough on flat surfaces but again, isn't great at uphill slopes. I have the hardest time getting up the stairs, though without the side effects SL does. (bouncing)

 

I was thinking to add a timer to constantly monitor my current altitude when walking and compare it with that from a second ago and if i'm currently higher than a second ago, to increase my avatar's speed but that might be adding to lag. I'm really almost obsessively aware of not trying to cause lag.

 

So, i'd like to add this "speed control" to my heel sound thingy and add this extra feature to it, but i need to find a way to deal with slopes. As of now i'm a little stuck. Would any of you have a viable solution for such a thing?

 

Thanks, Caith.

  • Thanks 1
Link to comment
Share on other sites

For controlling movement, I personally would recommend llSetForce() rather than llMoveToTarget(). It doesn't work if the avatar is in flying mode however. If you're already using raycast, you can use that to get the normal of the object beneath the avatar. If the rotation of the avatar is 'myRot', and the normal of the walking surface is 'normal', then your direction of travel should be (untested):

vector up = llRot2Up(myRot);
vector dirOfTravel = llRot2Fwd(myRot)*llRotBetween(normal,up);

which you can use to set your movement force in that direction while the forward key is pressed.

(the direction of travel when moving left would be llVecNorm(normal%dirOfTravel); or just llRot2Left(myRot)*llRotBetween(normal,up);)

2 hours ago, CaithLynnSayes said:

I was thinking to add a timer to constantly monitor my current altitude when walking and compare it with that from a second ago and if i'm currently higher than a second ago, to increase my avatar's speed but that might be adding to lag. I'm really almost obsessively aware of not trying to cause lag.

I dislike fast timers as well, but it's not so bad when you realize that a control event handler fires 45 times per second. (equivalent to a timer of about 0.022)

Edited by Quistess Alpha
  • Thanks 1
Link to comment
Share on other sites

The 'Duck Walk' doesn't actually do any modification of your avatar's walk speed, instead it uses inverse kinematics and some more advanced animation tools to modify the playing animation so that your feet move in-sync with your avatar's current walking speed. Unfortunately, the workings of this are locked off to regular animations.

Anyway - If you're using a control event, you can use llGetVel to get your avatar's current velocity (useful to find out if you're on a slope), combine this with llMinEventDelay (to get around what Quintess said, that the control event triggers stupid fast), then use some math to work out the force required to maintain speed (so if your desired speed is 9 m/s, you'd use llSetForce as Quintess said) and use the control event with llGetVel to monitor your current speed vs actual speed. On slopes llGetVel will report that you've slowed down, so you can use math to apply an offset to your desired walk speed (e.g. if the desired speed is 9 m/s, but the reported speed is 4 m/s, then you'd apply a +5 m/s offset to your llSetForce to maintain speed), and with the monitoring you can also detect when you're going overspeed to take that offset value off.

(You could also use the llGetVel to detect vertical speed, instead of relying on the reported forward speed)

  • Like 1
Link to comment
Share on other sites

In regards to sounds, it would be neat if materials & collision sounds in objects could be read by script remotely.

It would also be neat if we could assign a collision sound to each of the 4 terrain levels and those sounds and terrain low/high heights be able to read by script.

Anyone know if any shoes also try to make water splashing sounds when you move in ankle/shin high water?

Link to comment
Share on other sites

8 hours ago, Jenna Huntsman said:

anyway - If you're using a control event, you can use llGetVel to get your avatar's current velocity (useful to find out if you're on a slope), combine this with llMinEventDelay (to get around what Quintess said, that the control event triggers stupid fast), then use some math to work out the force required to maintain speed (so if your desired speed is 9 m/s, you'd use llSetForce as Quintess said) and use the control event with llGetVel to monitor your current speed vs actual speed. On slopes llGetVel will report that you've slowed down, so you can use math to apply an offset to your desired walk speed (e.g. if the desired speed is 9 m/s, but the reported speed is 4 m/s, then you'd apply a +5 m/s offset to your llSetForce to maintain speed), and with the monitoring you can also detect when you're going overspeed to take that offset value off.

Yes, you could adjust the force strength instead of the direction, and you could probably get it to work. But I think moving uphill at normal speed/force is better than trying to move more quickly/forcefully into the hill. to each their own tho. To be fair, your approach would be more likely to deal elegantly with the force of gravity. (in my approach, you might have to multiply the force by 9.8*dirOfTravel.z or something)

Edited by Quistess Alpha
  • Like 1
Link to comment
Share on other sites

  • 8 months later...

Ok, so i tackled this one again. In OpenSim though, but I'll eventually convert it to work in SL as well i suppose.

It goes a little something like this:

vector current;
vector new;
float xvalue;
float yvalue;
float zvalue;
string currentSpeed;
string defaultWalkingSpeed;
key attachedAgent;
 
default
{
    state_entry()
    {
        //osSetOwnerSpeed(0.5);
        vector current = llGetPos();
        llSetTimerEvent(0.15); // I know, horrible!
    }
    attach(key attachedAgent)
    {
        if (attachedAgent != NULL_KEY)
            {
                osSetOwnerSpeed(0.4);
            }
    }
    changed(integer change)
    {
        if (change & CHANGED_REGION)
        {
            llSleep(0.5);
            llResetScript();
        }
    }
    timer()
    {
        new = llGetPos();
        if ( new. x < current .x)
        {
         xvalue = current .x - new .x;
        }
        if ( new .x > current .x)
        {
         xvalue =  new .x - current .x ;
        }
          if ( new .x == current .x)
        {
         xvalue =  new .x - current .x ;
        }
        if ( new.y < current .y)
        {
         yvalue = current .y  - new .y  ;
        }
        if ( new .y > current .y)
        {
         yvalue =  new .y - current .y ;
        }
        if ( new .y == current .y)
        {
         yvalue =  new .y - current .y ;
        }
        if ( new.z < current .z)
        {
         zvalue = current .z  - new .z  ;
        }
        if ( new .z > current .z)
        {
         zvalue =  new .z - current .z ;
        }
         if ( new .z == current .z)
        {
         zvalue =  new .z - current .z ;
        }

        float beforevalue = llSqrt((yvalue * yvalue) + (xvalue * xvalue));
        float aftervalue =  llSqrt((beforevalue * beforevalue) + (zvalue * zvalue));

        current = new;
        string currentSpeed = llRound((7.2 * aftervalue)); //Getting KM/u (kph) value, i'm European :D)
        
        integer NowWalking = llGetAgentInfo(llGetOwner()) & AGENT_WALKING;
        integer NowRunning = llGetAgentInfo(llGetOwner()) & AGENT_ALWAYS_RUN;
        
        if (NowWalking) {
            llSetText((string)currentSpeed + " KM/u\n Walking", <1,1,1>, 1);
        }
        else if (NowRunning) {
            llSetText((string)currentSpeed + " KM/u\n Running", <1,1,1>, 1);
        }
        else {
             llSetText((string)currentSpeed + " KM/u\n Idle", <1,1,1>, 1);
        }
        defaultWalkingSpeed = "8";
        if (currentSpeed < defaultWalkingSpeed && NowWalking) {
            osSetOwnerSpeed(0.6);
        }
        else if (currentSpeed > defaultWalkingSpeed && NowWalking) {
            osSetOwnerSpeed(0.4);
        }
        else if (NowRunning) {
            osSetOwnerSpeed(1.2); // Speed up because we're running!
        }
    }
}

I'm fully aware that this isn't the best approach but it seems to work in OS well enough for me. It has a very short timer that i don't like all that much so i may need to find a better solution to this. I just combined a snippet from Outworldz with what i already had. The snippet is a simple speedometer. All i'm doing is taking its current speed value and changed it when needed. So if my walking speed dips down (going uphill, stairs, etc) i just speed up my avatar a bit by increasing osSetOwnerSpeed a tad. I also check if the avatar is walking or running. In case of running i also change osSetOwnerSpeed to an even higher value because well... we're running, aren't we :)

 

Feel free to criticize this and add your improvements or and use it. I know its a working concept that needs improving.

Link to comment
Share on other sites

rather than use a timer can do it in the control event and get the state with llGetAnimation. Example:
 

control (key id, integer level, integer edge)
{
   // have got permissions for the movement controls _FWD, _BACK, and strafe _LEFT, _RIGHT

   .. if prim underfoot
   {    
      // get our animated state
      string anim = llGetAnimation(id);
      if (anim == "CrouchWalking")
      {
         .. play sound "CrouchWalking"
         .. sleep script for duration of crouchwalking step

      }
      else if (anim == "Walking")
      {
         .. play sound "Walking"
         .. sleep script for duration of walking step
      }
      else if (anim == "Running")
      {
         .. play sound "Running"
         .. sleep script for duration of running step
      }
   }
}

 

  • Like 1
Link to comment
Share on other sites

20 minutes ago, Mollymews said:

rather than use a timer can do it in the control event and get the state with llGetAnimation. Example:
 

control (key id, integer level, integer edge)
{
   // have got permissions for the movement controls _FWD, _BACK, and strafe _LEFT, _RIGHT

   .. if prim underfoot
   {    
      // get our animated state
      string anim = llGetAnimation(id);
      if (anim == "CrouchWalking")
      {
         .. play sound "CrouchWalking"
         .. sleep script for duration of crouchwalking step

      }
      else if (anim == "Walking")
      {
         .. play sound "Walking"
         .. sleep script for duration of walking step
      }
      else if (anim == "Running")
      {
         .. play sound "Running"
         .. sleep script for duration of running step
      }
   }
}

 

And there it is, a much better solution. Thank you :) 

  • Like 1
Link to comment
Share on other sites

3 hours ago, Mollymews said:
.. sleep script for duration of crouchwalking step

would it be more/less efficient to dynamically change the minimum event delay? if you sleep during a rapid fire event (control/touch) my intuition would expect the piled up events to lead to some unexpected things, but I could be wrong.

Edited by Quistess Alpha
Link to comment
Share on other sites

46 minutes ago, Quistess Alpha said:

would it be more/less efficient to dynamically change the minimum event delay? if you sleep during a rapid fire event (control/touch) my intuition would expect the piled up events to lead to some unexpected things, but I could be wrong.

I checked: timer, touch_start, touch, touch_end, collision_start, collision, collision_end and control do not get aggressively queued, there seems to be at most one extra event of each type lined up. Sleeping in the event handler appears perfectly safe.

Edited by Frionil Fang
  • Thanks 1
Link to comment
Share on other sites

8 hours ago, Quistess Alpha said:

would it be more/less efficient to dynamically change the minimum event delay? if you sleep during a rapid fire event (control/touch) my intuition would expect the piled up events to lead to some unexpected things, but I could be wrong.

intuitively it makes sense to use llMinEventDelay as is what the function is designed to do. In this case have the control event fire at a regular interval equal to the length of the animation step

what I have found tho over my time with LSL (and I could be wrong about this) is that llMinEventDelay applies to all events, so has the same effect as llSleep. Except that llMinEventDelay continues to process inputs, suppressing (not queuing) the inputs that fall outside the delay interval, whereas llSleep stops the script accepting all inputs for the delay interval

so I think that in this case llSleep is more efficient from the server pov.  I could be completely wrong about this tho. Is just what I think

 

Edited by Mollymews
accpeting
  • Thanks 1
Link to comment
Share on other sites

Caith, suggest you experiment with both llSleep and llMinEventDelay. Getting the timing close is the goal, so experimenting with both is a good thing to do. And go with the one that gives you the better experience

ps. About timing.  The server timing doesn't always conform to real time. Nor does the viewer. So getting the sound (server side initiated)  to sync with the animation step (client side) is not easy in all situations. So you may have to go with close enough is good enough

pps. We can to some degree, calibrate the time delay (thru averaging) on the server side (either llSleep or llMinDelay), but is nothing we can do about client side calibration 

Edited by Mollymews
close
  • Thanks 1
Link to comment
Share on other sites

i just take this opportunity to put in another plug for llPlayGesture("mygesture"). A gesture asset contained in Object Contents

the thing about gestures is that the gesture playlist is synchronised on the viewer. Which would I think be very good, and we wouldn't have to hack our way into trying to achieve what we are doing here in this case

gestures by current design have to reference full permission animations and sounds (internally by uuid)  It could be changed so the gesture allows all permissions assets to be included in the playlist, and the most restrictive asset permissions then applied to the gesture asset.  Example: Gesture contains a no-transfer animation.  Gesture is now also no-transfer

which I think would satisfy the ownership question of the assets referenced in the gesture

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Update: 

So i've been tinkering with this on and off for some time. The speed control works really well in OS now. I haven't brought it in SL yet. My avatar walking speed is around 3 - 4 KM/u, typical human walking speed and the walking animation i'm using is rather perfect. No moonwalking whatsoever. As soon as i walk up a hill/stairs/etc my avatar gets pushed by increasing osSetOwnerSpeed by +0.1. If it still doesn't get up the stairs, i give it another push and so on. I do this by checking if the avatar is walking and what it's speed is. If it is walking and its speed is below 3 to 4 KM/u, it gets pushed, helping it up the stairs. If it's above 4 KM/u it most likely means it is going down a hill/stairs/etc so i then decrease its speed.

Now, since i now have control over its speed, i've decided i want the script to know if the avatar is actually going up or down, not just by comparing actual speed and what it should be. So i'm now monitoring the Z axis. Why you ask? I already am managing it's speed and helping it up the stairs... Well, since i like making animations, i want to make 'going up and down the stairs'- animations, so i need to know if it is actually going up or down.

I'm still tinkering with that but as soon as i got a working concept on that, i'll post an updated script here.

  • Like 1
Link to comment
Share on other sites

I had a go at it; applying a constant upwards force while moving seems to fix stairs without using any complex dirty tricks.

a redacted version of my script:

integer gnSpeeds = 3;
list gSpeeds = [1.25,5.0,7.0];
integer gSpeedIndex = 0;

default()
{ //... request permisions and all that
  llTakeControls(CONTROL_FWD/*|CONTROL_BACK|CONTROL_LEFT|CONTROL_RIGHT*/,TRUE,FALSE);
  // speed control is most important going forward;
  // other directions have a bug/confusion between looking camera-forward and looking movement-forward.
    control(key ID, integer level,integer edge)
    {
        if(level&edge&CONTROL_FWD)
        {   if(llGetAndResetTime()<0.2)
            {   ++gSpeedIndex;
            }else
            {   gSpeedIndex=0;
            }
            llSetForce(<0,0,4.5>,FALSE);
        }else if(~level&CONTROL_FWD)
        {   llResetTime();
            llSetForce(<0,0,0>,FALSE);
        }
        if(gSpeedIndex>=gnSpeeds) gSpeedIndex=(gnSpeeds-1);
        float speed = llList2Float(gSpeeds,gSpeedIndex);
        vector vel= speed*/*llVecNorm(*/ //superfluous since not using other directions.
            < 1, 0,0.0>/*!!(level&CONTROL_FWD) +
            <-1, 0,0.0>*!!(level&CONTROL_BACK)+
            < 0, 1,0.0>*!!(level&CONTROL_LEFT)+
            < 0,-1,0.0>*!!(level&CONTROL_RIGHT))*/;
        vector v = llGetVel();
        vel.z=v.z;
        llSetVelocity(vel,TRUE);
    }
}

it's interesting to note that the anti-gravity increases your observed forward speed slightly.

  • Like 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 678 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...