Укуси себя
Урок 10 из 14 курса Игра на Unity: 3D-змейка на Coddy.
Змея погибает, когда её голова сталкивается с собственным хвостом, так же как и при столкновении со стеной. Проверка выполняется там же: для next, клетки впереди, до перемещения головы, поэтому после окончания игры голова остаётся на месте.
Один сегмент безопасен. На этом шаге последний сегмент тоже перемещается вперёд, освобождая свою клетку, поэтому голова, которая следует за концом хвоста в эту клетку, никогда его не коснётся. Змея из четырёх кубиков может так бесконечно кружить по квадрату 2 на 2. Проверка не учитывает последний сегмент, останавливаясь на один элемент раньше конца:
// каждый элемент, кроме последнего
for (int i = 0; i < items.Count - 1; i++)
{
Debug.Log(items[i].name);
}Когда и стены, и хвост смертельно опасны, игру можно проиграть, а в следующей главе мы добавим счёт и возможность сыграть снова.
Задание
СреднеВ Step(), после проверки стены: если next — клетка любого сегмента хвоста, кроме последнего, вызови Die() и выполни возврат, чтобы голова не двигалась.
Нажми Play, вырасти змейку и укуси свой хвост, затем нажми Check.
Попробуйте сами
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Snake : MonoBehaviour
{
public float tick = 0.15f; // секунды между двумя шагами
public int edge = 7; // клетки поля расположены от -edge до edge
public bool alive = true;
public List<Transform> tail = new List<Transform>();
public Transform segmentPrefab;
private float timer;
private Vector3 direction = Vector3.right; // направление движения головы на последнем шаге
private Vector3 nextDirection = Vector3.right; // поворот, ожидающий следующего шага
void Update()
{
if (!alive) return;
Keyboard keyboard = Keyboard.current;
if (keyboard != null)
{
if (keyboard.upArrowKey.wasPressedThisFrame || keyboard.wKey.wasPressedThisFrame) Turn(Vector3.forward);
if (keyboard.downArrowKey.wasPressedThisFrame || keyboard.sKey.wasPressedThisFrame) Turn(Vector3.back);
if (keyboard.leftArrowKey.wasPressedThisFrame || keyboard.aKey.wasPressedThisFrame) Turn(Vector3.left);
if (keyboard.rightArrowKey.wasPressedThisFrame || keyboard.dKey.wasPressedThisFrame) Turn(Vector3.right);
}
timer += Time.deltaTime;
if (timer >= tick)
{
timer -= tick;
Step();
}
}
void Turn(Vector3 newDirection)
{
if (newDirection != -direction)
{
nextDirection = newDirection;
}
}
void Step()
{
direction = nextDirection;
Vector3 next = transform.position + direction;
if (Mathf.Abs(next.x) > edge || Mathf.Abs(next.z) > edge)
{
Die();
return;
}
Vector3 tailEnd = tail.Count > 0 ? tail[tail.Count - 1].position : transform.position;
for (int i = tail.Count - 1; i > 0; i--)
{
tail[i].position = tail[i - 1].position;
}
if (tail.Count > 0)
{
tail[0].position = transform.position;
}
transform.position = next;
if (GameManager.Instance.IsFood(next))
{
tail.Add(Instantiate(segmentPrefab, tailEnd, Quaternion.identity));
GameManager.Instance.Eat();
}
}
public bool Occupies(Vector3 cell)
{
if (transform.position == cell) return true;
foreach (Transform segment in tail)
{
if (segment.position == cell) return true;
}
return false;
}
void Die()
{
alive = false;
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.