#JRPG Follow Jitter Issue

1 messages · Page 1 of 1 (latest)

thorn island
#
public class FollowerManager : MonoBehaviour
{
    [SerializeField]
    private int _followerDistance;

    public Follower leader;
    private Vector2 lastLeaderPosition;

    public List<Follower> followerList = new List<Follower>();
    private List<Vector2> followerPath = new List<Vector2>();

    private void Start()
    {
        AllocateFollowerPath();
    }

    private void FixedUpdate()
    {
        // update position of each follower
        for (int i = 0; i < followerList.Count; i++)
        {
            followerList[i].transform.position = followerPath[_followerDistance * (i + 1)];
        }

        ShiftFollowerList();
    }

    private void ShiftFollowerList()
    {
        Vector2 currentPos = leader.transform.position;

        if (currentPos == lastLeaderPosition || followerPath.Count == 0) return;

        // This is responsible for shifting the entire array as the player continues to move
        for (int i = followerPath.Count - 1; i > 0; i--)
        {
            followerPath[i] = followerPath[i - 1];
        }

        // Set the new position and record last position
        followerPath[0] = currentPos;
        lastLeaderPosition = currentPos;
    }
     
    private void AllocateFollowerPath()
    {
        int target = (_followerDistance * (followerList.Count + 1)) - followerPath.Count;
        // Only allocate the necessary amount of spaces for the followers
        for (int i = 0; i < target; i++)
        {
            followerPath.Add(transform.position);
        }
    }

    private void DeallocateFollowerPath()
    {
        int target = ((followerList.Count + 1) * _followerDistance) - _followerDistance;
        for (int j = followerPath.Count - 1; j > target; j--)
        {
            followerPath.RemoveAt(j);
        }
    }
}

Hey all. I'm currently working on an Earthbound-inspired JRPG follow script for my game, and I've been having some issues with getting the follow quite right.

What I'm trying to do is make each follower in the party mimic the players exact movements while following behind at a distance (similar to games like Deltarune or Earthbound)

While the code above does mimic the movements of the leader, the issue is that the followers stutter and jitter (which is very noticeable when the camera is following the leader). I had a feeling this has to do with update loops, so I've tried moving around the order of my code but I haven't had any luck with it.

A couple notes:

  • The leader of the party hard sets the velocity of its rigidbody in a FixedUpdate loop.
  • The camera movement strictly follows the leader without any smoothing applied in a LateUpdate loop (this makes it so there's no camera jitter when locked onto the leader)
  • I'm not using cinemachine
  • I've tried hard setting script execution order to apply all movement before the camera follows, but it changing execution order doesn't seem to change anything
  • I've tried ShiftFollowerList() in LateUpdate and Update, but this seems to make the followers frame dependent

Not really sure what to do, I've been stuck on this for a bit. If you have any ideas or need clarification please let me know, I would greatly appreciate the help! ^^

pallid panther
#

Well your followers are moving in FixedUpdate

#

that alone is cause for jitter

thorn island
#

I've tried Update and LateUpdate, there's still jitter

pallid panther
#

you also need to make sure your rigidbody has interpolation turned on

#

and that there's nothing in your code that breaks the interpolation

thorn island
#

yeah interpolation is on

pallid panther
#

how is the main character moving

#

can you show the code for that?

thorn island
#
public class UnitMovement : MonoBehaviour
{
    [SerializeField]
    private float _unitSpeed;

    [SerializeField]
    private float _runSpeed;

    private Rigidbody2D rb;
    private Vector2 frameInput;
    private bool isRunning;
    private bool isMoving;

    public bool IsRunning { get { return isRunning; } }
    public bool IsMoving { get { return isMoving; } }
    public Vector2 Velocity { get { return rb.linearVelocity; } }


    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        if (GameManager.Instance.GameState != GameManager.EGameState.Playing)
        {
            rb.linearVelocity = Vector2.zero;
            return;
        }

        // Get axis
        float x = Input.GetAxisRaw("Horizontal");
        float y = Input.GetAxisRaw("Vertical");

        frameInput = new Vector2(x, y);

        // Get speed
        isRunning = Input.GetKey(KeyCode.X);
    }

    private void FixedUpdate()
    {
        float speed = isRunning ? _runSpeed : _unitSpeed;
        rb.linearVelocity = new Vector2(frameInput.x, frameInput.y).normalized * speed;
    }
}
pallid panther
#

how are you handling the looking right/left of the character sprite?

thorn island
#

It's in its own seperate script

pallid panther
#

yes I imagine so, I mean I would like to see how the code works

thorn island
#
    private void Update()
    {
        if(_movement.Velocity.x != 0)
        {
            _renderer.flipX = _movement.Velocity.x < 0;
        }

        if (_movement.IsMoving)
        {
            _hankAnimator.Play(WALK_HASH);
        }
        else
        {
            _hankAnimator.Play(IDLE_HASH);
        }

        if(_movement.IsRunning)
        {
            _hankAnimator.SetFloat("Speed", _runSpeedMultiplier);
        }
        else
        {
            _hankAnimator.SetFloat("Speed", _walkSpeedMultiplier);
        }
    }```
pallid panther
#

ok good

thorn island
#

ah i see, were you checking to see if I was scaling the player?

pallid panther
#

I was seeing if you were modifying the Transform in any way

thorn island
#

icic

pallid panther
#

including scaling yes

thorn island
#

so the party followers directly set their position, so they aren't really using their rigidbody velocity or anything

#

could that be the problem?

pallid panther
#

do they have Rigidbodies?

thorn island
#

yeah

pallid panther
#

yeah that's a problem

#

is there a reason they need Rigidbodies?

thorn island
#

nah

#

I'll remove em

#

yeah that doesn't seem to change anything tho

pallid panther
#

so what's the current setup

thorn island
#

the party followers dont have anything on them, they are just sprites

#

the leader has the movement code with a rigidbody and box collider

pallid panther
#

Ok i think the follower manager script needs some work

#

you need to do something like:

  • In each FixedUpdate, record the main player position in a list, and kick out the oldest position. You want to keep a list of positions that's long enough where Time.fixedDeltaTime * number of elements in the queue == number of followers * followerDistance.
  • In LateUpdate, you position each follower at the interpolated position from that list corresponding to their follower number and interpolated properly along that list.
#

So like... Let's say the list has 100 positions (2 seconds worth of motion at 50 physics tickers per second).
If there are three followers, the first follower is 33% of the way between the 33rd position and the 34th poisition in the list. The second is 66% of the way between the 66th position and the 67th position, and the last follower is at the last point.

You will need a small T adjustment for the current frame as well.

#

basically you need to interpolate the followers along the list of discrete points based on the current time

#

An AI tool can probably help you refine this more and turn it into actual code.

thorn island
#

This makes sense, I'll tinker around with it for a bit and get back to you soon - I appreciate the help!

pallid panther
#

but this idea where you are setting followerrs to the exact recorded positions in the list

#

isn't workable

#

especially since framerates are not fixed

thorn island
#

yeah true, and putting that code in a normal update loop made it completely frame dependent and would fuck up everything when vsync was turned off lol