利用unity3D制作贪吃蛇

版权声明:本篇文章由IT_CREATE整理 https://blog.csdn.net/IT_CREATE/article/details/82027541

源码资源https://download.csdn.net/download/it_create/10625733

开始界面

运行界面

MainUIController.cs

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

public class MainUIController : MonoBehaviour {
    private static MainUIController _instance;//定义一个静态的私有MainUIController型的变量
    public static MainUIController Instance //提供一个外部访问的静态成员函数,其他脚本无法修改,只能访问,返回一个_instance
    {
        get
        {
            return _instance;
        }
    }
    public int score = 0;//定义分数变量
    public int length = 0;//定义长度变量
    public Text msgText; //定义模式信息文本变量,在界面显示,public的变量可以在unity3d操作界面进行指代
    public Text scoreText; //定义分数文本变量,在界面显示
    public Text lengthText; //定义长度文本变量,在界面显示
    public Image bgImage; //定义Image类型的变量,指代的就是界面上的Bg
    private Color tempColor; //定义颜色变量
    public bool isPause = false;//定义暂停的布尔类型变量,用于判断是否暂停,默认没有暂停
    public Image pauseImage; //定义暂停/继续背景图片
    public Sprite[] pauseSprites = new Sprite[2]; //定义暂停/继续的图片组
    public bool hasboder = true;//定义边界变量,默认是有边界的


    void Awake()//开始就唤醒给_instance赋值,不然_istance指向null
    {
        _instance = this;
    }

    void Start()
    {
        if(PlayerPrefs.GetInt("border",1) == 0) //默认是有边界的,如果等于0就相当于调到了自由模式
        {
            hasboder = false; //变成false就是设成没有边界
            foreach(Transform t in bgImage.gameObject.transform)//遍历bgImage下的所有子物体,交给t
            {
                t.gameObject.GetComponent<Image>().enabled = false; //在主场景中bg下的子物体就是上下左右四个边界,这里就是禁用四个边界,即没有边界
            }
        }
    }
	
	void Update ()
    {
		switch(score/100)//判断分数达到不同值改变它的颜色并改变显示它处于不同的阶段
        {
            case 0:
            case 1:
            case 2:
                break;
            case 3:
            case 4:
                ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor);//将改变后的颜色解析输出到tempColor
                bgImage.color = tempColor; //将tempColor赋给bgImage,在显示界面看到效果
                msgText.text = "阶段" + 2; //在显示界面显示阶段2
                break;
            case 5:
            case 6:
                ColorUtility.TryParseHtmlString("#CDFFDBEE", out tempColor);
                bgImage.color = tempColor;
                msgText.text = "阶段" + 3;
                break;
            case 7:
            case 8:
                ColorUtility.TryParseHtmlString("#EBFFDBFF", 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) //分数更新函数,默认情况下吃到一个食物分数加5,长度加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);//加载0号场景,回到首页
    }
}

StartUIController.cs

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

public class StartUIController : MonoBehaviour {

    public Text lastText; //定义上次得分、长度文本变量,对应的是开始场景上的“上次”文本处
    public Text bestText; //定义最佳得分、长度文本变量,对应的是开始场景上的“最佳”文本处
    public Toggle blue; //这是个组件,定义组合按钮blue,在开始场景中它是加入了组的,在同组中相当于选择的时候只有一个可以被选中,它和yellow是互斥的
    public Toggle yellow;
    public Toggle border;
    public Toggle noborder;

    void Start() //开始函数,一开始就执行
    {
        if(PlayerPrefs.GetString("sh","sh01") == "sh01")//读取保存的信息同sh01比对,如果没有选择,默认是sh01给sh,这里是判断是否是小蓝蛇,GetString就是读取setString保存的信息的
        {
            blue.isOn = true; //条件满足的条件下,true就是将开始场景中blue勾选的意思
            PlayerPrefs.SetString("sh", "sh01");
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");
        }
        else
        {
            yellow.isOn = true; //条件满足的条件下,true就是将开始场景中yellow勾选的意思
            PlayerPrefs.SetString("sh", "sh02");
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
        if(PlayerPrefs.GetInt("border",1) == 1)//GetInt就是读取SetInt保存的信息的
        {
            border.isOn = true; //条件满足的条件下,true就是将开始场景中的boder勾选的意思
            PlayerPrefs.SetInt("border", 1);
        }
        else
        {
            noborder.isOn = true;//条件满足的条件下,true就是将开始场景中的noboder勾选的意思
            PlayerPrefs.SetInt("border", 0);
        }
    }

    void Awake()//唤醒函数
    {
        lastText.text = "上次:长度" + PlayerPrefs.GetInt("lastl", 0) + ",分数" + PlayerPrefs.GetInt("lasts", 0);//在开始场景显示上次的记录,这里是从保存的记录中获取,默认的是没玩过是0,玩过之后有记录就会被覆盖
        bestText.text = "最佳:长度" + PlayerPrefs.GetInt("bestl", 0) + ",分数" + PlayerPrefs.GetInt("bests", 0);// 在开始场景显示最佳的记录,这里是从保存的记录中获取,默认的是没玩过是0,玩过之后有记录就会被覆盖
    }

    public void BlueSelected(bool isOn)//小蓝蛇选择函数,这种类型的函数是挂载到开始界面blue上的,此函数相当于是定义了小蓝蛇的样子并保存起来它的属性
    {
        if(isOn)
        {
            //将蛇头和后面两个蛇身保存起来,这里保存的白蓝色蛇身,相当于小蓝蛇,此函数相当于是定义了小蓝蛇的样子并保存起来它的属性
            PlayerPrefs.SetString("sh", "sh01");//SetString就是保存的意思,这保存的是蛇头信息
            PlayerPrefs.SetString("sb01", "sb0101");
            PlayerPrefs.SetString("sb02", "sb0102");

        }
    }
    public void YellowSelected(bool isOn) //小黄蛇选择函数,这种类型的函数是挂载到开始界面yellow上的,此函数相当于是定义了小黄蛇的样子并保存起来它的属性
    {
        if (isOn)
        {
            //将蛇头和后面两个蛇身保存起来,这里保存的蓝黄色蛇身,相当于小黄蛇,此函数相当于是定义了小黄蛇的样子并保存起来它的属性
            PlayerPrefs.SetString("sh", "sh02");//SetString就是保存的意思,这保存的是蛇头信息
            PlayerPrefs.SetString("sb01", "sb0201");
            PlayerPrefs.SetString("sb02", "sb0202");
        }
    }
    public void BoderSelected(bool isOn) //边界模式选择,这种类型的函数是挂载到开始界面border上的,此函数相当于是定义了边界模式的属性并保存起来它的属性
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 1);//设置为边界模式,并且定义了boder等于1时为边界模式,setInt就是保存的意思
        }
    }
    public void NoBoderSelected(bool isOn)//自由模式选择,这种类型的函数是挂载到开始界面noboder上的
    {
        if (isOn)
        {
            PlayerPrefs.SetInt("border", 0);//设置为无边界(自由)模式,并且定义了boder等于0时为自由模式,setInt就是保存的意思
        }
    }

    public void StartGame()//开始游戏函数,对应开始场景上的开始按钮
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);//加载1号场景,即main场景
    }

}

foodmaker.cs

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

public class foodmaker : MonoBehaviour {
    private static foodmaker _instance;//定义一个静态的私有foodmaker型的变量
    public static foodmaker Instance //提供一个外部访问的静态成员函数,其他脚本无法修改,只能访问,返回一个_instance
    {
        get
        {
            return _instance;
        }
    }

    public int xlimit = 27;//定义食物x轴方向的最大限制,一个单位相当于一个步长step的大小,这个值相当于是坐标上具体的值除以步长得到的值,因为蛇头运动是按照一个步长一个步长的瞬间移动
    public int ylimit = 10;//定义食物y轴方向最大的限制,y轴上下都是10步,整个显示区是呈现x,y轴左右对称的,坐标轴原点在显示区中心
    public int xoffset = 10; //这个是左边分数、模式显示区x轴方向的步长宽度
    public GameObject foodPrefab; //定义食物的预制体
    public GameObject rewardPrefab; //定义奖励预制体
    public Sprite[] foodSprites;  //定义食物的图片
    private Transform foodHolder; //定义放置食物位置的容器

    void Awake() //开始就唤醒给_instance赋值,不然_istance指向null
    {
        _instance = this;
    }

	void Start () {
        foodHolder = GameObject.Find("FoodHolder").transform;//查找到FoodHolder这个定义的容器的位置
        MakeFood(false);//调用食物生成函数,开始时不生成奖励
	}
	void Update () {
		
	}
    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); //设置食物的父物体为foodHolder,就是将food放置在foodHolder容器下面
        int x = Random.Range(-xlimit + xoffset, xlimit); //设置食物在x轴上的范围
        int y = Random.Range(-ylimit, ylimit); //设置食物在y轴上的范围
        food.transform.localPosition = new Vector3(x * 15, y * 15, 0);//设置食物的随机生成具体位置,乘以15是因为步长为15,保证蛇头能够触碰到
        if(isReward)
        {
            GameObject reward = Instantiate(rewardPrefab); //实例化奖励预制体
            reward.transform.SetParent(foodHolder, false); //设置奖励的父物体为foodHolder,就是将food放置在foodHolder容器下面
            x = Random.Range(-xlimit + xoffset, xlimit); //设置奖励在x轴上的范围
            y = Random.Range(-ylimit, ylimit); //设置奖励在y轴上的范围
            reward.transform.localPosition = new Vector3(x * 15, y * 15, 0);//设置奖励的随机生成具体位置,乘以15是因为步长为15,保证蛇头能够触碰到

        }
    }
}

SnakeHead.cs

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

public class SnakeHead : MonoBehaviour {
    public int step;//建立蛇头一步的距离的变量
    public float velocity=0.35f;//设置蛇头的速度
    private int x;
    private int y;
    private Vector3 headpos;//建立蛇头的当前位置变量
    public GameObject bodyprefab;//蛇身的预制体
    public List<Transform> bodyList = new List<Transform>();//用于存放蛇身位置的链表
    public Sprite[] bodySprites = new Sprite[2];//指定生成的长度为2,即这个数组存放的蛇身的颜色有两种
    public Transform canvas; //定义放置蛇身位置的容器
    private bool isDie = false; //定义是否死亡的布尔变量,默认是没有死亡false
    public GameObject dieEffect;//定义死亡爆炸特效
    public AudioClip eatClip; //定义吃食物音效
    public AudioClip dieClip; //定义死亡音效

    void Start()
    {
        InvokeRepeating("move", 0, velocity);//设置重复调用函数move,实现蛇头不停的运动,每隔0.35秒调用一次
        x = 0; y = step;//初始设置x,y值
    }

