Scripted motor can't get up steps/slopes


#1

Hi, I think this is a question for @leviathan.

I have a locomotion script that takes over Controller.Standard.LX and LY, and in doing so, causes the ‘action’ motor to get disabled in MyAvatar::updateMotors (_isPushing=false, _isBeingPushed=true), leaving just the scripted motor active.
So far so good.

But then in CharacterController::applyMotor, there’s a test for (motor.hTimescale == motor.vTimescale), in which case the stepping-up code isn’t applied. I can’t work around it in the script because I don’t have a way to set the two timescales individually there.

Does that test for (motor.hTimescale == motor.vTimescale) still make sense, now that the ‘else’ block applies the stepping-up code?

Many thanks,
Phil


#2

Phil, good question.

The scripted motor API was added before we had the stepping-up logic, and at that time both motors only had one timescale. The horizontal (H) and vertical (V) timescales were split out to make the stepping-up behavior possible. The particular logic you’re pointing at is effectively an optimization: when the two timescales are identical then it doesn’t make sense to decompose the motor’s velocity and apply the components separately: you’d end up with the same effect as just doing it as one vector but it would cost more CPU cycles.

The only code in the else case not about computing the motor’s effect is the bit that sets MyAvatar::_steppingUp to true. The assumption when I wrote it was: only the default motor has asymmetrical H and V timescales, and it is the only thing that needs to know about “stepping up”.

Perhaps we should expose the scripted motor’s H and V timescales to JS? Maybe… but first some overview of the default motor:

The default motor changes the V timescale dynamically: normally it leaves it very long, which makes the vertical component of the motor weak: the avatar descends to the floor, especially after walking off a ledge. However when the avatar encounters a navigable “step” the default motor reduces the V timescale (making its effect kick in faster) and also dynamically adds a V component to the motor, pointing it up even though the “control” signal from the user is: forward.

This “stepping up” adjustment logic is trickier than one might expect and requires correct information at low latency to reduce a “hop” effect at the top of each step. Which means: it is easier to implement in C++ than JS because the C++ has access to the correct data faster. As such, I don’t know how useful it would be to try to make the scripted motor able to handle stepping, unless it was done at the C++, in which case maybe we should expose some of the default motor’s parameters to JS? Maybe we only need one motor (the default one) but give it more JS knobs? Or maybe we need an arbitrary number of JS motors but supply a finite number of types: simple, default, airplane?

I’m interested in enhancing the motor API to make it possible for you to accomplish your goals. Perhaps you could describe what you’re really want to do and we can brainstorm how to get it done, in C++ and/or JS.

Are you trying to speed up the avatar’s walk to be a run? Or are you trying to make a flying jetpack that can “step over” (not get stuck on) low obstacles?


#3

Hi, thanks for the detailed reply @leviathan!

What I’m trying to do is give the user some options to control their walking in VR. I want them to be able to look around while walking in a straight line; I want them to have analogue speed & direction on the Vive touchpad, etc. I started by adding these changes in the C++ code, which works well, but when I saw how powerful the scripting was, I assumed that putting them in a script would be recommended - a “locomotion options” app that I’d submit to the Marketplace (?)

My current code bases its walk direction frame on the real-world hips direction of the user, estimated from the orientation of a hand and my best guesses about how the hand might be posed while walking (resting at side / pointing / swinging). Obviously not perfect because we can twist our hands any way we want relative to our body, but it definitely does the job and the option to average both hands will only improve it (for people who don’t carry a drink around with them in VR).

The main thing I’m wondering now is: does that sound like the sort of stuff we’d prefer to put in the C++ code, or in a Marketplace script, or in a default script?* These sorts of locomotion options are tiny bits of code, but can make the whole experience of being in High Fidelity more comfortable and more appealing.

*just remembered: some domains might have their own scripted custom movement for avatars, so staying in the C++ would have the advantage of not conflcting with that.

Thanks!


#4

Actually, the original aspiration was to keep the locomotion logic as much on the JS side as possible reduce the need for complicated C++ logic. The idea being people could create custom UI/behavior. Meanwhile, it is a bummer when the avatar can’t navigate when it happens to be running zero scripts, hence the default C++ logic. Now the scriptable API is too limited.

