贪吃蛇案例学习笔记

贪吃蛇案例学习笔记

搭建开始场景

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUoxYwfL-1680535339596)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328173715457.png)]

做2D游戏一般吧canvas的渲染模式选成camera

  • overlay是保证ui一直在最前方渲染,蒙在画面最上方,一般用于3D游戏的小地图等
  • 2D游戏使用UGI做实现的话一般会调整成camera模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAC3PAI8-1680535339597)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328174113408.png)]

调整完之后把摄像机拉进来,否则会报错

这之后相机和canvas会合并成一个框

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SB556wBa-1680535339598)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328174442010.png)]

按住alt键在这里一键吸附四个角

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSSYrdH8-1680535339599)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328174715851.png)]

这里是跟左边选择上下拉伸

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDgqRlTV-1680535339599)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328175529219.png)]

即使是在UI选择创建image,也能通过附加button组件变成按钮

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8r0gBuB-1680535339600)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328175611025.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8e3oqHES-1680535339600)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328175625224.png)]

这两个组件能做阴影跟描边,凸显按钮的感觉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gSM0dG1A-1680535339601)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328191941448.png)]

一组ui需要挂上Toggle Group来说明是一个组,同一时间只能有几个激活

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6rHtqT0o-1680535339602)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328192229337.png)]

要在这里吧组给拖进来

完成游戏场景的界面

锚点一般靠哪里近归到哪里

给游戏场景添加可视的边界

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qekIr6G4-1680535339602)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328195410941.png)]

碰撞器的锚点肯定是上边界在上面做锚点,下边界在下面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rNUUhGqj-1680535339602)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328195532198.png)]

注意box colider 2D对ugi的控件不能自动识别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWH7LCsT-1680535339603)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328200106157.png)]

用image组件实现可视化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzRY7x1m-1680535339604)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328200215456.png)]

碰撞器的长宽是独立的,因此改变transform的长宽,让红色进入到画面内,而实际上的碰撞器还是在画面外的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1svo6Ytc-1680535339605)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328200447914.png)]

通过offset偏移量把碰撞器的位置设置回去,上面走了多少下面就走相反的

游戏逻辑的实现思路讲解

贪吃蛇所有的移动都是对蛇头而言的

蛇头(SH)的功能:

  1. 移动
  • 自身移动

  • 带动蛇身(SB)移动 - 让蛇头去管理蛇身

  1. 吃食物
  • 增加蛇身长度 - Add SB
  • 销毁食物
Move SH

移动方式:闪现/突变,而不是靠插值慢慢移动过去

可以通过直接操纵transform实现

每隔一段时间移动 用Invoke Repeat搞定

Move SB

主要由两种方法移动蛇身:

  1. 蛇头先走一步,让后面每一个都继承前面的位置,比较麻烦
  2. 只挪动最后一个蛇身到蛇头后面,其他的原样不动,比较巧妙

但是因为是双色蛇身,所以选择使用第一种,因为要根据奇偶性给他们上颜色,只挪动一个节点的话会导致蛇身节点颜色陷入混乱,如果是单色蛇身,就可以用第二种方法实现

吃东西

每吃一个食物,在后面加一个节点

死亡
  1. 撞到边界死亡
  2. 撞到任意一个蛇身死亡
传送

基于移动方式,只需要把蛇头传送过去,其他蛇身会自动跟随

因此不需要考虑蛇身是怎么移动的

只需要通过一个指令把对应的transform参数变为负的

制作蛇头并让其移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWzdZquf-1680535339605)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230328202650635.png)]

先把蛇头做成预制体

因为移动是通过直接改变transform参数实现的,因此还不需要加刚体一类的组件

只有蛇头移动的脚本/不完善还

using UnityEngine;

public class SnakeHead : MonoBehaviour
{
    public float velocity = 0.35f;//速度
    public int step;//声明蛇每一步走的距离
    private int x;
    private int y;//下次移动位置的增量值
    private Vector3 headPos;//定义全局坐标下蛇头的位置

    //需要一开始就调用Move方法
    //用 Start 方法
    void Start()
    {
        //InvokeRepeating 重复调用
        //接受三个参数 方法名 执行后多长时间调用 每隔多少时间调用一次
        InvokeRepeating("Move", 0, velocity);

        //贪吃蛇一开始是静止的,因此要在Start里一开始就让贪吃蛇朝一个方向走起来
        x = 0; y = step;//开局贪吃蛇就会往右走
    }




    //根据按键输入,设置xy增量的值
    void Update()
    {
        //用四个if输入完成增量变化
        if (Input.GetKey(KeyCode.W))//不是GetKeyDown是因为玩家会一直按着键不放 KeyCode.w代表着上键
        {
            x = 0; y = step;
        }
        if (Input.GetKey(KeyCode.A))
        {
            x = -step; y = 0;
        }
        if (Input.GetKey(KeyCode.S))
        {
            x = 0; y = -step;
        }
        if (Input.GetKey(KeyCode.D))
        {
            x = step; y = 0;
        }
    }


    //先用move方法实现蛇头移动
    void Move()
    {
        headPos = gameObject.transform.localPosition;
        gameObject.transform.localPosition = new Vector3(headPos.x + x, headPos.y + y, headPos.z);//用增量坐标替换现在的位置
    }

}

完善蛇头的移动并让其可以加速

还得再wasd的时候图像发生位置旋转

用四元数的方法去啊 Quaternion.Euler,通过三个浮点数返回一个旋转实现

 void Update()
    {
        //用四个if输入完成增量变化
        if (Input.GetKey(KeyCode.W))//不是GetKeyDown是因为玩家会一直按着键不放 KeyCode.w代表着上键
        {
            //用四元数的方法去啊 Quaternion.Euler,通过三个浮点数返回一个旋转
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);//0 0 0是初始方向,即 形向上的防线
            x = 0; y = step;
        }
        if (Input.GetKey(KeyCode.A))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90);
            x = -step; y = 0;
        }
        if (Input.GetKey(KeyCode.S))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0; y = -step;
        }
        if (Input.GetKey(KeyCode.D))
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step; y = 0;
        }
    }

如何解决蛇不应该立马转头的问题,如果立马往反方向走的话,就直接碰到身子寄了

需要额外加一个判断条件,按上生效的条件是没有想吓走,按右生效的条件是没有向左走…

 void Update()
    {
        //用四个if输入完成增量变化
        if (Input.GetKey(KeyCode.W) && y != -step)//不是GetKeyDown是因为玩家会一直按着键不放 KeyCode.w代表着上键
        {
            //用四元数的方法去啊 Quaternion.Euler,通过三个浮点数返回一个旋转
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);//0 0 0是初始方向,即 形向上的防线
            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.S) && y != step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);
            x = 0; y = -step;
        }
        if (Input.GetKey(KeyCode.D) && x != -step)
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90);
            x = step; y = 0;
        }
    }

加速功能的实现

原理:按下左shift加速,抬起左shift的时候恢复原来的速度

 //加速功能
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            //因为在上面循环调用的时候,已经给定了固定速度了,因此速度不会刷新
            //要使用CancelInvoke方法
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity - 0.2f);//减少数值跑得更快
        }
        if (Input.GetKeyUp(KeyCode.LeftShift))
        {
            //同理恢复速度
            CancelInvoke();
            InvokeRepeating("Move", 0, velocity);//恢复速度
        }

第一个食物的随机生成

食物要生成在30的整数倍的地方,这是蛇移动会经过的地方

测试得知,上下距离碰撞器有11步数 在-11*30到 11乘以30的地方生成食物

向右有21个距离

向左有14个距离

因为好多不同食物都共用一个预制体,所以先把预制体的图片取消掉

  • 生成食物的套路:开始的时候生成一个,然后没吃掉一个再生成新的,如果上一个食物一直没有被吃掉的话,就不生成下一个食物;

空物体用来归类的话最好保持上下左右的缩放,锚点和canvas一样

处理食物被吃掉且被吃掉后再生成

碰撞检测

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uQZSYDme-1680535339605)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230403175850875.png)]

相邻两个碰撞器擦肩而过也会发生碰撞的,因此不管是蛇还是食物,碰撞器实际上都应该比30小一圈

刚体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BbKiJDUT-1680535339606)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230403180314615.png)]

注意需要时动态的刚体

注意不启用重力

给食物上刚体

给Food加Tag

给蛇头做检测

用OnTruggerEnter2D方法

两种拿到tag的写法

  1. 常用

    if (collision.tag == "Food")
            {
    
            }
    
  2. //也可以写成collision.tag=="Food"
            if (collision.gameObject.CompareTag("Food"))
            {
    
            }
    
拿到后销毁

OnTriggerEnter2D方法内

//也可以写成collision.tag=="Food"
        if (collision.gameObject.CompareTag("Food"))
        {
            //销毁
            Destroy(collision.gameObject);

        }

类下

//通过类名,从静态的单例访问到他
    private static FoodMaker _instance;
    public static FoodMaker Instance
    {
        get
        {
            return _instance;
        }
    }

在awake里面赋值

   void Awake()
    {
        _instance = this;
    }
通知生成一个食物
//通知生成一个食物
            FoodMaker.Instance.MakeFood();

处理蛇身的生成

先通过gui - image制作蛇身的预制体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84tg4tzK-1680535339607)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230403183455453.png)]‘

每一块想隔的距离是30,但是为什么要设置大小是40呢?因为这样的话能互相遮盖住一部分

用List集合存储所有的蛇身
public List<RectTransform> bodyList = new List<RectTransform>();

注意需要引入命名空间

using System.Collections.Generic;

拿到预制体和数组

//拿到蛇身的预制体和对应的图片
    //因为有两种图片,所以设置成数组
    public GameObject bodyPrefab;
    public Sprite[] bodySprites = new Sprite[2];//指定数组长度是2
用Grow方法写生成

先判断奇偶数//就和if的用法一样

int index = (bodyList.Count % 2 == 0) ? 0 : 1;

然后实例化出来

void Grow()
    {
        GameObject body = Instantiate(bodyPrefab);
    }

给材质

 body.GetComponent<Image>().sprite = bodySprites[index];

设置父物体

body.transform.SetParent(canvas, false);
  private Transform canvas;//拿到canvas

最后再把身子加到集合里面

 bodyList.Add(body.transform);//只需要把坐标加进去就好

处理蛇身的移动之方法一

通过Awake方法拿到Canvas

 void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
    }
用移动最后一个蛇身的方法实现移动(只是实现,不使用)
bodyList.Last().localPosition = headPos;//很好理解。直接用 Last 方法拿到最后一个蛇身的位置然后设置到原先头的位置

处理一下集合

bodyList.Insert(0, bodyList.Last());//更新集合 Insert 插入
        bodyList.RemoveAt(bodyList.Count - 1);//删除集合中最后一个数

再加上调用方法

根据报错,最初的时候bodylist返回了 null

因此做一步判断,至少有一个身子的时候,才会执行这些代码

 if (bodyList.Count > 0)
        {
            bodyList.Last().localPosition = headPos;//很好理解。直接用 Last 方法拿到最后一个蛇身的位置然后设置到原先头的位置
            bodyList.Insert(0, bodyList.Last());//更新集合 Insert 插入
            bodyList.RemoveAt(bodyList.Count - 1);//删除集合中最后一个数
        }
两个问题
  1. 每次都会在0 0 0 处闪现生成

    解决办法:

    • 把生成位置设置在画面之外,让玩家看不到
    • 或者,在Move方法里调用Grow();而不是在吃到后调用
  2. 蛇的花纹明显不对

处理蛇身的移动之方法二

移动所有蛇身的方法

利用for循环完成坐标的移动

for循环的条件

for (int i =bodyList.Count-2;i >=0;i--)
            {

            }

通过下标访问蛇身的坐标

bodyList[i + 1].localPosition = bodyList[i].localPosition;
 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;                                                            //第一个蛇身移动到蛇头移动前的位置
        }
        
    }

让蛇可以通过边界进行传送

给蛇身加上Body标签

通过Body标签死亡
 else if (collision.gameObject.CompareTag("Body"))
        {
            Debug.Log("Die");//暂时替代,还没写死亡方法
        }
        else
        {
            Debug.Log("Die");
        }
写边界穿越

通过switch语句

操纵xyz的正负数实现穿越

 switch (collision.gameObject.name)
            {
                case "Up":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y, transform.localPosition.z);
                    break;
                case "Down":
                    transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y, transform.localPosition.z);

                    break;
                case "Left":
                    break;
                case "Right":
                    break;

            }

问题:蛇头在边界会不断来回鬼畜跳跃

因为碰到便捷的时候就开始不断穿越了

解决方法:穿越一次之后对应坐标对应+/-30,相当于多走了一个格子,就碰不到边界了

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

奖励目标的生成与获取

先做额外的prefab

生成reward

现持有reward的prefab

拓展生成食物的方法,想给个形参

public void MakeFood(bool isReward)

设置百分之20的几率

 FoodMaker.Instance.MakeFood(false);
            if (Random.Range(0, 100) < 20)
            {
                FoodMaker.Instance.MakeFood(true);
            }

优化,只调用一次代码,概率命中的时候生成食物加奖励,否则只生成食物

FoodMaker.Instance.MakeFood((Random.Range(0, 100) < 20)?true:false);//检测通过的话创建true,否则就创建false

分数与长度的记录以及背景颜色的切换

先新建一个脚本控制UI

拿到基本的命名空间和数据成员

using UnityEngine;
using UnityEngine.UI;

public class MainUIController : MonoBehaviour
{
    public int score = 0;
    public int length = 0;
    public Text msgText;
    public Text scoreText;
    public Text lengthText;

}

用一个方法来更新UI

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

把单例复制过来,改一下类的名字

然后用Awake方法给单例的_instance赋值

然后在蛇头里调用这个方法

阶段等级

说明:从三百分开始,没两百分换一个等级

获取背景

  public Image bgImage;

用switch语句在update方法里实现

void Update()
    {
        switch (score / 100)
        {
            case 3:
                break;
            case 5:
                break;
            case 7:
                break;
            case 9:
                break;
            case 11:
                break;
        }
    }

在switch语句里修改颜色

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

暂停游戏与返回菜单的开发

对阶段转换的脚本进行优化

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("#CCEEFFFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#CCFFDBFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 4;
                break;
            case 9:
            case 10:
                ColorUtility.TryParseHtmlString("#EBFFCCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 5;
                break;
            default:
                ColorUtility.TryParseHtmlString("#FFDACCFF", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "无尽模式";
                break;
        }
    }
暂停

创建一个暂停方法

public void Pause()
    {

    }

给一个暂停的bool值

   public bool IsPause = false;

给暂停按钮和对应图标

    public Button pauseButton;
    public Sprite[] pausesprites;

暂停的代码

public void Pause()
    {
        IsPause = !IsPause;
        if (IsPause)
        {
            Time.timeScale = 0;//时间停止
            IsPause = false;
            pauseButton.GetComponent<Image>().sprite = pausesprites[1];//切换对应图片
        }
        else
        {
            Time.timeScale = 1;
            pauseButton.GetComponent<Image>().sprite = pausesprites[0];
        }
    }
空格热键触发问题

Edit - ProjectSetting - Input

在这里找到submit

在里面吧space/空格删掉

暂停时按下加速仍然能移动的问题

在加速的代码上加一段限制条件,只有在没有停止的时候才会加速

if (Input.GetKeyDown(KeyCode.LeftShift) && MainUIController.Instance.isPause == false)
主业场景制作

需要把两个场景放到setting里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1DjWyvuH-1680535339609)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230403211500844.png)]

主业按钮的制作
 public void Home()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }

蛇死亡的处理和游戏得分的记录

Die方法
void Die()
    {
        CancelInvoke();
    }

给一个isDie的布尔值

private bool isDie = false;

在每个输入前加上死亡判断

if (Input.GetKey(KeyCode.A) && x != step && MainUIController.Instance.isPause == false && isDie == false)

在Die方法里把isDie设为true

void Die()
    {
        CancelInvoke();
        isDie = true;
    }
爆炸特效

先拿到物体

 public GameObject dieEffect;

直接实例化出来,也不用在乎什么时候销毁,因为场景马上要重载了

void Die()
    {
        CancelInvoke();
        isDie = true;
        Instantiate(dieEffect);
    }

使用协程

 IEnumerator GameOver(float t)
    {
        yield return new WaitForSeconds(t);
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }

协程方法的调用

StartCoroutine(GameOver(1.5f));
死亡后得分的记录

要在场景被重载之前记录

PlayerPrefs.SetInt("last1", MainUIController.Instance.length);
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);
最佳得分判断
if (PlayerPrefs.GetInt("bests",0) < MainUIController.Instance.score)
        {
            PlayerPrefs.SetInt("best1", MainUIController.Instance.length);
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);
        }

对于用户设置的储存

给start场景写个脚本控制UI

先拿到显示得分的文本组件

public TextAlignment lastText;
    public TextAlignment bestText;

在awake方法里把得分给拿到

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

在引擎里挂载赋值即可

开始按钮的制作

加载场景的套路

public void StartGame()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
如何把四个Main场景的皮肤和模式带到Start场景里

使用playerprebs的讨论

需要先定义四个

 public void BlueSelect(bool isON)
    {

    }
    public void YellowSelect(bool isON)
    {

    }
    public void BorderSelect(bool isON)
    {

    }
    public void NoBorderSelect(bool isON)
    {

    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjQrz8gg-1680535339612)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230403221014816.png)]

bool值可以直接在这边动态传参数

写具体选项的内容

通过记录三个string值:奇数和偶数的文件名来记录当前状态

颜色的文件信息储存

public void BlueSelect(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
    }
    public void YellowSelect(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }

至于边界模式则用int代替bool的用法,规定int0 true,int 1 false

  public void BorderSelect(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 1);
        }
    }
    public void NoBorderSelect(bool isOn)
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 0);
        }
    }
问题:需要在一开始的时候从用户手中读取文件信息,记录下来并且按照用户的选择设置好

还需要持有四个toggle

 public Toggle blue;
    public Toggle yellow;
    public Toggle border;
    public Toggle noBorder;

通过在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");
        }

在start方法里做边界的判断

if (PlayerPrefs.GetInt("border", 1) == 1)
        {
            border.isOn = true;
            PlayerPrefs.SetInt("border", 1);
        }
        else
        {
            noBorder.isOn = true;
            PlayerPrefs.SetInt("border", 0);
        }

完成换肤与其他配置读取

换肤的制作 - Resources文件夹

把需要在层中更换的资源放到Resources文件夹下

在awake里使用Resources.Load方法加载

 void Awake()
    {
        canvas = GameObject.Find("Canvas").transform;
        //通过Resources.Load(string path)方法加载资源,path的书写不需要加Resources/以及文件扩展名
        gameObject.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh", "sh02"));
        bodySprites[0] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01", "sh0201"));
        bodySprites[1] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02", "sh0202"));
    }
边界的处理

利用foreach循环拿到所有的transform组件,通过所有的transform组件拿到image组件,拿到gameobject组件

void Start()
    {
        if (PlayerPrefs.GetInt("border", 1) == 0)
        {
            hasBorder = false;
            foreach(Transform t in bgImage.gameObject.transform)
            {
                t.gameObject.GetComponent<Image>().enabled = false;//禁用掉这个组件
            }
        }
    }

在蛇头的碰撞判断处用if语句做补充

 if (MainUIController.Instance.hasBorder)
            {
                Die();
            }
            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;

                }
            }
声音的添加
bgm

简单粗暴,直接使用audio source组件,拖进bgm,勾选loop

蛇吃东西和死亡的添加

先拿到音频

 public AudioClip eatClip;
    public AudioClip dieClip;

声音的触发

吃东西

//注意:声音会跟位置有关,要注意看发生物体的位置,在这个案例里,就是要看摄像机的位置

而此时摄像机的z是-10,这样的话选择声音位置是0.可能声音就会比较小

距离摄像机越近,声音越大

 void Grow()
    {

        AudioSource.PlayClipAtPoint(eatClip, Vector3.zero);
        }
死亡
 void Die()
    {
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero);
        }
发布游戏与解决发布时出现的诡异bug
image-20230403230904476

n.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;

            }
        }

#### 声音的添加

##### bgm

简单粗暴,直接使用audio source组件,拖进bgm,勾选loop

#### 蛇吃东西和死亡的添加

先拿到音频

```C#
 public AudioClip eatClip;
    public AudioClip dieClip;

声音的触发

吃东西

//注意:声音会跟位置有关,要注意看发生物体的位置,在这个案例里,就是要看摄像机的位置

而此时摄像机的z是-10,这样的话选择声音位置是0.可能声音就会比较小

距离摄像机越近,声音越大

 void Grow()
    {

        AudioSource.PlayClipAtPoint(eatClip, Vector3.zero);
        }
死亡
 void Die()
    {
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero);
        }
发布游戏与解决发布时出现的诡异bug
image-20230403230904476

选择这个能解决分辨率问题,因为能跳出让玩家手动选择分辨率的栏目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值