    void Awake()//唤醒函数
    {
        canvas = GameObject.Find("Canvas").transform;//查找到Canvas这个容器的位置
        //通过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", "sb0201"));
        bodySprites[1] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02", "sb0202"));
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)&&MainUIController.Instance.isPause==false && isDie ==false)//获取按键空格键按下操作,且不处于暂停状态,且不处于死亡状态
        {
            CancelInvoke();//取消当前调用的InvokeRepeating
            InvokeRepeating("move", 0, velocity - 0.3f);//设置新的函数,改变蛇头运动的频率
        }
        if(Input.GetKeyUp(KeyCode.Space)&&MainUIController.Instance.isPause == false && isDie == false)//获取按键空格键抬起操作
        {
            CancelInvoke();//取消当前调用的InvokeRepeating
            InvokeRepeating("move", 0, velocity);//设置新的函数,改变蛇头运动的频率,退回到原始频率
        }
        if (Input.GetKey(KeyCode.W) && MainUIController.Instance.isPause == false && isDie == false)//获取按键W,按下w,设置蛇头向上的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 0);//蛇头原始的方向
            x = 0;y = step; 
        }
        if (Input.GetKey(KeyCode.S) && MainUIController.Instance.isPause == false && isDie == false)//获取按键S,按下s,设置蛇头向下的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, 180);//改变蛇头的方向,向下旋转180度
            x = 0;y = -step;
        }
        if (Input.GetKey(KeyCode.A) && MainUIController.Instance.isPause == false && isDie == false)//获取按键A,按下a,设置蛇头向左的偏移量        

        { gameObject.transform.localRotation = Quaternion.Euler(0, 0, 90); //改变蛇头的方向,向左旋转90度
            x = -step; y = 0;
        }
        if (Input.GetKey(KeyCode.D) && MainUIController.Instance.isPause == false && isDie == false)//获取按键D,按下d,设置蛇头向右的偏移量
        {
            gameObject.transform.localRotation = Quaternion.Euler(0, 0, -90); //改变蛇头的方向,向右旋转90度
             x = step; y = 0;
        }
    }

    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;//第一个蛇身移动到蛇头移动前的位置
        }
    }

    void Grow()//生成蛇身
    {
        AudioSource.PlayClipAtPoint(eatClip, Vector3.zero); //播放吃食物音效
        int index = (bodyList.Count % 2 == 0) ? 0 : 1;//因为蛇身的颜色是交叉呈现的,所以蛇身的颜色是两种不同的,即蛇身的奇偶部分呈现交叉,所以给奇偶部分赋值0或1两个索引
        GameObject body = Instantiate(bodyprefab,new Vector3(2000,2000,0),Quaternion.identity);//实例化蛇身在屏幕看不见的位置
        body.GetComponent<Image>().sprite = bodySprites[index];//获取蛇身上的Image组件给他附上图片
        body.transform.SetParent(canvas, false); //设置蛇身的父物体为canvas,就是将蛇身放置在canvas容器下面
        bodyList.Add(body.transform);//将生成的蛇身添加进这个链表里
    }

    public void Die() //死亡函数
    {
        AudioSource.PlayClipAtPoint(dieClip, Vector3.zero); //播放死亡音效
        CancelInvoke();//取消移动
        isDie = true;
        Instantiate(dieEffect);//实例化爆炸粒子特效 
        PlayerPrefs.SetInt("lastl", MainUIController.Instance.length);//保存最后一次长度,将length存放在lastl中,这个PlayerPrefs类是系统有的
        PlayerPrefs.SetInt("lasts", MainUIController.Instance.score);//保存最后一次得分,将score存放在lasts中
        if(PlayerPrefs.GetInt("bests",0)<MainUIController.Instance.score)//判断最好的得分是否小于当前得分,PlayerPrefs.GetInt("bests",0)表示在初始的时候没玩过的时候,默认是0,之后就会用bests替换
        {
            PlayerPrefs.SetInt("bestl", MainUIController.Instance.length);//如果满足了条件,就会将当前长度替换保存在bestl中
            PlayerPrefs.SetInt("bests", MainUIController.Instance.score);//如果满足了条件,就会将当前得分替换保存在bests中
        }
        StartCoroutine(GameOver(1.5f)); //调用协程函数GameOver
    }

    IEnumerator GameOver(float t) //协程函数(分步执行,遇到条件(yield return)会挂起,直到条件满足才会被唤醒继续执行后面的代码
    {
        yield return new WaitForSeconds(t);//等待t秒后继续执行下面
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);//重载当前场景
    }

    private void OnTriggerEnter2D(Collider2D collider)//检测碰撞并传入碰撞信息
    {
        if (collider.gameObject.CompareTag("food")) //检测碰撞到的是不是标签为food
        {
            Destroy(collider.gameObject); //销毁碰撞到的物体
            MainUIController.Instance.UpdateUI(); //调用MainUIController中的UpdateUI()函数,就是分数更新
            Grow();
            foodmaker.Instance.MakeFood(Random.Range(0,100)<20?true:false); //通过访问脚本foodmaker公有的Instance并调用里面的MakeFood函数生成新的食物,有20%的几率生成奖励
        }
        else if(collider.gameObject.CompareTag("Reward")) //检测碰撞到的是奖励
        {
            Destroy(collider.gameObject); //销毁碰撞到的物体
            MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);//随机加上50到150的分数,长度默认加1
            Grow();
        }
        else if(collider.gameObject.CompareTag("body"))//检测碰撞到身体后死亡
        {
            Die();
        }
        else//检测到碰撞到四面墙壁后的后续反应
        {
            if(MainUIController.Instance.hasboder)
            {
                Die();
            }
            else
            {
                switch (collider.gameObject.name)
                {
                    case "up":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 15, transform.localPosition.z);
                        //碰撞到上面的时候改变蛇头的位置,因为上下墙面对称,所以直接取相反数,加上15是在改变成相反位置后加上一个步长,因为改变方向后他又会和下面墙碰撞,所以加一个步长避免和下墙碰撞
                        break;
                    case "down":
                        transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 15, transform.localPosition.z);
                        //碰撞到下面的时候改变蛇头的位置,因为上下墙面对称,所以直接取相反数,减15是在改变成相反位置后减一个步长,因为改变方向后他又会和上面墙碰撞,所以减一个步长避免和上墙碰撞
                        break;
                    case "left":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 135, transform.localPosition.y, transform.localPosition.z);
                        //碰撞到左面的时候改变蛇头的位置,因为左右墙面存在着10个步长的距离,所以直接去相反数,加上135是在改变成相反位置后加上9个步长,少的那个步长是避免和右墙碰撞
                        break;
                    case "right":
                        transform.localPosition = new Vector3(-transform.localPosition.x + 165, transform.localPosition.y, transform.localPosition.z);
                        //碰撞到右面的时候改变蛇头的位置,因为左右墙面存在着10个步长的距离,所以直接去相反数,加上165是在改变成相反位置后加上11个步长,多的那个步长是避免和左墙碰撞
                        break;
                }
           }
            
        }
    }
}

猜你喜欢

转载自blog.csdn.net/IT_CREATE/article/details/82027541