(利用UGUI)
格子每格30
box collider2d不识别ugui的大小,需要自己设置size
游戏逻辑–实现思路
蛇头
- 移动
- (每隔一段时间invoke repeat)(突变Transform)
- (差值loop、movetowards)
- 碰撞食物
- 传送
- 蛇头
- 死亡
- 碰撞边界
- 碰撞自己身体
蛇身
- 移动
- (继承前一个的位置;从后往前挪)(双色蛇身:奇偶性赋值图片)
- (尾巴 移到蛇头后一个)
- 增加
食物
- 随机生成
- 销毁
设置
固定分辨率:
file——build setting——player setting——resolution
取消勾选default full screen;设置为1280 x 720
Display resolution dialog选择disabled
公司名称:company name
导入资源:
project面板右键——import package——custom package添加unitypackage类型资源——import
- Scenes场景
- Scripts脚本
- audio音频
- effects特效
- fonts字体
- sprites图片
- background
- icon(道具)
- snakepart(蛇)
- UI
开始场景
canvas
- bg(image)
- 背景点缀图片(image)
- controlPanel(image)
- 皮肤(text)(Toggle group)
- Toggle1蓝色
- background
- checkmark
- 小蛇–蛇头蛇身组合(image)
- label:科技小蛇
- background
- Toggle2黄色
- background
- checkmark
- 小蛇–蛇头蛇身组合(image)
- label:小黄人蛇
- background
- Toggle1蓝色
- 模式(text)(Toggle group)
- Toggle1
- background
- checkmark
- label:边界模式(outline)
- background
- Toggle2
- background
- checkmark
- label:自由模式(outline)
- background
- Toggle1
- 分数(text)
- 上次:长度0,分数0(text)
- 最好:长度0,分数0(text)
- 皮肤(text)(Toggle group)
- title(text)(shadow)
- go(image)(Button组件)(outline)(shadow)
- 开始(text)
canvas:
render mode——screen space camera(多用于2d游戏)
(overlay:多用于3d,ui一直显示在最上层,hud抬头显示)
设置render camera为主摄像机main camera
**背景图片:**image
填充:anchor设置alt下最右下角
设置source image为背景图片
**左边栏:**image
设置source image为unity自带的background图片
color:透明度a为120
左侧填充:anchor设置alt下最左下角
**标题:**text
贪吃蛇
居中
选择字体
anchor设置 中上
**开始按钮:**image(Button组件)(outline)(shadow)
设置source image为go
set native size
左边栏控制:
皮肤、模式、分数:
居中、斜体、字体
anchor设置 中上
*分数:*text
anchor设置 中上
*模式:*创建Toggle
label——边界模式、自由模式
background——anchor设置 左中
checkmark——anchor设置alt下最右下角
模式添加Toggle group,Toggle设置Toggle group
皮肤:创建Toggle
background——anchor设置alt下最右下角
checkmark——anchor设置 最左上角
label——anchor设置alt 最右上角——科技小蛇、
background下面添加image小蛇
每个隔着35
(面板上 位置 可以写计算式子)
游戏场景
复制开始场景,进行修改
canvas
- bg
- 边界 上下左右(image)(box collider 2d)
- panel
- 模式
- 得分
- 长度
- 首页按钮底图(image)(Button)
- 首页图片(image)
- 暂停按钮底图(image)(Button)
- 暂停图片(image)
**模式:**无尽模式
设计的时候按照最大的来
得分、长度:(第一行)
0(第两行)
按钮:
底部图片设置为unity自带的UIsprite
子物体设置图片
**边界:**空物体
anchor设置 中上、下、左、右
添加collider、设置size
添加image、图片为自带的background、颜色红色
蛇头
image
设置图片
大小45
制作为预制体Prefab
添加脚本SnakeHead
》SnakeHead
自行移动(格子)
键盘控制移动
蛇头朝向变化
不能反向移动
按空格加速移动
public float velocity=0.35f;//移动的间隔时间
public int step;//一步走多少
private int x;//x坐标 增量
private int y;//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);
}
//Input.GetKey监测键盘按下一次(GetKeyDown是一直按住)
if(Input.GetKey(KeyCode.W) && y!=-step)//上up(按下w键 蛇头不在向下走)
{
//改变蛇头的朝向
gameObject.transform.localRotation=Quaternion.Euler(0,0,0);//四元数Quaternion.Euler() 3个浮点数(对应unity面板里的rotation的xyz)
//改变移动的方向
x=0;
y=step;
}
if(Input.GetKey(KeyCode.S) && y!=step)//下down(按下S键 蛇头不在向上走)
{
gameObject.transform.localRotation=Quaternion.Euler(0,0,180);
x=0;
y=-step;
}
if(Input.GetKey(KeyCode.A) && x!=step)//左left(按下A键 蛇头不在向右走)
{
gameObject.transform.localRotation=Quaternion.Euler(0,0,90);
x=-step;
y=0;
}
if(Input.GetKey(KeyCode.D) && x!=-step)//右right(按下D键 蛇头不在向左走)
{
gameObject.transform.localRotation=Quaternion.Euler(0,0,-90);
x=step;
y=0;
}
}
void Move()//蛇头移动
{
headPos = gameObject.transform.localPosition;
//UGUI下的rectTransform是Transform的子类(直接用Transform,相当于返回rectTransform)
//(不能直接点出rectTransform)(可能需要getcomponent.rectTransform?)
//canvas有缩放值,ugui的坐标与世界坐标可能有差异》使用本地坐标localPosition
gameObject.transform.localPosition=new Vector3(headPos.x+x,headPos.y+y,headPos.z);//改变位置
}
食物
**食物预制体:**foodPrefab
image
大小40
制作为预制体Prefab
canvas下的空对象 foodHolder
食物的父物体
填充:anchor设置alt下最右下角
canvas外面的空物体 ScriptsHolder
添加脚本FoodMaker
设置foodPrefab食物预制体
设置foodSprites食物图片数组
》FoodMaker
随机生成食物(位置 在格子内)
设置食物的父物体
using UnityEngine.UI;
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);//生成随机数Random.Range(包含0,不包含 数组的长度)
GameObject food=Instantiate(foodPrefab);//克隆预制体
food.GetComponent<Image>().sprite=foodSprites[index];//需要引入命名空间
//设置父物体
food.transform.SetParent(foodHolder,false);//设置父对象(,是否保存世界坐标)
//预制体实例化在canvas外边,设置父物体后自动随着canvas缩放改变 false不进行缩放
//随机(格子数)位置
int x=Random.Range(-xlimit+xoffset,xlimit);
int y=Random.Range(-ylimit,ylimit);
food.transform.localPosition=new Vector3(x*30,y*30,0);//设置位置 每格30
}
蛇头 吃 食物
蛇头:
添加碰撞器box collider2d
设置size为28
(碰撞器要比30格子小,等于30会监测到相邻的情况)
勾选is trigger
(能检测到碰撞,不会被挡住)
(一般2个中的1个勾选?)
添加刚体rigidbody
gravity设为0
(2个物体碰撞,只需要1个物体有刚体)
蛇身:
添加碰撞器box collider2d
设置size为28
设置tag为food
add tag——food
》SnakeHead
private void OnTriggerEnter2D(Collider2D collision)//进入触发检测
{
if(collision.tag=="Food")
//也可以写成if (collision.gameObject.CompareTag("Food"))
{
Destroy(collision.gameObject);//销毁(碰撞器。游戏物体)
FoodMaker.Instance.MakeFood();//生成新食物(利用单例)
}
}
》FoodMaker
单例
private static FoodMaker _instance;//静态 可以用类名访问,不用对象了
public static FoodMaker Instance//外部可以访问的成员
{
get//可以访问,不能修改
{
return _instance;
}
}
//赋值
void Awake()
{
_instance=this;
}
蛇身
image
大小40
添加box collider2d
设置size为28
制作为预制体Prefab
》SnakeHead
奇偶数选择图片
碰撞食物后,增加蛇身
蛇身移动
(赋值public变量)
using UnityEngine.UI;
//不能Transform转rectTransform??
//教程public List<RectTransform> bodyList=new List<RectTransform>();
//修改后
public List<Transform> bodyList=new List<Transform>();//身体
public GameObject bodyPrefab;//蛇身预制体
public Sprite[] bodySprites=new Sprite[2];//蛇身图片 指定长度2
private Transform canvas;//canvas位置
void Awake()
{
canvas=GameObject.Find("Canvas").transform;//找到游戏物体
}
void Grow()
{
//身体奇偶数 决定图片
int index=(bodyList.Count%2==0)?0:1;//三元表达式
GameObject body=Instantiate(bodyPrefab);//克隆蛇身预制体
body.GetComponent<Image>().sprite=bodySpprites[index];//设置图片
//设置父物体
body.transform.SetParent(canvas,false);//设置在canvas下,不保存世界坐标
//蛇身列表 增加 位置
bodyList.Add(body.transform);
}
蛇身 移动方式 一(不适用)
最后一个移动到头后面(原来的头的位置)
花纹会变化,不符合需求
》SnakeHead
先生成在外边
using ;//List.Last需要引入命名空间
void Move()
{
//...
if(bodyList.Count>0)
{
bodyList.Last().localPosition=headPos;//最后一个的位置 变为原来头的位置
bodyList.Insert(0,bodyList.Last());//列表最开始 插入原来的最后一个
bodyList.RemoveAt(bodyList.Count-1);//移出原来最后的一个
}
}
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=bodySpprites[index];
body.transform.SetParent(canvas,false)
bodyList.Add(body.transform);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag=="Food")
{
Destroy(collision.gameObject);
Grow();//吃完食物后,增加蛇身
FoodMaker.Instance.MakeFood();
}
}
蛇身 移动方式 二
后一个移动到前一个位置
从后往前挪动
》SnakeHead
void Move()
{
//...
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()
{
int index=(bodyList.Count%2==0)?0:1;
GameObject body=Instantiate(bodyPrefab,new Vector3(2000,2000,0),Quaternion.identity);//克隆(对象,位置,旋转四元数)
body.GetComponent<Image>().sprite=bodySpprites[index];
body.transform.SetParent(canvas,false)
bodyList.Add(body.transform);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag=="Food")
{
Destroy(collision.gameObject);
Grow();//吃完食物后,增加蛇身
FoodMaker.Instance.MakeFood();
}
}
碰撞身体、边界
身体
预制体 添加tag为body
》SnakeHead
private void OnTriggerEnter2D(Collider2D collision)
{
//...
else if(collision.tag=="Body")//身体
{
Debug.Log("die");
}
else//边界
{
Debug.Log("die");//边界模式
}
}
边界传送
private void OnTriggerEnter2D(Collider2D collision)
{
//...
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;
}
}
}
奖励
image
设置图片——大小30
添加box collider2d——设置size为28
设置tag为Reward
制作为预制体Prefab
》FoodMaker
在ScriptsHolder上赋值rewardPrefab
isReward判断是否生成额外奖励
public GameObject rewardPrefab;//奖励预制体
void Start()
{
//...
MakeFood(false);//第一次生成,不生成奖励
}
public void MakeFood(bool isReward)
{
//...
if(isReward)
{
GameObject reward=Instantiate(rewardPrefab);
reward.transform.SetParent(foodHolder,false);
x=Random.Range(-xlimit+xoffset,xlimit);
y=Random.Range(-ylimit,ylimit);
reward.transform.localPosition=new Vector3(x*30,y*30,0);
}
}
》SnakeHead
随机概率,生成额外奖励
检测碰撞
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag=="Food")
{
Destroy(collision.gameObject);
Grow();
//20%的概率 生成食物和奖励;否则只生成食物
FoodMaker.Instance.MakeFood((Random.Range(0,100)<20)?true:false);
}
else if(collision.tag=="Reward")
{
Destroy(collision.gameObject);
Grow();
}
//...
}
分数、长度、阶段等级
创建脚本
ScriptsHolder挂载脚本
赋值public变量
》MainUIController
单例
更新UI
背景颜色切换
using UnityEngine.UI;
//单例
private static MainUIController _instance;
public static MainUIController Instance
{
get
{
return _instance;
}
}
public int score=0;//分数
public int length=0;//长度
public Text msgText;
public Text scoreText;
public Text lengthText;
public Image bgImage;//背景图片
private Color tempColor;//存储颜色
//单例赋值
void Awake()
{
_instance=this;
}
void Update()
{
//初始文字为 阶段1
switch(score/100)
{
case 0:
case 1:
case 2:
break;
case 3:
case 4:
ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);//16进制颜色(字符,输出) 蓝色
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;
}
}
public void UpdateUI(int s=5,int l=1)//更新UI
{
score+=s;
length+=l;
scoreText.text="得分:\n"+score;// 换行符\n
lengthText.text="长度:\n"+length;
}
》SnakeHead
更新得分、长度
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag=="Food")
{
Destroy(collision.gameObject);
MainUIController.Instance.UpdateUI();//普通食物的得分
Grow();
FoodMaker.Instance.MakeFood((Random.Range(0,100)<20)?true:false);
}
else if(collision.tag=="Reward")
{
Destroy(collision.gameObject);
MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);//奖励的得分 随机
Grow();
}
//...
}
暂停、返回菜单
暂停
在暂停按钮物体上的Button组件,设置click事件
为ScriptsHolder的pause方法
》MainUIController
暂停Time.timeScale
图标改变
public bool isPause;
public Image pauseImage;//暂停图片对象
public Sprite[] pauseSprites;//图片
public void Pause()
{
isPause=!isPause;//按下按钮,状态取反
if(isPause)//暂停
{
Time.timeScale=0;//时间冻结
pauseImage.sprite=pauseSprites[1];
}
else
{
Time.timeScale=1;//时间进行
pauseImage.sprite=pauseSprites[0];
}
}
默认空格键暂停,与加速移动冲突
》edit——project setting——input manger——Submit——删除space
蛇头移动
》SnakeHead
void Update()
{
if(Input.GetKeyDown(KeyCode.Space) && MainUIController.Instance.isPause==false)
{}
...
...
//所有按键 if条件 都增加不能是暂停状态
}
主页按钮
file——build setting——add Scene增加场景
0开始场景
1游戏场景
绑定Button的click事件
》MainUIController
public void Home()
{
UnityEngine.SceneManagement.SceneManager.LoadScene(0);//加载场景
}
死亡、记录得分
》SnakeHead
协程 停止后再继续
停止移动
死亡特效
回到起始状态
//using System.Collections;//协程的命名空间
private bool isDie=false;
public GameObject dieEffect;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space) && MainUIController.Instance.isPause==false && isDie==false)
{}
...
...
//所有按键 if条件 都增加不能是死亡状态
}
void Die()
{
CancelInvoke();//停止移动
isDie=true;
Instantiate(dieEffect);//死亡特效
//记录得分
PlayerPrefs.SetInt("lastl",MainUIController.Instance.length);//记录得分 (键,值)
PlayerPrefs.SetInt("lasts",MainUIController.Instance.score);
//最佳得分
if(PlayerPrefs.GetInt("bests",0)<MainUIController.Instance.score)//获取(键,不存在的话默认值)
{
PlayerPrefs.SetInt("bestl",MainUIController.Instance.length);
PlayerPrefs.SetInt("bests",MainUIController.Instance.score);
}
StartCoroutine(GameOver(1.5f));//调用协程
}
//协程 等待一定时间再继续
IEnumerator GameOver(float t)
{
yield return new WaitForSeconds(t);
UnityEngine.SceneManagement.SceneManager.LoadScene(1);//加载游戏场景
}
//碰撞身体后调用die
private void OnTriggerEnter2D(Collider2D collision)
{
//...
else if(collision.tag=="Body")//身体
{
Die();
}
else//边界
{
Die();//边界模式 碰撞边界死亡
}
}
用户存储
开始场景
创建空物体ScriptsHolder
挂载脚本StartUIController
设置public参数
》StartUIController
选项卡数据
绑定Toggle的Value Changed事件
用动态方法,bool参数
开始游戏按钮,跳转游戏场景
绑定开始Button的click事件
//using UnityEngine.UI;
public Text lastText;
public Text bestText;
public Toggle blue;
public Toggle yellow;
public Toggle border;
public Toggle noBorder;
//获取记录得分
void Awake()
{
lastText.text="上次:长度 "+PlayerPrefs.GetInt("lastl",0)+",分数 "+PlayerPrefs.GetInt("lasts",0);
bestText.text="最佳:长度 "+PlayerPrefs.GetInt("bestl",0)+",分数 "+PlayerPrefs.GetInt("bests",0);
}
//获取设置
void Start()
{
//皮肤
if(PlayerPrefs.GetString("sh","sh01") == "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");
}
//模式
if(PlayerPrefs.GetInt("border",1) == 1)//读取数据 默认值为1
{
border.isOn=true;
PlayerPrefs.SetInt("border",1);
}
else
{
noBorder.isOn=true;
PlayerPrefs.SetInt("border",0);
}
}
//皮肤
public void BlueSelected(bool isOn)
{
if(isOn)
{
PlayerPrefs.SetString("sh","sh01");//蛇头图片文件的名称
PlayerPrefs.SetString("sb01","sb0101");//蛇身图片文件的名称
PlayerPrefs.SetString("sb02","sb0102");
}
}
public void YellowSelected(bool isOn)
{
if(isOn)
{
PlayerPrefs.SetString("sh","sh02");
PlayerPrefs.SetString("sb01","sb0201");
PlayerPrefs.SetString("sb02","sb0202");
}
}
//模式
public void BorderSelected(bool isOn)
{
if(isOn)
{
PlayerPrefs.SetInt("border",1);//没有setBool方法,只能setInt
}
}
public void NoBorderSelected(bool isOn)
{
if(isOn)
{
PlayerPrefs.SetInt("border",0);
}
}
//开始游戏 跳转游戏场景
public void StartGame()
{
UnityEngine.SceneManagement.SceneManager.LoadScene(1);
}
配置读取
获取文件
创建Resources文件夹
添加游戏中需要动态加载的文件
通过文件名加载文件Resources.Load()
》SnakeHead
皮肤
//通过Resources.Load(string path)方法加载资源,path的书写不需要Resources/ 以及文件扩展名
//如果有重名文件 写泛型Resources.Load<Image>(string path)
//写了泛型 可以自动转换类型
void Awake()
{
//...
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"));
}
边界
边界的显示
》MainUIController
public bool hasBorder=true;
void Start()
{
if(PlayerPrefs.GetInt("border",1)==0)
{
hasBorder=false;
foreach(Transform t in bgImage.gameObject.transform)//遍历 父物体下的所有子物体
{
t.gameObject.GetComponent<Image>().enabled=false;//禁用Image组件
}
}
}
》SnakeHead
边界模式:死亡
自由模式:传送
//...
else
{
if(MainUIController.Instance.hasBorder)
{
Die();
}
else
{
//传送
//switch...
}
}
声音
游戏场景
main camera挂载audio source
赋值背景音乐bgm
勾选loop
》SnakeHead
赋值音效
吃食物音效
死亡音效
public AudioClip eatClip;
public AudioClip dieClip;
void Grow()
{
AudioSource.PlayClipAtPoint(eatClip,Vector3.zero);
//...
}
void Die()
{
AudioSource.PlayClipAtPoint(dieClip,Vector3.zero);
//...
}
发布游戏
file——build setting
playersetting设置图标icon
build
注册表
win+R
regedit
5235

被折叠的 条评论
为什么被折叠?



