贪食蛇(unity实现)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43701019/article/details/98899193

——个人笔记

蛇的移动

  • 经典的贪食蛇主要功能就是移动和吃东西生长。这次主要介绍两种常用的移动方式,第一种是比较正常的就是头移动,然后后面各个身体移动上一个身体的位置。第二种头移动,身体最后一个(尾巴)移动到头原来的位置。

    • 两种方法的图示:
      第一种方式:头为绿色,身体为粉红,①开始是第一条,然后②头向前移动,身体就跟着移动,并且为上一个的位置,转弯也一样。这种就是移动头部,然后后面的就是移动到上一个的位置。
      在这里插入图片描述
      第二种方式:对比索引的数字,位置没有区别(达到的效果一样),但是原理完全不一样,这一种会省去了很多移动,只要移动头,然后把尾巴(身体最后的一个)移到头原来的位置。
      在这里插入图片描述
    //放在蛇头的脚本
    public List<Transform> bodyList = new List<Transform>();//身体
    public float velocity = 0.35f;//每次移动的间隔时间
    public int step;//每次移动的步长
    private int x;
    private int y;
    private Vector3 headPos;//头的位置
    
    void Move()
    {
        headPos = gameObject.transform.localPosition;   //保存下来蛇头移动前的位置
         //蛇头向期望位置移动
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, headPos.z); 
        if (bodyList.Count > 0)
        {
            //方法一
            //bodyList.Last().localPosition = headPos; //将蛇尾移动到蛇头移动前的位置
            //bodyList.Insert(0, bodyList.Last());    //将蛇尾在List中的位置更新到最前
            //bodyList.RemoveAt(bodyList.Count - 1);    //移除List最末尾的蛇尾引用
    
            //方法二(从后面移动方便,如果从前面移动就要记录移动前的位置再向前移动)
            for (int i = bodyList.Count - 2; i >= 0; i--)  //从后往前开始移动蛇身
            {
                bodyList[i + 1].localPosition = bodyList[i].localPosition;  //每一个蛇身都移动到它前面一个节点的位置
            }
            bodyList[0].localPosition = headPos;  //第一个蛇身移动到蛇头移动前的位置
        }
    }
    

控制蛇

  • 我们move()方法要不停调用,那么我们会这样写InvokeRepeating("Move", 0, velocity);,如果我们想按空格加速,怎么办?把velocity加大就行?没那么简单,按下空格,我们就CancelInvoke(); InvokeRepeating("Move", 0, velocity - 0.2f);知道什么意思嘛?第一句取消重复调用,第二句重新重复调用,间隔时间缩短也就是加速。懂了吧?那么松开空格,我们再一次取消,然后重新重复调用,间隔时间恢复就行了。其实我们按下w,a,s,d我们也应该取消再重新重复调用move,为什么呢?因为Update间隔和我们设置的间隔不一致,那么比如本来蛇是向左走的,当我们手速够快连续按了sd,蛇会直接调头撞到身体。这样会影响体验的哦。

    void Start()
        {
            InvokeRepeating("Move", 0, velocity);
            x = 0;y = step;
        }
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && MainUIController.Instance.isPause == false && isDie == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity - 0.2f);
        }
        if (Input.GetKeyUp(KeyCode.Space) && MainUIController.Instance.isPause == false && isDie == false)
        {
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
        if (Input.GetKeyDown(KeyCode.W) && y != -step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
            x = 0;y = step;
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
        if (Input.GetKeyDown(KeyCode.S) && y != step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0; y = -step;
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
        if (Input.GetKeyDown(KeyCode.A) && x != step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step; y = 0;
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
        if (Input.GetKeyDown(KeyCode.D) && x != -step && MainUIController.Instance.isPause == false && isDie == false)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step; y = 0;
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);
        }
    }
    

食物(道具)生成

public class FoodMaker : MonoBehaviour
{
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }
   //最大最小和偏移,出现有偏移值是因为我有一部分用来做分数显示之类的
    public int xlimit = 21;
    public int ylimit = 11;
    public int xoffset = 7;
    
    public GameObject foodPrefab;//食物prefab
    public GameObject rewardPrefab;//道具prefab
    public Sprite[] foodSprites;//不同食物精灵
    private Transform foodHolder;//生成食物的父级

    void Awake()
    {
        _instance = this;
    }

    void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood(false);
    }
	//这个很简单,理解就行
    public void MakeFood(bool isReward)
    {
        int index = Random.Range(0, foodSprites.Length);
        GameObject food = Instantiate(foodPrefab);
        food.GetComponent<Image>().sprite = foodSprites[index];
        food.transform.SetParent(foodHolder, false);
        int x = Random.Range(-xlimit + xoffset, xlimit);
        int y = Random.Range(-ylimit, ylimit);
        food.transform.localPosition = new Vector3(x * 30, y * 30, 0);
        //是否生成道具
        if (isReward)
        {
            GameObject reward = Instantiate(rewardPrefab);
            reward.transform.SetParent(foodHolder, false);
            x = Random.Range(-xlimit + xoffset, xlimit);
            y = Random.Range(-ylimit, ylimit);
            reward.transform.localPosition = new Vector3(x * 30, y * 30, 0);
        }
    }
}

蛇的成长

  • 这个也很简单,我们吃到食物(给食物贴上food标签),我们就调用生成食物的方法,然后,我们就生成一个蛇身,蛇身的初始位置要放在屏幕外,接着把该transform添加到bodyList的最后,那么下一次移动就会把这个蛇身自动添加到屏幕了。

    void Grow()
    {
       //2000,2000,0只是保证它在屏幕外而已
        GameObject body = Instantiate(bodyPrefab, new Vector3(2000, 2000, 0), Quaternion.identity);       
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }
    
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            Grow();
            //参数只是控制生成道具的概率
            FoodMaker.Instance.MakeFood((Random.Range(0, 100) < 20) ? true : false);
        }
        //else省略了
     }   
    

模式设置

  • 第一,传统的就是撞到边界就死亡,那么你就在边缘设置一些带有碰撞体的空物体包裹着游戏界面,如果蛇头撞到了那么就死亡,你想重新开始,那么可以重新加载该场景而实现重新开始。
  • 第二,没有边界,我们同样保留第一种的碰撞体,这次碰到了就不要直接死亡了,要把头部传送,上方就传到下方,以此类推,不用理会身体怎么动,因为我们在上面已经处理好了身体是随头的位置移动而移动的。

最高分数记录,音乐开关记录这一类我们可以用unity里面的((PlayerPrefs.GetString(key值)与PlayerPrefs.SetString(key值,value值)这两个函数,这个就可以解决简单的保存和读取了)

如果要了解保存游戏的知识可以到我写的利用json来保存游戏与读取游戏学习一下。


大家一起努力,加油!不懂可以问哦~

猜你喜欢

转载自blog.csdn.net/weixin_43701019/article/details/98899193