Игрушка «Змейка» / Snake в 2D

Будем делать игрушку, которая появилась в далеких 70-х, но, тем не менее, остается популярной до сих пор. Выпущено так же и официальное продолжение классической «Змейки». Впрочем, существует огромное множество клонов, что по сути уже не важно, где там официально или не официально. Мы пополним дружные ряды клонов, еще одним, что-то больше похожее на классику, или около того. В любом случае, основная задача в том, чтобы понять, как делается движение хвоста змейки, это может пригодиться для будущих проектов, да и в целом тоже. А всё действо будет проходить в двухмерном пространстве.


Создаем новый 2D проект. Нарисуем белый квадрат, обычный 100х100 квадрат, из этого спрайта будем делать змейку. В проекте, в настройках камеры, ставим параметр Size на 20. Сделаем на основе спрайта несколько префабов. Первый, назовем Bonus, на спрайт просто добавляем 2D бокс коллайдер и уменьшаем размер коллайдера в два раза, и ставим его в режим триггера. Смысл в том, чтобы контакт с коллайдером срабатывал только после перекрытия объектов, а не когда они рядом. Плюс, цепляем скрипт:

using UnityEngine;
using System.Collections;

public class Bonus : MonoBehaviour {

	public float timeout = 10;

	void Start () 
	{
		Destroy(gameObject, timeout);
	}

	void OnTriggerEnter2D(Collider2D other) 
	{
		Snake.tailCount++;
		Destroy(gameObject);
	}
}

Тут у нас время жизни бонуса, а сам он просто добавляет еще один элемент "хвоста".

Добавляем еще один префаб. Почти всё тоже-же самое, но выключаем триггер и ставим тег Player, и без скрипта. Это будет часть нашей змейки, назовем ее Tail. Она будет подцепляться в конец хвоста.

Теперь финальный префаб. Как и предыдущий, но размеры коллайдера один к одному, дополнительно добавляем Rigidbody2D с замороженной позицией по X и Y и вращением, еще отключаем гравитацию, в общем как на скриншоте:

Игрушка «Змейка» / Snake в 2D

Назовем наш префаб Snake и вешаем на него одноименный скрипт:

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

public class Snake : MonoBehaviour {

	public KeyCode left = KeyCode.A;
	public KeyCode right = KeyCode.D;
	public KeyCode up = KeyCode.W;
	public KeyCode down = KeyCode.S;

	public int size = 32;
	public float shift = 1;
	public float timeoutMove = 0.5f;
	public float timeoutBonus = 5;
	public GameObject _tail;
	public GameObject _bonus;
	private float curTimeout;
	private Vector3[,] pos;
	private List<GameObject> tail;
	private Vector3 tail_pos;
	private Vector3 tail_last;
	private int t_Count;
	private float h, v;

	public static int tailCount;
	public static bool lose;

	void Start () 
	{
		lose = false;
		tailCount = 1;
		t_Count = tailCount;
		tail = new List<GameObject>();
		tail.Add(this.gameObject);
		float posX = -shift*size/2-shift;
		float posY = Mathf.Abs(posX);
		float Xreset = posX;
		pos = new Vector3[size, size];
		for(int y = 0; y < size; y++)
		{
			posY -= shift;
			for(int x = 0; x < size; x++)
			{
				posX += shift;
				pos[x,y] = new Vector3(posX, posY, 0);
			}
			posX = Xreset;
		}

		tail[0].transform.position = pos[size/2, size/2];
		StartCoroutine (AddBonus());
	}

	IEnumerator AddBonus()
	{
		GameObject clone = Instantiate(_bonus) as GameObject;
		clone.transform.position = pos[Random.Range(0, size), Random.Range(0, size)];
		yield return new WaitForSeconds (timeoutBonus);
		if(!lose) StartCoroutine (AddBonus());
	}

	void Move(int count)
	{
		tail_last = tail[tail.Count-1].transform.position;

		tail_pos = tail[0].transform.position;
		tail[0].transform.position = new Vector3(tail_pos.x + h, tail_pos.y + v, 0);

		Vector3 tmp = Vector3.zero;

		for(int j = 1; j < count; j++)
		{
			tmp = tail[j].transform.position;
			tail[j].transform.position = tail_pos;
			tail_pos = tmp;
		}
	}

	void Update () 
	{
		curTimeout += Time.deltaTime;
		if (curTimeout > timeoutMove) 
		{
			curTimeout = 0;
			Move(tailCount);
		}

		if(t_Count != tailCount)
		{
			GameObject clone = Instantiate(_tail) as GameObject;
			clone.name = "Tail_" + tail.Count;
			clone.transform.position = tail_last;
			tail.Add(clone);
		}
		t_Count = tailCount;

		if(Input.GetKeyDown(left))
		{
			h = -shift;
			v = 0;
		}
		else if(Input.GetKeyDown(right)) 
		{
			h = shift;
			v = 0;
		}
		else if(Input.GetKeyDown(down)) 
		{
			v = -shift;
			h = 0;
		}
		else if(Input.GetKeyDown(up)) 
		{
			v = shift;
			h = 0;
		}

		if(lose)
		{
			Debug.Log("Вы проиграли!");
			enabled = false;
		}
	}

	void OnCollisionEnter2D(Collision2D other) 
	{
		if(other.collider.tag == "Player")
		{
			lose = true;
		}
	}
}

Логика работы. На основе размера size и шага shift, создается сетка позиций, она нужна чтобы рандомно раскидывать бонусы по ней. Сама змейка собирается в массиве, первый элемент которого - текущий объект, то есть, голова змеи. Игрок контролирует направление только для головы, все остальные части перемещаются поочередно, меняясь местами, от начала головы и до кончика хвоста. Позицию кончика хвоста мы запоминаем отдельно, только после этого происходит смена позиций. Это нужно для того, чтобы прицеплять новый блок хвоста.

Скачать проект:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Комментариев 8

Офлайн
Light 24 апреля 2019
Yulya, сцену еще надо открыть.
Офлайн
Yulya 24 апреля 2019
Цитата: Light
alexxx777, unitypackage надо открывать через unity.

Здравствуйте, а как запустить проект? Я открыла unity, импортировала туда ваш проект, но как запустмть игру? На экране она не появилась
Офлайн
Light 16 сентября 2018
world_iluminatoin, переменная timeoutMove.
Офлайн
world_iluminatoin 16 сентября 2018
А какая часть кода отвечает за скорость?
Офлайн
maxim_7777 1 ноября 2017
Очень интересно, а главное на простых примерах можно чему-то научиться.
Хотелось бы найти такую же змейку, только чтобы она плавно перемещалась по сетке вместе со звеньями тела. Сам сделал только голову. Управляется и перемещается так как надо, а вот тело не хочет двигаться по сетке. Даже не знаю как реализовать. Не подскажите куда копать, как заставить звенья тела тоже двигаться по сетке вслед за головой?
Офлайн
Light 4 июня 2017
alexxx777, unitypackage надо открывать через unity.
Офлайн
alexxx777 3 июня 2017
Здравствуйте !
Подскажите пожалуйста как открыть файл snake.unitypackage через IntelliJ IDEA.
Или как загрузить код через IntelliJ IDEA.
Офлайн
Zaar Salih 18 ноября 2015
Ваш портал настоящая находка. Жду с нетерпением когда вы покажите как делать крестики нолики :3
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика