愤怒的小鸟 AngryBird Unity(打弹弓的那种)

【SiKi学院Unity】Unity初级案例 - 愤怒的小鸟
AngryBird_20210602_2018.4.32f1

59.8M
提取码:4k36

00 Spring Joint 2D

EnableCollision 支点与悬挂物,而不是悬挂物与悬挂物

在这里插入图片描述

AutoConfigureDistance

在这里插入图片描述

Distance

在这里插入图片描述

DampingRatio

重力设为0,方便比较
在这里插入图片描述

Frequency

在这里插入图片描述

03 资源导入 场景简单搭建

切图

在这里插入图片描述
暂时没体会到“方法”的作用
在这里插入图片描述
在这里插入图片描述
带很多子图的
在这里插入图片描述

(了解)图标大小

(了解)图层顺序

视频多了为小鸟新建Player层
在这里插入图片描述

04 SpringJoint组件介绍

弹弓右,静态2D刚体,挂到小鸟弹簧组件上
小鸟,SpringJoint2D(弹簧),2D圆形碰撞器

以“遛狗”为例,“已连接锚点”是“你”,“锚点”是“狗”

在这里插入图片描述

(了解) 坐标系

屏幕坐标是左下原点
Unity世界坐标默认是中心原点

(问题)一点小鸟跑到摄像机的位置

一点,z值变得和主相机一样,两种方法:
01 限死z为0
02 减去相机的z

以上操作一点,小鸟(0,0,0)

Unity踩坑记录 从屏幕坐标系转换到世界坐标系

    Vector3 FollowMouse(Vector3 targetPos)//跟随鼠标移动
    {
        Vector3 screenPos = Camera.main.WorldToScreenPoint(targetPos);//转屏幕
        screenPos = Input.mousePosition - Camera.main.transform.position;//屏幕中操作
        targetPos = Camera.main.ScreenToWorldPoint(screenPos);//转世界

        return targetPos;
    }

在这里插入图片描述
在这里插入图片描述

限制距离

    Vector3 LimitDistance(Vector3 movePoint, Vector3 basePoint, float maxDistance)//让movePoint始终在距离内
    {
        if (Vector3.Distance(movePoint, basePoint) > maxDistance)
        {
        
            pos = (movePoint - basePoint).normalized;//归一
            movePoint = basePoint + maxDistance * pos;
        }

        return movePoint;
    }
        }

(问题)弹射出去、弹力大小

问题是弹射后往下掉:auto configure distance不要打钩
在这里插入图片描述

08 猪的受伤

(问题)切片失败

sprite2D,多个,crunch都试过了,没有用,只切了一块需要用的
ctrl+D几个“蓝绿”框,然后手动框图
在这里插入图片描述

Enemy

速度太大就死掉,不然受伤

    private void OnCollisionEnter2D(Collision2D self)
    {
        float speed = self.relativeVelocity.magnitude;
        if (speed > maxSpeed)
        {
            Destroy(gameObject);
        }
        else if (speed < maxSpeed && speed > minSpeed)
        {
            GetComponent<SpriteRenderer>().sprite = hurt;
        }
    }

09 弹弓的线LineRedender

在玩家isClick中调用

    public Transform leftPos;
    public Transform rightPos;

    public LineRenderer leftLine;
    public LineRenderer rightLine;


    public static Slingshot _instance;

	void Awake()
    {
        _instance = this;
    }
        
 Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    public void Line()
    {
        leftLine.SetPosition(0, leftPos.position);
        leftLine.SetPosition(1, Player._instance.transform.position);

        rightLine.SetPosition(0, rightPos.position);
        rightLine.SetPosition(1, Player._instance.transform.position);
    }

10 敌人死亡动画,得分

查看所需的图片

这13个
171
160
136
128
116
107
96
88
36
自动切坏,自己切的3个
16 31 34
这个Ctrl点得很不人性化,一不小心点空白处,或者没按着ctrl,全没了
动画名== 自己写的那个, 状态机名 = = 物体名 = =第一张图的名字
动画取消循环
在这里插入图片描述

(问题)切坏了

删掉多余的,自己切
缺少框就ctrl+D,复制几个(前面说的是复看时加的)
在这里插入图片描述

读取精灵图

暂时用这种方法(用过阿里云的,觉得DataHub这个词挺好的)
实际还是读表、json最好。每种敌人对应相应的得分,受伤,死亡特效
在这里插入图片描述

(问题)死亡在敌人做,敌人被销毁位置没了

得分Sprite,死亡特效体的位置

setParent错误,敌人会被销毁,连带他们也很快被销毁
transform.position,可以,敌人被销毁,transform.position已经使用了,不会影响(用玩家或敌人的transfrom都可以,两者相撞在同一个位置触发敌人的死亡函数)

11 游戏逻辑的判定,实现多只小鸟的飞出

(了解)公有但隐藏

建议做成片段,不然每次补方括号就很烦

  [HideInInspector]

分解

左边的箭头是鼠标按下、抬起的动作
(后面有重构了)
在这里插入图片描述

(问题)让鸟柔和上来

设置发射位置
到发射位置
Slingshot弹弓的意思

transform.position = Slingshot._instance.startPos.position;//一开始设置,柔和些

重构

从分数那里入手

敌人碰撞检测,检测速度
敌人速度大于死亡速度,死亡
销毁自身,在列表移除自己,;提供分数(玩家)

玩家接收提供的分数,+=,通知UI更新显示
玩家销毁,在列表中移除自己,GameController中进行StepAfterShoot()

UI更新显示分数

Bird

press = = drag
release = = up
为了追求和系统函数命名一样,把开始的press、release,改成drag,up

    private void OnMouseDrag(){}
    private void OnMouseUp(){}

在这里插入图片描述
放代码太多,看流程就行
在这里插入图片描述

Enemy

放代码太多,也是看流程就行
在这里插入图片描述

(了解)碰撞,触发的参数命名

最好用other,self,用collision,collider容易混

12 添加小鸟飞出的拖尾效果

1

Unity 武器拖尾效果

高度,高
desireTime,宽
在这里插入图片描述
在这里插入图片描述

(了解) 折叠代码 region

(了解) 代码“连坐”添加组件(添加组件A自动加上组件B,移除也无效)

[RequireComponent(typeof(MeshFilter))]

在这里插入图片描述

(TODO:) 菜单添加组件

[AddComponentMenu("PocketRPG/Weapon Trail")]

在这里插入图片描述

(问题) 鸟变重

突然会,暂时通过改变刚体的重力

到后面发现是天空背景添加碰撞盒的原因

(问题) 线的位置

运行前有问题,运行后不会。
不理解,虽然可以设置enabled

下面的透视相机,改成正交相机就解决了。毕竟省了几段enabled的代码
在这里插入图片描述
相机正交后,游戏面板就正常了
在这里插入图片描述

(现象) 相机透视

相机透视,正交、z=0,就正常
在这里插入图片描述

(问题) 碰撞盒作用不正常

两个2D碰撞盒都“负距离”了·
在这里插入图片描述

(问题)搭好的架子一运行就倒下

因为都是Ctrl+D复制的
天空背景添加了碰撞盒,相当于一直受到碰撞
架子的死亡速度为0,一倒下就销毁

15 添加失败,胜利的游戏UI界面

(了解)

确定16:10
拖出地面(2D/Sprite,不缩放),拉近相机到地面占高度1/5(天空占4/5)。这是UGUI之外的屏幕大小
UGUI挂MainCamera,调整缩放系数,到分数、消息提示框之类的合适

(问题) 切图不准

在这里插入图片描述

Bg渐暗

Ctrl给FailUI的Bg做渐暗(透明)动画,取消循环时间(播放一次)
在GameController的失败路径,激活FailUI

(了解) 看图

可以拉上拉下,之前忘了,都是打开“画图”软件
在这里插入图片描述

星星

16 修改火花粒子系统

在局部下缩放,初始大小
在这里插入图片描述

17 渲染层级关系

相机(深度)
layer
order

相机深度

AB 图(画布)分别有渲染摄像机:
B的相机的深度=-2,-1>-2,显示A
B的相机的深度=0,0>-1,显示B
B的相机的深度=-1,谁在层级的后面显示谁
在这里插入图片描述

layer图层

在这里插入图片描述

(了解 )相机渲染层与排序图层区别

相机渲染层,相机对谁“睁一只眼闭一只眼”,最高9层
在这里插入图片描述
在这里插入图片描述

order层内排序

谁大谁上
在这里插入图片描述

18 把粒子系统显示在UI之前

z轴与排序图层

