Forum rules - please read before posting.

Straight to Cursor movement + Run Threshold

I was playing around with the 'Straight to Cursor' movement type, and I noticed that once you reach the run threshold, the character will always run no matter how close the cursor is, as long as you keep the button held. Wouldn't it make sense for the character to start walking again if the distance fell under the threshold?

Comments

  • edited March 2023

    Oh, interesting, the above observation is only true if the player's pathfinding update frequency is set to 0. There's some confusing behaviour here because if the update frequency is 0, then the 'straight to cursor' movement is really snappy (i.e. it responds to cursor direction changes instantly) with no apparent performance hit, and it does update from walk to run if the distance starts under the threshold and then crosses it, but it does not update the run back to walk when the cursor distance falls under the threshold.

    If you set the update frequency to 1, then the movement is not snappy at all (i.e. if the cursor suddenly changes directions, the character will take 1 second to update their walking direction), but it WILL switch from run to walk when the distance falls under the threshold and an update happens.

    To get both behaviours (snappy change of direction + run>walk update), you need to set the update frequency to something like 0.2. The issue is that this comes with a performance hit, which I assume comes from pathfinding recalculations which are unnecessary here.

    The ideal behaviour imho is for the 'straight to cursor' movement to always respond the way it does when the update frequency is 0 (i.e. instantly detecting changes of direction without a performance hit), but including a threshold check that doesn't rely on the pathfinding update time (which already exists, but only one way). I'd like to have a non-0 update for other reasons (when interacting with moving hotspots), but for that purpose I can use a much less demanding update time of 1 or even 2.

  • I've also just managed to make the character run out of the navmesh with 'straight to cursor' movement (very easy to do if you have a peninsula-like shape sticking out of the navmesh), so I've decided that while this would be a nice thing to have together with 'pathfind when single-clicking?', I'm going to stick with point & click movement only, which was working perfectly. Can't afford spending time on this at the moment.

  • Thanks for the details - I will investigate.

  • edited April 2023

    Just a quick update: I've tried this again after rolling back the two Perform2DRaycast functions in AC's UnityVersionHandler, and if you have "auto stick to navmesh checked?", then there are no issues with the player keeping to the navmesh with 'straight to cursor' movement. All good on this front.

    Decoupling this type of movement from pathfinding update time would be great though! Something I've noticed is that when the update time is 0, the player just follows the cursor position without producing any paths (no path gizmos on the scene view if you select the player). If the update time is higher than 0, it seems to work by producing a path and then updating it according to the frequency you set.

    So, in short, once this is decoupled, then the only issue is making the character go back to walking once the cursor distance falls under the threshold again.

  • edited April 2023

    Also, is there a built-in check to know whether the 'straight to cursor' movement is engaged at the moment? If the above can be done, I'm thinking about using it in addition to point & click movement, but there's a little issue where I need the player to go somewhere outside the navmesh during cutscenes, so I need to be able to toggle "auto stick to navmesh checked?", preferably automatically. I can write a script to allow for that myself. I know I could just check if the game is running a gameplay-blocking cutscene, but if possible, I'd rather check if the 'straight to cursor' movement is engaged instead.

    I could also use this check to display some UI indication that this type of movement is engaged (like in Superbrothers: Sword & Sworcery EP).

  • Something I've noticed is that when the update time is 0, the player just follows the cursor position without producing any paths (no path gizmos on the scene view if you select the player)

    When the pathfinding update time is zero, the character will bee-line to the cursor each frame. Since this is happening every frame, this does not involve pathfinding - as performance would suffer.

    When the update time is non-zero, the character is able to make the pathfinding calculation.

    This is as intended - the coupling is required. Threshold issue aside, what is the main problem you're having with this behaviour?

    Also, is there a built-in check to know whether the 'straight to cursor' movement is engaged at the moment?

    You can use a custom script to read:

    if (KickStarter.settingsManager.movementMethod == MovementMethod.StraightToCursor) {}
    

    Any Manager field can be accessed through script - right-click the field's label to get an API reference to it.

  • edited April 2023

    This is as intended - the coupling is required. Threshold issue aside, what is the main problem you're having with this behaviour?

    The issue is that if you use a high pathfinding update time (say, higher than 0.4s) with 'straight to cursor' movement, player control starts feeling very sluggish. The character will take quite a long time to change directions, etc. It just feels unresponsive.

    But if you use a low update time (say, 0.2s), though it feels a lot more responsive, it can also cause massive performance issues in certain situations. If the player holds the mouse button and does a fast circular motion, specially as the cursor runs through areas of the navmesh that demand more than one pathfinding node to be reached, the game can slow down by A LOT. This can be alleviated by tweaking the navmesh's accuracy, but we could aim for much lower system requirements if the character just beelined to the cursor.

    Any Manager field can be accessed through script - right-click the field's label to get an API reference to it.

    Oh, I didn't express myself clearly. I know you can access the reference there. I just meant that if you use 'straight to cursor' movement, and then select 'single-clicking also moves player?' and 'pathfind when single-clicking?', you effectively get 'point & click' movement with 'straight to cursor' movement on top of that when you click and hold.

    I use a click marker to indicate where the player is pathfinding to. When this is taking place, I want the cursor to be the default one. When I asked if there was a way to check if the 'straight to cursor' movement was engaged, I meant to allow the possibility of changing the mouse cursor while the character is beelining somewhere as the player holds the mouse button.

    Being able to run that check would open up a couple of other possibilities too: if you still think it's better not to decouple 'straight to cursor' movement from pathfinding by default, for example, I could always write a script to change the player update time to 0 when that type of movement is being performed (vs when the player is still or is pathfinding somewhere via point & click, all different actions despite if (KickStarter.settingsManager.movementMethod == MovementMethod.StraightToCursor) {} returning true in all cases).

    I could also use such a script to turn off 'auto stick to navmesh?' when the player is not walking around holding the mouse button. If the player is clicking/pathfinding their way around, the navmesh will be respected anyway without that option, and that allows us to place the player outside the navmesh on occasion (for cutscenes that demand it, etc) without being constantly pushed back into the navmesh. The player only needs to be pushed into the navmesh (via 'auto stick to navmesh?') when they are beelining after the cursor.

  • This can be alleviated by tweaking the navmesh's accuracy, but we could aim for much lower system requirements if the character just beelined to the cursor.

    You'd get beelining / instant reactions if you zero the update time. You can set this per-character in the latest release through script, if necessary. Otherwise, it'd probably be a case of using A* to allow for faster pathfinding calculations.

    I meant to allow the possibility of changing the mouse cursor while the character is beelining somewhere as the player holds the mouse button.

    When beelining, the Player is still technically on a path - just one of a single node. To differentiate between a single-click pathfind, and held-down following, you should be able to read the drag state:

    if (AC.KickStarter.playerInput.GetDragState () == AC.DragState.Player) { }
    
  • Thanks! I think I can make it work with that check. Then the only issue left would be the threshold one.

  • Then the only issue left would be the threshold one.

    This should be addressed in the latest release, v1.77.1.

  • Hi Chris, I've just tested this in the latest 1.77.1 release, and it seems that if the update time is 0, the player will still always run once the cursor has gone beyond the threshold, even if you move the cursor back within the threshold.

  • I can't recreate the issue, can you share your Settings Manager?

    I should note that the threshold check is based on the distance between the Player and their destination on the NavMesh - not strictly the cursor position.

  • edited April 2023

    Chris, I think the issue is that this is wrong:

    When beelining, the Player is still technically on a path - just one of a single node.

    This is only true when the update time is higher than 0. When the update time is 0, the player is definitely not pathfinding. Remember the script I described above that I use to keep the marker on the screen while the player is pathfinding, and then change a variable (linked to an animator parameter) when the player has completed the path? Like this:

    if (AC.KickStarter.player.IsPathfinding())
    {
        this.GetComponent<AC.Variables>().GetVariable(0).BooleanValue = true;
    }
    else
    {
        this.GetComponent<AC.Variables>().GetVariable(0).BooleanValue = false;
    }
    

    If the update time is higher than 0 and I click and hold the mouse button, two things will happen: (1) the player will start pathfinding to the point where I clicked, and (2) after 0.3s (the default click/hold separation), the player will start pathfinding to where the cursor is. This means that the marker stays where it was placed even when we start moving the player to a different place, since the IsPathfinding check will continue to be true.

    HOWEVER, when the update time is 0, the marker plays its exit animation if we click and hold the mouse button as soon as the click/hold separation is reached. This means that the IsPathfinding check is returning false while the player is beelining to the cursor. Selecting the player on the hierarchy confirms that their path component is not displaying a gizmo.

    Again, I think it's good that this feature doesn't use pathfinding when the update time is 0. It feels more responsive AND is less resource-intensive. It also makes my little script above work as intended (the marker shouldn't stay on the screen while the player is walking elsewhere!) But this check isn't currently working because, as you said, the threshold check is based on the distance between the Player and their destination on the NavMesh, and in this case the player has no destination.

    This is my settings manager: https://www.mediafire.com/file/6g60358h402kp01/Unblessed_SettingsManager.asset/file

  • edited April 2023

    Another observation: it's easy to tell the player is not pathfinding when the update time is 0 because the player will not respect the navmesh unless "auto stick to navmesh?" is checked. But when it's checked, it works very well!

    And finally, if this isn't too much to ask, if the top threshold issue can be fixed, it would also be really handy to include a bottom threshold as well where the player stops moving if the mouse cursor is too close to the player. This is not much of an issue when the update time is higher than 0, but when it's 0, due to the instant feedback, the character can look very goofy switching directions and idle/walk animations every frame.

  • When the update time is 0, the player is definitely not pathfinding.

    Quite right - my apologies.

    In this mode, the Player's movement is similar to Direct control - where they simply move forward in a given direction. I can't recreate any issue with the "Run threshold" being ignored, however.

    If you'd like to dive into the code to see where the issue might lie, this check is made in AC's PlayerMovement script with the line:

    bool run = (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold);
    

    it would also be really handy to include a bottom threshold as well where the player stops moving if the mouse cursor is too close to the player

    This movement method will observe AC's Destination accuracy slider. Subtracting this value from 1.1 will give you the bottom threshold.

  • edited April 2023

    Chris, I found where the issue is. It's not the line you pointed out above, but a block of code similar to it:

                        bool run;
                        if (KickStarter.settingsManager.singleTapStraight && KickStarter.settingsManager.doubleClickMovement == DoubleClickMovement.MakesPlayerRun && KickStarter.settingsManager.singleTapStraightPathfind && KickStarter.player.isRunning)
                        {
                            run = true;
                        }
                        else
                        {
                            run = (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold);
                        }
    

    The condition KickStarter.player.isRunning initially returns false (when the threshold has not been reached), but once (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold) is true, then KickStarter.player.isRunning will also be true, and run will be stuck as true. To fix this, you can do this:

                        bool run;
                        if (KickStarter.settingsManager.singleTapStraight && KickStarter.settingsManager.doubleClickMovement == DoubleClickMovement.MakesPlayerRun && KickStarter.settingsManager.singleTapStraightPathfind && KickStarter.player.isRunning)
                        {
                            run = (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold);
                        }
                        else
                        {
                            run = (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold);
                        }
    

    As for the destination accuracy slider, I haven't played with it a lot, but from a cursory glance, it seems that even a very low accuracy doesn't result in a big enough bottom threshold (and I'm afraid it would interfere with more precise pathfinding elsewhere, like when I tell an NPC to walk to a marker and play an animation there, which needs to be perfectly aligned?). I'll investigate it further and get back to you though.

  • edited April 2023

    Messing with the accuracy slider didn't produce the intended results (and would cause other issues as I pointed out), but I found a very easy way to get the behaviour I want. By modifying this line (there are two of them, but so far I've only modified the second one, which is the one that applies to the specific manager settings I'm using), you get a bottom threshold:

    if (moveDirection.magnitude > KickStarter.settingsManager.GetDestinationThreshold ())

    to

    if (moveDirection.magnitude > KickStarter.settingsManager.GetDestinationThreshold (4))

    4 works perfectly as an offset for this game specifically, but obviously this depends on sprite size, etc. Would it be possible for you to expose this number as a manager setting? If anything, it'd make things easier when updating AC.

  • To fix this, you can do this:

    An equivalent would be to replace the whole block with:

    bool run = (moveDirection.magnitude > KickStarter.settingsManager.dragRunThreshold);
    

    I'll give some thought about a possible change officially.

    Would it be possible for you to expose this number as a manager setting?

    This'd be best handled as a hidden variable in PlayerMovement that can be set through custom script - I'll give this some thought as well.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.