1、monobehavior中的重要内容
延时函数:
a、延迟函数
延迟函数的使用:Invoke,参数1:函数名,字符串;参数2:延迟时间,秒为单位。
eg:Invoke(“函数名” ,秒);调用的函数一定要存在,如果调用的函数名不存在,那么因为调用不到会进行警告。
传入的第一个参数是函数的字符串,而且传入的函数没法传入参数,只能包裹一层。虽然直接传入带参函数不行,但是可以将它封装一下,然后在用延时函数是可以的。
b、延时重复函数
使用:invokeRepeating
参数1:函数名字符串;参数2:第一次执行的延时函数;参数3:之后每次执行的间隔;时间以秒为单位。
c、取消延时函数
CancelInvoke();取消所有演示函数。
CancelInvoke(“函数名”);取消指定的函数的延时函数。
IsInvoking()/ IsInvoking(“函数”);判断是否有延时函数/是否有指定的函数。
在unity中,脚本依附的对象失活或脚本失活,延时函数可以继续进行。但是脚本依附的对象销毁或脚本移除,延时函数就无法执行了。
协同程序:
unity中允许多线程,只是新开的线程无法访问unity中相关的内容。unity中的多线程要记得关闭。
命名空间:using System.Threading
方法:Thread
a、协同程序是什么
协程不是多线程,主要作用就是将代码分段分时执行,不卡主线程。主要运用于异步加载文件、场景、异步下载文件、批量创建等场合。
b、协程的使用
继承mono类的可以开协程;先申明一个协程的关键:返回值为IEnumerator类型及其子类。函数通过yield return进行返回。协程可见本质是个迭代器。
unity中引入了StartCoroutine()方法来开启协程,在()中传入协程函数就行了。
也引入了StopAllCoroutine()方法来关闭所有协程,而关闭指定的协程,也是传入指定的协程。
c、关于yieldreturn后不同内容的含义
1、下一帧执行:yield return+数字/null ;在update和lateupdate之间执行。
2、等待指定秒后执行:yield return + new waitForSeconds(数字);在update和lateupdate之间执行。
3、等待下一个物理帧更新时候执行:yield return + new waitForFixedUpdate() ;在fixedupdate和碰撞检测相关函数后执行。
4、等待相机和GUI渲染后执行:yield return + new waitForEndOfFrame ;在lateupdate之后渲染处理完毕后执行。
5、一些特殊类型的对象,如异步加载相关函数返回的对象;在update和lateupdate之间执行。
6、跳出协程。yield break
协程开启后组件和物体销毁,协程不执行,物体失活协程不执行,相关组件失活协程执行。
2、resource资源动态加载
特殊文件夹
Application.dataPath 工程路径获取,该方式 获取到的路径 一般情况下 只在 编辑模式下使用。
Resources 资源文件夹
路径获取:
一般不获取
只能使用Resources相关API进行加载:Application.dataPath + "/Resources"
需要我们自己将创建
作用:
资源文件夹
1.需要通过Resources相关API动态加载的资源需要放在其中
2.该文件夹下所有文件都会被打包出去
3.打包时Unity会对其压缩加密
4.该文件夹打包后只读 只能通过Resources相关API加载
StreamingAssets 流动资源文件夹
路径获取:Application.streamingAssetsPath
需要我们自己将创建
作用:
流文件夹
1.打包出去不会被压缩加密,可以任由我们摆布
2.移动平台只读,PC平台可读可写
3.可以放入一些需要自定义动态加载的初始资源
persistentDataPath 持久数据文件夹
路径获取:Application.persistentDataPath
不需要我们自己将创建
作用:
固定数据文件夹
1.所有平台都可读可写
2.一般用于放置动态下载或者动态创建的文件,游戏中创建或者获取的文件都放在其中
Plugins 插件文件夹
路径获取:
一般不获取
需要我们自己将创建
作用:
插件文件夹
不同平台的插件相关文件放在其中,比如IOS和Android平台
Editor 编辑器文件夹
路径获取:
一般不获取:Application.dataPath + "/Editor"
需要我们自己将创建
作用:
编辑器文件夹
1.开发Unity编辑器时,编辑器相关脚本放在该文件夹中
2.该文件夹中内容不会被打包出去
Standard Assets 默认资源文件夹
路径获取:
一般不获取
需要我们自己将创建
作用:
默认资源文件夹
一般Unity自带资源都放在这个文件夹下
代码和资源优先被编译
resource资源同步加载
作用:1.通过代码动态加载Resources文件夹下指定路径资源
2.避免繁琐的拖曳操作
常用资源类型:
1.预设体对象——GameObject
2.音效文件——AudioClip
3.文本文件——TextAsset
4.图片文件——Texture
5.其它类型——需要什么用什么类型
注意:
预设体对象加载需要实例化
其它资源加载一般直接用
资源同步加载 普通方法:
1.预设体对象 :
第一步:要去加载预设体的资源文件(本质上 就是加载 配置数据 在内存中)
eg:Object obj = Resources.Load("Cube");
第二步:如果想要在场景上 创建预设体 一定是加载配置文件过后 然后实例化
eg:Instantiate(obj);
2.音效资源
第一步:就是加载数据
eg:Object obj= Resources.Load("Music/BKMusic");
第二步:使用数据 我们不需要实例化 音效切片 我们只需要把数据 赋值到正确的脚本上即可
eg: public AudioSource audioS;
audioS.clip = obj as AudioClip;
audioS.Play();
3.文本资源
文本资源支持的格式:.txt / .xml / .bytes / .json / .html / .csv
eg:TextAsset ta = Resources.Load("Txt/Test") as TextAsset;
对于资源同名,resource可以使用其他的API进行加载
eg:TextAsset ta = Resources.Load("Tex/TestJPG", typeof(TextAsset)) as TextAsset;
资源同步加载 泛型方法
用as太麻烦,所以常用这个方法。
eg:TextAsset ta2 = Resources.Load<TextAsset>("Tex/TestJPG");
resource资源异步加载
加载过大的资源可能会造成程序卡顿 ,Resources异步加载 就是内部新开一个线程进行资源加载 不会造成主线程卡顿。
Resources异步加载方法:
异步加载 不能马上得到加载的资源 至少要等一帧
eg: private Texture tex;
void Start()
{
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");
rq.completed += LoadOver;
}
private void LoadOver( AsyncOperation rq)
{
print("加载结束");
//asset 是资源对象 加载完毕过后 就能够得到它
tex = (rq as ResourceRequest).asset as Texture;
print(Time.frameCount);
}
这里通过异步加载中的完成事件监听 使用加载的资源,也是第一种方法。
注意:一定要等加载结束过后 才能使用
另一种就是采用协程异步加载
IEnumerator Load()
{
//迭代器函数 当遇到yield return时 就会 停止执行之后的代码
//然后 协程协调器 通过得到 返回的值 去判断 下一次执行后面的步骤 将会是何时
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");
//第一部分
//Unity 自己知道 该返回值 意味着你在异步加载资源
//yield return rq;
//Unity 会自己判断 该资源是否加载完毕了 加载完毕过后 才会继续执行后面的代码
//判断资源是否加载结束
while(!rq.isDone)
{
//打印当前的 加载进度
//该进度 不会特别准确 过渡也不是特别明显
print(rq.progress);
yield return null;
}
tex = rq.asset as Texture;
//yield return null;
第二部分
//yield return new WaitForSeconds(2f);
第三部分
}
两者异同:
1.完成事件监听异步加载
好处:写法简单
坏处:只能在资源加载结束后 进行处理
“线性加载”
2.协程异步加载
好处:可以在协程中处理复杂逻辑,比如同时加载多个资源,比如进度条更新
坏处:写法稍麻烦
“并行加载“
resource资源卸载
Resources重复加载资源会浪费内存吗?
答:Resources加载一次资源过后,该资源就一直存放在内存中作为缓存,第二次加载时发现缓存中存在该资源会直接取出来进行使用,但是 会浪费性能(每次加载都会去查找取出,始终伴随一些性能消耗)
如何手动释放掉缓存中的资源?
答:1.卸载指定资源:可以用Resources.UnloadAsset() 方法 ,但最好不要用在GameObject对象上,会作用于实例化的对象,一般用于不需要实例化的内容。
2、卸载未使用的资源:可以用Resources.UnloadUnusedAssets()方法,一般用于过场景和GC时使用
3、场景异步切换
场景同步加载: SceneManager.LoadScene()方法。
同步场景加载缺陷:
在切换场景时,Unity会删除当前场景上所有对象,并且去加载下一个场景的相关信息,如果当前场景对象过多或者下一个场景对象过多,这个过程会非常的耗时, 会让玩家感受到卡顿,所以异步切换就是来解决该问题的。
场景异步加载方法:
1、通过事件回调函数,异步加载。
例如:
void start()
{
AsyncOperation ao = SceneManager.LoadSceneAsync("Test");
//当场景异步加载结束后 就会自动调用该事件函数
//我们如果希望在加载结束后 做一些事情 那么就可以在该函数中写处理逻辑
ao.completed += (a) =>
{
print("加载结束");
};
ao.completed += LoadOver;
}
private void LoadOver(AsyncOperation ao)
{
print("LoadOver");
}
2、通过协程 ,异步加载。
这个方法需要注意的是,加载场景会把当前场景上,没有特别处理的对象都删除了,从而导致在协程中的部分逻辑无法执行,因为找不到了。。为了解决这个问题,让处理场景加载的脚本依附的对象,在过场景时不被删除即可。
void start()
{
DontDestroyOnLoad(this.gameObject);
StartCoroutine(LoadScene("Test"));
}
IEnumerator LoadScene(string name)
{
AsyncOperation ao = SceneManager.LoadSceneAsync(name);
//协程的好处 是异步加载场景时 可以在加载的同时 做一些别的逻辑
print("异步加载过程中 打印的信息");
yield return ao;
print("异步加载结束后 打印的信息");
}
4、lineRenderer相关
LineRenderer是Unity提供的一个用于画线的组件,使用它我们可以在场景中绘制线段,一般可以用于: 1绘制攻击范围 2武器红外线3辅助功能 4其它画线功能。
LineRender参数相关:
LineRender代码相关:
GameObject line = new GameObject();
line.name = "Line";
LineRenderer lineRenderer = line.AddComponent<LineRenderer>();
//首尾相连
lineRenderer.loop = true;
//开始结束宽
lineRenderer.startWidth = 0.02f;
lineRenderer.endWidth = 0.02f;
//开始结束颜色
lineRenderer.startColor = Color.white;
lineRenderer.endColor = Color.red;
//设置材质
m = Resources.Load<Material>("M");
lineRenderer.material = m;
//设置点
//一定注意 设置点 要 先设置点的个数
lineRenderer.positionCount = 4;
//接着就设置 对应每个点的位置
lineRenderer.SetPositions(new Vector3[] { new Vector3(0,0,0),
new Vector3(0,0,5),
new Vector3(5,0,5)});
lineRenderer.SetPosition(3, new Vector3(5, 0, 0));
//是否使用世界坐标系
//决定了 是否随对象移动而移动
lineRenderer.useWorldSpace = false;
//让线段受光影响 会接受光数据 进行着色器计算
lineRenderer.generateLightingData = true;
物理系统之范围检测
物理系统之碰撞检测:
碰撞产生的必要条件:
1.至少一个物体有刚体
2.两个物体都必须有碰撞器
碰撞和触发:
碰撞会产生实际的物理效果
触发看起来不会产生碰撞但是可以通过函数监听触发
碰撞检测主要用于实体物体之间产生物理效果时使用
什么是范围检测?
在指定位置进行范围判断,从而得到处于指定范围内的对象
如何进行范围检测?
必备条件:想要被范围检测到的对象 ,必须具备碰撞器
注意点:
1.范围检测相关API 只有当执行该句代码时 进行一次范围检测 它是瞬时的
2.范围检测相关API 并不会真正产生一个碰撞器 只是碰撞判断计算而已
范围检测API:
1.盒状范围检测Physics.OverlapBox()
参数一:立方体中心点
参数二:立方体三边大小
参数三:立方体角度
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
这里需要注意,参数四里的指定层级不是指的是某一层的数,也不是直接使用LayerMask.NameToLayer("UI")作为参数进行传入,而是需要对其进行二进制转换的,也就是需要通过编号左移构建二进制数,这样每一个编号的层级 都是 对应位为1的2进制数,我们通过位运算 可以选择想要检测层级。
另一个API是Physics.OverlapBoxNonAlloc(),用法和上面类似,只是多要传入一个数组进行储存。
2、球形范围检测Physics.OverlapSphere()
参数一:中心点
参数二:球半径
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
另一个API是Physics.OverlapSphereNonAlloc(),用法和上面类似,只是多要传入一个数组进行储存。
3.胶囊范围检测Physics.OverlapCapsule
参数一:半圆一中心点
参数二:半圆二中心点
参数三:半圆半径
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
另一个API是Physics.OverlapCapsuleNonAlloc(),用法和上面类似,只是多要传入一个数组进行储存。