Rigidbody具有完全真实物理的特性,⽽CharacterController可以说是受限的 Rigidbody,具有⼀定的物理效果但不是完全真实的。
答:射线是3D世界中一个点向一个方向发射的一条无终点的线,在发射轨迹中与其他物体发生碰撞时,它将停止发射 。
Hinge Joint,可以模拟两个物体间用一根链条连 接在一起的情况,能保持两个物体在一个固定距 离内部相互移动而不产生作用力,但是达到固定 距离后就会产生拉力。
两个物体都必须带有碰撞器Collider,其中一个物体还必须带有Rigidbody刚体
碰撞器是触发器的载体,而触发器只是碰撞器身 上的一个属性。
当Is Trigger=false时,碰撞器根据物理引擎引发 碰撞,产生碰撞的效果,可以调用 OnCollisionEnter/Stay/Exit函数; 当Is Trigger=true时,碰撞器被物理引擎所忽略, 没有碰撞效果,可以调用OnTriggerEnter/Stay/ Exit函数。
如果既要检测到物体的接触又不想让碰撞检测影 响物体移动或要检测一个物件是否经过空间中的 某个区域这时就可以用到触发器
射线是3D世界中一个点向一个方向发射的一条无 终点的线,在发射轨迹中与其他物体发生碰撞 时,它将停止发射 。
从一个起点向一个方向发射一条物理射线,返回碰撞到的物体的碰撞信息
都在 rigidbody系列函数中。
穿透(碰撞检测失败)
FixedUpdate,每固定帧绘制时执行一次,和Update不同的是FixedUpdate是渲染帧执行,如果你的渲染效率低下的时候FixedUpdate调用次数就会跟着下降。
FixedUpdate比较适用于物理引擎的计算,因为是跟每帧渲染有关。 Update就比较适合做控制。
简单来说在一个Canvas下,需要相同的material,相同的纹理以及相同的Z值。例如UI上的字体Texture使用的是字体的图集,往往和我们自己的UI图集不一样,因此无法合批。还有UI的动态更新会影响网格的重绘,因此需要动静分离。
Sprite作为UI精灵使用,Texture作用模型贴图使用。
多屏幕分辨率下的UI布局一般考虑两个问题:
为了解决这两个问题,在Unity UGUI体系中有两个组件可以来解决问题,分别是布局元素的Rect Transform和Canvas的Canvas Scaler组件。
CanvasScaler中UI Scale Mode有三种模式,Constant Pixel Size、Scale With Screen Size、Constant Physical Size,其中第二个就是根据屏幕分辨率来进行缩放适配。在这个模式下,有两个参数,一个是我们在开发过程中的标准分辨率,一个是屏幕的匹配模式,通过这里面的设置,就可以完成多分辨率下的适配问题。
缩放模式:
Property: | Function: |
---|---|
UI Scale Mode | Canvas中UI元素的缩放模式 |
Constant Pixel Size | 使UI保持自己的尺寸,与屏幕尺寸无关。 |
Scale With Screen Size | 屏幕尺寸越大,UI越大 |
Constant Physical Size | 使UI元素保持相同的物理大小,与屏幕尺寸无关。 |
Constant Pixel Size、Constant Physical Size实际上他们本质是一样的,只不过 Constant Pixel Size 通过逻辑像素大小调节来维持缩放,而 Constant Physical Size 通过物理大小调节来维持缩放。 |
Text是像素渲染放大之后就会模糊,使用Text父物体的放大缩小会影响子物体Text的清晰度, TMPText不会,它是网格渲染TMPText会把字体生成一个类似于贴图的东西然后读取贴图的坐标来获取对应的文字,更换文字的消耗会比Text大。 TMPText更适用于不会变动的文字,特别是在量大的情况下,性能比Text高一些,需要经常变动的问题用Text好点,TMPText在字体库很大的情况下查找更换会比较慢。
主要有关节动画、⻣骼动画、单一网格模型动画(关键 帧动画)。
用户提供的模型骨架和Unity的骨架结构进行适配,是一种骨架映射关系。 方便动画的重定向
AnimationType有三种类型 Humanoid人型:可以动画重定向,游戏对象挂载animator,子类原始模型+重定向模型,设置原始模型和使用模型的AnimationType为Humanoid类型 Generic非人型 Legacy旧版 Avator Mask身体遮罩,身体某一部分是否受到动画影响 反向动力学 IK,通过手或脚来控制身体其他部分
动画淡入淡出
SkinnedMesh蒙皮网格动画 分为骨骼和蒙皮两部分 骨骼是一个层次结构,存储了骨骼的Transform数据 蒙皮是mesh顶点附着在骨骼之上,顶点可以被多个骨骼影响,决定了其权重等, 还有将顶点从Mesh空间变换到骨骼空间~
动画分层
身体部位动画分层,比如我只想动动头,身体其他部分不发生动画,可以方便处理动画区分
Animation和Animator 虽然都是控制动画的播放,但是它们的用法和相关语法都是大有不同的。Animation控制一个动画的播放,而Animator是多个动画之间相互切换,并且Animator有一个动画控制器,俗称动画状态机。
Animator利用它做动画的切换是很方便的,但是它有一个缺点就是占用内存比Animation大。
进程
线程
协程
在Unity中只有主线程才能访问Unity3D的对象、方法、组件。当主线程在执行一个对资源消耗很大的操作时,在这一帧我们的程序就会出现帧率下降,画面卡顿的现象!
那这个时候我们就可以利用协程来做这件事,因为协程是伴随着主线程运行的,主线程依旧可以丝滑轻松的工作,把脏活累活交给协程处理就好了!简单来说:协程是辅助主线程的操作,避免游戏卡顿。
从程序的角度讲,协程的核心就是
迭代器
。 想要定义一个协程方法有两个因素,第一:方法的返回值为 IEnumerator 。第二,方法中有 yield关键字。 当代码满足以上两个条件时,此方法的执行就具有了迭代器的特质,其核心就是 MoveNext方法。 方法内的内容将会被分成两部分:yield 之前的代码和 yield 之后的代码。yield之前的代码会在第一次执行MoveNext时执行, yield之后的代码会在第二次执行MoveNext方法时执行。 而在Unity中,MoveNext的执行时机是以帧为单位的,无论你是设置了延迟时间,还是通过按钮调用MoveNext,亦或是根本没有设置执行条件,Unity都会在每一帧的生命周期中判断当前帧是否满足当前协程所定义的条件,一旦满足,当前帧就会抽出CPU时间执行你所定义的协程迭代器的MoveNext。 注意,只要方法中有yield语句,那么方法的返回值就必须是 IEnumerator ,不然无法通过编译。
线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。
官方案例)
function Start() {
// - After 0 seconds, prints "Starting 0.0"
// - After 0 seconds, prints "Before WaitAndPrint
Finishes 0.0"
// - After 2 seconds, prints "WaitAndPrint 2.0" // 先打印"Starting 0.0"和"Before WaitAndPrint
Finishes 0.0"两句,2秒后打印"WaitAndPrint 2.0" print ("Starting " + Time.time );
// Start function WaitAndPrint as a coroutine. And continue execution while it is running
// this is the same as WaintAndPrint(2.0) as the compiler does it for you automatically
// 协同程序WaitAndPrint在Start函数内执行,可以视 同于它与Start函数同步执行.
StartCoroutine(WaitAndPrint(2.0));
print ("Before WaitAndPrint Finishes " + Time.time );
}
function WaitAndPrint (waitTime : float) {
// suspend execution for waitTime seconds // 暂停执行waitTime秒
yield WaitForSeconds (waitTime);
print ("WaitAndPrint "+ Time.time );
}
启动协程
停止协程
作用:一个协同程序在执行过程中,可以在任意位置使 用yield语句。yield的返回值控制何时恢复协同程序向 下执行。协同程序在对象自有帧执行过程中堪称优 秀。协同程序在性能上没有更多的开销。 缺点:协同程序并非真线程,可能会发生堵塞。
更多协程内容:Unity零基础到入门 ☀️| 小万字教程 对 Unity 中的 协程 ❤️全面解析+实战演练❤️
//获取的目录路径最后不包含 /
//获得的文件路径开头包含 /
Application.dataPath; //Asset文件夹的绝对路径
//只读
Application.streamingAssetsPath; //StreamingAssets文件夹的绝对路径(要先判断是否存在这个文件夹路径)
Application.persistentData ; //可读写
//资源数据库 (AssetDatabase) 是允许您访问工程中的资源的 API
AssetDatabase.GetAllAssetPaths; //获取所有的资源文件(不包含meta文件)
AssetDatabase.GetAssetPath(object) //获取object对象的相对路径
AssetDatabase.Refresh(); //刷新
AssetDatabase.GetDependencies(string); //获取依赖项文件
Directory.Delete(p, true); //删除P路径目录
Directory.Exists(p); //是否存在P路径目录
Directory.CreateDirectory(p); //创建P路径目录
AssetDatabase //类库,对Asset文件夹下的文件进行操作,获取相对路径,获取所有文件,获取相对依赖项
Directory //类库,相关文件夹路径目录进行操作,是否存在,创建目录,删除等操作
PlayerPrefs类是一个本地持久化保存与读取数据的类 PlayerPrefs类支持3中数据类型的保存和读取,浮点型,整形,和字符串型。
分别对应的函数为:
SetInt();保存整型数据;GetInt();读取整形数据; SetFloat();保存浮点型数据; GetFlost();读取浮点型数据; SetString();保存字符串型数据; GetString();读取字符串型数据;
using UnityEditor;
using System.IO;
public class CreateAssetBundles //进行AssetBundle打包
{
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string dir = "AssetBundles";
if (Directory.Exists(dir) == false)
{
Directory.CreateDirectory(dir);
}
BuildPipeline.BuildAssetBundles(dir, //路径必须创建
BuildAssetBundleOptions.ChunkBasedCompression, //压缩类型***
BuildTarget.StandaloneWindows64);//平台***
}
}
None | Build assetBundle without any special option.(LAMA压缩,压缩率高,解压久) |
---|---|
UncompressedAssetBundle | Don’t compress the data when creating the asset bundle.(不压缩,解压快) |
ChunkBasedCompression | Use chunk-based LZ4 compression when creating the AssetBundle. |
(压缩率比LZMA低,解压速度接近无压缩)|
第一种
IEnumerator Start()
{
string path = "AssetBundles/wall.unity3d";
AssetBundleCreateRequest request =AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request;
AssetBundle ab = request.assetBundle;
GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");
Instantiate(wallPrefab);
}
第二种
IEnumerator Start()
{
string path = "AssetBundles/wall.unity3d";
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
yield return request;
AssetBundle ab = request.assetBundle;
GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");
Instantiate(wallPrefab);
}
第三种
IEnumerator Start()
{
string uri = @"http://localhost/AssetBundles/cubewall.unity3d";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);
yield return request.Send();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
GameObject wallPrefab = ab.LoadAsset<GameObject>("Cube");
Instantiate(wallPrefab);
}
第四种WWW(无依赖)
private IEnumerator LoadNoDepandenceAsset()
{
string path = "";
if (loadLocal)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
path += "File:///";
#endif
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
path += "File://";
#endif
path += assetBundlePath + "/" + assetBundleName;
//www对象
WWW www = new WWW(path);
//等待下载【到内存】
yield return www;
//获取到AssetBundle
AssetBundle bundle = www.assetBundle;
//加载资源
GameObject prefab = bundle.LoadAsset<GameObject>(assetRealName);
//Test:实例化
Instantiate(prefab);
}
第四种WWW(有依赖)
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class LoadAssetsDemo : MonoBehaviour
{
[Header("版本号")]
public int version = 1;
[Header("加载本地资源")]
public bool loadLocal = true;
[Header("资源的bundle名称")]
public string assetBundleName;
[Header("资源的真正的文件名称")]
public string assetRealName;
//bundle所在的路径
private string assetBundlePath;
//bundle所在的文件夹名称
private string assetBundleRootName;
private void Awake()
{
assetBundlePath = Application.dataPath + "/OutputAssetBundle";
assetBundleRootName = assetBundlePath.Substring(assetBundlePath.LastIndexOf("/") + 1);
Debug.Log(assetBundleRootName);
}
IEnumerator LoadAssetsByWWW()
{
string path="";
//判断是不是本地加载
if(loadLocal)// loadLocal=true为本地资源
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
path+="File:///";
#endif
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
path+="File://";
#edif
}
//获取要加载的资源路径【bundle的总说明文件】
path+=assetBundle+"/"+assetBundleRootName;
//加载
WWW www=WWW.LoadFromCacheOrDownload(path,version);
yield return www;
//拿到其中的bundle
AssetBundle manifestBundle=www.assetsBundle;
//获取到说明文件
AssetBundleManifest manifest=manifest.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//获取资源的所有依赖
string[] dependencies=manifest.GetAllDependencies(assetBundleName);
//卸载Bubdle和解压出来的manifest对象
manifestBundle.Unload(true);
//获取到相对路径
path =path.Remove(path.LastIndexOf("/")+1);
//声明依赖的Bundle数组
AssetBundle[] depAssetBundle=new AssetBundle[dependencies.Length];
//遍历加载所有的依赖
for(int i=0;i<dependencies.Length;i++)
{ //获取到依赖Bundle的路径
string depPath=path+ dependencies[i];
//获取新的路径进行加载
www=WWW.LoadFromCacheOrDownload(depPath,version);
yield return www;
//将依赖临时保存
depAssetBundles[i]=www.assetsBundle;
}
//获取路径
path+=assBundleName;
//加载最终资源
www=WWW.LoadFromCacheOrDownload(path,version);
//等待下载
yield return www;
//获取到真正的AssetBundle
AssetBundle realAssetBundle=www.assBunle;
//加载真正的资源
GameObject prefab=realAssetBundle.LoadAsset<GameObject>(assetBundle);
//生成
Instantiate(prefab);
//卸载依赖
for(int i-0;i<depAssetBundle.Length;i++)
{
depAssetBundle[i].Unload(true);
}
realAssetBundle.Unload(true);
}
}
AssetBundle.Unload(bool),T true卸载所有资源
false只卸载没使用的资源,而正在使用的资源与AssetBundle依赖关系会丢失,调用Resources.UnloadUnusedAssets可以卸载。
或者等场景切换的时候自动调用Resources.UnloadUnusedAssets。
ScriptableObject是一个数据容器,它可以用来保存大量数据。
更多详细内容可以看下面文章:Unity零基础到进阶 | Unity中Scriptable Object介绍学习