重回童年的经典系列☀️|【贪吃蛇小游戏】近两万字完整制作过程+解析+源码

1,562 阅读14分钟

我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

在这里插入图片描述

  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 呆呆敲代码的小Y 原创🙉
  • 📢未来很长,值得我们全力奔赴更美好的生活✨

📣前言

  • 今天给大家带来一款经典的贪吃蛇小游戏,相信大家应该都应该玩过
  • 包括小时候在经典的诺基亚手机上,也是百玩不厌,算是最经典的游戏之一了!
  • 那今天就来学习一下怎样制作这个经典的贪吃蛇小游戏吧!

🎬贪吃蛇小游戏制作

在这里插入图片描述

请添加图片描述


🎥游戏介绍

  • 本篇小游戏使用Unity引擎制作一款经典的贪吃蛇小游戏

  • 跟小时候玩过诺基亚手机上的玩法类似,然后使用一套自己的素材制作

  • 有两种游戏模式 和 两款小蛇的皮肤,每当吃到食物就会变长!

  • 本篇教程使用的Unity的版本是2018.4.24,不同版本可能会出现略微差异

  • 文章字数挺多,其实整个游戏只用了四个脚本,直接结合源码看文章效果更佳!


🏳️‍🌈打开Unity 新建一个项目,导入素材资源包

第一步还是老样子,先打开UnityHub,新建一个项目

这里要注意的是选择一个2D项目,因为贪吃蛇是一个2D游戏

所以我们只需要创建一个 2D项目 而不需要 3D项目!如下所示 在这里插入图片描述

创建完了之后我们需要导入一个贪吃蛇的素材包,这个素材包在文章后面我会和源码一起分享!

先来看看导入之后这个资源包中都有什么内容吧! 在这里插入图片描述

导入之后工程目录结构如下:

有一个声音文件夹模型文件夹文字文件夹精灵图片文件夹

里面都包含几个简单的贪吃蛇会用到的对应文件

🏳️‍🌈新建一个场景,设置 开始场景 的界面

在工程下新建一个场景Sence

然后新建一个Image,然后将画布的渲染模式改为摄像机渲染 在这里插入图片描述Image设置跟画布一个大小 在这里插入图片描述

把资源包中的这个背景拖到Image上,这样的话Image就会作为一个游戏背景来显示~ 在这里插入图片描述

  • 然后把image改名为bg,然后再新建一个Image,改名为ControllerPanel
  • 这个新建的Image作为一个开始场景中的侧边栏,调整一下位置,放到Canvas左侧!
  • 然后还要新建一个Text文本组件,调整一下Text的位置,放到Canvas上边侧就好
  • 然后输入贪吃蛇,改个字体大小,字体选择我们资源包中的效果。效果如下:

在这里插入图片描述 然后还需要加一个Button作为一个 开始按钮

那就来新建一个Button ,然后选择资源包中的这个Go图片作为点击按钮,并将Button下的Text本文改为开始。效果如下: 在这里插入图片描述 然后还可以给这个开始按钮加一个outlineshadow组件来做一个阴影效果! 如下所示: 在这里插入图片描述 然后我们可以在背景图bg下面新建一些Image加一些小图片做一个点缀效果

当然这不是必须要做的哈,知识单纯的让看起来可以更美观一些,要不然有些空空的

图片都是资源包中的,只需要更换一下图片素材就好了。效果如下: 在这里插入图片描述

🏳️‍🌈设置 开始场景 的 侧边栏

这个侧边栏会用来显示皮肤模式选择分数显示,在一开始的游戏展示中我们也看到了,所以这里需要来设置一下

我们在ControPanle下新建一个Text文本框,输入皮肤两个字,然后调整一下字体大小和位置

如果不知道设置多少比较好,可以参考下我设置的,参数和效果如下: 在这里插入图片描述 然后照葫芦画瓢,继续加一个模式选择分数显示,效果如下: 在这里插入图片描述 三个标题添加好了,但是里面还有小标题,再来继续设置一下

先来设置一下分数

分数Store下新建一个Text文本作为上次成。,输入:上次:长度0,分数0

