Unity2D幻径游戏-续集

1,831 阅读9分钟

之前的太多了,切换到这章记录 跳转上章

销毁玩家特效

  • 新建一个空的游戏物体
  • 添加粒子特效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