We definitely would like to make it possible for people to tweak the avatar’s motion controls via JS, and we’re willing to enhance and maybe even remove (gasp!) JS API to do so. Perhaps one way forward would be for you (or someone) to hack the C++ to provide the JS API you would like to use and then consider how to get that through approval/testing.

The default motor is inaccessible to JS (except for enable/disable) but maybe it should be exposed more fully. It has a few parameters and behaviors that the existing scripted motor does not. It isn’t clear to me right now how best to make then available, but I’ll think about it and we can brainstorm in this thread.


#5

Great, yes I’ll do as you suggested (if I’ve understood correctly): hack-in the necessary API so that my script will drive the default motor instead of the current scripted one.

I’ll show you the code once it’s working, to see what you think. Thanks!


#6

Phil, have you made any progress on this project? I had an internal dev asking me about this issue because he too wants to make a running script that uses special input to drive the avatar speed, but he needs the avatar to be able to walk up steps. We brainstormed about how might be the best way to proceed. I forget where we ended up in the end but it sounds like he wants to overhaul this scripted motor API this week. Perhaps we could collaborate with you, or reconcile yours and our design so that we don’t end up with two completely different works.


#7

Hi,

Yes, I hacked it so that the scripted motor velocity is added early to the action velocity, in UpdateActionMotor. A single motor is then added using that combined velocity.
I changed UpdateMotors into a function that just transforms the action velocity into world space, before the scripted one is added to it.
I had to change hasDriveInput to consider the scripted velocity, not just the controller inputs, otherwise the scripted walking (that steals those inputs) would get crouchy after going over obstacles/slopes.

Those quick changes have got my walking script working as I wanted, but at the moment they’re also breaking the default hovering movement and reversing the angle of the default walking.

One other thing I did was change ViveControllerManager.cpp to give analogue direction and speed (and no teleport button) on the left pad.

If you’d like me to send you anything, please let me know.

Thanks,
Phil


#8

Hi Phil,

I’m the internal dev who was also interested in this issue.

After some further discussions with @leviathan on various strategies, we think the following is the best way forward for this:

-Keep the dual motor system the way it is now with a default walking motor in index 0 and scripted motor at index 1 using addMotors/clearMotors each frame

-Default motor at index 0 behavior remains unchanged

-Scripted motor at index 1 gets a script-exposed motorMode option which is “simple” by default or alternatively “dynamic”

-“Simple” mode for scripted motor behaves exactly as it does now which will maintain existing content’s behavior

-“Dynamic” mode for scripted motor behaves just like default motor using avatar reference frame and default motor’s time scales (to properly allow for stepping)

In our use cases we would set MyAvatar.motorMode to “dynamic” and set MyAvatar.motorVelocity to whatever. The avatar would then move just like it would via the default motor with step up functionality modifying the target velocity. The default motor will not be exposed to JS like it is now (except it can be disabled) and will still be available to the user via controller/keyboard input.

We think these changes should cover both your needs as well as our needs for our running script.

What do you think?

-dback


#9

Hi @dback, thanks for the info.

That is sounding like a good plan to me : )

I don’t know if it’s important to mention or not, but for my use case I’m only interested in generating world-space velocities; I don’t want the avatar orientation to affect my walking because I’m doing my own estimation of the user’s real-world orientation (not influenced by head angle).
Whereas you suggested the Dynamic mode would use the avatar reference frame - is that something we’d be able to choose in the script like we can currently?
And I wonder if the avatar frame is really the best default frame for that. I thought the avatar’s orientation loosely followed the current movement (and head angle), in which case is it a meaningful reference frame for new movement? I’ve been thinking of the avatar orientation more as something sort of cosmetic and not reliable, if you see what I mean.


#10

Hi Phil,

Makes sense. Thinking about it more, it should be possible for us to handle the target velocity being in world and camera frame and then convert it to avatar frame before passing it to addMotor since it needs to be in avatar frame later on for dynamic mode.

We will try it and will report back if it doesn’t work out.

-dback


