【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)
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
高度,高
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);
}