最近开始接触unity,做了个项目,不过是初学者,踩了不少坑,留下一些踩坑的经历。
有不对的地方欢迎大家指点~谢谢
一、Update()和FixedUpdate()
——————————————————————
Update()
刚接触的小伙伴一旦创建了C#脚本就能看到一个Update,并且逻辑统统都往这里面写,但是越往后会发现越控制不了自己的代码,有些效果和自己的预期不同。
这是因为Update是和帧率相关的。而帧率由于不同的设备或者在不同时刻由于渲染变化导致的性能差异是会变化的,这就导致Update的更新间隔不同,以致于最终获得的效果可能是不同的。
FixedUpdate()
FixedUpdate顾名思义就是固定刷新,不会受到帧率的影响,刷新频率可以自己设置,可以再编辑器里菜单中Edit——Project Settings——Time中的Fixed timestep设置。由于知道了Update的不稳定性,所以FixedUpdate就是专门处理那些需要稳定处理的逻辑——与物理引擎相关的计算(Rigidbody刚体相关)。
所以如何浅显的理解这二者呢,可以把Update视做渲染同步,FixedUpdate试做物理同步。
比如此时有一个方块正在做直线运动,当每次渲染时,不论Update的执行间隔有多少,是否准确,拿到的计算的值都是从FixedUpdate中取出来的。也许一个Update的间隔中执行了5次,10次FixedUpdate,但最终方块的位置相对于时间而言都是准确的。不会因为跳帧等不确定因素而影响到最终结果。
二、Time.deltaTime
——————————————————————
很多初学者应该都遇到过这样的代码:在Update里面写了:
transform.Translate(Vector3.right * 60 * Time.deltaTime);
这句话的本意是什么呢,是每秒钟运行60,所以里面的60很好理解,但是Time.deltaTime又是什么意思呢。
看了上面的Update的小伙伴应该已经想到肯定是和帧率相关。如何理解呢。我们先假定现在的帧率是60帧,所以Update的运行时间应该是1/60秒。所以想到达到每秒60的需求,那么每帧的需要移动的距离就是1/60 * 60,这不难理解。但是这其中的问题出在哪呢。
就是这1/60秒出了问题,那就是这个时间我们人为计算出来的。代码可能并不是这么运行的,从标题一中解释了Update的刷新间隔是不确定的,所以如果这么写,那么最终计算出来的每秒运行的距离也是无法控制的。所以Time.deltaTime的作用就出来了。这就表示每帧的增量,这个值是固定的吗?来测试一下:
很明显,这个值是会变化的,所以可以把Time.deltaTime理解为一种补偿措施。
Time.deltaTime*60就可以理解为我只能确保每秒走60的距离。不能保证每帧走过的距离是相同的。每一帧有可能走的多也可能走的少。
不过如果想获得精确的计算,还是推荐使用FixedUpdate;
三、全局实例
——————————————————————
写项目时也许经常会遇到切换场景的需求。但是当切换场景时却发现另外一个场景需要的东西也被销毁了。方法应该有很多。我就写我用过的2个方法。
一、不销毁(主要用于实例)
使用:GameObject.DontDestroyOnLoad(gameObject);这个方法来控制切换到其他场景时不会销毁。
但是如果使用了这个方法就会发现,重新切回这个场景的时候,场景里不光保留了之前的那个实例,还会创建一个新的实例,所以需要把新的那个销毁掉。
private void Awake()
{
if (GameData.Instance.isGlobalUIInit)
{
Destroy(this.gameObject);
}
else
{
if (!GameData.Instance.isGlobalUIInit)
{
GameData.Instance.isGlobalUIInit = true;
}
GameObject.DontDestroyOnLoad(gameObject);
}
}
在这里我是用变量的方式判断需要的实例是否存在的,当然也可以用查找的方式,如果已经有需要的实例,那么切换场景回来的时候就把新建的这个销毁掉。这就保证始终都是最初的那个实例(这里需要保证存留的那个旧的,如果保留新的。那么里面的参数都会被初始化)。
一、新建一个只加载一次的场景作为游戏入口(主要用于数据)
这个方法可能比较笨,但确实非常实用,创建一个只作为游戏入口的场景,这个场景里需要的东西只需要挂上一个不销毁即可,就可以保证全局使用。但通常都保留数据相关即可。这个场景只作为一个逻辑场景。没有UI,只作为入口,加载完需要的数据后,紧接着就进入主场景。
场景的初始加载顺序可以在菜单中的File——Build Settings中设置,第一个即为默认场景入口。
四、判断是否点击到了UI
——————————————————————
当我们控制点击事件时,通常需要判断是否点击到了UI上,然后进行逻辑处理,但是最开始发现在PC端上有用,一旦打包移动端出来就不生效了,所以需要分开判断。
EventSystem.current.IsPointerOverGameObject()方法,是用来判断鼠标是否点击在UI上。
Input.touch是移动端的触摸操作
(这里当时是让我郁闷的地方。PC和移动端的控制是不一样的。)
public bool UICheck()
{
if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.Android)
{//移动端
bool ifClick = false;
if (Input.touchCount > 0)//触摸数量大于0
{
if (Input.GetTouch(0).phase == TouchPhase.Began)//手指touch的状态为按下屏幕,参数里的0标识第一个触碰的手指
{
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))// Touch.fingerId: 一个Touch的标识,用来标识确定的touch
{
ifClick = true;
}
}
}
return ifClick;
}
else
{
//PC端,这里的else就是PC端不太准确,但我这里由于没有用到其他平台,所以直接用else判断PC端了
return EventSystem.current.IsPointerOverGameObject();
}
}
剩下的都是些小问题小优化了
五、IOS打包出来声音很小
——————————————————————
打包出来声音很小有可能是Prepare iOS for Recording的问题,在打包时,ios的PlayerSettings中找到Prepare iOS for Recording,把前面的对勾取消掉就好,勾选了可能会导致声音会从话筒中出来,而不是扬声器中出来,当然这可能只是出现的一个原因。还有众多其他可能的原因。
六、尽量少用outline描边
——————————————————————
OutLine是UGUI中给Text添加描边的功能,但是这个描边的原理是在这个text的基础上,又画了四遍,然后通过位移在上下左右4个方向都显示出来,效率很低。并且这个outline也只能用于描边需求不是很粗的情况下,如果很粗就会出现对不起的情况。
并且如果使用的过多,会导致verts(摄像机视野内渲染的顶点数)过多而性能消耗过于严重,像outline这样的原理而产生的效果。会使这个text的顶点数直接增加4倍。所以如果需求量多的话。慎用~这对性能优化很重要。
最后来一句,碰撞体多的时候,记得给碰撞体分层,不然真的很难受。
有不对的地方欢迎大家指点~谢谢