之前的太多了,切换到这章记录 跳转上章
销毁玩家特效
- 新建一个空的游戏物体
- 添加粒子特效paricle System
- 打开Shape面板 修改扩散范围为圆型Circle ,半径Radius改成0.3
- 打开Emission(发射)面板 Rate over Time改成0, 将Bursts(爆发)加上,count改成两个常量之间随机40~60 间隔0.01 ,将loop勾掉,只有一次,(start speed)发射时间改成1.5,(start lifetime)存活时间2s,(start size)为0.1
- Renderer指定(Material)材质
- 修改层级-显示 Renderer指定sorting layer 为 GUI
- 修改一个自己喜欢的颜色
存成预制体
实例化死亡的特效
- 打开对象池,创建销毁对象的list
- 打开ManagerVars脚本
创建对象
public GameObject deathEffect;
返回对象池脚本 ,添加
for (int i = 0; i < initSpawnCount; i++)
{
InstantiateObject(vars.deathEffect, ref deathEffectList);
}
面板中指定死亡的预制体 修改粒子显示的位置
暂停
分数增加
检测到下一个平台,成绩+1,广播一个+成绩的事件码F12
直接广播,直接不适用
//下一个平台 private GameObject lastHitGo = null;
gameManager添加监听
private int gameScore;
显示游戏成绩
GamePanel
脚本中,awake
监听定义修改成绩事件码UpdateScoreText
添加更新成绩的方法
记得销毁,接着调用在每次成绩增加的时候,广播
- 刚运行的时候成绩增加2个 (最开始在平台上,调用方法进行判断,符合平台不相等条件,广播了事件)
增加一个判断,第一次不加成绩return出去,指定物体赋值,不然就一直return出去,没有成绩加(hair~)
lastHitGo = hit.collider.gameObject;
平台掉落
平台生成一段时间掉落,受成绩影响
每次里程碑到达,更新掉落时间
platformSpawner
脚本中,添加更新掉落时间的方法
private void Update()
{
if (GameManager.Instance.IsGameStarted &&
GameManager.Instance.IsGameOver == false)
//开始,没有结束
{
UpdateFallTime();
}
}
UpdateFallTime方法中定义
/// <summary>
/// 里程碑
/// 掉落时间*系数
/// </summary>
public int milestoneCount=10;
public float fallTime;
public float minFallTime;//最少掉落时间
public float multple;//系数
方法
/// <summary>
/// 更新平台掉落时间
/// </summary>
private void UpdateFallTime()
{
if (GameManager.Instance.GetGameScore()>milestoneCount)
//大于里程碑,更新掉落时间
{
milestoneCount *= 2;
fallTime *= multple;
if (fallTime<minFallTime)
{
fallTime = minFallTime;
}
}
}
返回platformSpawner
脚本
解决错误,添加fallTime
返回PlatformSprite脚本,定义 private Rigidbody2D my_body;
在awake中拿到组件。
面板中设置初始掉落时间2,最小 0.5,系数0.9
游戏还没有运行,页面中的平台就已经掉落了
解决平台掉落时机错误的Bug
- 增加判断
脚本
PlayController
中,定义判断是否移动的条件private bool isMove=false;//玩家是否移动
检测到触摸屏幕时,如果玩家没有移动,就设置成true,广播玩家移动的事件码PlayerIsMove。 - 在gameManager中监听
public bool PlayerIsMove { get; set; }
EventCenter.AddListener(EventDefine.PlayerIsMove,PlayerMove);
PlayerMove
方法中PlayerIsMove=true
销毁EventCenter.RemoveListener(EventDefine.PlayerIsMove, PlayerMove);
- PlatformSprite 在update方法中,做判断
if (GameManager.Instance.IsGameStarted == false|| GameManager.Instance.PlayerIsMove==false) return;
第三种游戏结束的判断
平台距离摄影机太远
钻石的随机生成
首先,拿到钻石的精灵渲染器,为它添加层级,标签,碰撞器,还有刚体组件并且将Z轴限定,不让物体旋转,将其设置成预制体
对象池ObjectPool
中,定义private List<GameObject> diamondList = new List<GameObject>();//钻石
,
转到脚本ManagerVars
中添加容器:吃钻石的字段public GameObject diamondPre;
放入对象池中进行初始化,并添加获取钻石的方法
for (int i = 0; i < initSpawnCount; i++)
{
InstantiateObject(vars.diamondPre, ref diamondList);
}
脚本PlatformSpawner
,生成平台的方法中增加钻石
//随机生成钻石
int ranSpawnDiamond = Random.Range(0,10);
if (ranSpawnDiamond==6 && GameManager.Instance.PlayerIsMove)
{//人物当前是移动的,避免第一步就吃到钻石--实例化钻石
GameObject go = ObjectPool.Instance.GetDiamond();
go.transform.position = new Vector3(
platformSpawnPosition.x,
platformSpawnPosition.y+0.5f,0);
go.SetActive(true);//显示
}
测试: 面板中将预制体给定
吃钻石
PlayController脚本中添加监听
/// <summary>
/// 都没有勾选isTriggen
/// 次碰撞器刚体接触到另个刚体
/// </summary>
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag=="Picker")
{//吃到钻石-隐藏
collision.gameObject.SetActive(false);
//广播事件码
EventCenter.Broadcast(EventDefine.AddDiamond);
}
}
GameManager脚本中添加钻石
private int gameDiamond;//钻石
awake中添加监听EventCenter.AddListener(EventDefine.AddDiamond, AddGameDiamond);
,销毁
/// <summary>
/// 增加钻石
/// </summary>
private void AddGameDiamond()
{
gameDiamond++;
EventCenter.Broadcast(EventDefine.updateDiamondText,gameDiamond);//钻石数量广播出去
}
在UI面板中显示,将gamePanel
脚本中监听钻石的修改方法
测试:
创建结束的界面
创建游戏结束的脚本
PlayController
脚本中
测试:
bug直接就显示了效果不好,我下面的1.5改成延迟1s,以下做出修改是因为。。
- 注意 第三种死亡(摄影机跟随不到物体的死亡)跑的慢
携程延迟时间必须小于平台掉落时间,否则无法打开结束,陷入循环状态 可!
- 除了使用监听的方式显示,之前都是使用实例化的方式
删除了此脚本的面板显示是因为在gameover脚本中已经有写过
将粒子的层级改成和游戏一层级,解决其显示在结束面板的位置
再来一局
重新加载场景
引入using UnityEngine.SceneManagement;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);//加载页面
重新创建脚本
商店面板
空物体当做内容,铺满父物体,是可以滑动的区域 先把父物体隐藏
子物体与父物体一样宽,这样拖拽的时候才会显示完全,锚点在左边,Pivot x为0,以左边为基准滑动
Tip:出错(怪哉-物体的时候,皮肤没有),视频中此处修改了一下将image组件清除了,不要清除错了(你需要清除空物体身上的这个image)
皮肤的规整生成
生成ShopPanel脚本,在ManagerVars脚本中拿到所有的皮肤,还有预制体
public List<Sprite> skinSpriteList = new List<Sprite>();public GameObject skinChooseItemPre;
测试:面板上拖上预制体,皮肤
宽度是默认设置好的,如果有多余的皮肤出现~~
parent.GetComponent<RectTransform>().sizeDelta = new Vector2((vars.skinSpriteList.Count+2) * 160,302);//设置宽度 默认为0
判断当前选中的皮肤 变大
皮肤的滑动尺寸
使用DoTween插件需要引入命名空间
using DG.Tweening;
容器ManagerVars脚本中
public List<string> skinNameList = new List<string>();皮肤的名字
面板定义4个名字
盎(ang)格鲁(lu)人
忍者
霍比特人
小僵尸
ShopPanel脚本中添加
字段private Text txt_Name;
方法
/// <summary>
/// 当前皮肤名字
/// </summary>
private void RefreshUI(int selectIndex)
{
txt_Name.text = vars.skinNameList[selectIndex];
}
返回按钮
- 定义
private Button btn_Back;
- 初始化awake,
btn_Back = transform.Find("btn_Back").GetComponent<Button>();
btn_Back.onClick.AddListener(OnBackOnClick);
显示MainPanel面板 so,先到MainPanel中修改,添加监听方法 移除监听 移除监听
数据类
gameData脚本中,设置get一下
数据的储存与读取、序列化与反序列化
在GameManager中编写
awake方法中实例化data = new GameData();
初始化游戏数据
定义private ManagerVars vars;
初始化vars = ManagerVars.GetManagerVars();
/// <summary>
/// 初始化游戏数据
/// </summary>
private void InitGameData()
{
//读取数据
Read();
//如果数据不为空
if (data!=null)
{
//本地是否第一次读取
isFirstGame = data.GetIsFitstGame();
}
else
{
isFirstGame = true;
}
if (isFirstGame)
{
isMusicOn = true;
bestScoreArr = new int[3];
selectSkin = 0;//第一个皮肤
skinUnlocked = new bool[vars.skinSpriteList.Count];//数组长度
skinUnlocked[0] = true;//未解锁的第一个
diamondcount = 10;//钻石
Save();
}
else
{
isMusicOn = data.GetIsMusicOn();
bestScoreArr = data.GetBestScoreArr();
selectSkin = data.GetSelectSkin();
skinUnlocked = data.GetSkinUnlocked();
diamondcount = data.GetDiamondcount();
}
}
商店
皮肤未解锁设置成灰色 商城判断皮肤有没有解锁 商城页面显示,若是锁定,显示购买的按钮
之前创建的时候没有加上Button控件,请注意!!!
皮肤的购买
拿到钻石数
添加Save()方法
在ShopPanel面板中,脚本初始化钻石数
txt_Diamond = transform.Find("Diamond/Text").GetComponent<Text>();
刷新的方法中获得所有钻石并赋值
txt_Diamond.text = GameManager.Instance.getAllDiamond().ToString();
可以再面板中定义皮肤的价格
设置当前皮肤的方法中,添加一个保存的方法
面板中添加购买的方法
判断第一次游戏的时候加上 测试:断开运行重新开始
玩家皮肤的更换
有一点,此时的玩家面朝我们,这样是不正确的。
skinSpriteList是指所有的皮肤,重新定义一个
//玩家商店购买的皮肤
public List<Sprite> characterSkinSpriteList = new List<Sprite>();
调用spriteRenderer.sprite = vars.characterSkinSpriteList[skinIndex];
更换皮肤,面板中皮肤集合中选结尾为b的图片
断开重新游戏,需要重新选择皮肤,,,修改一下
不能再awake方法中调用,因为GameManage的初始游戏是在awake方法中,于是在创建一个获得皮肤的方法接收
商城中打开显示当前使用的皮肤
MainPanel脚本中 shop面板中显示当前选择的皮肤 应该是-160 测试:初次运行就显示隐者的皮肤 直接定位到当前选择的皮肤
遇到一个问题,选择皮肤没有确定,点击返回按钮再次进入显示的不是游戏的皮肤,是我刚刚滑动没有选择的皮肤 这样就解决了,其中Start方法就可以不使用了。
- 再来一局的bug 连点两次速度较快时,人物跳跃两次
提示信息
- 提示钻石不足,不能购买 从无到有,逐渐消失 灰色背景使用图片,子级文字铺满全屏,设置颜色大小,取消Raycast Target不在UI面板上进行点击操作 父级图片挂载脚本(DoTween)实现动画效果 使用广播监听是否能够购买 提示信息脚本
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class hint : MonoBehaviour
{
private Image img_Bg;
private Text txt_Hit;
private void Awake()
{
img_Bg = GetComponent<Image>();
txt_Hit = GetComponentInChildren<Text>();
//隐藏
img_Bg.color = new Color(img_Bg.color.r,img_Bg.color.g, img_Bg.color.b, 0);
txt_Hit.color = new Color(txt_Hit.color.r, txt_Hit.color.g, txt_Hit .color.b, 0);
//监听广播 -提示信息
EventCenter.AddListener<string>(EventDefine.Hint,Show);
}
private void OnDestroy()
{
//监听广播 -提示信息
EventCenter.RemoveListener<string>(EventDefine.Hint, Show);
}
private void Show(string text)
{
//拒绝多次点击-等待上一个点击结束
StopCoroutine("Dealy");//必须使用引号“”的方式,才能将携程关闭 开启的时候也得使用引号
transform.localPosition = new Vector3(0, -70,0);
transform.DOLocalMoveY(0,0.3f).OnComplete(()=>
{//结束后调用方法-开启携程
StartCoroutine("Dealy");
});
img_Bg.DOColor(new Color(img_Bg.color.r,img_Bg.color.g,img_Bg.color.b,0.4f),0.1f);
txt_Hit.DOColor(new Color(txt_Hit.color.r, txt_Hit.color.g, txt_Hit.color.b, 1), 0.1f);
}
private IEnumerator Dealy()
{
yield return new WaitForSeconds(1f);
transform.DOLocalMoveY(70,0.3f);
//隐藏
img_Bg.DOColor(new Color(img_Bg.color.r, img_Bg.color.g, img_Bg.color.b, 0), 0.1f);
txt_Hit.DOColor(new Color(txt_Hit.color.r, txt_Hit.color.g, txt_Hit.color.b,0), 0.1f);
}
}
IEnumerable Dealy()携程的使用
遇到这个问题,做一个记录
- 当时用结束方法使用之前的方法是没有效果的
StartCoroutine(Dealy());
开启携程StopCoroutine(Dealy());
是无法关闭的
使用字符串的方式关闭
StartCoroutine("Dealy"); StopCoroutine("Dealy");//必须使用引号“”的方式,才能将携程关闭 开启的时候也得使用引号
重置游戏数据界面
主页添加重置按钮事件
鼠标按下侧角Alt+Shift等比放大
重置游戏数据
制作重置面板
- gameManager脚本中添加重置方法
- ResetPanel脚本
using UnityEngine.SceneManagement;//引入命名
/// <summary>
/// 监听YES按钮点击
/// </summary>
private void OnYesButtonClick()
{
GameManager.Instance.ResetData();
//重新加载当前场景
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);//当前场景对应下标
}
/// <summary>
/// 监听NO按钮点击
/// </summary>
private void OnNoButtonClick()
{
img_Bg.DOColor(new Color(img_Bg.color.r, img_Bg.color.g, img_Bg.color.b, 0), 0.3f);
//委托 - 动画播放完,界面隐藏
dialog.transform.DOScale(Vector3.zero, 0.3f).OnComplete(()=>
{
//广播事件码--音效
EventCenter.Broadcast(EventDefine.PlayClickAudio);
gameObject.SetActive(false);
});
}
成绩的保存、最高分获取
/// <summary>
/// 保存成绩
/// </summary>
/// <param name="score"></param>
public void SaveScore(int score)
{
//将数组转换成List
List<int> list = bestScoreArr.ToList();
/**
* 从小到大排序
* 提示需要传入的comparison就是(x, y) => (x.CompareTo(y))
* x,y做比较
*
* 需要从大到小所以加一个-号
*/
list.Sort((x, y) => (-x.CompareTo(y)));
bestScoreArr = list.ToArray();//50 20 10
int index = -1;
for (int i = 0; i < bestScoreArr.Length; i++)
{
if (score>bestScoreArr[i])
{
index = i;
}
}
//没有符合的条件
if (index == -1) return;
//从重新-冒泡排序
for (int i = bestScoreArr.Length-1; i>index ; i++)
{
//前一位给后一位赋值
bestScoreArr[i] = bestScoreArr[i] - 1;
}
bestScoreArr[index] = score;
Save();
}
/// <summary>
/// 获得最高分
/// </summary>
/// <param name="score"></param>
public int GetBestScore()
{
//Linq提供的方法
return bestScoreArr.Max();
}
修改一下页面
成绩的排行界面
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RankPanel : MonoBehaviour
{
private Button bg_btn;
public Text[] txt_Scores;
private GameObject go_RankList;
private void Awake()
{
EventCenter.AddListener(EventDefine.ShowRankPanel,Show);
bg_btn = transform.Find("bg_btn").GetComponent<Button>();
bg_btn.onClick.AddListener(OnImageButtonClose);
go_RankList = transform.Find("RankList").gameObject;
//隐藏
bg_btn.GetComponent<Image>().color = new Color(
bg_btn.GetComponent<Image>().color.r,
bg_btn.GetComponent<Image>().color.g,
bg_btn.GetComponent<Image>().color.b,0);
go_RankList.transform.localScale = Vector3.zero;
gameObject.SetActive(false);
}
public void Show()
{
gameObject.SetActive(true);
//显示
bg_btn.GetComponent<Image>().DOColor(new Color(
bg_btn.GetComponent<Image>().color.r,
bg_btn.GetComponent<Image>().color.g,
bg_btn.GetComponent<Image>().color.b,0.3f),0.3f);
//旋转
go_RankList.transform.DOScale(Vector3.one,0.3f);
}
private void OnDestroy()
{
EventCenter.RemoveListener(EventDefine.ShowRankPanel, Show);
}
private void OnImageButtonClose()
{
//隐藏
bg_btn.GetComponent<Image>().DOColor(new Color(
bg_btn.GetComponent<Image>().color.r,
bg_btn.GetComponent<Image>().color.g,
bg_btn.GetComponent<Image>().color.b,0), 0.3f);
go_RankList.transform.DOScale(Vector3.zero, 0.3f).OnComplete(()=> {
gameObject.SetActive(false);
});
}
}
- 监听
EventCenter.AddListener(EventDefine.ShowRankPanel,Show);
添加方法
public void Show()
{
gameObject.SetActive(true);
//显示
bg_btn.GetComponent<Image>().DOColor(new Color(
bg_btn.GetComponent<Image>().color.r,
bg_btn.GetComponent<Image>().color.g,
bg_btn.GetComponent<Image>().color.b,0.3f),0.3f);
//旋转
go_ScoreList.transform.DOScale(Vector3.one,0.3f);
}
移除监听
private void OnDestroy()
{
EventCenter.RemoveListener(EventDefine.ShowRankPanel, Show);
}
在主面板中广播
- 更新成绩
- 成绩的显示 测试 游戏重置,排行榜清空
- 游戏结束面板中显示一下排行榜
音效
玩家预制体身上添加组件Audio Source,将Play On Awake取消
打开脚本PlayController
,定义private AudioSource m_AudioSource;
初始化m_AudioSource = GetComponent<AudioSource>();
,跳落的时候播放音效
说成碰撞障碍物,更好点
面板中将声音指定
- 创建音效类 在GameManager中添加Audio Sound 取消初始就播放的选项,挂载
音效类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 公用音效类 点击按钮播放音效
/// </summary>
public class AudioSound : MonoBehaviour
{
private AudioSource m_AudioSource;
private ManagerVars vars;
private void Awake()
{
m_AudioSource = GetComponent<AudioSource>();
vars = ManagerVars.GetManagerVars();
EventCenter.AddListener(EventDefine.PlayClickAudio, PlayAudio);
}
private void OnDestroy()
{
EventCenter.RemoveListener(EventDefine.PlayClickAudio,PlayAudio);
}
private void PlayAudio()
{
m_AudioSource.PlayOneShot(vars.buttonClip);
}
}
将点击事件设置一下广播, EventCenter.Broadcast(EventDefine.PlayClickAudio);//广播事件码--音效
音效的关闭或开启
设置音效是否开启
//是否开启音效
public Sprite isMusicOn, isMusicOff;
- 游戏修改
监听到按钮点击将音效重新设置
- 游戏开启 控制器初始化监听
打包到手机上测试
图标拖上去,自适应其他大小 需要更改一下Gradle。。没有秘钥签名
bug
游戏时,玩家会后退
添加返回
没错,没有返回 =-=
自己动手编写一个,Application.Quit();
退出游戏
检测手机返回按钮
结尾
跟随SKIN学院老师_Carson学习好玩的游戏创作(www.sikiedu.com/course/375) xiexie