源码资源
https://download.csdn.net/download/it_create/10625733
或https://gitee.com/dxl96/qt_resource/blob/master/snakes.zip
开始界面
运行界面
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;
}
}
}
}
}