Unity c#
Unreal Engine cpp
Cocos Creator js
在Unity中,程序的入口点是一个名为void Main()
的静态方法,它位于一个名为Program
的类中。这个类和方法是由Unity自动生成的,你不需要手动创建或修改它们。
在Unity项目中,程序的入口点由Unity引擎负责管理。当你运行游戏或应用程序时,Unity会自动创建一个GameManager
的游戏对象,并将其附加到一个tag为MainCamera
的摄像机上。然后,Unity会在GameManager
上调用Awake()
、Start()
和Update()
等方法,这些方法是你编写的脚本中的入口点。
因此,你可以将你的逻辑代码写在这些方法中,以便在游戏开始时执行初始化操作,并在游戏运行时更新游戏状态。你也可以创建自己的脚本,并将其附加到游戏对象上,以便在特定事件发生时调用相应的方法。
总结起来,Unity的程序入口点是由Unity引擎管理的,你可以通过编写脚本的Awake()
、Start()
和Update()
等方法来定义你的逻辑代码的入口点。
首先地图大小调小点
地图高度整体拔高,为了后面能绘制低谷
图形以三角形网格形式存储,三角形越少,性能越高
图形的渲染需要网格模型(mesh)+材质(material)
可通过修改tag和icon进行区分不同的物体
不同图层可通过相机拍摄过滤掉
预设体相当于类,变体相当于类的继承
绘制好后拖到assetsf中就会生成后.prefab预设体文件
预设体的实例独有的组件会有+标志,可通过覆盖到预制件中,即修改双向可同步
将修改后的实例拖到assets时选择变体,跟父类相同的组件会同步变化
type : directional是无穷远的光,跟角度有关,跟位置无关
area仅烘焙,先将flags设为contribute UI,然后window->rendering->light
mode: realtime baked 阴影的实时计算和提前计算,性能消耗差距
shadow type: 光线的阴影很消耗性能,软阴影消耗最大光周围一圈
图层,接受光照的图层
投影摄像机,正常视角,常用于3D
正交摄像机(orthographic),三视图,常用于2D
清除标志,在scene中不显示,在摄像机中添加skybox组件,会显示到Game框中,清除标记仅深度会将不同相机叠加
多相机深度越高越优先显示
范围控制 : 视野(filed of view)和近远面(clipping plane)
viewport rect控制相机显示在game框的范围,用0-1之间的数表示
给相机添加target texture(空的,用于存储)会将拍摄内存存储在纹理之中,而不会渲染到game之中了,这个纹理可以拖到游戏内使用
选中摄像机 ctrl+shift+F将game与scene视图同步,就不需要手调了
利用射线碰撞来获得坐标,进行移动,而不是直接使用鼠标位置,避免悬空移动
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;//利用碰撞组件检测射线和平面碰撞
bool res = Physics.Raycast(ray, out hit);//out表示接受返回值
if(res)
{
transform.position = hit.point;
}
//多检测,检测到碰撞后不停止,一直贯穿,直到没有碰撞
RaycastHit[] hits = Physics.RaycastAll(ray,100,1<<10);//碰撞距离,碰撞图层10
}
}
初始化
Awake():
OnEnable():
Start():
执行
Update():
FixedUpdate():
LateUpdate():
销毁
OnDisable():
OnDestroy():
实例化
public class App : MonoBehaviour
{
public UIManager ui;//拖拽绑定
void Start()
{
ui = GetComponent<UIManager>();//如果已经挂载了该脚本组件
ui =new UIManager();
}
}
执行顺序
任选一个脚本打开excution order进行编排,更方便管理
通过awake与start优先级区分
用于空间变换,可实现坐标,旋转,缩放
基本方法
Vector3 v1 = Vector3.forward;
Vector3 v2 = Vector3.up;
Vector3.Angle(v1, v2);
Vector3.Distance(v1, v2);
Vector3.Dot(v1, v2);//点乘
Vector3.Cross(v1, v2);//叉乘
Vector3.Lerp(v1, v2, 0.5f);//线性插值,Linear Interpolation,用于平滑过度,返回中间向量 = v1+(v2-v1)*0.5f
Debug.Log(v1.magnitude);//模长
Debug.Log(v1.normalized);//模长
四元数
欧拉角(0-360°)
四元数
满足以下定义,高维虚数
避免万向锁,即线性代数旋转乘法后秩降低的不可逆
几何意义,ijk构成三维空间,实数轴为第四维垂直于这个三维空间
q = a + b i + c j + d k i 2 = j 2 = k 2 = i j k = − 1 将其用欧拉公式表示 , q = ∣ q ∣ ∗ ( c o s ( θ ) + u ∗ s i n ( θ ) ) q = a+bi+cj+dk\\ i^2=j^2=k^2=ijk=-1\\ 将其用欧拉公式表示,\\ q = |q| * (cos(θ) + u * sin(θ)) q=a+bi+cj+dki2=j2=k2=ijk=−1将其用欧拉公式表示,q=∣q∣∗(cos(θ)+u∗sin(θ))
其中u表示旋转角度的单位向量,类似于复数中的i
四元数计算旋转 P为被旋转的点的四元数形式,Q为旋转的单位四元数
P ′ = Q ∗ P ∗ Q − 1 P' = Q * P * Q^-1 P′=Q∗P∗Q−1
private void Awake()
{
Debug.Log("debug");
Debug.LogWarning("warning");
Debug.LogError("error");
}
void Update()
{
Debug.DrawLine(Vector3.zero, Vector3.one, Color.red);
Debug.DrawRay(Vector3.zero, Vector3.up, Color.blue);
}
所有物体都有一个自己的类进行管理
public GameObject Prefab;//放在全局,可通过在unity中拖动网格赋值,用于创建新网格
// Start is called before the first frame update
void Start()
{
//访问属性
Debug.Log(gameObject.name);
Debug.Log(gameObject.tag);
Debug.Log(gameObject.activeInHierarchy);//继承后的激活状态
Debug.Log(transform.position);
//获取组件
BoxCollider collider = GetComponent();
GetComponentInChildren(collider);
GetComponentInParent(collider);
//获取网格
GameObject test = GameObject.Find("sphere");
test = GameObject.FindWithTag("Player");
//修改属性
test.SetActive(false);
//创建组件
gameObject.AddComponent();
Instantiate(Prefab);
Instantiate(Prefab,Vector3.zero,Quaternion.identity);//生成实例于原点不旋转
//销毁
Destroy(test);
}
Debug.Log(Time.time);//游戏开始到现在的时间
Debug.Log(Time.timeScale);//游戏速度
Debug.Log(Time.fixedDeltaTime);//固定间隔时间,默认0.02s
Debug.Log(Time.deltaTime);//每帧的时间间隔
//只读,后面的文件会加密
Debug.Log(Application.dataPath + "/userdata.dat");
//系统的数据路径
Debug.Log(Application.persistentDataPath);
//只读,但不会加密
Debug.Log(Application.streamingAssetsPath);
//临时文件夹
Debug.Log(Application.temporaryCachePath);
//控制是否在后台运行
Debug.Log(Application.runInBackground);//失去交点后是否继续运行
//打开URL
Application.OpenURL("https://space.bilibili.com/696782160/favlist?fid=1763669060&ftype=create");
//退出游戏
Application.Quit();
维持网格父子级关系,实现位置和缩放
void Start()
{
//获取位置(全局,相对)
Debug.Log(transform.position);
Debug.Log(transform.localPosition);
//获取旋转(四元数,欧拉角)
Debug.Log(transform.rotation);
Debug.Log(transform.localRotation);
Debug.Log(transform.eulerAngles);
Debug.Log(transform.localEulerAngles);
//获取缩放
Debug.Log(transform.localScale);
//方向向量,z代表前,y代表上,x代表右
Debug.Log(transform.forward);
Debug.Log(transform.up);
Debug.Log(transform.right);
//父子关系
Debug.Log(transform.parent.gameObject);
Debug.Log(transform.childCount);
//查找子物体(名称,下标)
Transform trans = transform.Find("Child");
trans = transform.GetChild(0);
//解绑和捆绑
transform.DetachChildren();
trans.SetParent(transform);
Debug.Log(trans.IsChildOf(transform));
}
void Update()
{
//转向和旋转
transform.LookAt(Vector3.zero);
transform.Rotate(Vector3.up, 1);//自转,绕自转轴
transform.RotateAround(Vector3.zero, Vector3.up, 1);//公转,点向式确定转轴
transform.Translate(Vector3.forward * 0.1f);//以每帧0.1的速度向前走,闪现就是不按帧调用
}
游戏->场景->物体->组件
先file->building setting将场景添加到scene in build
using UnityEngine.SceneManagement;
//SceneManager.LoadScene(1);//build setting里的序号
SceneManager.LoadScene("MyScene");
SceneManager.LoadScene("scene", LoadSceneMode.Additive);
//获取当前场景信息
Scene scene = SceneManager.GetActiveScene();
Debug.Log(scene.isLoaded);
Debug.Log(scene.path);
Debug.Log(scene.buildIndex);
Debug.Log(scene.name);
GameObject[] sc = scene.GetRootGameObjects();
Debug.Log(sc.Length);
//场景管理
Scene newScene = SceneManager.CreateScene("newScene");
Debug.Log(SceneManager.sceneCount);
SceneManager.UnloadSceneAsync(newScene);//卸载场景
分工合作,将耗时代码让其他执行(多线程,协程)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class test : MonoBehaviour
{
AsyncOperation operation;
void Start()
{
StartCoroutine(LoadScene());
}
//协程异步加载场景
IEnumerator LoadScene() {
operation = SceneManager.LoadSceneAsync(1);
operation.allowSceneActivation = false;//加载完后不自动跳转
yield return operation;//yield创建迭代器,将加载结果一段段的返回,实现异步加载
}
float timer = 0;
void Update()
{
Debug.Log(operation.progress);//进度条0-0.9
timer+= Time.deltaTime;
if(timer > 5)
{
operation.allowSceneActivation = true;
}
}
}
void Update()
{
//鼠标点击 0左键 1右键 2滚轮
if(Input.GetMouseButtonDown(0))
{
Debug.Log("按下了鼠标左键");
}
//鼠标长按
if(Input.GetMouseButton(0))
{
Debug.Log("长按鼠标左键");
}
if(Input.GetKeyDown(KeyCode.A))
{
Debug.Log("按下了A");
}
if (Input.GetKey(KeyCode.A))
{
Debug.Log("长按A");
}
}
用于模拟输入,用于表示一个连续的数值,在-1~1之间,实现PC,掌机等不同平台兼容
在Project Setting->input manager进行管理
只有horizontal和Vertical是轴(wasd控制方向),其他基本都是模拟按键,入fire
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 dir = new Vector3(horizontal, 0, vertical);
Debug.Log(horizontal + " " + vertical);
if (Input.GetButtonDown("Jump"))
{
Debug.Log("按下了跳跃");
}
}
void Start()
{
//开启多点触摸
Input.multiTouchEnabled = true;
}
void Update()
{
//单点触摸
if(Input.touchCount == 1)
{
Touch touch = Input.touches[0];
Debug.Log(touch.position);
switch (touch.phase)
{
case TouchPhase.Began:
break;
case TouchPhase.Moved:
break;
case TouchPhase.Stationary:
break;
case TouchPhase.Ended:
break;
case TouchPhase.Canceled:
break;
}
//多点触摸
if(Input.touchCount==2)
{
Touch touch1 = Input.touches[0];
Touch touch2 = Input.touches[1];
}
}
音乐和音效
如何播放 1.摄像机的audio listener,只需要一个摄像机有该组件就行
2.网格的audio sourse组件
//依旧是拖动绑定资源
public AudioClip music;
public AudioClip soundEffect;
private AudioSource player;
void Start()
{
player = GetComponent<AudioSource>();
player.clip = music;
player.loop = false;
player.volume = 1.0f;
player.Play();
}
void Update()
{
//音乐循环播放的暂停与继续
if(Input.GetKeyDown(KeyCode.Space))
{
if(player.isPlaying) player.Pause();
else player.UnPause();
//player.Play();
//player.Stop();用这两个控制会重新播放
}
//音效播放
if(Input.GetMouseButtonDown(0))
{
player.PlayOneShot(soundEffect);
}
}
对于重复性声音,比如脚步,枪声,需要考虑重叠
//脚步
if((horizontal != 0 || vertical != 0)&&isGround ==true)
if(audio.isPlaying == false)
audio.Play();
else audio.Stop();
先创建一个长方形网格,添加vidio player组件,并添加一个新纹理,在组件中绑定网格和纹理,视频会播放到纹理然后再通过纹理绑定网格,进行显示,也可以将纹理绑定到UI->raw image中播放
using UnityEngine.Video;//视频需要使用
//其他就跟音频一样了
private VideoPlayer player;
void Start()
{
player = GetComponent<VideoPlayer>();
}
void Update()
{
}
private CharacterController player;
void Start()
{
player = GetComponent<CharacterController>();
}
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 dir = new Vector3(horizontal, 0, vertical);
player.SimpleMove(dir*2);//自动以秒为单位,translate以帧为单位
//transform.Translate(dir * 2 * Time.deltaTime);也可乘上每帧时间,变成秒为单位移动,但Move不能进行物理交互
}
刚体,如果取消该组件就可以实现穿墙?
碰撞的对象是组件mesh collider,刚体实现碰撞
is Kinematic 勾上后就不会移动,或者在constraints三个维全部冻结(人物把旋转冻结,npc把行走冻结)
collision detection选用离散的(discrete)会导致高速物体穿墙,但节省性能
产生条件,两物体都有collider组件,其中一个有rigidbody组件
对于飞弹击中后的爆炸效果,在效果后的销毁应该在各自独立的网格里
飞弹
public GameObject Explosion;
private void OnCollisionEnter(Collision collision)//Stay,Exit;碰撞后停留,碰撞结束
{
Instantiate(Explosion,transform.position,Quaternion.identity);
Destroy(gameObject);
Debug.Log(collision.gameObject.name);//被撞者的信息
}
爆炸
void Update()
{
timer += Time.deltaTime;
if(timer > 1) { Destroy(gameObject); }
}
跟碰撞差不多,把其中一个的collider勾选istrigger(就不会产生碰撞了),所以一般选用无网格的当触发器
通过机关开门
private void OnTriggerEnter(Collider other)
{
GameObject door = GameObject.Find("door");
if(door != null)
{
door.SetActive(false);
}
}
Hinge Joint铰链关节,即将刚体的一条边固定,可实现门绕轴开关,宝箱打开
轴的位置,中心为0,长宽是按比例计算,控制anchor和axis,即点向式
入anchor (-0.5,0,0) axis(0,1,0)绕着左边垂线为轴
use motor实现自动旋转
Spring Joint弹簧,添加connected body
Fixed Joint,固定连接,可设置最大断开链接的力
create physic material
可设置摩擦系数,弹力,以及多材质组合时的计算方法(平均,最小,最大)
effect->particle system
prewarm 提前加载到稳定状态,不会重头开始
simulation space 已生成的粒子 local整体移动,world,不跟随移动
emissions 调节发射数量,发射方使(突发,渐变)
利用animation组件,先添加组件,然后到window->animation->animation 创建动画,因为动画是属性的改变,所以之后需要添加属性
然后对关键帧进行属性数值修改
或者录制关键帧,将动画添加到animations中,可通过代码控制播放什么动画
void Update()
{
if(Input.GetMouseButtonDown(0))
{
GetComponent<Animation>().Play("right move");
}
}
利用animator控制器组件,更方便管理,将动画状态进行导图可视化
添加组建后,创建animator controler,然后绑定物体后再跟旧版一样创建动画,一定要先绑定
然后双击animator controler
通过脚本控制
private Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
animator.Play("right");
}
}
通过控制器控制,添加过渡动画
添加动画触发条件 : 动作切换时使用
parameter -> trigger 跳跃等用trigger,走,跑等用bool
过渡线->condition
去掉过渡线的有退出时间,以便于按键后立刻执行其他动作,去掉过渡时间使动作更跟手,加上使动作更自然,不那么生硬
运动脚本
public class test : MonoBehaviour
{
private Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector3 dir = new Vector3(horizontal, 0, vertical);
if (Input.GetKeyDown(KeyCode.F)) {
animator.SetTrigger("wave");
}
if (Input.GetKeyDown(KeyCode.Space)){
animator.SetTrigger("jump");
}
if(dir!=Vector3.zero) {
transform.rotation = Quaternion.LookRotation(dir);
animator.SetBool("IsWalk", true);
transform.Translate(Vector3.forward*2*Time.deltaTime);
}else{
animator.SetBool("IsWalk", false);
}
}
}
动作一般在fbx(filebox)文件中,我们常用其中的animation属性页,
Debug.Log(animator.GetFloat("Test"));
void leftfoot()
{
Debug.Log("左脚");
}
void rightfoot()
{
Debug.Log("右脚");
}
形成过渡动作
blend tree,创建后会默认使用参数列表中的第一个float参数,范围0-1
将不同动作混合在一起,如将走跟跑混合,做成起跑动作
将不同动作组合,多动作通过权重直接相加,通过骨骼遮罩(avatar mask)(绿色代表该层控制的骨骼部分)(avatar 阿凡达 化身)
能够将身体的不同部分分别播放动画,如举着枪行走,拿着刀行走
先勾选图层的IK(Inverse Kinematics)
身体控制本来应该是从中间到四周,在需要锁定一个点(如头朝向某个点)时,应该是先转头,然后再计算其他身体部位的位置
可用于npc面对玩家时改变朝向
public Transform target;//绑定目标
private void OnAnimatorIK(int layerIndex)
{
animator.SetLookAtWeight(1);//头部IK
animator.SetLookAtPosition(target.position);
//身体其他部分IK
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
animator.SetIKPosition(AvatarIKGoal.RightHand, target.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, target.rotation);
}
登录界面,开始菜单,角色信息,背包,商城…
UI->canvas
render mode : overlay在最上层,打开背包 world 实现3d UI
UI Scale Mode : 设成scale with screem size,用以固定分辨率,如1920*1080
为了适配不同分辨率,能够自动调节,于是出现了锚点和轴心点,例如控制一个image的坐标
锚点是一个x形准星,可拉开成矩形,其为长宽为一个0-1的范围,即锚点咱canvas的比例
image坐标是相对于锚点(anchor)四条边的坐标,从而在屏幕不同时,图片也进行拉伸,维持到四条边的位置,
人物头像之类,将所有锚点拉到左上角
轴心点,image中间的小圆圈,将坐标轴设为轴心可拉动轴心,用于代表图片进行各种计算,其他部分进行跟随,如修改轴心点后,绕某点旋转
预设中黄点代表锚点,蓝点代表轴心点
图片添加mask组件,会让子图片只显示在父图范围内的部分
旧版
rich text Text Text
利用标签进行格式
勾选best fit进行自适应调整大小
新版
依赖于text mesh pro框架(TMP),创建后根据提示导入框架
设置自动字号,类似于best fit
控件content size filter,进行垂直适应,选择合适大小,会自动将文本显示完
按钮,输入框,选项和下拉框,新旧版唯一不同就是基于的文本版本的不同
可设置是否可交互(在触发事件后解锁)
按钮
图片有不同类型,图片不可用可能是类型错误
写一个public函数,用于添加响应
public void ButtonClick()
{
Debug.Log("click");
}
输入框
占位文本 text
content type 设为password后会用*
遮盖密码,设置光标
添加事件与上面相同,获取文本内容通过下面代码
public TMP_InputField inputField;
public void Inputting()
{
Debug.Log(inputField.text);
}
下拉列表
dropdown,进行选择,分辨率,语言选择,利用脚本添加新选项
void Start()
{
Dropdown dropdown = GetComponent<Dropdown>();
List<Dropdown.OptionData>options = dropdown.options;
options.Add(new Dropdown.OptionData("俄罗斯"));
dropdown.options = options;
}
选择框
toggle,控制多个toggle只能且必须选择一个,如性别选择
在第一个toggle中添加逐渐toggle group,并将其拖到toggle组件的group里
然后将第一个toggle拖到后续控件的toggle组件的group里,后续更多一样,实现多选一
滑动条
将Handle Slide Area(活动条当前进度的圆点指示)删掉就可以做血条了
滚动条
scroll view 用于显示服务器列表,传送点,世界地图
将多个控件进行分类,如人物状态由多个控件显示,在改变时更方便和清晰
File->BuildSetting
将需要运行的场景Scene拖拽到ScenesInBuild列表里
点击PlayerSettings,打开PlayerSettings配置CompanyName和ProductName
返回BuildSettings窗口,点击Build,选择路径创建文件夹,开始打包.exe文件及其他dll和资源文件。
打包完毕后可以看到.exe程序及其他dll文件和文件夹,.exe文件必须在此目录里执行,不可以随意更改位置,可以将整个文件夹进行转移。
Managed目录:存放unity引擎、第三方和游戏逻辑托管dll 如:Assembly-CSharp.dll、Assembly-CSharp-firstpass.dll
Mono目录:Mono虚拟机相关的文件
Plugins目录:第三方Native dll库
Resources目录 :资源文件
unity default resources:unity引擎自带缺省资源
unity_builtin_extra:内置shader(如:Standard.shader)、缺省material等 里面99%是shader文件
StreamingAssets\Windows目录:项目工程中的StreamingAssets文件夹的内容不会压缩原封不动的拷贝到该目录下
globalgamemanagers:所有gameobject、shader、monobehaviour脚本、mesh、material、transform、audio,texture、spirite等的总览以及setting信息
globalgamemanagers.assets:playersetting中用到的资源
resources.assets:项目工程中的Resources文件夹的内容会打包到该文件中,但如果有资源被某场景使用,该资源则会存储于该场景对应的sharedassets<0~n>.assets中
level0、level1、level2、level44、level165:地图关卡文件,有几张地图就对应几个level文件
sharedassets0.assets、sharedassets1.assets、sharedassets2.assets、sharedassets44.assets、sharedassets165.assets:各关卡引用的texture,material,shader,material,animator,monobehaviour脚本等外部资源
ECS,即 Entity-Component-System(实体-组件-系统) 的缩写,其模式遵循组合优于继承原则,游戏内的每一个基本单元都是一个实体,每个实体又由一个或多个组件构成,每个组件仅仅包含代表其特性的数据(即在组件中没有任何方法),例如:移动相关的组件MoveComponent
包含速度、位置、朝向等属性,一旦一个实体拥有了MoveComponent
组件便可以认为它拥有了移动的能力,系统便是来处理拥有一个或多个相同组件的实体集合的工具,其只拥有行为(即在系统中没有任何数据),在这个例子中,处理移动的系统仅仅关心拥有移动能力的实体,它会遍历所有拥有MoveComponent
组件的实体,并根据相关的数据(速度、位置、朝向等),更新实体的位置。
实体与组件是一个一对多的关系,实体拥有怎样的能力,完全是取决于其拥有哪些组件,通过动态添加或删除组件,可以在(游戏)运行时改变实体的行为。
程序流很清晰,所有操作都在代码上进行,不需要过多关注于unity细节上,静态可读性好
UI只做获取输入输出的作用,不处理逻辑
分层 app>facades>manager>models
一般不采用拖拽绑定,在共享工程时容易出错,通过代码绑定,使用AA包进行绑定
AA包,即插件Addressables,需要先安装
添加后不能使用可以用assembly definition,将引用的那个dll(unity.Addressables),包含到我们的代码assembly-Csharp.dll
window->asset management->addressables->groups,然后用组来统一管理资源
-Unity(游戏开发引擎)
-Visual Studio(编程)
-Git(源代码管理)
-Trello(项目管理)
-Aseprite(像素动画制作)
-Pinterest(游戏素材参考)
-XMind(思维导图)
-Todoist(代办事项管理)
-Evernote(知识管理)
-Twitter(游戏开发社交)
-Discord(游戏开发交流) -blender开源的建模免费软件,十分方便迅速
-mixmo免费的动画库,虽然动画不多,u3d的动画库,虚幻还要骨骼重定向