Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Making several GameObjects follow a GameObject's movement (2D)

Discussion in 'Scripting' started by NotMyUsernameAgain, Mar 1, 2018.

  1. NotMyUsernameAgain

    NotMyUsernameAgain

    Joined:
    Sep 28, 2017
    Posts:
    126
    Hi everyone,

    I'm trying to make a worm like enemy which consists of many GameObjects, and if the head does a certain moving pattern, all the GameObjects in the back should mimic that movement, i.e. trail the path.

    I tried many methods and the only thing I came up right now is, to spawn empty GameObjects eveytime the head changes direction and if the other GameObjects enters that empty GameObject, they will change direction accordingly.

    It works, however the movement sometimes doesn't sync perfectly, resulting in that the GameObjects aren't that neatly connected anymore.

    I think there should be a much better scripting way.
    I'd appreciate it, if anyone can give me any suggestions.
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Create a list of points and rotations. Every X seconds, store the position and orientation of the head in that list at index 0 and then loop through all segments and tell them to move towards the position and rotation of the appropriate index (0 = directly behind the head, 1 = segment #2, etc.). Prune the list of any excess entries.
     
  3. NotMyUsernameAgain

    NotMyUsernameAgain

    Joined:
    Sep 28, 2017
    Posts:
    126
    This was also my idea first, however what I was concered about is,
    if my head changes direction 2 times, how do I tell my other parts to go to point 1 first and then to point 2?
    Especially if I have many points the GameObjects need to go to, I don't know how to write the code.

    Also, if I do it like this way, can I be certain that gap between the GameObjects is always the same, no matter which speed my GameObjects go?

    Since the method I'm using right now (with the colliders) does has this problem, especiall if I move my GameObjects faster.
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    They'll always follow the segment in front of them, so if you're sampling at 20 times a second, the player isn't going to notice or care that the head went left for one frame before going right. You can always up the sample rate if you need it to be "basically 1:1".

    Whether you have gaps or not is up to you. You can either have the segment move exactly to the point (little to no gap depending on sample rate), or move in that point's general direction instead (maintain whatever distance you started with).
     
  5. NotMyUsernameAgain

    NotMyUsernameAgain

    Joined:
    Sep 28, 2017
    Posts:
    126
    I just remebered that there is a built in component called joints in unity.
    I've never used them before so I'm not quite sure but is it possible to connect my GameObjects like this, so they always keep their distance to each other?
     
  6. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    Grozzler has given you the answer. Basically it creates a history of the head movement which the segments will sample to calculate their position.

    Each segment would have an offset into the list of head positions, and the further down the list the offset is the further back the segment will be.

    Each segment's offset will be fixed, and as each new head position is added to the front of the list the oldest one is removed (once the max list length is reached). The maximum list length would be calculated by multiplying the number of segments by the index gap between each segment.
     
  7. NotMyUsernameAgain

    NotMyUsernameAgain

    Joined:
    Sep 28, 2017
    Posts:
    126
    OK, I'm sorry that I haven't tried that method up to now.

    First of all I've never used a list or points before so I don't really have an idea of how it works.

    However I tried it with Vector3 checkings, but the problem is:

    - I have 6 GameObjects
    - the head moves down and then turns left
    - the other parts follow the head and also turns left at the same point (check via if Vector3 is equal)
    - the head changes direction again, but the other parts are still moving downwards

    So which way would be best to check this?

    As I already said, I never used lists before so if that would be the solution, I'd appreciate it if someone can explain a bit more for me.
     
  8. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    The segments don't really need any sort of logic on them other than "move and rotate towards this point".

    I whipped up a quick example for you, lots of room for improvement:
    Code (csharp):
    1.  
    2.     public class SnakeController : MonoBehaviour
    3.    {
    4.        public const float SAMPLE_RATE = 0.15f;
    5.  
    6.        public Transform fire;
    7.  
    8.        public Transform head;
    9.        public Transform segmentPrefab;
    10.  
    11.        private List<SegmentController> segments;
    12.  
    13.        private List<Vector3> positions;
    14.        private List<Quaternion> rotations;
    15.  
    16.        private float sampleTime = 0f;
    17.        private float pitch = 0f;
    18.  
    19.        private void Start()
    20.        {
    21.            segments = new List<SegmentController>();
    22.  
    23.            positions = new List<Vector3>();
    24.            rotations = new List<Quaternion>();
    25.  
    26.            Vector3 scale = head.transform.localScale;
    27.  
    28.            for (int i = 0; i < 10; ++i)
    29.            {
    30.                scale -= new Vector3(0.15f, 0.15f, 0.15f);
    31.  
    32.                Transform segment = Instantiate<Transform>(segmentPrefab);
    33.                segment.position = head.position;
    34.                segment.localScale = scale;
    35.                segment.name = "Segment " + i;
    36.  
    37.                SegmentController controller = segment.gameObject.AddComponent<SegmentController>();
    38.                segments.Add(controller);
    39.            }
    40.  
    41.            Destroy(segmentPrefab.gameObject);
    42.  
    43.            fire.gameObject.SetActive(false);
    44.        }
    45.  
    46.  
    47.        private void Update()
    48.        {
    49.            if (Input.GetKeyDown(KeyCode.Space) == true)
    50.                fire.gameObject.SetActive(true);
    51.            if (Input.GetKeyUp(KeyCode.Space) == true)
    52.                fire.gameObject.SetActive(false);
    53.  
    54.  
    55.            head.transform.position += head.transform.forward * 15.0f * Time.deltaTime;
    56.  
    57.            pitch -= Input.GetAxis("Vertical") * 90.0f * Time.deltaTime;
    58.  
    59.            head.transform.rotation = Quaternion.Euler(pitch, 0f, 0f);
    60.  
    61.            sampleTime += Time.deltaTime;
    62.  
    63.            // this is where you add data to the list:
    64.            if (sampleTime >= SAMPLE_RATE)
    65.            {
    66.                sampleTime -= SAMPLE_RATE;
    67.  
    68.                positions.Insert(0, head.transform.position);
    69.                rotations.Insert(0, head.transform.rotation);
    70.  
    71.                if (positions.Count > segments.Count)
    72.                    positions.RemoveAt(positions.Count - 1);
    73.  
    74.                for (int i = 0; i < segments.Count; ++i)
    75.                {
    76.                    if (i >= positions.Count)
    77.                        return;
    78.  
    79.                    segments[i].Move(positions[i], rotations[i]);
    80.                }
    81.            }
    82.        }
    83.    }
    84.  
    85.     public class SegmentController : MonoBehaviour
    86.    {
    87.        private Coroutine coroutine;
    88.  
    89.        public void Move(Vector3 position, Quaternion rotation)
    90.        {
    91.            if (coroutine != null)
    92.                StopCoroutine(coroutine);
    93.  
    94.            coroutine = StartCoroutine(Follow(position, rotation));          
    95.        }
    96.  
    97.        private IEnumerator Follow(Vector3 position, Quaternion rotation)
    98.        {
    99.            float duration = SnakeController.SAMPLE_RATE;
    100.            float time = 0f;
    101.  
    102.            Vector3 startPosition = transform.position;
    103.            Vector3 endPosition = position;
    104.  
    105.            Quaternion startRotation = transform.rotation;
    106.            Quaternion endRotation = rotation;
    107.  
    108.            while(time <= duration)
    109.            {
    110.                time += Time.deltaTime;
    111.  
    112.                float t = time / duration;
    113.  
    114.                transform.position = Vector3.Lerp(startPosition, endPosition, t);
    115.                transform.rotation = Quaternion.Slerp(startRotation, endRotation, t);
    116.  
    117.                yield return null;
    118.            }
    119.  
    120.            coroutine = null;
    121.        }
    122.    }
    123.  
    ... and I added some fire breathing because you gotta love what you do.

    Result:
     
    Munchy2007 likes this.
  9. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    Spoken like a natural born programmer :)
     
    GroZZleR likes this.