Make an object follow another object's path (Snake game)
Good day, community. I am trying to make a 3D version of the game "Snake", but I am running into serious problems trying to make the first snake segment follow the head of the snake.
First, some explanation of how I am going about making this game: the body and head of the snake are sphere GameObjects. You can only control the head and, whenever you eat an item, the snake will "grow" a "segment" (anything but the head of the snake I will refer to as a segment) and immediately follow either the head or another segment that is directly ahead of it. For simplicity, I am trying to make just one segment follow the head of the snake, as once I figure how to make that one segment follow the head it will be the same logic for the rest of the segments.
Now, after multiple failed attempts at making the segment follow the head (including the use of different kinds of joints), I arrived at the conclusion that the best way to go about it is to have the segment follow the path that the head makes as it moves.
Let me now explain how i move the head: the head is moved applying a velocity to the rigidbody component of the sphere. So, every time I press an arrow key, I set the direction to either a Vector3.right, Vector3.left, Vector3.back or Vector3.forward. Then, in FixedUpdate, I set the rigidbody's velocity to this new direction multiplied by a certain speed. Whenever I press a directional key, i also register the position where the turn in direction took place. The idea is this: whenever I change the direction of the head I register both the position and turn direction so that, when the body segment that follows the head reaches this registered position where the turn took place, it will then change its velocity in the direction that the head turned to in that position. In this way, I can effectively make the segment follow the head's path by sending this position and turn direction to the segment so that it can make a comparison, each LateUpdate, that enables it to know when it has reached that turning position so that it changes direction at that position and adjust its velocity accordingly.
Now, as some of you may already expect, I have failed miserably in trying to make this equality comparison work. Since the positions are vectors, where each component is a float, the comparisons fail 100% of the time and the segment is not able to change its direction at the exact position that the head made its turn. Not even Mathf.Approximately() worked. So i had to subtract the components of both the head's turning position and that of the segment's current position and, if the difference is less than 0.0999999 (which is the number that I found worked most of the time), then go ahead and turn the direction so that when the next FixedUpdate comes, it will change its velocity in the direction that the head made its turn at that exact same position.
The problem with this approach is that it sometimes does not work. Sometimes the comparison seems to fail and the segment flies past the position it was supposed to make the turn. I have no idea why it is happening. The other problem i have is that if i don't make the segment's spawning position AT LEAST 2 units in the opposite direction of the head's current direction, it will miss the turning position 100% of the time. So that means that if I try to make the spheres follow each other as close as possible (in this case, at most 1 unit away from the head's position at any time), then it will not make the turns that it has to make. This makes for a very ugly looking separation between segments.
Thanking you in advance, I would like to know if there is either a more elegant solution to what I am trying to achieve, or at least a little help into what I may do to fix the current implementation. I am attaching the code of the segment that is supposed to follow the head. The naming for the objects are in Spanish, but it may not be that hard to figure out what is going on from a non-Spanish speaking person's perspective.
I have also properly commented the code, so that you can understand what I am doing at each sentence.
using UnityEngine;
using System.Collections.Generic;
using Clases;
public class ControladorSegmento : MonoBehaviour {
//This is a generic list of Objects that store both turning position and turning direction that the head has made
public List<PosicionCambioDireccion> listaPosicionesCambioDireccion;
//This would be the head in the case of only one segment that is following
public GameObject lider;
//Speed
public float rapidez;
//Direction
public Vector3 direccion;
//This is an index that is used to iterate over the list of turning points
private int indiceCambioDireccion;
// Use this for initialization
void Start () {
indiceCambioDireccion = 0;
listaPosicionesCambioDireccion = new List<PosicionCambioDireccion>();
//First find the Head of the snake so that we can access its position in order to determine segment spawning position
lider = GameObject.Find("Cabeza");
//Set the position of the new segment 2 units right behind the head
transform.position = lider.GetComponent<Transform>().position - lider.GetComponent<ControladorCabeza>().direccion * 2f;
//Get the current direction of the head so that the segment inmediately moves in its direction
direccion = lider.GetComponent<ControladorCabeza>().direccion;
}
void LateUpdate () {
//Check if there has been a change in direction that the segment has to follow
if ((listaPosicionesCambioDireccion.Count > 0) && (listaPosicionesCambioDireccion.Count > indiceCambioDireccion)) {
//Compare how close we are to the turning position. If we are sufficiently close, change segment direction
if (Mathf.Abs(transform.position.x - listaPosicionesCambioDireccion[indiceCambioDireccion].posicion.x) < 0.0999999 &&
Mathf.Abs(transform.position.z - listaPosicionesCambioDireccion[indiceCambioDireccion].posicion.z) < 0.0999999) {
//Change segment direction at the current position
direccion = listaPosicionesCambioDireccion[indiceCambioDireccion].direccion;
//Increment turning positions list index so that we get the next turning point in the list (if there is any)
indiceCambioDireccion++;
}
}
}
void FixedUpdate() {
//Change the velocity
GetComponent<Rigidbody>().velocity = direccion * rapidez;
}
}
Thank you so very much in advance. I am still really new working with Unity and it seems I have still much to learn, but after close to two weeks trying to find a solution to the apparent simple task of just making an object follow another, and failing, I have no choice but to turn to you guys. And again, if you have a more elegant solution to what I am trying to achieve, please let me know. Right know I am aware that this implementation could cause a sort of memory leak in the sense that, as long as the head keeps changing direction, the list of turning points that is stored is going to keep growing and growing which obviously is something one should avoid.