然后调整一下大小和位置,参数和效果如下: 在这里插入图片描述 然后继续照葫芦画瓢,添加一个最好成绩,参数和效果如下: 在这里插入图片描述 然后接下来搞一下模式的设置

我们在模式Mode下新建一个Toggle选择框,用于做模式选择的时候使用 在这里插入图片描述 改一下名字为:Border,然后设置一下文字大小等参数,效果如下: 在这里插入图片描述 然后照葫芦画瓢再来整一个自由模式,参数效果如下: 在这里插入图片描述 这里只是为了做一个UI效果,具体怎样设置都可以,只是作为一个参考!

然后还没完,因为正常游戏中两种模式只能选择一个,所以我们在这里需要添加一个组件用于控制

那就是Toggle Group组件,这是UGUI自带的一个组件,目的就是遇到这多个选择框只能选择一个的时候使用

我们给Mode对象身上添加一个Toggle Group组件,然后在两个边界模式的Toggle组件属性中的Group设置成父物体身上的Toggle Group组件,效果如下: 在这里插入图片描述 请添加图片描述

然后还有一个皮肤选择没有设置,再来设置一下

同理也是需要添加一个Toggle组件,然后设置一下位置和大小

然后我们对这个Toggle组件做一个简单的配置,使用我们的素材给他装饰的好看一些

这一步也是自己看兴趣设置,哪怕使用一个默认的Toggle组件不做装饰也是可以的!

配置好了之后如下图所示: 在这里插入图片描述

如果详细的配置信息没有看明白,可以结合后面的源码来进行更详细的参数设置 因为在文中可能有地方语言描述真的很费劲,所以结合源码工程观看,学习体验效果更佳!

然后照葫芦画瓢再来配置一个其他颜色的,直接复制一个,然后调整位置修改一下

如下所示: 在这里插入图片描述 然后同模式选择一样,皮肤也只能选择一个来进行游戏

所以这里还是需要添加一个Toggle Group组件给Skin对象添加上

然后给两个小蛇的对象Bule和yellowGroup属性中将父物体添加上,效果如下: 在这里插入图片描述 请添加图片描述 然后开始界面的UI基本上算是搭建完毕了,接下来就是下一步了!

🏳️‍🌈新建一个场景,设置 游戏场景 的界面

开始场景我们设置完了,还需要一个进行游戏的场景设置

我们在Sence文件夹中新建一个场景Main作为游戏场景

然后跟开始场景中一样,对侧边栏进行设置,设置的参数和开始场景并没有很大的区别

直接来看一下我设置的即可,就是单纯的UI大小位置设置,查看更详细的设置参照源码即可,很简单就不赘述了! 在这里插入图片描述


🏳️‍🌈给游戏场景添加可视化边界

基本的游戏场景设置完了,在我们的小蛇碰到边界的时候是会触发不同的效果的

  • 边界模式中,碰到边界就会死亡,结束游戏
  • 自由模式中,碰到边界会进行一个穿透传送效果

那接下来就来设置一下游戏的边界处理

我们在bg下新建一个Image作为上边界,给这个Image添加一个碰撞体BoxCollider然后调整图片和碰撞体的大小

参数和效果如下所示: 在这里插入图片描述 这里的话不同的电脑可能参数会有略微差异,根据自己的视图大小调整这个边界的位置和大小即可

最终效果是让这个游戏视图的上下所有边界都完整覆盖即可!如下所示: 在这里插入图片描述


🏳️‍🌈制作小蛇并让其移动

现在我们游戏场景有了,那接下来就需要一条小蛇来进行游戏了

所以我们在Canvas下新建一个Image命名为SnakeHead并将我们素材中的一个小蛇头的图片拖上去

调整一下这个Image的大小为45即可,如下所示 在这里插入图片描述 然后就是编写一个SnakeHead脚本代码让小蛇动起来,新建一个脚本,将一下代码放进去即可!

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

public class SnakeHead : MonoBehaviour
{
    public float velocity = 0.35f;
    public int step;
    private int x;
    private int y;
    private Vector3 headPos;

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

    void Move()
    {
        headPos = gameObject.transform.localPosition;                                               //保存下来蛇头移动前的位置
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, headPos.z);  //蛇头向期望位置移动
    }
}

通过键盘按键来让小蛇进行不同方向的移动,并设置一个时间和一个移动速度即可,效果如下: 请添加图片描述


🏳️‍🌈随机生成食物、食物被吃掉 和 生成新的食物

现在小蛇可以进行移动了,还需要添加一个食物的自动生成,所以再接着来做这个食物的随机生成

随机生成食物也很简单,先来判断一下食物可以生成的范围,肯定就是游戏边界内,不能生成到游戏场景外面

新建一个脚本 FoodMaker 用来随机生成一个食物

using UnityEngine;
using UnityEngine.UI;

public class FoodMaker : MonoBehaviour
{
    public int xlimit = 21;
    public int ylimit = 11;
    public int xoffset = 7;
    public GameObject foodPrefab;
    public Sprite[] foodSprites;
    private Transform foodHolder;

    void Start()
    {
        foodHolder = GameObject.Find("FoodHolder").transform;
        MakeFood();
    }
    public void MakeFood()
    {
        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);
    }
}

使用ylimit 和xlimit来控制一个随机生成的边界

通过foodSprites数组来控制随机生成食物的图片

然后这样就可以随机生成食物了,后面会写方法,当小蛇吃掉一个食物的时候再生成新的食物就好了!

上面说了食物的一个随机生成,那接下来要对食物被吃掉和生成一个新的食物来进行逻辑处理

我们要从蛇头这边来介入,因为只有当蛇头碰到食物之后,食物才会消失,是通过碰撞检测来完成的

所以要给蛇头添加一个碰撞体刚体,来进行一个碰撞检测在这里插入图片描述 然后在SnakeHead脚本中添加代码用来检测食物

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            FoodMaker.Instance.MakeFood((Random.Range(0, 100) < 20) ? true : false);
        }
    }

这样的话当蛇头碰到食物之后,食物就会消失,然后调用生成食物的方法继续生成新的食物啦!


🏳️‍🌈小蛇吃食物变长 和 蛇身的移动

现在小蛇可以进行吃食物了,而且食物也会被吃掉之后随机生成新的,但是蛇身还没有变长

所以现在来写一下蛇身变长的逻辑和代码,还是在SnakeHead脚本中添加代码

声明一个List用来蛇身的处理,后续蛇身移动的时候会用到

  public List<Transform> bodyList = new List<Transform>();
  
    void Grow()
    {
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;
        GameObject body = Instantiate(bodyPrefab, new Vector3(2000, 2000, 0), Quaternion.identity);
        body.GetComponent<Image>().sprite = bodySprites[index];
        body.transform.SetParent(canvas, false);
        bodyList.Add(body.transform);
    }

将这段代码在蛇吃到食物之后调用即可,这样的话就可以在吃到食物之后蛇身变长了!

然后是蛇身的移动代码,使用List存储蛇身,然后让蛇身的每一个后边的蛇身位置换到蛇身前边的位置

这样就可以形成一个蛇不断移动的效果,蛇身的后一个节点和前一个节点之间就可以正常跟随蛇头移动了!

    void Move()
    {
        headPos = gameObject.transform.localPosition;                                               //保存下来蛇头移动前的位置
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, headPos.z);  //蛇头向期望位置移动
        if (bodyList.Count > 0)
        {
            //由于我们是双色蛇身,使用此方法达到显示目的
            for (int i = bodyList.Count - 2; i >= 0; i--)                                           //从后往前开始移动蛇身
            {
                bodyList[i + 1].localPosition = bodyList[i].localPosition;                          //每一个蛇身都移动到它前面一个节点的位置
            }
            bodyList[0].localPosition = headPos;                                                    //第一个蛇身移动到蛇头移动前的位置
        }
    }

🏳️‍🌈蛇身边界传送效果

边界模式下,当蛇碰到边界时就是触发死亡,这个操作很简单

我们在边界设置了碰撞体,所以当蛇碰到边界时就会触发一个回调

因为我们蛇身移动的逻辑是跟随前一个结点移动,所以只需要蛇头的边界传送,蛇身就会自动跟随达到我们想要的效果

所以我们只需要判断蛇头到达边界的时候,改动一下蛇头的位置就好了!

还是在SnakeHead脚本中添加代码,关键代码如下

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Food"))
        {
            Destroy(collision.gameObject);
            MainUIController.Instance.UpdateUI();
            Grow();
            FoodMaker.Instance.MakeFood((Random.Range(0, 100) < 20) ? true : false);
        }
        else
        {
                switch (collision.gameObject.name)
                {
                    case "Up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 30, transform.localPosition.z);
                        break;
                    case "Down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 30, transform.localPosition.z);
                        break;
                    case "Left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 180, transform.localPosition.y, transform.localPosition.z);
                        break;
                    case "Right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 240, transform.localPosition.y, transform.localPosition.z);
                        break;
                }
            }
        
    }

🏳️‍🌈分数和长度的记录

现在蛇的基本移动变长食物生成等等都做好了

那接下来就是分数的增加了,只需要在吃掉食物的时候调用一下就好了!

新建一个脚本MainUIController,用于 控制分数的增加等级的判定

代码也很简单,当吃掉食物就进行调用即可!

    public int score = 0;
    public int length = 0;
    public Text scoreText;
    public Text lengthText;
    
    public void UpdateUI(int s = 5, int l = 1)
    {
        score += s;
        length += l;
        scoreText.text = "得分:\n" + score;
        lengthText.text = "长度:\n" + length;
    }

然后在这个脚本中在写一下根据不同的分数阶段,进行场景背景的一个变化

这就是一个游戏玩法的增加了,代码如下:

        switch (score / 100)
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#FFF3CCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            default:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "无尽阶段";
                break;
        }

🏳️‍🌈游戏暂停 和 菜单键 的设置

到这里的话游戏基本功能就是完成了,这一步是将游戏场景中的暂停键返回菜单键的功能给加上

之前知识添加了UI,并没有写实际的方法,所以这里给加上

