Игрушка «Змейка» / 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, создается сетка позиций, она нужна чтобы рандомно раскидывать бонусы по ней. Сама змейка собирается в массиве, первый элемент которого - текущий объект, то есть, голова змеи. Игрок контролирует направление только для головы, все остальные части перемещаются поочередно, меняясь местами, от начала головы и до кончика хвоста. Позицию кончика хвоста мы запоминаем отдельно, только после этого происходит смена позиций. Это нужно для того, чтобы прицеплять новый блок хвоста.

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

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

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

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

Здравствуйте, а как запустить проект? Я открыла unity, импортировала туда ваш проект, но как запустмть игру? На экране она не появилась
Офлайн
Light 24 апреля 2019
Yulya, сцену еще надо открыть.
Офлайн
DAZL 15 октября 2019
Сейчас не работает и выдает ошибки.Перепроверьте.
Офлайн
Light 15 октября 2019
DAZL, всё работает, проверял на версии 2019.2.9
Офлайн
MrApk 23 августа 2020
Zaar Salih,
советую вам попробовать сделать самому)
Офлайн
Rama 10 сентября 2020
Yulya,
открой сцену называется он Game
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика