How to move a GameObject after it has been initiated?

Hi guys,

I’m just starting to learn Unity and am developing Snake as a way to learn the engine and C#.

I’m having a lot of trouble getting the snake body to work. At the moment, I have a snake head (just one cube that I can control), and I want to write a script so that when I press spacebar, it creates a body segment. I can get the script to instantiate a new body segment every time I hit space bar, but I can’t move the body segments after they’ve been initiated.

I know this is a pretty basic topic and there’s a lot of stuff here about moving gameobjects, but I’ve been searching this forum and reading answers for a while, and still can’t quite figure it out. Could you guys take a look a the, and let me know what I’m doing wrong?

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

public class SnakeController : MonoBehaviour {
	
	enum moveDirection{up, down, left ,right};
	
	moveDirection d;
	
	List<Vector3> bodyPositions = new List<Vector3>();
	
	Vector3 headPosition = new Vector3(0, 0, 0);
	
	public GameObject snakeBodyPrefab;
	
	private GameObject clone;
	
	// Use this for initialization
	void Start () {
			
		
        
	}
	
	// Update is called once per frame
	void Update ()
	{
		
		bodyPositions.Add(transform.position);
		headPosition = transform.position;
	
		
		//Control the movement
		if (Input.GetKey("up"))
			d = moveDirection.up;
		
        if (Input.GetKey("down"))
			d = moveDirection.down;
		
		if (Input.GetKey("right"))
			d = moveDirection.right;
		
		if (Input.GetKey("left"))
			d = moveDirection.left;
		
		if (d == moveDirection.up) {
			transform.position += new Vector3(0, 0, 0.1f);
		}
		
		if (d == moveDirection.down) {
			transform.position += new Vector3(0, 0, -0.1f);
		}
		
		if (d == moveDirection.right) {
			transform.position += new Vector3(0.1f, 0, 0);
			
		}
		
		if (d == moveDirection.left) {
			transform.position += new Vector3(-0.1f, 0, 0);
		}
		
		
		
		Debug.Log(headPosition.x.ToString());
		//Control growth of snake
		if (Input.GetKey(KeyCode.Space)) {
			Debug.Log("grow");
			
			clone = Instantiate(snakeBodyPrefab, transform.position, Quaternion.identity) as GameObject;
		    
			clone.transform.Translate(new Vector3(0,0,0));
		}
	}

}

This is the part I’m mainly having trouble with:

clone = Instantiate(snakeBodyPrefab, transform.position, Quaternion.identity) as GameObject;
		    
clone.transform.Translate(new Vector3(0,0,0));

If I don’t include the second line, it’s fine, and I just keep dropping body segments where I move. But when I try to move these segments, I get “NullReferenceException: Object reference not set to an instance of an object”

Any advices would be greatly appreciated. Thank you,

Matt

The logic is wrong. If you want a single script to control all body segments, you will need an additional List< GameObject> with the segment objects, so that you know what to move. Another point: you should rotate the head to the movement direction, and make each segment look to the one to which it’s appended.

Actually, an easier way to make a snake game in Unity is to move only the head, and use a script in each segment to make it follow the segment ahead. The snake body will follow a smoother trajectory, but maybe it’s ok in your case.

  • Head script:

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

public class HeadScript : MonoBehaviour {

  enum moveDirection{up, down, left ,right};
  moveDirection d = moveDirection.up;

  public GameObject snakeBodyPrefab;
  public int initialSize = 5;
  public float speed = 1.5f; // speed to move
  public float length = 1.0f; // segment length
  public float turnSpeed = 5.0f; // speed to turn

  Transform tail;

  void Start(){
    tail = transform; // the head is the tail, initially
    for (int i = 0; i < initialSize; i++){
      AddSegment(); // create initial segments
    }
  }

  void Update(){
    // control the direction (reversion not allowed!)
    if (Input.GetKey("up") && d != moveDirection.down)
      d = moveDirection.up;
    if (Input.GetKey("down") && d != moveDirection.up)
      d = moveDirection.down;
    if (Input.GetKey("right") && d != moveDirection.left)
      d = moveDirection.right;
    if (Input.GetKey("left") && d != moveDirection.right)
      d = moveDirection.left;
    // define the direction:
    Vector3 dir = Vector3.forward;
    if (d == moveDirection.up) {
      dir = Vector3.forward;
    }
    if (d == moveDirection.down) {
      dir = Vector3.back;
    }
    if (d == moveDirection.right) {
      dir = Vector3.right;
    }
    if (d == moveDirection.left) {
      dir = Vector3.left;
    }
    // rotate gradually to the desired direction:
    transform.forward = Vector3.Slerp(transform.forward, dir, turnSpeed * Time.deltaTime);
    // move the head in its forward direction:
    transform.Translate(Vector3.forward * speed * Time.deltaTime);
    //Control growth of snake
    if (Input.GetKeyDown(KeyCode.Space)) {
      AddSegment();
    }
  }

  // add segment behind the current tail
  void AddSegment(){
    // calculate the position behind the tail:
    Vector3 pos = tail.position - length * tail.forward;
    // create the segment at the calculated position:
    GameObject segm = (GameObject) Instantiate(snakeBodyPrefab, pos, tail.rotation);
    // get its SegmentScript:
    SegmentScript segScript = segm.GetComponent< SegmentScript>();
    // make new segment follow the old tail:
    segScript.segmentAhead = tail;
    // the tail now is the new segment:
    tail = segm.transform;
  }
}

  • Segment script:

using UnityEngine;
using System.Collections;

public class SegmentScript : MonoBehaviour {

  public Transform segmentAhead; // which segment this one follows

  float length;

  void Start(){
    length = Vector3.Distance(segmentAhead.position, transform.position);
  }
  
  void Update(){
    // look to the segment ahead:
    transform.LookAt(segmentAhead);
    // calculate the new position keeping the distance to segmentAhead:
    Vector3 newPos = segmentAhead.position - length * transform.forward;
    transform.position = newPos; // move the segment
  }
}