打磨简单的贪吃蛇游戏

Polishing Simple Snake Game

我一直在按照我发现的教程在 Unity(C#) 中创建一个简单的贪吃蛇游戏:

https://www.youtube.com/watch?v=U8gUnpeaMbQ&t=1s&ab_channel=Zigurous

我发现这是一个非常好的教程,到最后我有一个非常好的蛇游戏,但是,我想更进一步,让移动更愉快,添加尾巴,Gameover,等等

现在我的问题是,如果玩家快速连续按 2 个可接受的方向试图抓住一些食物,蛇的头会跳过食物,完全错过食物。

这是由于以下代码造成的:

private void Update() //Gets Key Inputs and execute Commands
{   
    if (Input.GetKeyDown(KeyCode.UpArrow) )
    {
        while(tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if(_direction != Vector2.down)
        {
            _direction = Vector2.up;
            tempPosition = _segments[0].position;
        }
        
    }
    else if (Input.GetKeyDown(KeyCode.LeftArrow) )
    {
        while (tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.right)
        {
            _direction = Vector2.left;
            tempPosition = _segments[0].position;
        }
    }
    else if (Input.GetKeyDown(KeyCode.RightArrow) )
    {
        while (tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.left)
        {
            _direction = Vector2.right;
            tempPosition = _segments[0].position;
        }
    }
    else if (Input.GetKeyDown(KeyCode.DownArrow) )
    {            
        while (tempPosition == _segments[0].position)
        {
            for(int i = _segments.Count -1; i>0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.up)
        {
            _direction = Vector2.down;
            tempPosition = _segments[0].position;
        }
    }

如您所见,按下一个键会立即移动 Snake 的头部,从而导致该问题。

然而,如果没有这样编码,快速连续按下 2 个键会导致蛇与自身发生碰撞(假设蛇向右移动,如果快速连续按下向上和向左,蛇会在被击中之前开始向左移动能够向上移动,与它相撞 body).

完整代码如下:

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

public class Snake : MonoBehaviour
{
private Vector2 _direction = Vector2.right;
public List<Transform> _segments = new List<Transform>();
public Transform segmentPrefab;
public Transform tail;
public int initialSize = 4;
public int score = 0;
private Vector3 tempPosition;
public GameObject food;
public Text gameOver;
private void Start()
{
    ResetState();
}
private void Update() //Gets Key Inputs and execute Commands
{   
    if (Input.GetKeyDown(KeyCode.UpArrow) )
    {
        while(tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if(_direction != Vector2.down)
        {
            _direction = Vector2.up;
            tempPosition = _segments[0].position;
        }
        
    }
    else if (Input.GetKeyDown(KeyCode.LeftArrow) )
    {
        while (tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.right)
        {
            _direction = Vector2.left;
            tempPosition = _segments[0].position;
        }
    }
    else if (Input.GetKeyDown(KeyCode.RightArrow) )
    {
        while (tempPosition == _segments[0].position)
        {
            for (int i = _segments.Count - 1; i > 0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.left)
        {
            _direction = Vector2.right;
            tempPosition = _segments[0].position;
        }
    }
    else if (Input.GetKeyDown(KeyCode.DownArrow) )
    {            
        while (tempPosition == _segments[0].position)
        {
            for(int i = _segments.Count -1; i>0; i--)
            {
                _segments[i].position = _segments[i - 1].position;
            }
            this.transform.position = new Vector3(
                Mathf.Round(this.transform.position.x + _direction.x),
                Mathf.Round(this.transform.position.y + _direction.y),
                0.0f
                );
        }
        if (_direction != Vector2.up)
        {
            _direction = Vector2.down;
            tempPosition = _segments[0].position;
        }
    }
    if(Input.GetKeyDown(KeyCode.R))
    {
        ResetState();
    }
}
private void FixedUpdate() //Handles moviment
{
    if (gameOver.gameObject.activeSelf == false)
    {            
        for (int i = _segments.Count - 1; i > 0; i--)
        {
            _segments[i].position = _segments[i - 1].position;
        }
        this.transform.position = new Vector3(
            Mathf.Round(this.transform.position.x + _direction.x),
            Mathf.Round(this.transform.position.y + _direction.y),
            0.0f
            );
    }
}
/*Instantiates a new segment, sets it's position to tail position,
  destroys tail from list and adds new segment in it's place, adds new tail at end*/
private void Grow() 
{
    Transform segment = Instantiate(this.segmentPrefab);
    segment.position = _segments[_segments.Count - 1].position;
    Destroy(_segments[_segments.Count - 1].gameObject);
    _segments.Remove(_segments[_segments.Count - 1]);
    _segments.Add(segment);
    Transform segmenttail = Instantiate(this.tail);

    segmenttail.position = _segments[_segments.Count - 1].position;

    _segments.Add(segmenttail);
}
private void ResetState()
{
    gameOver.gameObject.SetActive(false);
    tempPosition.x = 1000;
    score = 0;
    for (int i = 1; i < _segments.Count; i++)
    {
        Destroy(_segments[i].gameObject);
    }
    _segments.Clear();
    _segments.Add(this.transform);
    for (int i = 1; i < initialSize; i++)
    {
        _segments.Add(Instantiate(this.segmentPrefab));
    }
    _segments.Add(Instantiate(this.tail));
    this.transform.position = Vector3.zero;
    this.GetComponent<SpriteRenderer>().enabled = (true);
    food.GetComponent<Food>().RandomizePosition();
}
private void OnTriggerEnter2D(Collider2D other)
{
    if (other.tag == "Food")
    {
        Grow();
        score++;
    }
    else if(other.tag == "Obstacle")
    {
        for (int i = 1; i < _segments.Count; i++)
        {
            Destroy(_segments[i].gameObject);
        }
        this.GetComponent<SpriteRenderer>().enabled=(false);
        _segments.Clear();
        food.gameObject.SetActive(false);
        gameOver.gameObject.SetActive(true);
    }

}

}

tl;dr: 在一个简单的贪吃蛇游戏中,当快速连续按下两个方向时,如何确保蛇在向第二个方向移动之前先向第一个方向移动而不会出现错误。

谢谢!

制作一个红色的立方体,控制蛇的运动方向,以及遇到食物和吃食物的功能。在Update()中,WSAD和方向键控制蛇头的移动方向。并且蛇头向上移动时不能向下移动,蛇头向左移动时不能向右移动。

         void Update () {
         if (Input.GetKey(KeyCode.W)||Input.GetKey("up")&&direction!= 
Vector2.down)
    {
        direction = Vector2.up;
    }
    if (Input.GetKey(KeyCode.S) || Input.GetKey("down") && direction != Vector2.up)
    {
        direction = Vector2.down;
    }
    if (Input.GetKey(KeyCode.A) || Input.GetKey("left") && direction != Vector2.right)
    {
        direction = Vector2.left;
    }
    if (Input.GetKey(KeyCode.D) || Input.GetKey("right") && direction != Vector2.left)
    {
        direction = Vector2.right;
    }

}

蛇与食物碰撞后,body会长出一段。当遇到食物时,它会先破坏食物,然后再增加自己的长度body。此时设置的碰撞位标志会变为真,body长度会增加。但是撞到自己的时候,撞到墙的时候就会死,这个时候会导入到一开始的场景中。

  void OnTriggerEnter(Collider other)
  {
    if (other.gameObject.CompareTag("Food"))
    {
        //Debug.Log("hit it!");
        Destroy(other.gameObject);
        flag = true;

    }
    else
    {
        //SceneManager.LoadScene(0)
        Application.LoadLevel(1);
    }
}

body每次增长的算法就是吃蛇的难度。网上很多算法都是用listlinked列表实现的。 linked列表的节点表示蛇增减很方便。移动时,只需要添加一个头节点,将其移除即可。尾节点就够了,要吃东西,只需要加一个头节点就可以了。这个算法绝对是巧妙的,但是因为网上太多了,下面是用linked list实现的另一种snake-eating算法。蛇头一动不动,最后的body挪到了前方,然后缓缓向后移动。下面蓝色方块(body部分的一个设置)一步步移动,可以看到这个效果。蛇的 body 部分的代码贴在下面。如果食物被吃掉,则标志为真。这是在蛇的body中插入一个预制的Cube,蛇的body就会变长。当没有食物时,它会查看此时body的数字。当数字大于0时,最后一个放在最前面,一直循环到最后。

  void Move()
  {
    Vector3 VPosition = transform.position;
    transform.Translate(direction);
    if (flag)
    {
        GameObject bodyPrefab = (GameObject)Instantiate(gameObjecgtBody, VPosition, Quaternion.identity);
        Body.Insert(0, bodyPrefab.transform);
        flag = false;
    }
    else if (Body.Count > 0)
    {
        Body.Last().position = VPosition;
        Body.Insert(0, Body.Last());
        Body.RemoveAt(Body.Count - 1);
    }
}

食物的出现是一个随机过程。这时,食物出现在一个随机的位置。 InvokeRepeating("ShowFood", 1, 4);意思是4秒后会调用ShowFood()函数,此时它会随机出现在ShowFood中。食物。 下面是 ShowFood() 函数的代码

void ShowFood()
 {
    int x = Random.Range(-30, 30);
    int y = Random.Range(-22, 22);
    Instantiate(SSFood, new Vector2(x,y), Quaternion.identity);
    
  }

特别注意的是,在制作蛇头和body的时候,如果碰撞体积body设置为unit 1,那么蛇的侧面body也会击中食物,触发对撞机。所以将collider的音量设为0.8,略小于1.I也是从网上找的资料,希望对你有帮助,这个link是源码https://github.com/xiaogeformax/Snake/tree/master/Snake5.2