Menu
CoddyTech

Tự cắn mình

Bài học 10 trên 14 trong khóa học Game Unity: rắn 3D của Coddy.

Con rắn chết khi đầu đâm vào đuôi của chính nó, giống như khi đâm vào tường. Phép kiểm tra diễn ra ở cùng vị trí: tại next, ô phía trước, trước khi đầu di chuyển, nên đầu vẫn ở nguyên chỗ khi trò chơi kết thúc.

Một đoạn thân thì an toàn. Ở bước này, đoạn cuối cũng di chuyển lên, rời khỏi ô của nó, nên đầu rắn đi theo đuôi vào ô đó sẽ không chạm vào đoạn thân ấy. Một con rắn gồm bốn khối lập phương có thể đi vòng quanh một hình vuông 2 nhân 2 mãi theo cách này. Phép kiểm tra bỏ qua đoạn cuối bằng cách dừng trước đoạn cuối một vị trí:

// Mọi phần tử trừ phần tử cuối cùng
for (int i = 0; i < items.Count - 1; i++)
{
    Debug.Log(items[i].name);
}

Khi cả tường lẫn đuôi đều gây chết, bạn có thể thua trò chơi. Chương tiếp theo sẽ thêm điểm số và cách chơi lại.

challenge icon

Thử thách

Trung bình

Cắn phải đuôi sẽ kết thúc trò chơi.

  1. Trong Step(), sau khi kiểm tra tường, lặp qua đuôi bằng i < tail.Count - 1, bỏ qua đoạn cuối cùng.
  2. Nếu tail[i].position == next, gọi Die() rồi trả về, để đầu rắn không di chuyển.

Nhấn Chơi, làm rắn dài ra rồi rẽ vào đuôi: rắn sẽ dừng lại. Sau đó nhấn Kiểm tra.

Tự làm thử

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

public class Snake : MonoBehaviour
{
    public float tick = 0.15f;   // Số giây giữa hai bước di chuyển
    public int edge = 7;         // Các ô trên bảng trải từ -edge đến edge
    public bool alive = true;
    public List<Transform> tail = new List<Transform>();
    public Transform segmentPrefab;
    private float timer;
    private Vector3 direction = Vector3.right;       // Hướng di chuyển của đầu rắn ở bước trước
    private Vector3 nextDirection = Vector3.right;   // Hướng rẽ chờ đến bước tiếp theo

    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;
    }
}

Tất cả bài học trong Game Unity: rắn 3D

Tự luyện tập: Playground Unity