Unity之贪吃蛇游戏
大纲
UI制作:
1.Start界面
(0)背景:
(1)皮肤
(2)模式
(3)得分:长度,分数
2.Game界面
(0)背景
(1)阶段
(2)得分
(3)长度
(4)碰撞器边界
2.小蛇的行为活动:
(1)wasd移动蛇头,f加速
(2)吃食物,并使蛇身增长,(链表节点+1)
(3)使蛇身跟着一起移动(都为本地坐标)
(4)蛇死亡
3.食物系统:
(1)生成普通食物
(2)生成奖励
4.UI与数据交互
1.StartUI
(1)最高和上次的数据显示
(2)皮肤和模式选择:结合Toggle使用动态函数设置(Set)皮肤和边界的数据,注意皮肤是需要被加载的资源,设置的是文件名。 还需要再Start设置(Get)开始时的默认模式和皮肤。
2.GameUI
(1)背景变化
(2)暂停和回家
(3)是否有边界
3.Snake: 获取蛇的皮肤数据
新知识:
1.结合Toggle使用动态函数动态的获取Toggle的isOn;
2.使用PlayerPrefs.GetString(键,值)和PlayerPrefs.GetString(键)等储存和获取信息。
3.通过Resources.Load(string path)方法加载资源,注意文件名一定为Resources。
4.单例模式的使用
一.制作UI界面
导入资源。
1.Start界面
其中背景为image组件
开始游戏为一个按钮
皮肤,模式,得分,长度,分数为为Text;
皮肤,模式的选择用Toggle+Toggle Group控制
2.然后新建一个场景制作游戏时的UI
二.小蛇的行为活动
制作完了UI之后我们开始处理小蛇,首先我们先让它的蛇头动起来,首先创建一个image图片然后让拖入相应的蛇头文件,然后创建相应的脚本挂在小球上,脚本如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class snake : MonoBehaviour
{
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;
private void Start()
{
speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step )
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step )
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F) )//加速
{
CancelInvoke();
InvokeRepeating("Move", 0, speed - 0.45f);
}
if (Input.GetKeyUp(KeyCode.F))//停止加速
{
CancelInvoke();
InvokeRepeating("Move", 0, speed);
}
}
/// <summary>
/// 移动
/// </summary>
void Move()
{
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
}
}
三.食物系统
由于小蛇的行为活动中的吃食物需要食物,所以我们先转到食物系统。
生成普通食物和随机奖励。
创建image组件并拖入食物图片,然后把它变成一个预制体,然后添加标签为food,同上创建奖励,添加reward标签,然后创建一个空组并命名为ScriptHoler,然后在上面挂载脚本名为makeFood的脚本。注意由于食物在游戏中时时存在且唯一,所以我们可以把它设置为单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class makeFood : MonoBehaviour
{
public GameObject foodPerfatic;
public GameObject foodReward;
int xlimit,ylimit; //食物生成的位置的限定值
int xoffset; //由于地图的横向位置不对称所以会有一个偏移量。
int x, y;
Transform canvas;获取画布以便设置父对象
public Sprite[] foodSprites= new Sprite[8];//食物的种类的照片
//单例模式
static private makeFood _instant;
static public makeFood Instant
{
get { return _instant; }
}
private void Awake()
{
_instant = this;
canvas= GameObject.Find("Canvas").transform;//获取名字为Canvas的组件
}
void Start()
{
ylimit = 11;
xlimit = 20 ;
xoffset = 9;
CreatFood();
}
// Update is called once per frame
public void CreatFood()
{
x = Random.Range(-(xlimit-xoffset),xlimit)*30;//Random.Range是前开后闭区间
y = Random.Range(-ylimit, ylimit)*30;
GameObject food = Instantiate(foodPerfatic);
food.transform.SetParent(canvas, false);//设置生成食物的父对象,并使它保持未设置父对象之前的状态(缩放,旋转,坐标等值不变)
int index = Random.Range(0, foodSprites.Length);
food.GetComponent<Image>().sprite = foodSprites[index];//设置食物的图片
food.transform.localPosition = new Vector3(x, y, 0);
bool isnoreward = Random.Range(0, 100) < 20 ? true : false;//设置有1/5的概率生成奖励
if (isnoreward)
{
x = Random.Range(-(xlimit - xoffset), xlimit) * 30;
y = Random.Range(-ylimit, ylimit) * 30;
GameObject reward = Instantiate(foodReward);
reward.transform.SetParent(canvas, false);
food.transform.localPosition = new Vector3(x, y, 0);
}
}
}
四.小蛇的行为活动
1.(吃食物)
生成食物后我们移动蛇头发现碰食物不能并能使食物消失,所以我们需要添加碰撞检测,
我们先给食物的预制体添加2d盒型碰撞器,并把它的范围x,y设置为一个比step(每次移动的距离)稍小的值,如果x,y比step大的话,就会出现擦边就吃到食物的现象,然后再勾上is Trigger。
然后再给蛇头添加2d盒型碰撞器,x,y的值同食物一样,然后再加上一个2d刚体并把它的Gravity Scale设置0。然后更新snake 的脚本如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class snake : MonoBehaviour
{
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;
private void Start()
{
speed=0.5f
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step )
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step )
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F) )//加速
{
CancelInvoke();
InvokeRepeating("Move", 0, speed - 0.45f);
}
if (Input.GetKeyUp(KeyCode.F))//停止加速
{
CancelInvoke();
InvokeRepeating("Move", 0, speed );
}
}
/// <summary>
/// 移动
/// </summary>
void Move()
{
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "food")
{
Destroy(collision.gameObject);
makeFood.Instant.CreatFood();
}
else if (collision.tag == "reward")
{
Destroy(collision.gameObject);
}
}
}
2.(长身体和蛇身移动)
吃到食物后蛇身应该增长(实质就是给链表加一个节点),然后蛇身并随之移动(移动的核心思想就是把前一个蛇身的坐标赋值给后一个蛇身,然后把未移动前的蛇头坐标赋值给第一个蛇身。
然后更新snake 的脚本如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class snake : MonoBehaviour
{
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
public static float speed;
List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
public Transform bodyPrefab;//身体的预制体
public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
Transform canvas;//获取画布以便设置父对象
private void Start()
{
speed=0.5f
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
canvas = GameObject.Find("Canvas").transform;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step )
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step )
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F))
{
CancelInvoke();
InvokeRepeating("Move", 0, speed - 0.45f);
}
if (Input.GetKeyUp(KeyCode.F) )
{
CancelInvoke();
InvokeRepeating("Move", 0, speed);
}
}
/// <summary>
/// 生长
/// </summary>
/// <param name="collision"></param>
void Grow()
{
Transform newbody = Instantiate(bodyPrefab);
newbody.transform.SetParent(canvas, false);
int index = bodylist.Count % 2 == 0 ? 1 : 0;
newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
bodylist.Add(newbody);
}
void Move()
{
Debug.Log(transform.position);
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
if (bodylist.Count > 0)
{
for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
{
bodylist[i + 1].localPosition = bodylist[i].localPosition;
}
bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "food")
{
Destroy(collision.gameObject);
Grow();
makeFood.Instant.CreatFood();
}
else if (collision.tag == "reward")
{
Destroy(collision.gameObject);
Grow();
}
}
}
运行效果如下
3.(死亡判定)
蛇死亡一种是撞到边界,一种是蛇头撞到蛇身。然后 游戏边界的UI部分已完成(实质上就是盒型碰撞器标签为borad),然后我们更新snake脚本如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class snake : MonoBehaviour
{
//move函数
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
public static float speed;
//grow函数
public Transform bodyPrefab;//身体的预制体
public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
Transform canvas;//获取画布以便设置父对象
//death函数
bool isDia;//用于判断蛇是否死亡
private void Start()
{
speed=0.5f
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
canvas = GameObject.Find("Canvas").transform;
isDia = false;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step&&!isDia)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F) && !isDia)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed-0.45f);
}
if (Input.GetKeyUp(KeyCode.F) && !isDia)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed);
}
}
/// <summary>
/// 生长
/// </summary>
/// <param name="collision"></param>
void Grow()
{
Transform newbody = Instantiate(bodyPrefab);
newbody.transform.SetParent(canvas, false);
int index = bodylist.Count % 2 == 0 ? 1 : 0;
newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
bodylist.Add(newbody);
}
void Move()
{
Debug.Log(transform.position);
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
if (bodylist.Count > 0)
{
for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
{
bodylist[i + 1].localPosition = bodylist[i].localPosition;
}
bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身
}
}
public void death()
{
isDia = true;
CancelInvoke();//取消对move函数的调用
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "food")
{
Destroy(collision.gameObject);
Grow();
makeFood.Instant.CreatFood();
}
else if (collision.tag == "reward")
{
Destroy(collision.gameObject);
Grow();
}
else if (collision.tag == "body")
{
death();
}
else
{
death();
}
}
}
UI与数据交互
Game
(1)分数变化,游戏背景变化,蛇的速度变化
(2)暂停和回家按钮
创建一个脚本命名为GameUICtroll ,添加以下内容,并挂载到ScriptHoler上去,然后将各组件一一对应。值得注意的是GameUICtroll 也符合单例模式的要求,可以把其设置为单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameUICtroll : MonoBehaviour
{
static private GameUICtroll _instant;
static public GameUICtroll Instant
{
get { return _instant; }
}
public bool isPause;
public int grade;
public int Length;
public Text ScoreText;
public Text LengthText;
public Text JieDuanText;
public Transform BGM;//用于储存游戏背景图片
public Sprite[] pauseSprite = new Sprite[2];
public Transform PauseButton;
private Color temcolor;
private void Awake()
{
//如果这里不初始时间流速,那么在游戏里按了暂停在返回家,再开始游戏就还会是暂停。
Time.timeScale = 1;
_instant = this;
}
void Start()
{
isPause = false;
grade = 0;
}
/// <summary>
/// 增加分数,以及分数增加后对游戏背景和小蛇速度的处理
/// </summary>
/// <param name="score"></param>
/// <param name="length"></param>
public void AddGrade(int score=10,int length=1)
{
Length+=length;
grade += score;
switch (grade / 100)
{
case 2:
case 3:
snake.speed = 0.35f;
ColorUtility.TryParseHtmlString("#CCEEFFFF", out temcolor);//解析一个表示颜色的字符串
BGM.GetComponent<Image>().color = temcolor;
JieDuanText.text = "阶段" + 2;
break;
case 4:
case 5:
snake.speed = 0.2f;
ColorUtility.TryParseHtmlString("#CCFFDBFF", out temcolor);
BGM.GetComponent<Image>().color = temcolor;
JieDuanText.text = "阶段" + 3;
break;
default:
break;
}
ScoreText.text = "分数\n" + grade;
LengthText.text = "长度\n" + Length;
}
/// <summary>
/// 暂停功能
/// </summary>
public void Pause()
{
isPause = !isPause;
if (isPause)
{
PauseButton.GetComponent<Image>().sprite = pauseSprite[1];
Time.timeScale = 0;
}
else
{
PauseButton.GetComponent<Image>().sprite = pauseSprite[0];
Time.timeScale = 1;
}
}
/// <summary>
/// 回到游戏开始界面
/// </summary>
public void Hoom()
{
SceneManager.LoadScene(0);
}
}
然后我们在snake脚本里调用GameUICtroll 脚本里的AddGrade()函数,并记录死亡时的分数和设置死亡后重开的游戏协程等操作,更新如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class snake : MonoBehaviour
{
//move函数
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
public static float speed;
//grow函数
public Transform bodyPrefab;//身体的预制体
public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
Transform canvas;//获取画布以便设置父对象
//death函数
bool isDia;//用于判断蛇是否死亡
private void Start()
{
speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
canvas = GameObject.Find("Canvas").transform;
isDia = false;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step&&!isDia&&GameUICtroll.Instant.isPause)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step && !isDia&&!GameUICtroll.Instant.isPause)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step && !isDia&&!GameUICtroll.Instant.isPause)
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step && !isDia&&!GameUICtroll.Instant.isPause)
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F) && !isDia&&!GameUICtroll.Instant.isPause)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed - 0.45f);
}
if (Input.GetKeyUp(KeyCode.F) && !isDia&&!GameUICtroll.Instant.isPause)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed);
}
}
/// <summary>
/// 生长
/// </summary>
/// <param name="collision"></param>
void Grow()
{
Transform newbody = Instantiate(bodyPrefab);
newbody.transform.SetParent(canvas, false);
int index = bodylist.Count % 2 == 0 ? 1 : 0;
newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
bodylist.Add(newbody);
}
void Move()
{
Debug.Log(transform.position);
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
if (bodylist.Count > 0)
{
for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
{
bodylist[i + 1].localPosition = bodylist[i].localPosition;
}
bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身
}
}
public void death()
{
isDia = true;
CancelInvoke();//取消对move函数的调用
//PlayerPrefs以键值对的形式将数据保存在文件中,我们可以根据名称取出上次保存的值
PlayerPrefs.SetInt("LastS", GameUICtroll.Instant.grade);
PlayerPrefs.SetInt("LastL",GameUICtroll.Instant.Length);
if (PlayerPrefs.GetInt("LastS", 0) > PlayerPrefs.GetInt("BestS", 0))
{
PlayerPrefs.SetInt("BestS", GameUICtroll.Instant.grade);
PlayerPrefs.SetInt("BestL", GameUICtroll.Instant.Length);
}
StartCoroutine("Die");
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "food")
{
Destroy(collision.gameObject);
Grow();
makeFood.Instant.CreatFood();
}
else if (collision.tag == "reward")
{
Destroy(collision.gameObject);
Grow();
}
else if (collision.tag == "body")
{
death();
}
else
{
death();
}
}
IEnumerator Die()
{
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
Start界面
(1)皮肤和模式选择:结合Toggle使用动态函数设置(Set)皮肤和边界的数据,注意皮肤是需要被加载的资源,设置的是文件名。 还需要再Start设置(Get)开始时的默认模式和皮肤。
(2)最高和上次的数据显示
新建脚本名为StartUI的脚本,并创建一个空对象ScriptGet。把脚本挂载到上面,脚本内容如下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class StartUI : MonoBehaviour
{
public Toggle blue;
public Toggle yellow;
public Toggle broad;
public Toggle noBroad;
public Text LastText;
public Text BestText;
void Start()
{
//显示上次和最高的分数
LastText.text = "上次: " + "分数 " + PlayerPrefs.GetInt("LastS") + "长度 " + PlayerPrefs.GetInt("LastL");
BestText.text = "最高: " + "分数 " + PlayerPrefs.GetInt("BestS") + "长度 " + PlayerPrefs.GetInt("BestL");
//设置刚开始的默认皮肤和用户选择了其它皮肤游戏结束时默认还是进入游戏时选择的皮肤
if (PlayerPrefs.GetString("sh","sh01")=="sh01")
{
blue.isOn = true;
}
else
{
yellow.isOn = true;
}
//模式同皮肤一样
if (PlayerPrefs.GetInt("borad", 0) ==1)
{
broad.isOn = true;
}
else
{
noBroad.isOn = true;
}
}
//该函数连接Toggle组件,当Toggle组件isOn变化时会动态的返回Toggle组件isOn的值
public void Blue(bool isOn)
{
if (isOn)
{
PlayerPrefs.SetString("sh","sh01");
PlayerPrefs.SetString("sb01", "sb0101");
PlayerPrefs.SetString("sb02", "sb0102");
}
}
public void Yellow(bool isOn)
{
if (isOn)
{
PlayerPrefs.SetString("sh", "sh02");
PlayerPrefs.SetString("sb01", "sb0201");
PlayerPrefs.SetString("sb02", "sb0202");
}
}
public void Classica(bool isOn)
{
if (isOn)
{
PlayerPrefs.SetInt("borad", 1);
}
}
public void Free(bool isOn)
{
if (isOn)
{
PlayerPrefs.SetInt("borad", 0);
}
}
public void GoToGame()
{
SceneManager.LoadScene(1);
}
}
然后使该脚本定义的UI与我们创建的UI一一对应。
最后我们根据用户的选择动态的返回数据到游戏里。
GameUICtroll 脚本更新如下。主要是对是否有无边界的处理
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameUICtroll : MonoBehaviour
{
static private GameUICtroll _instant;
static public GameUICtroll Instant
{
get { return _instant; }
}
public bool isPause;
public int grade;
public int Length;
public Text ScoreText;
public Text LengthText;
public Text JieDuanText;
public Transform BGM;
public Sprite[] pauseSprite = new Sprite[2];
public Transform PauseButton;
private Color temcolor;
public bool isnoborad; //储存从用户获取是否有无边界
private void Awake()
{
if (PlayerPrefs.GetInt("borad") == 0)
{
isnoborad = false;
foreach (Transform item in BGM)//把背景的边界去掉,也就是背景下四个碰撞器上的Image组件停用
{
item.GetComponent<Image>().enabled = false;
}
}
else
{
isnoborad = true;
}
//如果这里不初始时间流速,那么在游戏里按了暂停在返回家,再开始游戏就还会是暂停。
Time.timeScale = 1;
_instant = this;
}
void Start()
{
isPause = false;
grade = 0;
}
/// <summary>
/// 增加分数,以及分数增加后对游戏背景和小蛇速度的处理
/// </summary>
/// <param name="score"></param>
/// <param name="length"></param>
public void AddGrade(int score=10,int length=1)
{
Length+=length;
grade += score;
switch (grade / 100)
{
case 2:
case 3:
snake.speed = 0.35f;
ColorUtility.TryParseHtmlString("#CCEEFFFF", out temcolor);//解析一个表示颜色的字符串
BGM.GetComponent<Image>().color = temcolor;
JieDuanText.text = "阶段" + 2;
break;
case 4:
case 5:
snake.speed = 0.2f;
ColorUtility.TryParseHtmlString("#CCFFDBFF", out temcolor);
BGM.GetComponent<Image>().color = temcolor;
JieDuanText.text = "阶段" + 3;
break;
default:
break;
}
ScoreText.text = "分数\n" + grade;
LengthText.text = "长度\n" + Length;
}
/// <summary>
/// 暂停功能
/// </summary>
public void Pause()
{
isPause = !isPause;
if (isPause)
{
PauseButton.GetComponent<Image>().sprite = pauseSprite[1];
Time.timeScale = 0;
}
else
{
PauseButton.GetComponent<Image>().sprite = pauseSprite[0];
Time.timeScale = 1;
}
}
/// <summary>
/// 回到游戏开始界面
/// </summary>
public void Hoom()
{
SceneManager.LoadScene(0);
}
}
snake脚本里接收(皮肤)数据。snake脚本更新如下。其中我的Resources文件夹的文件如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class snake : MonoBehaviour
{
//move函数
int step;//每次移动的距离
int x, y;//蛇横纵需要移动的距离
Vector3 HeadPosition;//储存蛇头坐标
List<Transform> bodylist = new List<Transform>();//定义一个链表储存蛇身
public static float speed;
//grow函数
public Transform bodyPrefab;//身体的预制体
public Sprite[] bodySprite = new Sprite[2];//蛇身体的图片
Transform canvas;//获取画布以便设置父对象
//death函数
bool isDia;//用于判断蛇是否死亡
private void Start()
{
//Resources.Load(string name);通过文件名加载文件名为Resources里的文件
transform.GetComponent<Image>().sprite = Resources.Load<Sprite>(PlayerPrefs.GetString("sh"));
bodySprite[0] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb02"));
bodySprite[1] = Resources.Load<Sprite>(PlayerPrefs.GetString("sb01"));
speed = 0.5f;//通过调用move的时间间隔来控制蛇的移动速度
step = 30;
InvokeRepeating("Move", 0, 0.3f);
x = step;
canvas = GameObject.Find("Canvas").transform;
isDia = false;
}
private void Update()
{
if (Input.GetKey(KeyCode.W) && y != -step&&!isDia)//蛇的移动不能反向,所以需要y != -step的判断
{
transform.rotation = Quaternion.Euler(0, 0, 0);//按w键蛇头应该要旋转的角度
y = step;
x = 0;
}
else if (Input.GetKey(KeyCode.S) && y != step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, 180);//按s键蛇头应该要旋转的角度
y = -step;
x = 0;
}
else if (Input.GetKey(KeyCode.A) && x != step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, -90);//按a键蛇头应该要旋转的角度
y = 0;
x = -step;
}
else if (Input.GetKey(KeyCode.D) && x != -step && !isDia)
{
transform.rotation = Quaternion.Euler(0, 0, 90);//按d键蛇头应该要旋转的角度
y = 0;
x = step;
}
if (Input.GetKeyDown(KeyCode.F) && !isDia)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed - 0.45f);
}
if (Input.GetKeyUp(KeyCode.F) && !isDia)
{
CancelInvoke();
InvokeRepeating("Move", 0, speed);
}
}
/// <summary>
/// 生长
/// </summary>
/// <param name="collision"></param>
void Grow()
{
Transform newbody = Instantiate(bodyPrefab);
newbody.transform.SetParent(canvas, false);
int index = bodylist.Count % 2 == 0 ? 1 : 0;
newbody.GetComponent<Image>().sprite = bodySprite[index];//当蛇身为奇数和偶数这两种不同情况时需要设置不同的照片
newbody.localPosition = new Vector3(2000, 2000, 0);//先把新生成的蛇身块的坐标设置为屏幕外,Move移动时自动添加到相应位置
bodylist.Add(newbody);
}
void Move()
{
Debug.Log(transform.position);
HeadPosition = transform.localPosition;
transform.localPosition = new Vector3(HeadPosition.x + x, HeadPosition.y + y, HeadPosition.z);
if (bodylist.Count > 0)
{
for (int i = bodylist.Count - 2; i >= 0; i--)//从后往前,依次把上一个蛇身的坐标赋值下一个
{
bodylist[i + 1].localPosition = bodylist[i].localPosition;
}
bodylist[0].localPosition = HeadPosition;//把还未移动前的蛇头坐标赋值给第一个蛇身
}
}
public void death()
{
isDia = true;
CancelInvoke();//取消对move函数的调用
//PlayerPrefs以键值对的形式将数据保存在文件中,我们可以根据名称取出上次保存的值
PlayerPrefs.SetInt("LastS", GameUICtroll.Instant.grade);
PlayerPrefs.SetInt("LastL",GameUICtroll.Instant.Length);
if (PlayerPrefs.GetInt("LastS", 0) > PlayerPrefs.GetInt("BestS", 0))
{
PlayerPrefs.SetInt("BestS", GameUICtroll.Instant.grade);
PlayerPrefs.SetInt("BestL", GameUICtroll.Instant.Length);
}
StartCoroutine("Die");
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "food")
{
Destroy(collision.gameObject);
Grow();
makeFood.Instant.CreatFood();
}
else if (collision.tag == "reward")
{
Destroy(collision.gameObject);
Grow();
}
else if (collision.tag == "body")
{
death();
}
else
{
if (GameUICtroll.Instant.isnoborad)
{
death();
}
else
{
//自由模式无边界
switch (collision.name)
{
//当碰到碰撞器名字为up的碰撞器,注意使蛇头的位置到最下面的边界在向上一个身位,
//如果不向上移动一个身位他就会碰到最下面一个边界的碰撞器。如此往复
case "up":
transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y + 30, 0);
break;
case "down":
transform.localPosition = new Vector3(transform.localPosition.x, -transform.localPosition.y - 30, 0);
break;
case "left":
transform.localPosition = new Vector3(-transform.localPosition.x + 9 * 30 - 30, transform.localPosition.y, 0);
break;
case "right":
transform.localPosition = new Vector3(-transform.localPosition.x + 9 * 30 + 30, transform.localPosition.y, 0);
break;
default:
break;
}
}
}
}
IEnumerator Die()
{
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
最后游戏基本完成,效果如下。