节点上,视频是挂在一个root的父节点,我直接取sub子节点,并进行相应的缩放,暂时没什么差别
顺序上,直接修改z轴。视频是拍顺图层,Player>UI(显示优先权);爆炸归于Player层,就是以下2D精灵图的排序图层

(了解) 2D精灵/图像/原始图像

2D精灵图不属于UGUI,3D没有Sprite
//
图像属于UGUI,常用,经常snap(恢复原生大小),实际图片是sprite,有.sprite
原始图像属于UGUI,不常用(个人经验)
在这里插入图片描述

(了解) UGUI的排序

2D精灵图独立排序
//
UGUI由Canvas管理排序,可以添加子画布覆盖排序
在这里插入图片描述

19 让星星一颗一颗的显示

(代码)剩多少敌人扣多少星星

冗余了,后面改了

   public void  StepAfterShoot()
    {
        if (enemyList.Count <= 0)
        {
            UIController._instance.ShowMessage("游戏通关");
            UIController._instance.ShowStar(3);
            
        }
        else 
        {
            if (birdList.Count <= 0)
            {
                UIController._instance.ShowMessage("游戏失败");
                UIController._instance.failUI.SetActive(true);

                if(enemyList.Count ==2 )
                    UIController._instance.ShowStar(3-2);
                else if (enemyList.Count == 1)
                    UIController._instance.ShowStar(3-1);

            }
            else 
            {
                NextBird();//下一只鸟
            }
        }
    }

(了解) List只声明的赋值

长度未确定,不能用[i]方式,报超出范围错误

    public List<Enemy> enemyList;

    void Start()
    {
        childCount = transform.childCount;

        for (int i = 0; i < transform.childCount; i++)
        {
            enemyList.Add( transform.GetChild(i).GetComponent<Enemy>() );
        }
    }

20 添加暂停动画

动画

为PauseUI,做两个动画,要素是面板的一进一出,Bg的透明度
//
动画是Panel从左边移动到靠边
动画需在“录制”下才生效
//
NGUI有原生的Tween组件,自带了Reverse功能。UGUI有没有暂时不知道,所以只能做两个Clip
在这里插入图片描述

动画器

默认空状态
在这里插入图片描述

GameController

取反

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Pause();
        }
    }
    public void Pause()
    {
        isPause = !isPause;

        UIController._instance.Pause(isPause);
    }

Bird上

限制按键

private void OnMouseDrag()
    {
        if (!GameController._instance.isPause)
        {
            isDrag = true;
        }
    }
    private void OnMouseUp()
    {
        if (!GameController._instance.isPause)
        {
            isDrag = false;
            isUp = true;
        }
    }

UI上

    public void Pause(bool isPause)
    {
        pauseUI.SetActive(isPause);
        if (isPause)
            pauseUI.GetComponent<Animator>().SetBool("isPause",isPause);
    }

22 添加鼠标注册事件

(需求) unity 点击物体能自动展开选项卡

想法都是暂时没遇到方案

双击按钮,折叠的还不展开
在这里插入图片描述

(需求) 点击方法名一改,Unity中Button的点击方法就丢失

能不能像层级这种,挂上节点后,节点名不管怎么改,挂上的节点也不会丢失

(问题) 层级前后

一开始PauseUI是激活的,带透明的Bg
PauseButton放上面被这透明的Bg挡住,导致按钮不可点击,
01 PauseUI一开始不激活。一激活挡住PauseButton(这时候就体现最上面那个播放按钮的作用了),符合需求(不激活可能也会降低性能消耗?)【先用这个】
02 按钮移到PauseUI的下面。“黑暗之光”教面板的Tween进来又出去,都是激活的。
在这里插入图片描述

(问题) 返回界面,新游戏的方法写在UIController,还是GameController(先它)

(代码) Home、NewHome按钮

GameController

    public void Pause()
    {
        isPause = !isPause;
        UIController._instance.Pause();
    }
    public void Home()
    {
       SceneManager.LoadScene(0);
    }
    public void NewGame()
    {
        SceneManager.LoadScene(1);
    }

//
UIController

    public void Pause()//鼠标事件不能传参?(没学过)
    {
        bool isPause = GameController._instance.isPause;
        pauseUI.SetActive(isPause);
        pauseUI.GetComponent<Animator>().SetBool("isPause",isPause);
    }

在这里插入图片描述

23 添加镜头跟随

挂在相机上,问题是Player销毁会导致target报空

