3
\$\begingroup\$

First programming question I've ever made in my short journey. I'm trying to create a script which moves members of your party in a line behind you (think Chronotrigger or Earthbound). Here's a clip of what I want it to look like.

Problem: Everything looks fine in the Game window when I play, but when I build the game to test, the characters begin to stack one on top of each other. I assume it has something to do with Update() frames dramatically increasing the frequency with which I enqueue the positions of the leading character (although I'm not experienced enough to say for sure, clarification on this would be helpful).

  • I've tried increasing the offset, but this solution is variable depending on the players computer fps so that wont work.
  • I've tried running both scripts in FixedUpdate() but a lot of my other systems run on Update() and so everything stutters.
  • I've tried enqueue'ing from an IEnumerator and waiting for .1 second between each enqueue but that stutters as well..

What I'm doing: The leading character has a script called PlayerTrail which creates a Queue of Vector3s. In Update the game object's transform.position is enqueued. The following character has a script called PlayerFollower which looks at the Queue and Vector3.MoveTowards the position, then dequeues that position and repeats. Each character only looks at the trail of the character in front of it.

Everything seemingly works fine and well in the Unity program, this issue only arises once I build the actual game.

Code Supplied Bellow:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerTrail : MonoBehaviour
{
    public Queue<Vector3> LeaderTrail;
    public Vector3 LastMovement;

    private void Start()
    {
        LeaderTrail = new Queue<Vector3>();
    }

    private void  Update()
    {
        if (this.transform.position != LastMovement)
        {
            LastMovement = transform.position;
            LeaderTrail.Enqueue(this.transform.position);
        }

    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerFollower : MonoBehaviour
{
    public GameObject leaderObject;
    public int Offset;
    public int moveSpeed;

    private PlayerTrail leaderTrail;
    private Animator animator;


    void Start()
    {
        animator = gameObject.GetComponent<Animator>();
        leaderTrail = leaderObject.GetComponent<PlayerTrail>();
    }

    void Update()
    {
        if (leaderTrail.LeaderTrail.Count < Offset)
        {
                animator.SetBool("isMoving", false);
                return;  
        }
        else
        {
            Vector3 direction = (leaderTrail.LeaderTrail.Peek() - transform.position).normalized;
            this.transform.position = Vector3.MoveTowards(this.transform.position, leaderTrail.LeaderTrail.Peek(), moveSpeed * Time.deltaTime);

            animator.SetBool("isMoving", true);
            animator.SetFloat("moveX", direction.x);
            animator.SetFloat("moveY", direction.y);

            Debug.DrawRay(this.transform.position, direction);

            leaderTrail.LeaderTrail.Dequeue();
        }
    }
}

What occurs in the Gameview Window What occurs in the Build of the game

\$\endgroup\$
1
  • 1
    \$\begingroup\$ In the video they keep a certain min distance if you do not move at all. Do they keep that in your editor version (the working one as well) when you do not move or do they stack there as well? Maybe your offset is better used as an offsetDistance to the person in front of you and not about how many vectors are in the list (which is dependend on how fast you populate the list) \$\endgroup\$
    – Zibelas
    Mar 4 '21 at 8:18
1
\$\begingroup\$

Instead of a counter of how many items you have in your queue, you should only move them towards the next part when they are further than a certain distance. Removed the animator for testing purpose. They try now to catch up to you as well in case you do not move or move faster than they can. You want as well to only dequeue the position once you reached it (or when you are close to it) to not stop moving towards it in case the player moves faster or they get stuck for a second (else the distance might get bigger between the followers)

Last you wrote that the character only looks at the character in front, but from your question it sounds like that only the leading character has the playertrail script. You should rather place a playertrail on each character you want to follow, means that if you have 4 Characters, you have 3 playertrail scripts (on 1, 2, 3) and 3x Followerscript (on 2, 3, 4). This way you have better control over the distance between the characters (this might be important if the size of characters is different). And the character does not need to know how many characters are in total to calculate different offsets.

public class PlayerFollower: MonoBehaviour
{
    public GameObject leaderObject;
    public float keepDistance;
    public int moveSpeed;    
    private PlayerTrail leaderTrail;
    
    void Start()
    {
        leaderTrail = leaderObject.GetComponent<PlayerTrail>();
    }

    void Update()
    {
        if (leaderTrail.LeaderTrail.Count >= 1) {
            if (Vector3.Distance(transform.position, leaderTrail.LeaderTrail.Peek()) >= keepDistance)
            {
                Vector3 direction = (leaderTrail.LeaderTrail.Peek() - transform.position).normalized;
                this.transform.position = Vector3.MoveTowards(this.transform.position, leaderTrail.LeaderTrail.Peek(),
                    moveSpeed * Time.deltaTime);    
                Debug.DrawRay(this.transform.position, direction);
            }
            if (Vector3.Distance(transform.position, leaderTrail.LeaderTrail.Peek()) < keepDistance)
            {
                leaderTrail.LeaderTrail.Dequeue();  
            }    
        }
    }
}
\$\endgroup\$
5
  • \$\begingroup\$ Sorry for being unclear, the set up is as you describe. Each character: -1 PlayerTrail(except the last character) -1 PlayerFollow(except the first character) - Only reads input from the character's position directly in front of them. Your solution works a lot better than the previous script(especially at higher FPS's) but I think think opposite issue has arisen. Dequeuing happens after a peek has already been established and moved toward, now positions are being checked roughly every 2nd update so now on LOWER FPS's stuttering happens.. Thank you so much for you help! \$\endgroup\$ Mar 4 '21 at 11:44
  • \$\begingroup\$ The dequeue should only happen if we do not need that position anymore. Means if the distance you set is big, they get dequeued right away, but if it is close, they move up. This should not introduce stuttering since they move the same as before. \$\endgroup\$
    – Zibelas
    Mar 4 '21 at 11:50
  • \$\begingroup\$ @PLasmaghost how low is your targetframe rate? Below 15 fps it does indeed have a bit of stuttering, but any kind of movement on this kind of framerate would look like it is stuttering. It can be improved as well to only add trail vectors that have a certain distance instead of just not exact the same \$\endgroup\$
    – Zibelas
    Mar 4 '21 at 12:34
  • \$\begingroup\$ At least 30 fps would be nice, for now I think your solution is probably fine. Given the nature of the game higher fps issues are more likely to be an experienced problem.. By certain distance do you mean adding a conditional in PlayerTrail to only enqueue >= offsetDistance? I'll try and see what I get with that. Again, thank you for you time! \$\endgroup\$ Mar 4 '21 at 12:48
  • \$\begingroup\$ Yes exactly, this would smooth out at least a bit how the followers are running after the leader. Checking against distance of lastEnqueued position and current position. This can be a smaller/ different value compared to the offsetDistance. \$\endgroup\$
    – Zibelas
    Mar 4 '21 at 12:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .