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

Posted 呆呆敲代码的小Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重回童年的经典系列☀️|贪吃蛇小游戏近两万字完整制作过程+解析+源码 建议收藏学习相关的知识,希望对你有一定的参考价值。

  • 📢博客主页:https://blog.csdn.net/zhangay1998
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉
  • 📢未来很长,值得我们全力奔赴更美好的生活✨


📣前言

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

🎬贪吃蛇小游戏制作


🎥游戏介绍

  • 本篇小游戏使用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

以上是关于重回童年的经典系列☀️|贪吃蛇小游戏近两万字完整制作过程+解析+源码 建议收藏学习的主要内容,如果未能解决你的问题,请参考以下文章

重回童年的经典系列☀️|炸弹人小游戏制作过程+解析 | 收藏起来跟曾经的小伙伴一起梦回童年!

曾经被诺基亚作为卖点的游戏,无数玩家的童年游戏,它就是...

用Python实现童年小游戏贪吃蛇

重回童年的经典系列 |捕鱼达人小游戏来啦~ 有源码下载文末送书

重回童年的经典系列 |捕鱼达人小游戏来啦~ 有源码下载文末送书

贪吃蛇需求分析