视频是挂小鸟上;
想挂相机上
在这里插入图片描述

    public Transform target;
    // clamp
    public float startX = 0f;
    public float endX = 15f;
    public float smooth = 3f;
    //

    // Update is called once per frame
    void Update()
    {
        if (target != null)
        {
            CameraFollowTargetFunc();
        } 
    }

    public void CameraFollowTargetFunc()
    {
        float posX = Mathf.Clamp(target.position.x, startX, endX);
        float posY = transform.position.y;
        float posZ = transform.position.z;

        transform.position = Vector3.Lerp(transform.position, new Vector3(posX, posY, posZ), smooth);
    }

做成片段

一个是方便,一个是提炼(练习命名,重新认识流程)
//
钳制

<Code Language="csharp"><![CDATA[Mathf.Clamp(current, start, end);]]>

线性插值

<Code Language="csharp"><![CDATA[Vector3.Lerp(start, end, smooth)]]></Code>

24 音乐的添加

(了解) 往上拉有声音

在这里插入图片描述

起飞

鸟的起飞是update的,需要加标志位让其只执行一次

        if (hasPlayed == false)
        {
            AudioSource.PlayClipAtPoint(DataHub._instance.birdFlyClip, transform.position);
            hasPlayed = true;
        }

(问题) 没声音

Wood,Pig都继承于Enemy,除了音效不同,其他差不多
失败:然后Wood,Pig的音效方法都用new(不了解),没用的,调用的是Enemy的(输出打印过),所以没声音
成功:重写虚函数可以(下一条是小结重载、重写、重定义)
//
父类Enemy

    //音效,木头,猪音效不一样
    virtual public void PlayCollisionAudioClip() { }
    virtual public void PlayDestroyedAudioClip() { }

    public void PlayAudioClip(AudioClip clip)//仅仅为了少写点
    {
        AudioSource.PlayClipAtPoint(clip, transform.position);
    }

//
子类Pig:Enemy(Wood的类似)

    override public void PlayCollisionAudioClip()
    {
        PlayAudioClip(DataHub._instance.pigCollisionClip);
    }
    override public  void PlayDestroyedAudioClip()
    {
        PlayAudioClip(DataHub._instance.pigDestroyedClip);
    }

(了解) 重载、重写、重定义

多态和虚函数重写
//01
重载(以下是Destroy的重载)
在这里插入图片描述

        public static void Destroy(Object obj);
        public static void Destroy(Object obj, [DefaultValue("0.0F")] float t);

//02
上一条【(问题) 没声音】就是重写

//03 重定义
C#的new关键字的几种用法
C++ 重载 覆盖(重写) 重定义(隐藏)的定义的区别
new新建,隐藏旧的
在这里插入图片描述

(问题) 不能延时(TODO:)

处理的是鸟的死亡音效(视频是作为受伤音效).
想让音效播放完几秒后在显示ResultUI。目前是音效和ResultUI一起发生
在这里插入图片描述

    void Dead()//死亡
    {
        StartCoroutine(A());

        Destroy(gameObject);
        GameController._instance.birdList.RemoveAt(0);
        GameController._instance.StepAfterShoot();
        //TODO:鸟死亡,没特效
    }

    IEnumerator A()
    {
        AudioSource.PlayClipAtPoint(DataHub._instance.birdCollisionClip, transform.position);
        yield return new WaitForSeconds(5f);     
    }

(了解) virtual public 和public virtual