#11

Great, thanks! I had a couple of little questions:

  1. If someone’s using my walking script, and they enter a game or vehicle that also tries to take-over the controller inputs, what would happen? Are the scripts in a stack so that the most recently-added one takes priority?

  2. Do you know what will be the correct way to get analogue direction and speed on the Vive touchpad? Currently it seems to require a hack in ViveControllerManager.cpp, because that doesn’t feed-out the raw axis values of the pressed-in pad, only the d-pad-style snapped ones (unless I’m missing something).

Thanks


#12

Hi @dback and @leviathan,

Just letting you know that the new dynamic motor mode is working well for me, except that it doesn’t resolve the need for this hack:

The problem should be easy to reproduce as long as the script takes over both Controller.Standard.LX and LY, like my one here does: http://djdrumphil.ca/hifi/locomotion.js

Thanks,
Phil


#13

Hi @Phil,

My apologies for the late response as I was out most of last week.

Glad to hear the dynamic mode has been mostly working out for you.

In regards to the hasDriveInput hack, I’ve taken a look trying out with your locomotion script, and I’m not sure what you mean by it getting crouchy after going over obstacles/slopes. Can you point to somewhere specifically where I can try that? Also, for the code hack itself, what exactly were the changes you made to get the result you needed?

-dback


#14

Hi @dback,
I should’ve mentioned that this only applies to HMD mode (tested on Vive).
[Update: only seems to happen if you’re standing and using your body to turn.]

With the script running, if you go here:
Medieval-Village/13.7683,-10.8279,-14.2153
there’s two buildings with a jetty on one side and a hill on the other. If you step over the railings of the jetty a couple of times, or walk up the slope and down again, you should see it leaves you walking a bit like this:

The following hack prevents it:

bool MyAvatar::hasDriveInput() const {
    bool rtn = fabsf(getDriveKey(TRANSLATE_X)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Y)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Z)) > 0.0f;

    // -----------------------
    // HACK, FIXES CROUCHY SCRIPTED WALKING AFTER STEPPING OVER OBSTACLES
    static bool doHack = true;
    if (!rtn && doHack)
    {
        float scriptedMotorSpeed = glm::length(_scriptedMotorVelocity);
        rtn = (scriptedMotorSpeed > 0.0f);
    }
    // -----------------------

    return rtn;
}

Thanks


#15

Hi @Phil,

I took a look at the spot you mentioned, and I was able to repro it on Vive there but not on Rift interestingly enough (which I had tested with originally the other day), and only when going up and down the slopes of the riverbed by the jetty.

In terms of the hack you needed, I suspected you might have done a glm::length(_scriptedMotorVelocity) > 0 check so that makes sense. Looking at where hasDriveInput is used, we would probably want to figure out which 2 spots in MyAvatar::FollowHelper::prePhysicsUpdate is causing the issue here, and then we can add something extra for this case there. It could maybe be:

bool dynamicScriptedMotorInUse = _scriptedMotorMode == SCRIPTED_MOTOR_DYNAMIC_MODE && glm::length(_scriptedMotorVelocity) > 0.0f;
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput || dynamicScriptedMotorInUse)) {
    activate(Vertical);
}

or something like that.

I am going to open a ticket to our developer relations team to continue following up with you on this issue so you have the continued support that you need.

-dback


#16

Thanks @dback. I noticed that it only seems to happen if you’re standing and using your body to turn. I updated the repro steps above.

Is there a link I could have to the ticket?


#17

Hey @Phil,

Thank you for the updated repro.

As far as the ticket goes, we are checking about getting a link to the ticket. So far our internal QA engineer has had a look and has verified it is a Vive-specific issue and did also notice the point you added about needing to be physically standing. We still need to consult with our other engineers about what fix/changes might be needed.

One other thing they noticed that I thought I should pass along in the meantime is the following:

Note that another issue with the script is that, while pressing the direction-pad, press and hold the jump-pad a few seconds. You will begin to fly in a certain direction. At this point, take your fingers off all buttons/pads and you’ll see your avatar continue to fly in a certain direction.

Will keep you posted once I hear of anything.

-dback