序言
本文记录Unity学习,仅供个人参考
游戏引擎
游戏引擎是指一些一边写好的可编辑电脑有戏系统或者一些交互式实时图像应用程序的核心组件。这些系统为游戏设计者提供各种编写游戏所需的各种工具,其目的在于让游戏设计者能容易和快速的做出游戏程序而不用从零开始。
工程文件夹
Assets:工程资源文件夹(美术资源、脚本等)
Library:库文件夹(unity自动生成管理)
Logs:日志文件夹,记录特殊信息(unity自动生成管理)
obj:编译产生中间文件(unity自动生成管理)
Packages:包配置信息(unity自动生成管理)
ProjectSettings:工程设置信息(unity自动生成管理)
引擎窗口
Scene和Hierarchy
场景窗口和层级窗口是息息相关的,层级窗口中看到的内容就是场景窗口中的显示对象
Hierarchy层级窗口
我们可以在这个窗口中创建或者拖入各种游戏对象,比如:模型、光源、图片、UI等内容,层级窗口中显示的就是一个场景中的所有对象。窗口中右键或者点击左上角+号键可以创建对象和操作对象
Scene场景窗口
我们可以在Scene窗口中查看并设置所有游戏对象
Game游戏窗口
游戏画面窗口,玩家能看到的画面内容。
Project工程窗口
工程资源窗口,所有的工程资源都会在该窗口中显示,显示的内容为Assets文件夹中的内容
默认文件夹:Scenes。里面有一个默认空场景
Packages:官方拓展包
Inspector检查窗口
查看场景中游戏对象关联的c#脚本信息
Console控制台窗口
由于查看调试信息的窗口,报错、警告、测试打印都可以显示在其中
默认未开启,可以在Window->General中开启,或者使用快捷键Ctrl+Shift+C
清空控制台、相同内容折叠显示、运行时清空、构建时清空、报错时暂停运行、是否显示错误信息、是否显示警告信息、是否显示打印信息
资源类型
图片格式:jpg、png、tga
模型格式:fbx、max、maya
音效:wav、mp3、ogg
文本:txt、json、bytes
视频:mp4
反射机制和游戏场景
unity反射机制
unity开发的本质就是在unity引擎的基础上利用反射和引擎提供的各种功能进行的拓展开发。
场景中对象的本质:都是依附于GameObject类的对象
Transform:GameObject在场景中必须要有表示自己所在位置的信息,tranform就是一个必不可少的剧本,相当于就是用一个transform对象和GameObject对象进行关联,用于设置和得到对象在世界中的位置角度缩放等信息
反射机制的体现:除了Trasform外,我们可以为GameObject关联各种脚本(c#脚本),让他按照我们代码逻辑中的命令来处理事情,而这个过程,就是利用反射new一个新的剧本对象和GameObject对象进行关联,让其执行命令
unity已经帮助我们实现了对象查找和关联,1.修改Inspector面板中Transform的内容,利用反射:已知对象,类名,变量名,通过反射为该对象设置变量值。2.新建一个脚本后,添加给一个指定的GameObject对象,利用反射:已知类名,可以获取所有公共成员,所以可以在Inspector面板上创建个公共字段信息
游戏场景的本质
游戏场景文件
后缀为.unity,它的本质就是一个配置文件,Unity有一套自己识别处理他的机制,但是本质就是把场景对象相关信息读取出来,通过反射来创建各个对象关联各个脚本对象
预设体和资源包导入导出
预设体:预先设置好的物体,用来保存单个物体的信息,拖入场景中即可成为预设体,文件后缀名是.prefab,
资源包后缀:.unitypackage
脚本基础
脚本规则
创建规则:1.不在vs中创建脚本。2.可以放在Assets文件夹下的任何位置。3.雷鸣和文件名必须一致,不然不能挂载(反射机制创建对象会通过文件名去找Type,2022之后已经不用了)。4.不是用中文名。5.没有特殊需求不用管命名空间。6.创建的脚本默认继承MonoBehavior。
MonoBehaviour基类
1.创建的脚本默认都继承MonoBehaviour继承了他才能够挂载载GameObject上。2.继承了MonoBehaviour的脚本不能new 只能挂。3.继承了MonoBehaviour的脚本不要去写构造函数,因为我们不回去new它,没有任何意义。4.继承了MonoBehaviour的脚本可以在一个对象上挂多个(如果没有加DisallowMutipleComponen特性)5.继承MonoBehaviour的类也可以再次被继承,遵循面向对象继承多态的规则
不继承MonoBehavior的类
1.不继承Mono的类,不能挂载在GameObject上。2.不继承Mono的类想怎么写怎么写如果要使用需要自己new。3.不继承Mono的类一般是单例模式的类(用于管理模块)或者数据结构类(用于存储数据)。4.不继承Mono的类 不用保留默认出现的几个函数。
执行的先后顺序
默认脚本内容
Editor\Data\Resources\ScriptTemplates 可以修改默认脚本内容
生命周期函数
帧的概念
游戏的本质就是一个死循环,每一次循环处理游戏逻辑就会更新一次画面之所以能看到画面在动是因为切换画面的速度到达一定时人眼就认为画面是流畅的,一帧就是一次循环。
fps:每秒钟的帧数。
人眼舒适放松时可视帧数是每秒24祯。游戏卡顿的原因:跑1祯游戏逻辑的运算量过大,或者cpu步豪,不能再一祯时间内处理完所有游戏逻辑。
unity的底层已经帮助我们做好了死循环。我们需要学习unity的生命周期函数,利用它做好的规则来执行我们的游戏逻辑就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
private void Awake() { Debug.Log("123"); Debug.LogError("出错了"); Debug.LogWarning("警告"); print("1234567"); }
private void OnEnable() { print("OnEnable"); }
void Start() { print("Start"); }
void FixedUpdate() { }
void Update() { print("Update"); }
void LateUpdate() { print("LateUpdate"); }
void OnDisable() { print("OnDisable"); }
void OnDestroy() { print("OnDestroy"); }
|
Inspector窗口可编辑的变量
Inspector中显示的可编辑内容就是脚本的成员变量。私有和保护无法显示编辑
让私有和保护的也可以被显示,加上强制序列化字段特性[SerializeField],所谓序列化就是把一个对象保存到一个文件或数据库中去。公共的可以显示编辑。公共的也不让其编辑,加上[HideInspector]特性可以不显示。大部分类型都能显示编辑(如数组、枚举等等)但是字典和自定义类型不能被unity显示。
自定义类型如何被访问:加入特性[System.Setializable]就可以访问,但是字典是无论如何不能被访问的。
辅助特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
|
Mono中的重要内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
print(this.gameObject.name);
print(this.transform.position); print(this.transform.eulerAngles); print(this.transform.lossyScale);
this.gameObject.transform;
this.enabled=false; public Lesson3 otherLesson3;
print(otherLesson3.gameObject.name); print(otherLesson.transform.position);
脚本名 t=this.GetComponent("脚本名")as 脚本名; print(t);
t=this.GetComponent(typeof(Lesson3_Test))as Lesson3_Test;
t=this.GetComponent<Lesson_Test>();
类名[] array=this.GetComponents<类名>(); print(array.Length); List<类名>list=new list<类名>(); this.GetComponents<类名>(list); print(list);
this.GetComponentInChildren<类名>(); print(t);
Test2[] t2=this.GetComponentsInChildren<类名>(true); print(t2.Length);
List<Test2>list2=new List<Test2>(); this.GetComponentInChildren<Test2>(true,list2);
this.GetComponentInParent<Test2>();
t2=GetComponentsInParents<Test2>(); print(t2.Length);
Test t3;
if(this.TryGetComponent<Test2>(out t3);) { }
|
最小单位GameObject
GameObject中的成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| print(this.gameObject.name); this.gameObject.name="田鼠浩二"; print(this.gameObject.name);
print(this.gameObjcet.activeSelf);
print(this.gameObject.isStatic);
print(this.gameObject.layer);
print(this.gameObject.tag);
print(this.gameObject.transform.position);
|
GameObject中的静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
public GameObject obj;
GameObject obj=GameObject.CreatePrimitive(PrimitiveType.Cube); obj.name="田鼠浩二";
GameObject obj2=GameObject.Find("田鼠浩二"); if(obj2!=null) { print(obj2.name); }
GameObject obj3=GameObject.FindWithTag("Player"); print(obj3.name); GameObject,obj4=GameObject=GameObject.FindObjectWithTag("Player");
GameObject []objs=GameObject.FindGameObjectsWithTag("Player"); print(objs.Length);
Test2 t=GameObject.FindObjectOfType<Test2>(); print(t.gameObject.name);
GameObject obj5=GameObject.Instantiate(obj);
GameObject.Destroy(obj5,5); GameObject.Destroy(this);
GameObject.DestroyImmediate(myobj);
GameObject.DontDestroyOnload(this.gameObject);
|
GameObject中的成员方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
GameObject obj6=new GameObject(); GameObject obj7=new GameObject("田鼠浩二创建的空物体"); GameObject obj8=new GameObject("顺便加脚本的空物体",typeof(Test),typeof(脚本名));
Test tes1=obj6.AddComponent<Test>();
if(this.gameObject.CompareTag("Player")) { print("标签是Player"); }
obj6.SetActive(false);
this.gameObject.SendMessage("TestFun");
void TestFun() { print("TestFun"); }
|
事件相关Time(一般写在Update函数中)
Time相关内容主要用于游戏中位移 计时 时间暂停等
时间缩放比例
1 2 3 4 5 6
| Time.timeScale=0;
Time.timeScale=1;
Time.timeScale=2;
|
帧间隔时间
1 2 3 4 5 6 7 8 9 10
|
print(Time.deltaTime);
print(Time.UnscaleDeltaTime);
|
游戏开始到现在的时间
1 2 3 4 5
|
print("游戏开始到现在的时间"+Time.time);
print(Time.UnScaleTime);
|
物理帧间隔时间 FixedUpdate
1 2 3 4
| print(Time.fixedDeltaTime);
print(Time.fixedUnScaleDeltaTime);
|
帧数
就是从开始到现在游戏跑了多少帧,也就是主循环执行了多少次
游戏对象(GameObject)位移、旋转、缩放、父子关系、坐标转换等相关操作都由它处理,它是unity提供的极其重要的类
位置与位移
Vector3
Vector3主要是用来表示三位坐标系中的一个点或者一个向量,是unity中提供的结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Vector3 v=new Vector3(); v.x=10; v.y=10; v.z=10;
Vector3 v2=new Vector3(10,10);
Vector3 v3=new Vector3(10,10,10);
print(Vector3.zero); print(Vector3.right); print(Vector3.left); print(Vector3.forward); print(Vector3.back); print(Vector3.Up); print(Vector3.down);
Vector3.Distance(v1,v2);
|
位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
this.transform.position;
print(this.transform.localPosition);
this.transfotm.position=new Vector3(10,10,10); this.transform.localPosition=Vector3.up*10;
print(this.transform.forward);
print(this.transform.left);
|
位移
路程=方向速度时间
计算位移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
this.transform.position=this.transform.position+this.transform.forward*1*Time.deltaTime;
this.transform.Translate(Vector3.forward*1*Time.deltaTime); this.transform.Translate(this.forward*1*Time.deltaTime,Space.World); this.transform.Translate(Vector3.forward*1*Time.deltaTime,Space.World);
this.transform.Translate(this.transform.forward*1*Time.deltaTime,Space.Self);
this.transform.Translate(Vector3.transform.forward*1*Time.deltaTime,Space.Self);
|
角度与旋转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
this.transform.eulerAngles
this.transform.localEulerAngles;
this.transform.Rotate(new Vector3(0,10,0)*Time.deltaTime);
this.transform.RatateAround(Vector3.zero,new Vector(0,10,0),1*Time.deltaTime);
|
缩放和看向
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
print(this.transform.lossyScale);
print(this.transform.localScale);
this.transform.localScale+=Vector3.one*Time.deltaTime;
this,transform.LookAt(Vector3.zero);
this.transform.LookAt(对象变量名);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
print(this.transform.parent.name);
this.transform.parent=null;
this.transform.parent=otherparent.transform;
this,transform.SetParent(null); this,transform.SetParent(otherparent.transform);
this.transform.SetParent(GameObject.Find("Object").transform,true);
this,tranform.DetachChildren();
this.transform.Find("");
print(this.transform.childCount);
this.transform.GetChild(0);
for(int i=0;i<this.transform.childCount;i++) { print(this.transform.GetChild(i).name); }
this.transform.isChildOf(父对象的transform);
print(son.GetSiblingIndex());
son.SetAsFirstSibling();
son.SetAsLastSibling();
son.SetSiblingIndex(编号);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
this.transform.InverseTransformPoint(Vector3.forward)
this.transform.InverseTransformDirection(Vector3.forward)
this.transform.InverseTransformVector(Vector3.forward);
this.transform.TransformPoint(Vector3.forward);
this.transform.TransformDirection(Vector3.forward)
this.transform.TransformVector(Vector3.forward);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
Input.mousePosition;
if(Input.GetMouseButtonDown(0)) { }
if(Input.GetMouseButtonUp(0)) { }
if(Input.GetMouseButton(0)) { }
print(Input.mouseScrollDelta);
Input.GetKeyDown(KeyCode.W)
Input.GetKeyDown("q");
Input.GetKeyUp(KeyCode.W);
Input.GetKey(KeyCode.W);
Input.GetAxis("Horizontal");
Input.GetAxis("Vertical");
Input.GetAxis("Mouse X");
Input.GetAxis("Mouse Y");
if(Input.anyKey) { print("有一个键按下"); }
Input.anyKeyDown;
print(Input.inputString);
string[] strs=Input.GetJoystickNames();
if(Input.GetButtonDown("Jump")) { }
if(Input.GetButtonUp("Jump")) { }
if(Input.GetButton("Jump")) { }
if(Input.touchCount>0) { Touch t=Input.touches[0]; }
|
screen相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
Resolution r=Screen.CurrentResolution print("当前屏幕分辨率的宽"+r.width+"高为"+r.height);
print(Screen.width); print(Screen.height);
Screen.sleepTimeout=SlepTimeout.NeverSleep;
Screen.fullScreen=true;
Screen.SetResolution(1920,1000,false);
|
camera相关
Camera可编辑参数
Clear Flags 如何清除背景 1.skybox 天空盒(3d游戏) 2.Solid Color 颜色填充(2d游戏) 3.Depth only 只画该层 背景透明(多个摄像机叠加使用) 4.Don’t Clear 不移除 覆盖渲染(基本上不会用,因为不会擦除上一帧的内容)
Cullling Mask 选择性渲染部分层级 可以指定只渲染对应层级的对象
Projection 1.Persective 透视模式(一般3d游戏) 2. orthographic 正交摄像机(一般2d游戏)
Clipping Planes 裁剪平面距离
Depth 渲染顺序上的深度(层级高的后被渲染)
Target Texture 渲染纹理 1.可以把摄像机画面渲染到一张图上(用于制作小地图)2.在project右键创建Render Texture
Occlusion Culling 是否已启用剔除遮挡 (被挡住的物体不会被渲染)
其他的之后找补
camera代码相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
Camera.main
Camera.allCamerasCount;
Camera[] allCamera=Camera.allCameras;
Camera.onPreCull +=(c)=> { };
Camera.onPreRender+=(c)=> { };
Camera.onPostRender+=(c)=> {
};
Camera.main.depth=10;
Vector3 v=Camera.main.WorldToScreenPoint(this.transform.position); print(v);
print(Input.mousePosition);
Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
核心系统
光源系统基础
省略
物理系统之碰撞检测
刚体
碰撞产生的必要条件:两个物体都有碰撞器,至少一个物体有刚体。
Addcompont Rigidbody。
碰撞器(collider)
种类:1.盒状 2.球状 3.胶囊 4.网格 5.轮胎 6.地形
共同参数:1. Is Trigger 是否是触发器。如果启用此属性,则该碰撞体将用于触发事件,并被物理引擎忽略,主要用于进行没有物理效果的碰撞检测 2.Material 物理材质,可以确定碰撞体和其它对象碰撞时的交互方式 3.Center 碰撞体在局部空间的中心点位置
常用碰撞器:盒状碰撞器(BoxCollider) :Size 碰撞体在XYZ方向上的大小。球状碰撞器(Spehere Collider):Radius 球形碰撞体的半径大小。胶囊碰撞器(Capsule Collider):Radius 胶囊体的半径。 Height 胶囊体的高度。Direction 胶囊体在对象局部空间中的轴向
异形物体使用多种碰撞器的组合:刚体对象的子对象碰撞器信息参与碰撞检测
不常用碰撞器:1.网格碰撞器(Mesh Collider) 2.环状碰撞器(Wheel collider) 3.地形碰撞器(Terrain Collider)
物理材质
创建物理材质
参数:1.Dunamic Friction:已在移动时使用的摩擦力,通常为0到1的值,类似于动摩擦系数。2.Static Friction:当对象静止时在表面上时使用的摩擦力。类似于静摩擦系数。3.Bounciness 表面的弹性,值为0不会反弹,1不消耗任何能量,相当于完全弹性碰撞,类似于弹性系数。4.Friction Combine:两个对象的摩擦力的组合方式。5.unce Combince:两个碰撞对象的弹性的组合方式。其模式于Friction Combine模式相同
碰撞检测函数
碰撞和触发响应函数属于特殊的生命周期函数,也是通过反射调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
private void OncollisionEnter(Collision collision) { print(this.name+"被"+collision.gameObject.name+"撞到了"); }
private void OncollisionExit(Collision collision); { }
private void OnCollisionStay(Collision collision) { }
private void OnTriggerEnter(Collider other) { }
private void OnTriggerExit(Collider other) { }
private void OnTriggerStay(Collider other) { }
|
刚体加力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
Rigidbody rigidBody; rigidBody=this.GetComponent<Rigidbody>();
rigidBody.AddForce(Vector3.forward*10);
rigidBody.AddRelativeForce(Vector3.forward*10);
rigidBody.AddTorque(Vector3.up*10);
rigidBody.AddRelativeTorque(Vector3.up*10);
rigidBody.velocity=Vector3.forward*5;
rigidBody.AddExplosionForce(10,Vector3.zero,10);
rigidBody.AddForce(Vector3.forward*10,ForceMode.Acceleration);
rigidBody.IsSleeping
|
音效系统
主要是面板操作
常用的音效格式 wav mp3 ogg aiff
音频文件属性设置
音频源和音频监听脚本
Component: Audio source(源脚本 出声用的)、Audio Listener(监听脚本 收声用的 关了听不到声音)
代码控制音频源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| AudioSource audioSource;
audioSource=this.GetComponent<AudioSource>(); if(Input.GetKeyDown(KeyCode.P)) { audioSource.Play(); }
if(Input.GetKeyDown(KeyCode.S)) { audioSource.Stop(); } if(Input.GetKeyDown(KeyCode.Space)) { audioSource.Pause(); } if(Input.GetKeyDown(KeyCode.X)) { audioSource.UnPause(); } if(Input.GetKeyDown(KeyCode.D)) { audioSource.PlayDelayed(5); }
if(audioSource.isPlaying) { print("播放中"); }
else { print("播放结束"); }
|
麦克风输入相关
1 2 3 4 5 6
| string[] strs= Microphone.devices; for(int i=0;i<strs,Length;i++) { print(strs[i]); }
|
剩下的之后找补
游戏场景切换和退出游戏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
if(Input.GetKeyDown(KeyCode.Space)) { SceneManager.LoadScene("场景名"); }
if(Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); }
|
鼠标隐藏锁定相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Cursor.visible=false;
Cursor.LockState=CursorLockMode.Locked;
Cursor.SetCursor(tex,Vector2.zero,CursorMode.Auto);
|
随机数和unity自带委托
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
int randomNum=Random.Range(0,100);
float randomNumf==Random.Range(1.1f,99.9f);
System.Action ac=()=> { print(""); };
System.Action<int,float>ac2=(i,f)=> { };
System.Func<int>fun1=()=> { };
UnityAction uac=()=> { };
UnityAction<String> uac1 = (s)=> { };
|
模型资源的导入
模型由什么构成:1.骨骼(骨骼)非必须 有动作的模型才需要 2.肉(网格面片)必需 决定了模型的轮廓 3.皮(贴图)必须 决定了模型的颜色效果
官方推荐使用FBX格式(.fbx)的模型文件 其他格式虽然支持 但是不推荐(dae 3ds dxf obj)
指导美术人员模型的导入,就是看Unity官方说明。导出的模型面朝向向朝模型坐标系的z轴
缩放大小单位注意
获取模型 AssetStore(推荐)淘宝(推荐)一些第三方的资源下载网站
获取资源一般有两种形式1.unity资源包(直接导入)2.原生的模型贴图资源(自己处理)