public virtual流行点
virtual(C# 参考)

25 添加黄色小鸟

(了解) protected

子类可以调用,编辑器面板看不到

(现象) unity不能重命名

重启

(问题) 第二只鸟无法点击操作

对比是YellowBird(空格键加2倍速的子类:Bird)
两坨线
在这里插入图片描述
//
弹弓的画线方法

        leftLine.SetPosition(0, leftPos.position);
        leftLine.SetPosition(1, projectile.position);

在这里插入图片描述

私有方法可继承不可访问

在YellowBird中update中调用Line,画线可以,但其他方法无效,说明YellowBird对Bird的继承有问题
C#父类的私有成员子类有没有继承?

私有方法可继承不可访问。
在这里插入图片描述
但是改成protected,public,也是没作用。然而不是没用,而是还不够

子类不继承父类的Start,Update

测试一个计时函数,父类在跑,子类不跑。
在这里插入图片描述
//
Start,Update补上去就可以

   void Start()
    {
        PreDragMouseButton();
    }

    // Update is called once per frame
    void Update()
    {
        //
        if (isDrag)//按下,预备发射
        {
            DragMouseButton();
        }

        if (!isUp)//比isDrag跟线时间更长
        {
            PreUpMouseButton();
        }
        else if (isUp)//松开
        {

            UpMouseButton();
        }
        //
    }

私有系统函数可继承访问

但是以下这种系统函数,子类可以继承执行到

    private void OnMouseDrag()
    {
        if (!GameController._instance.isPause)
        {
            isDrag = true;
        }
        UIController._instance.ShowMessage("按下");
    }

(代码) 加速

		......
        if (isUp && Input.GetKeyDown(KeyCode.LeftShift) && useSkill== false)
        {
            useSkill = true;         
        }
        if (useSkill)
        {
            ShowSkill();
            //
            coolTimer += Time.deltaTime;
            if (coolTimer > coolTime)
            {
                coolTimer = 0f;
                useSkill = false;
            }
        }
    }

    public override void ShowSkill()
    {
        if ( skillCount > 0)
        {
            skillCount--;
            //GetComponent<Rigidbody2D>().AddForce(-Vector2.left*Time.deltaTime * skillPower);//加力数值太大
            GetComponent<Rigidbody2D>().velocity *= skillPower;
            UIController._instance.ShowMessage("加速");//显示看看
        }
    }

26 添加绿色的小鸟

(代码)

同上黄色小鸟改作用的部分
改变方向,效果像回旋镖;
if这段代码会在update,update中执行的会封装成一个方法,让子类调用,这样子类的代码就少了点

  if (isUp && Input.GetKeyDown(KeyCode.LeftShift) && useSkill== false)
        {
            useSkill = true;         
        }
        if (useSkill)
        {
            ShowSkill();
            //
            coolTimer += Time.deltaTime;
            if (coolTimer > coolTime)
            {
                coolTimer = 0f;
                useSkill = false;
            }
        }
    }

    public override void ShowSkill()
    {
        if ( skillCount > 0)
        {
            skillCount--;
            //GetComponent<Rigidbody2D>().AddForce(-Vector2.left*Time.deltaTime * skillPower);//加力数值太大
            GetComponent<Rigidbody2D>().velocity *= skillPower;
            GetComponent<SpriteRenderer>().sprite = skillSprite;
            UIController._instance.ShowMessage("加速");//显示看看
        }
    }

27 添加黑色小鸟

需求

爆炸条件,做了一个大点的触发器,时刻记录在圈内的物体(进来又出去,就不会被炸),鼠标点击,圈内的物体受到爆炸会销
//
黑鸟爆炸,图片切换,没有爆炸特效,就用一张图替代下(也是触发器的范围大小)
在这里插入图片描述
在这里插入图片描述

(问题) 两个问题耦合在一起,分不开

(问题) 已经被销毁

//不清楚==null是否等价于Missing,百度看到一些回答:

try catch异常就是销毁了
移到屏幕看不见的地方
SetActive
C#怎么求两个列表的交集(EnemyController有一个活着的Enemy的enemyList,

在这里插入图片描述
//但就想Destroy
TrimDead();的作用是"Trim"掉"Missing…”的元素。
试着不加这一句,运行停了(Missing时不能通过相应的Enemy组件找到相应的gameObject)

(问题) List在for循环中Remove导致的越界

if (enemyList[i] != null)后,Missing解决,但发生越界,所以有(了解)的测试
//
具体的原因因为已经修改覆盖了,无法对比。总结就是,在for循环中删除 列表 ,会有以下两种情况:
1、.Count会变小
2、后面接上来,i不减减,就会跳过接上来的那个元素

(代码)

    public void TrimDead() //剔除一撞就死了,导致挂的Enemy也没了的物体
    {
        for (int i = 0; i < enemyList.Count; i++)
        {
            if (enemyList[i] == null)
            {
                enemyList.Remove(enemyList[i]);
                i--;
            }
        }
    }
    public override void ShowSkill()//鼠标左键触发
    {
        if ( skillCount > 0)
        {
            skillCount--;
            GetComponent<SpriteRenderer>().sprite = skillSprite;
            UIController._instance.ShowMessage("加速");//显示看看
            //
            AudioSource.PlayClipAtPoint(DataHub._instance.woodDestroyedClip, transform.position);//爆炸音效          
            boomFiledSprite.gameObject.SetActive(true);//显示爆炸范围
            
            //
            TrimDead();
            //杀敌
            while(enemyList.Count > 0)
            { 
                Destroy(enemyList[0].gameObject);               
            }
        }
    }