还是在MainUIController脚本进行编写代码,因为MainUIController脚本就是一个处理UI相关内容的

   public void Pause()
    {
        isPause = !isPause;
        if (isPause)
        {
            Time.timeScale = 0;
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseImage.sprite = pauseSprites[0];
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }

很简单就可以实现暂停和返回菜单的功能实现了!

🏳️‍🌈蛇死亡的处理 和 游戏得分记录

小蛇在游戏结束时要记录一个游戏得分,和一个游戏结束的效果

SnakeHead脚本中写一个Die 方法

    void Die()
    {
        isDie = true;
        Instantiate(dieEffect);
        PlayerPrefs.SetInt("lastl", MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
        if (PlayerPrefs.GetInt("bests", 0) < MainUIController.Instance.score)
        {
            PlayerPrefs.SetInt("bestl", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }
        StartCoroutine(GameOver(1.5f));
    }

这里使用PlayerPrefs存储一个分数数据给简单存储起来即可,然后死亡的时候释放一个死亡特效

在小蛇撞到边界和蛇身的时候调用即可!

🏳️‍🌈用户的设置和储存

我们需要在开始界面中显示模式的选择小蛇皮肤 的选择,还有上次游戏的分数最高分数的存储`

新建一个脚本StartUIController ,挂在场景中即可

using UnityEngine;
using UnityEngine.UI;

public class StartUIController : MonoBehaviour
{
    public Text lastText;
    public Text bestText;
    public Toggle blue;
    public Toggle yellow;
    public Toggle border;
    public Toggle noBorder;

    void Awake()
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("lastl", 0) + ",分数" + PlayerPrefs.GetInt("lasts", 0);
        bestText.text = "最好:长度" + PlayerPrefs.GetInt("bestl", 0) + ",分数" + PlayerPrefs.GetInt("bests", 0);
    }

    void Start()
    {
        if (PlayerPrefs.GetString("sh", "sh01") == "sh01")
        {
            blue.isOn = true;
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true;
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border", 1);
        }
        else
        {
            noBorder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void BlueSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }

    public void YellowSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

    public void BorderSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 1);
        }
    }

    public void NoBorderSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
}

这样的话用户选择的 小蛇的皮肤模式的选择 就可以带入到游戏场景中了!


🏳️‍🌈游戏打包及发布

游戏都做好啦,我们可以打包成一个PC端的游戏进行玩耍!

打包程序也很简单,File -> Build Setting -> Player Setting 在这里插入图片描述 然后设置一下游戏图标游戏名字,点击 Build 选择一个文件夹即可! 在这里插入图片描述 Build结束之后,就会在选择的文件夹中出现这样几个文件 在这里插入图片描述 我们点击这个小蛇的头像的exe文件就可以直接玩游戏啦!


🎄源码展示

由于本工程实际只有四个脚本完成,所以为了方便看代码学习,直接将所有代码展示出来

省的还需要打开Unity工程才能查看了

StartUIController脚本 :用于控制开始场景中的控制器

using UnityEngine;
using UnityEngine.UI;

public class StartUIController : MonoBehaviour
{
    public Text lastText;
    public Text bestText;
    public Toggle blue;
    public Toggle yellow;
    public Toggle border;
    public Toggle noBorder;

    void Awake()
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("lastl", 0) + ",分数" + PlayerPrefs.GetInt("lasts", 0);
        bestText.text = "最好:长度" + PlayerPrefs.GetInt("bestl", 0) + ",分数" + PlayerPrefs.GetInt("bests", 0);
    }

    void Start()
    {
        if (PlayerPrefs.GetString("sh", "sh01") == "sh01")
        {
            blue.isOn = true;
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true;
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border", 1);
        }
        else
        {
            noBorder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void BlueSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }

    public void YellowSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

    public void BorderSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 1);
        }
    }

    public void NoBorderSelected(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 0);
        }
    }

    public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
}

MainUIController脚本:用于控制游戏场景中的UI显示控制

using UnityEngine;
using UnityEngine.UI;

public class MainUIController : MonoBehaviour
{
    private static MainUIController _instance;
    public static MainUIController Instance
    {
        get
        {
            return _instance;
        }
    }

    public bool hasBorder = true;
    public bool isPause = false;
    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;
    public Image pauseImage;
    public Sprite[] pauseSprites;
    public Image bgImage;
    private Color tempColor;

    void Awake()
    {
        _instance = this;
    }

    void Start()
    {
        if (PlayerPrefs.GetInt("border", 1) == 0)
        {
            hasBorder = false;
            foreach (Transform t in bgImage.gameObject.transform)
            {
                t.gameObject.GetComponent<Image>().enabled = false;
            }
        }
    }

    void Update()
    {
        switch (score / 100)
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 2;
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#FFF3CCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            default:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "无尽阶段";
                break;
        }
    }

    public void UpdateUI(int s = 5, int l = 1)
    {
        score += s;
        length += l;
        scoreText.text = "得分:\n" + score;
        lengthText.text = "长度:\n" + length;
    }

    public void Pause()
    {
        isPause = !isPause;
        if (isPause)
        {
            Time.timeScale = 0;
            pauseImage.sprite = pauseSprites[1];
        }
        else
        {
            Time.timeScale = 1;
            pauseImage.sprite = pauseSprites[0];
        }
    }

    public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
}

FoodMaker脚本:用于控制食物的管理

using UnityEngine;
using UnityEngine.UI;

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;
    public GameObject rewardPrefab;
    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);
        }
    }
}

在这里插入图片描述


🎁游戏源码下载 和 效果展示

这里我把游戏的源码工程 、素材和打包好的exe文件统一放到一个压缩包中

有需要的小伙伴点击链接下载就好, 如果积分不够的小伙伴私信我也可以

我看到的时候就会发给你了啦!

贪吃蛇下载链接:https://download.csdn.net/download/zhangay1998/22490642

完整效果展示: 请添加图片描述


👥总结

  • 本篇文章对贪吃蛇小游戏做了一个详细的说明教程

  • 看起来挺多的,实际上只有 四个脚本 就可以完成这个游戏了

  • 源码也只不过 10M多点大小 ,只是制作过程稍微繁琐了一些,但是都是基础内容很适合新手学习!

  • 如果在看的过程中有哪一步没看懂或者博主没有说到,那可以结合源码工程观看即可!

  • 因为在文中可能有地方语言描述真的很费劲,所以结合源码工程观看, 学习体验效果更佳!

  • 博主辛苦写文不宜,大家可以三连一波支持博主呀!