(了解) 列表Remove后索引是不是自动接上来

在这里插入图片描述

    public List<int> list = new List<int> { 0, 1, 2, 3, 4 };

    // Start is called before the first frame update
    void Start()
    {
        list.Remove(list[2]);

        print(list.IndexOf(3));
    }

(重构) base.xxx()

1、if(skillCount > 0) { skillCount–;提到父类UpdateFunc()
2、ShowSkill()相同的部分提到父类

父类

    public void UpdateFunc()//为子类少写点,因为Upadate子类不能继承
    {
		......
        //
        if ( Input.GetKeyDown(KeyCode.LeftShift) && useSkill == false)
        {
            useSkill = true;//标志计时状态
        }
        if (useSkill && skillCount > 0)
        {
            skillCount--;
            ShowSkill();
            //
            coolTimer += Time.deltaTime;
            if (coolTimer > coolTime)
            {
                coolTimer = 0f;
                useSkill = false;
            }
        }
    }
    ......
    public  virtual void ShowSkill()
    {
        GetComponent<SpriteRenderer>().sprite = skillSprite;//发动技能图片变化
        UIController._instance.ShowMessage("加速");//显示看看
    }

子类

比如BlackBird

    void Update()
    {
        UpdateFunc();
    }

    public override void ShowSkill()//鼠标左键触发
    {
		base.ShowSkill();//继承家产
		//
        AudioSource.PlayClipAtPoint(DataHub._instance.woodDestroyedClip, transform.position);//爆炸音效,没有专属的,挑个接近的          
        boomFiledSprite.gameObject.SetActive(true);//显示爆炸范围
            
        //
        TrimDead();
        //杀敌
        while(enemyList.Count > 0)
        { 
            Destroy(enemyList[0].gameObject);               
        }     
    }
    public void TrimDead() //剔除一撞就死了,导致挂的Enemy也没了的物体
    {
        for (int i = 0; i < enemyList.Count; i++)
        {
            if (enemyList[i] == null)
            {
                enemyList.Remove(enemyList[i]);
                i--;
            }
        }
    }

28 处理黑色小鸟的爆炸问题

需求

鸟(拖尾)没了,爆炸特效还存在一会

(了解) override不能修改父类的访问符等级

在这里插入图片描述

(代码)

重写父类(父类是隔几秒检测位移是不是超过一定的距离)

    public override void IsDead()
    {
        boomFiledSprite.SetParent(transform.parent);
        Destroy(boomFiledSprite.gameObject, 1f);

        isDead = true;
    }

30 制作选择地图UI界面

(了解) UGUI的ScrollView

Content的范围控制着展示元素的范围,比如高度变低点,往下的元素就不会显示,上滑也没用
Content的范围控制着滑动条是否显示,范围内显示,范围外不显示
在这里插入图片描述

(了解) 声明赋值与Start

第一次声明赋值,组件里的值随之变化,但是再次修改,修改无效
只能在组件里(Unity编辑器),或者Start里修改

    [Tooltip("每个关卡获得的星星总数")] public List<int> collectedStarNumList = new List<int>() { 0,0,0,0,0, 0,0,};
    [Tooltip("每个关卡解锁需要的的星星")] public List<int> unlockNeedStarNumList = new List<int>() { 0,10,13,10,16, 10,19};//一开始是new List<int>() { 0,10,10,10,10,   10,10};

打印结果
在这里插入图片描述
在Start中赋值后
在这里插入图片描述

32 制作关卡选择界面

(现象) 有时Unity点不开,VS没发生这情况

(代码) LevelController

为Penel下的level赋值

    void Start()
    {
        Init();
    }
    void Init()
    {
        //每个面板
        for (int i = 0; i < transform.childCount; i++)
        {
            panelList.Add(transform.GetChild(i));
        }
        //
        for (int i = 0; i < panelList.Count; i++)
        {
            //面板下的关卡
            for (int j = 0; j < panelList[i].childCount; j++)
            {
                //为可读
                Transform level = panelList[i].GetChild(j);
                Transform lockTrans = level.GetChild(1);
                Transform unlockTrans = level.GetChild(0);
                //
                lockTrans.gameObject.SetActive(false);
                unlockTrans.gameObject.SetActive(true);
                //0关卡,1星星
                unlockTrans.GetChild(0).GetComponent<Text>().text = (j + 1).ToString();//关卡数
                unlockTrans.GetChild(1).GetComponent<Image>().sprite = rankList[2];//评价星星数
            }
        }
    }

//运行后
在这里插入图片描述

(代码) level

点击进入正式游戏场景
//
从索引2开始正式游戏场景
在这里插入图片描述
//左边是Panel,右边是每个Panel的Level数量
在这里插入图片描述

    public void OnButtonClick()
    {
        int panel=LevelController._instance.panelList.IndexOf(transform.parent);//第几个面板
        int level = int.Parse(transform.GetChild(0).GetChild(0).GetComponent<Text>().text ) ;//解锁节点的关卡数Text
        //
        int index = 2-1;//2是开始,关卡选择
        if (panel == 0)//当前Panel
        {
            index += level;
        }
        else if (panel > 0)
        {
            for (int i = 0; i < panel; i++)//前面的Panel的关卡
            {
                index += LevelController._instance.panelList[i].childCount;
            }
        }
        //
        SceneManager.LoadScene(index);
    }

33 添加地图选择和关卡选择界面的交互

点击Panel,激活相应的节点

    public void OnButtonClick()
    {
        if (isLocked == true)//未解锁无法进入
            return;
        if(isLocked == false)//还未用到
            isSelected = true;

        int index = PanelController._instance.panelList.IndexOf(this);//获得自己在父节点的子节点列表的索引
       
        //更换sky,grounp
        EnvController._instance.ChangeBg(index);
        //进入相应的Panel
        LevelController._instance.panelList[index].gameObject.SetActive(true);
        //隐藏Panel页面
        PanelController._instance.gameObject.SetActive(false);
    }

//这是第三个Panel的sky,ground,和关卡信息(共7Panel,分别1234567level【仅测试方法是否有效,实际内容的关卡只有一个】)
在这里插入图片描述

点击按钮,返回Panel

    public void OnBackButtonClick()
    {
        PanelController._instance.gameObject.SetActive(true);
        transform.parent.gameObject.SetActive(false);
    }

在这里插入图片描述

34 存储关卡星星数据(测试,源数据是在 LevelController统一赋值的)

(需求)

每关的星星,每个Panel的星星总数
在这里插入图片描述

level

    public int StarNum()//根据星星评价图得到星星数量
    {
        Sprite starSprite = transform.GetChild(0).GetChild(1).GetComponent<Image>().sprite;//得到那张星星评价的图
        int starIndex = LevelController._instance.rankList.IndexOf(starSprite);//到levelController找在rankList中的索引

        return starIndex;
    }

//已测试多次
在这里插入图片描述

LevelController

统计每个Panel下的Level的star总和,装进列表。
原本写在PanelController,发生了错误,
写在LevelController的Start中,也发生有的读取不到的错误;下图正常是放在Update(只能从Panel进入Level时调用一下这个方法)
在这里插入图片描述

    public List<int> StarNum()//计算每个Panel下的星星总数
    {
        for (int i = 0; i < panelList.Count; i++)
        {
            int star = 0;
            for (int j = 0; j < panelList[i].childCount; j++)
            {
                Transform level = panelList[i].GetChild(j);
                star += level.GetComponent<Level>().star;
            }
            starNumList[i]=star;
        }

        return starNumList;
    }

PanelController统一赋值】

这里也发生在Start中有问题,在Update中正常,
在这里插入图片描述

34 存储关卡星星数据

(重构)

分拆不固定的,做成预制体(视频是这样做的)
1、只需要一个场景,后面做新的关卡就是做新的预制体
2、根据预制体名字(Level01_01,第几个面板的第几关)进行数据读存(PlayerPrefs第几关多少颗星星)
在这里插入图片描述

(代码) 玩家通关后关卡星星的存储

在这里插入图片描述

    public void Save(string level, int star)
    {
        if (PlayerPrefs.HasKey(level))//有且大于
        {
            if (star < PlayerPrefs.GetInt(level))
            {
                return;
            }
        }

        //其他情况,有且大于;没有
        PlayerPrefs.SetInt(level, star);
    }

(代码) LevelController分发Level的名字

主要看名字的那段代码,有了名,就可以到PlayerPrefs取值

   void Init()
    {
        //每个面板
        for (int i = 0; i < transform.childCount; i++)
        {
            panelList.Add(transform.GetChild(i));
        }
        //
        for (int i = 0; i < panelList.Count; i++)
        {
            Transform panel = panelList[i];
            //面板下的关卡
            for (int j = 0; j < panel.childCount; j++)
            {
                //为可读
                Transform level = panel.GetChild(j);
                //
                string levelName;
                levelName = "Level";
                levelName += "0" + (i+1);
                levelName += "_";
                levelName += "0" + (j + 1);
                level.gameObject.name = levelName;
                //
                Transform lockTrans = level.GetChild(1);
                Transform unlockTrans = level.GetChild(0);
                lockTrans.gameObject.SetActive(false);
                unlockTrans.gameObject.SetActive(true);
                //0关卡,1星星
                unlockTrans.GetChild(0).GetComponent<Text>().text = (j + 1).ToString();//关卡数
                //
                int star = PlayerPrefs.GetInt(level.gameObject.name);
                unlockTrans.GetChild(1).GetComponent<Image>().sprite = rankList[star];//评价星星数
            }
        }
    }

在这里插入图片描述

(代码) LevelController统计每个Panel的星星

    void Update()
    {
        starNumList = StarNum();
    }
    public List<int> StarNum()//计算每个Panel下的星星总数
    {
        for (int i = 0; i < panelList.Count; i++)
        {
            int star = 0;
            for (int j = 0; j < panelList[i].childCount; j++)
            {
                Transform level = panelList[i].GetChild(j);
                star += level.GetComponent<Level>().star;
            }
            starNumList[i]=star;
        }

        return starNumList;
    }

在这里插入图片描述

(了解) 调用小鸟时用GetChild还是List

用GetChild的缺点是,比如鸟爆炸,鸟销毁了,爆炸特效还存在一会,那么特效的父节点要移出小鸟。类似的情况都要重复操作(前人不栽树,后人晒太阳);
如果不移出,像这种情况,GetChild子节点就脏了,遍历子节点就走不通(或者说情况复杂)
//
以上(代码也是一样),那个黄色的返回按钮应该放节点内,但会造成Panel和Level的污染

(了解) 节点内能调用的节点内解决

比如UI上有个NewGame的方法,一般GameController中会写,但按钮事件还是写复制相同代码的同节点的UIController。

37 解决星星个数更新问题

39 制作多个关卡

需求

只需要一个场景,后面做新的关卡就是做新的预制体
在这里插入图片描述

(重构)

之前是想多个场景做多个关卡,现在改成一个场景,多个预制体做多个关卡

(代码) level

点击哪个,就存储它的名字
在这里插入图片描述

    public void OnButtonClick()
    {
        PlayerPrefs.SetString("IsSelected", gameObject.name);//用于实例那一关

        SceneManager.LoadScene(2);//游戏主场景
    }

(代码) 主场景 LevelSelect

直接挂上去的()
在这里插入图片描述

    void Start()
    {
        string levelName;
        if (!PlayerPrefs.HasKey("IsSelected"))
            levelName = "Level01_01";
        else
            levelName = PlayerPrefs.GetString("IsSelected");

        for (int i = 0; i < levelList.Count; i++)
        {
            if(levelList[i].gameObject.name == levelName)
                Instantiate( levelList[i] );
        }
        
    }

//
或者Resources

    void Start()
    {
        string levelName;
        if (!PlayerPrefs.HasKey("IsSelected"))
            levelName = "Level01_01";
        else
            levelName = PlayerPrefs.GetString("IsSelected");

        GameObject go = Resources.Load<GameObject>(levelName);
        Instantiate(go);     
    }

40 snap settings使用介绍

snap settings,捕捉设置

在这里插入图片描述

手调第一次得到这个值后,以后复制移动各大概距离,按相应的xyz(水平是x),就自动吸附
在这里插入图片描述

平铺

碰撞器大小得相应调整
在这里插入图片描述

42-43 异步加载场景

(了解) 分辨率 相机 画布渲染模式

2D相机用正交
在这里插入图片描述

写死分辨率

分辨率我做的不好,主要游戏场景由16:10,改回1024:728,那些暂停UI,ResultUI发生过位置缩放问题

    void Start()
    {
    	Screen.SetResolution(1024, 768, false);//宽,高,不可修改
        Invoke("Load",2f);//延时
    }
    private void Load()
    {
        SceneManager.LoadSceneAsync(1);
    }

在这里插入图片描述

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值