Unity学习笔记--入门

Unity引擎学习


入门:

Unity中的坐标系:

左手坐标系(z轴正方向向里面)

x轴:平行屏幕向右正方向

y轴:平行屏幕向上正方向

Z轴:垂直屏幕向内为正方向

【补】openGL是右手坐标系 DirectX是左手坐标系

窗口:

窗口布局Layout:

  1. Scene

    这里我们可以看见所有的场景内容。

    窗口上的工具栏:

    有关场景窗口的操作:

  2. Hierarchy(层级窗口)

    我们可以在此窗口创建或者拖入各种游戏对象,例如模型、光源,ui等。

    在开发项目中,可以对窗口物体进行整理归类。

    常用的快捷键:F2 改名字 Ctrl C V D 赋值 粘贴 克隆 Delete删除

  3. Game

    Game窗口就是我们游戏运行时进行交互的窗口,显示的画面是scene场景中摄像机的对象。

  4. Project

    该窗口是存放游戏开发资源的层级窗口。

    可以导入的文件类型以及格式:

    图片:jpg,png,tga

    模型格式:fbx,max,maya

    音效:wav mp3 ogg

    文本:txt, json,bytes

    视频:mp4

  5. Inspector

    该窗口显示出具体的Hierarchy中具体的物体信息。

  6. Console

    控制台窗口,用于调试脚本,打印信息。

Inspector检查窗口:设置游戏具体的对象信息

Console控制台窗口:用于显示调试信息,报错,警告、打印信息等。

Scene场景信息:显示、操作游戏对象的地方(场景编辑器)

Game游戏窗口:游戏运行时,供与玩家交互的窗口

Hierarchy层级窗口:游戏物体的目录

Project工程窗口:游戏资源的文件夹

注:还有一些其它的窗口,可以在使用时候具体调用,Window菜单选择可以调用出对应窗口。

工具栏和父子关系

反射机制和游戏场景

程序在运行时候,可以查看其它程序集或者自身的元数据,而一个运行的程序查看本身或者其它程序的元数据的行为就叫反射。

例如我们在程序运行时候获取某个程序集(资源包)中的某个脚本(class),实例化它,调用其中的方法。

万物之父:

在unity中所有的物体对象的本质是一个GameObject.

而在场景中出现的一个GameObject的物体,必定有一个transform表示物体的位置信息,而我们是通过给GameObject中添加组件脚本来对其进行控制。

场景的本质:

场景的本质是一个配置文件,里面存储对各种对象的信息。

而场景在运行时候就是检查对象,加载对象身上的脚本,控制对象,这就是unity场景反射。

预设体和资源包的导入导出

预设体(Perfab)

预设体是一个保存单个对象的信息的单元。例如可以把自己配置好的(挂载着相应的组件、脚本等信息)一个物体拖到文件夹中自动生成该物体的预设体。预设体是一种"模板"。

预设体的修改:

  1. 在场景中显示的预设体,直接修改,修改之后在inspector窗口中点击点击应用保存即可应用到预设体中。

  2. 直接打开预设体进行修改编辑。

如果指向更改物体对象,不想影响预设体,则在修改之前断开和预制体的联系

资源的导入导出:

在project窗口中,右击菜单选择,导入/导出菜单

Unity脚本

脚本知识

1.设置脚本IDE(Edit-->Preference打开窗口)

2.创建脚本

  • 类名要和脚本名字一致(否则unity无法通过反射访问机制访问)
  • 建议不使用中文命名
  • 若想挂载到GameObjet上,类必须继承MonoBehavior
  • 建议使用命名空间管理

3.MonoBehavior

  • 继承MonoBehavior之后才可以挂载物体上(unity获取到类名通过反射找到该类型)

  • 继承MonoBehavior之后的类 不可以new(自然不需要些构造函数)

  • 继承MonoBehavior之后的脚本可以在一个物体上挂载多个(如果不想让物体继续挂载脚本之后,

    则在脚本内容中添加DIsallowMultipleComponent特性)

  • 符合继承规则,子类继承了继承着MonoBehavior类也可以挂载物体上

不继承Mono

则按照普通的类来进行编程。不能挂载到物体上,要自己new创建,有自己的构造函数,例如存储信息的类。

4.脚本的执行顺序(Edit-->ProjectSetting查看)

5.默认的脚本内容(可以通过unity编辑器内容中更改,一般不做修改)

生命周期函数

就是一副游戏画面,每秒24帧以上,我们可以认为画面流畅。unity内部已经按照一种"死循环"来不断地调用相关地函数(生命周期函数),这些生命周期函数就是脚本呢对象依附的GameObject对象从出生到消亡整个生命周期中会通过反射机制自动调用的一些特殊函数。

Awake

当对象(脚本)被创建时候会调用,一个对象只会调用一次

OnEnable

对象每次激活(setActive=true)时候执行

Start

只调用一次,在第一次帧更新之前调用。

区别:二者执行的时机不同,awake执行相当于构造函数,脚本一挂载就执行

FixedUpdate

物理效果逻辑写在此处,固定时间执行,(物理更新)

可以通过Edit-->ProjectSetting -->Time 来查看和修改

Update

帧更新函数,每一帧执行

LateUpdate

处理摄像机相关更新

等场景渲染完毕之后,再进行摄像机跟进,避免出现"穿帮"

OnDisable

与OnEnable对应,每次失活时候执行(setActive=false)

(当然,OnDestroy调用之前,此函数必定调用,脚本的失活~睡眠,,)

OnDestroy(脚本的永久失活~死亡)

当依附的GameObjet对象被销毁时候调用

Tips:

一个脚本可以挂载多个对象上,挂载之后,每个对象上都有自己归属的一个控制脚本。相当于理解类和对象的关系。

思考?如果一个GameObjet对象挂载着Test.cs脚本,但是GameObjet对象初始状态是未激活状态,

那么生命周期函数执行吗?哪个执行?

答:都不执行!

Inspector窗口显示可编辑变量

  1. protect/private不可以显示,public修饰变量显示
  2. SerializeField\]序列化变量,可以使private/protect修饰变量显示。*序列化是把一个对象保存到一一个文件中或者是数据库字段中。*

  3. 自定义类型数据可以显示:添加[System.Serializable]

其它的一些特性:

C# 复制代码
public struct Enemy
{
    public int defVal;
    public void Atk(){}
}

public class Test : MonoBehaviour
{
    public Enemy enemy;
    [Header("基础属性")]
    public string name;
    public int age;
    [Header("战斗属性")] 
    public int atk;
    public int def;
    [Tooltip("我是小提示,哈哈哈")] 
    //当鼠标悬停时候可以显示出提示信息
    public string ss;
    [Space()] 
    //空格一行
    public int a2;
    [Range(0, 100)] public int value;
    [Multiline(3)] public string test;//多行显示
    [TextArea(3, 5)] public string text2;//至少3行显示 超过5行则显示滚动条
    [ContextMenuItem("充值100¥","Shop")]
    //右击显示执行事件
    public float money = 125.35f;
    private void Shop()//无参无返回类型
    {
        money += 100f;
    }
    //脚本菜单栏中点击执行的方法
    [ContextMenu("测试函数执行")]
    private void TestFun()
    {
        print("测试函数执行了!!");
    }
}

问题:如何让公共成员不再Inspector面板上显示?又如何让似有或者保护成员在一在Inspector面板上显示呢?

答:[HindInInspector] 可以隐藏默认显示的公共成员

SerializeField\]序列化可以使默认隐藏的变量显示 问题:为什么加不同的特性,在inspector窗口上会有不同的效果? 答:因为Unity中式通过**反射**得到类的信息,然后在Inspector窗口中显示字段信息Unity内部通过反射获取字段的特性,当具有某种特性时候,便会做出对应的处理。 *注意* :**挂载在GameObjet对象上的脚本**,在脚本内部修改默认值不会影响外部的默认值(依旧是0) ![image-20231026160410135](https://file.jishuzhan.net/article/1718506563825569794/e2113ee80ef174e28e50b076cba95179.webp) 外部默认值在挂载时候确定的。若要更新外部默认值为脚本中所赋予新的数值(25),则需重新挂载脚本。 运行中修改的数值,不会保存!---在脚本运行中修改数值,运行结束后数值依旧是运行前的。 #### MonoBehaviour 1. 获取挂载的GameObject \~\~` this.gameobjet.name; transform.poistion;transform.localScale;` 2. 设置脚本的激活状态 --`this.enabled=false;` 重要方法: 1. 获取组件 `this.GetComponent("Test02")` `this.GetCompont()` 2. 获取多个脚本 `Test2[] array=this.GetComponents();` 3. 找子对象挂载的脚本(默认也会找自己身上的对象脚本) `this.GetComponentInChildren();//默认自己也找` `this.GetComponentInChildren(true);//失活的子对象也找` `Test02[] arrary02=this.GetComponentsInChildren(true);//包含自己 所有的子对象(后辈,孙子的孙子也会)脚本数组` 4. 得到父对象的挂载脚本(默认也会找自己身上的脚本) `this.GetComponentInParent();` `this.GetComponentsInParent();//获取所有前辈的所挂载脚本` 获取父子的多个脚本返回值可以是List\<\> 也可以是数组T\[\],且都是无线超级追踪!只要是前辈就找,只要是后辈都找。 4. 尝试获取的脚本 `if(this.TryGetComponent(out listTest)){` `//逻辑处理... ` `}` ### Unity重要组件和API #### **gameObject** 1. 成员变量 `this.gameobjet.name="TonyCube";//名字` `this.gameobjet.activeSelf;//是否激活状态` `this.gameobjet.isStatic;//是否是是静态` `this.gameobject.layer;//int类型的 层级编号` `this.gameobject.tag;//string 层级` `this.gameobjet.transform.poistion;//获取transform` 2. gameobjet中的静态方法 ```C# obj.name = "TonyCube"; //---------------查找对象--------------------------- //根据名字查找 效率低 在场景中遍历查找 无法查找到失活对象 GameObject objTarget = GameObject.Find("TonyCube"); //通过标签来查找(无法查找到失活对象) GameObject objTarget1=GameObject.FindWithTag("Player"); //通过标签来查找2 与上面方法一样(无法查找到失活的对象) GameObject objTarget2 = GameObject.FindGameObjectWithTag("Player"); //查找方法总结: //1. 找不到未激活的 //2. 如果多个相同条件的对象,随机返回某一个 无法准确指定的找到 //获取某个对象方法目前接触到的两种方式 //1. 拖拉赋值 //2. API调用获取 //--------------查找多个对象(失活找不到)------------ GameObject[] objects = GameObject.FindGameObjectsWithTag("Player"); //此方法是调用的Object中的,(unity中的Object非Unity中的Object,两个工具包中的同命名类) //效率比较低 //可以找到挂载场景物体上的Test类型脚本 GameObject.FindObjectOfType(); //----------实例化对象方法--- //克隆对象 GameObject.Instantiate("PerfabDemo"); //克隆 Instantiate("PerfabDemo2"); //----------删除对象--------- GameObjet.Destroy(obj); GameObject.Destroy(obj,5);//延时5s删除 //删除此脚本(不再挂载到物体上) ameObject.Destroy(this); //删除对象: //删除指定的游戏对象 //删除指定的脚本对象 //注意:不会马上移除,等下一帧进行移除。知识给对象打上要移除的标识 //在下一帧进行时候移除,相当于异步执行 //立即移除 GameObject.DestroyImmediate(obj); //继承Mono的也可以直接调用 Destroy(obj); DestroyImmediate(obj); //-----------过场景不移除------------- GameObject.DontDestroyOnLoad(obj); DontDestroyOnLoad(obj);//继承的Mono的可直接调用 ``` 3. GameObjet中的成员方法 ```C# //--------------创建物体---------------- GameObject obj1 = new GameObject(); GameObject obj2 = new GameObject("TonyCube"); //挂载Test脚本 GameObject obj3 = new GameObject("TonyCube",typeof(Test)); //--------------给对象添加脚本---------------- Test test2=obj.AddComponent(typeof(Test)) as Test; Test test=obj1.AddComponent(); //--------------获取脚本------------- Test test03=GetComponent("Test"); //--------------比较标签-------------- if (gameObject.CompareTag("Player")) { print("对象的标签是Player"); } else { print("对象的标签不是Player"); } //-------------设置激活失活----------- obj1.setActive(true); obj2.setActive(false); ``` 补:不建议使用的(效率低,耗性能) `this.gameObject.SendMessage("ThisFun");//找到自己脚本中的函数 执行它` `this.gameObject.BroadCastMessage("函数名");//广播行为 让自己以及子对象中有该函数的执行 ` `this.gameObject.SendMessageUpwards("函数名");//向父辈对象(包括自己)发送消息 并执行某函数` 简单理解: 开发游戏项目,就是开发一些脚本,通过脚本控制调用相关的资源,产生玩家产生相关的交互,并做出对应的反应。 #### Time ```C# //时间 //时间停止 Time.timeScale = 0; //恢复正常 Time.timeScale = 1; //倍速 Time.timeScale = 2; //-----最近一个帧渲染时间 print(Time.deltaTime); //不受Scale影响的帧调用时间 print(Time.unscaledDeltaTime); //游戏开始到现在执行的时间 print(Time.time); //不受scale影响的~ print(Time.unscaledTime); //物理帧间隔时间 print(Time.fixedDeltaTime); //不受scale影响的~ print(Time.fixedUnscaledDeltaTime); //帧数 //从游戏开始到现在已经渲染出帧的个数 print(Time.frameCount); ``` 常用的知识: 1. 时间缩放比例(暂停 倍速游戏) 2. 帧间隔时间 (计算位移相关内容) 3. 帧数(帧同步) #### Transform transform涉及到物体的位置、旋转、缩放等信息,是一个常用的坐标类。 ##### **Vector3** ```C# //Vector3 //Vector是一个白哦是三维向量的结构体 Vector3 v1=new Vector3(); v1.x=1; v1.y=2; v1.z=3; Vector3 v2=new Vector3(10,10);//默认z是0 Vector3 v3=new Vector3(15,15,20); //vector3的基本运算 Vector3 sum=v2+v3; print(v1-v2); print(v1*15); print(v3/2); //常用的 print(Vector3.zero);//(0,0,0) print(Vector3.right);//(1,0,0) print(Vector3.left);//(-1,0,0) print(Vector3.forward);//(0,0,1) print(Vector3.back);//(0,0,-1) print(Vector3.Up);//(0,1,0) print(Vector3.down);//(0,-1,0) //计算两个点之间的距离 print(Vector3.Distance(v1,v2)); ``` ##### **poistion 位置** 1. 相对世界坐标系位置信息 `print(transform.poistion);//相对于世界坐标系的` 2. 相对于父节点的坐标位置信息 ​ `print(transform.localPoistion);//相对于父节点的位置` Inspector面板上显示的坐标是相对于父对象坐标。 当无父对象或父对象的坐标为原点坐标时,localPoistion和poistion相同。 注:位置坐标不可以单独赋值,`transform.poisition.x=5;//错误,不被允许的`,要整体把坐标信息看作一个整体。 `transform.poistion=new Vector3(transform.poistion.x,15,transform.poistion.z);//只更改y数值` 对象本身的朝向:(好比你自己站在原地不动,那么你的位置坐标不变,你可以转身,扭头,这些朝向信息会更改) ```C# //对象的朝向 //正前方 print(transform.foward); //正右方 print(transform.right); //正顶方 print(transform.up); ``` 位移 1. 自行实现物体的移动 `transform.position += transform.forward * 1 * Time.deltaTime;` 2. API ` transform.Translate(transform.forward * 1 * Time.deltaTime,Space.World);//位移大小 相对世界坐标系` 区别:世界坐标系和自己坐标系,自己坐标系下的方向还是世界坐标系的方向 正常的移动! //自己坐标系 自己朝向的移动 transform.Translate(Vector3.forward*1*Time.deltaTime,Space.Self); *在控制人物移动时候,注意方向和方向所处的坐标系。* 移动函数两个参数的理解: 移动方向:Vector3.forward,(0,0,1) transform.forward(自己的朝向 也是在世界坐标系中的朝向) 移动方向的坐标系:Space.World Space.Self(默认不写) ```C# //搭配表示移动的方向 //1. 始终沿着世界坐标系的Z正方向移动 transform.Translate(Vector3.forward*1*Time.deltaTime,Space.World); //2.始终沿着自己的正前方(z轴方向/自己的面朝向方向)移动 transform.Translate(Vector3.forward*1*Time.deltaTime,Space.Self); //3.移动方向不好确定 自己面朝向是在世界标系下讲的,而放到自己坐标系下就有些奇怪 transform.Translate(transform.forward*1*Time.deltaTime,Space.Self); //4.自己坐标系下的自己面朝向走 transform.Translate(transform.forward*1*Time.deltaTime,Space.World); ``` ##### **角度和旋转:** ```C# //相对世界坐标系的角度(欧拉角) print(transform.eulerAngles); //进行父对象角度 print(transform.localEulerAngles); //注意:如果是控制子物体旋转 要更改其localEulerAngles数值 //与位置一样,只能整体赋值欧拉角,不可以单独修改某个方向的度数 //旋转------------- //---自转 //默认是自己坐标系 绕y轴旋转 transform.Rotate(new Vector3(0,10*Time.deltaTime,0)); //绕世界坐标系 y轴旋转 transform.Rotate(new Vector3(0,10*Time.deltaTime,0),Space.World); //---默认自己坐标系下旋转 transform.Rotate(Vector3.up,10*Time.deltaTime); transform.Rotate(Vector3.up,10*Time.deltaTime,Space.World); //---公转 //绕着某一点 的某个轴旋转 //绕着 0,0,0 点的 y轴旋转 transform.RotateAround(Vector3.zero,Vector3.up, 10*Time.deltaTime); ``` 注意:`eulerAngles范围是0~360 无负数` ##### 缩放和看向 **缩放:** ```C# //缩放 //相对于世界坐标系的大小 //只可以读 不可以改 print(transform.lossyScale); //相对于父对象的 print(transform.localScale); //变大/小 transform.localScale += Vector3.one + new Vector3(1, 1, 1); //用在update中 才会不停地面向某个物体(坐标点) //看向 盯着 原点 transform.LookAt(Vector3.one); //看向一个对象 transform.LookAt(targetObj); ``` ##### 父子关系 ```C# //父子关系 print(transform.parent.name); //设置父子关系 transform.parent = null; //使用API来设置关系 transform.SetParent(father.transform); //一般我们不会保留子对象相对于世界坐标系的信息 //true 则会保留在世界坐标系的 位置 大小 角度信息 在父物体坐标系下 相对位置转关系 //保持在世界坐标系下相同 transform.SetParent(father.transform,false); //父亲抛弃子对象 transform.DetachChildren(); //获取子对象 //按名字来查询子对象 //可以找到失活的对象的 //而gameobjet中查找的则不可以查找未激活的物体 print(transform.Find("儿子1")); //不能找到孙子物体 print(transform.Find("孙子位置上的物体").name); //遍历儿子 //打印儿子的数目(失活的也算) print(transform.childCount); //通过索引号的到子物体 print(transform.GetChild(0)); //遍历子对象 for (int i = 0; i < transform.childCount; i++) { print("儿子的名子"+transform.GetChild(i).name); } //获取所有的儿子 //包括父亲本身 还有孙物体 (后辈) Transform[] children = GetComponentsInChildren(); foreach (var child in children) { print(child.name); } //儿子的操作 //判断某个物体是否是自己的父对象 transform.IsChildOf(father.transform); //得到自己在兄弟姐妹中的编号 transform.GetSiblingIndex(); //将自己作为兄弟们中的老大 transform.SetAsFirstSibling(); //设置为老末 transform.SetAsLastSibling(); //设置指定的兄弟 //注意:如果超出编号范围 不会报错 会设置为老末 transform.SetSiblingIndex(2); ``` 练习题:给transform写一个拓展方法,在子对象中查某个物体,并支持在子对象的子对象中查找,返回出Transform ```C# public static class Tools { public static Transform myFindChild(this Transform father, string name) { Transform target=null; //首先找第一层子对象 target = father.Find(name); if (target != null) return target; //在子对象中找 for (int i = 0; i < father.childCount; i++) { //递归调用 target=father.GetChild(i).myFindChild(name); if (target != null) return target; } return target; } } ``` ##### 坐标转换 坐标转换在游戏开发中常常有所使用,这里我们介绍点、向量在本地坐标系下和世界坐标系下的相互转换的API。 我们可以将世界坐标系下的某个点转换到本地坐标系下,根据位置向量的正负大致判断该点在本地坐标系下的某个方位。(敌人在主角的哪个方位) 可以使用将某个本地坐标点转换到世界坐标系下,获取它在世界坐标系下的位置之后,方便产生的对应的效果。(玩家和怪物在某点激战,产生一个武器大招的效果等等) ```C# //--------------坐标转换-----------、 print(Vector3.forward); //世界坐标转本地坐标 //转换点 //转换到本地坐标的数值 受到缩放的影响 print(transform.InverseTransformPoint(Vector3.forward)); //转换方向 世界-->本地 不受缩放影响 print(transform.InverseTransformDirection(Vector3.forward)); //受到缩放的影响 print(transform.InverseTransformVector(Vector3.forward)); //本地坐标点转换成世界坐标系 //点的转换 本地坐标的点转换到世界坐标系中 受到缩放影响 print(transform.TransformPoint(Vector3.forward)); //不受缩放影响的 本地方向转换为世界方向 print(transform.TransformDirection(Vector3.forward)); //受缩放影响的 本地方向转换为世界方向 print(transform.TransformVector(Vector3.forward)); ``` #### Inpute输入 可以在设置中查看输入相关的设置(Edit--\>ProjectSetting) ![image-20231028113417148](https://file.jishuzhan.net/article/1718506563825569794/3c51b454b07f93fbe334978cfcee5b3e.webp) ```C# //屏幕坐标的原点 屏幕左下角 //获取鼠标的位置 print(Input.mousePosition); //检测鼠标按键 0 左键 1 右键 2中键 //仅仅在鼠标按下的一瞬间 执行一次 if (Input.GetMouseButtonDown(0)) { print("左键按下"); }else if (Input.GetMouseButtonDown(1)) { print("右键按下"); }else if (Input.GetMouseButtonDown(2)) { print("中键按下"); } //鼠标抬起 //仅仅在抬起一瞬间执行 if (Input.GetMouseButtonUp(0)) { print("左键抬起"); }else if (Input.GetMouseButtonUp(1)) { print("右键抬起"); }else if (Input.GetMouseButtonUp(2)) { print("中键抬起"); } //鼠标长按按下 if (Input.GetMouseButton(0)) { print("鼠标左键长按。。。"); } //鼠标中键滚动 y值 -1向下滚动 0未滚动 1向上滚动 print(Input.mouseScrollDelta); //键盘输入 //按下 //如果传入字符串 需要传入小写字符 if (Input.GetKeyDown(KeyCode.A)) { print("A键按下"); } if (Input.GetKeyUp(KeyCode.A)) { print("A键抬起"); } if (Input.GetKey(KeyCode.A)) { print("A键长按"); } //------------------------------ //检测默认轴输入 返回float类型数值 //键盘AD按下时 返回-1到1之间的数值 //可以自行设置 输入轴相关的 可以控制左右移动 print(Input.GetAxis("Horizontal")); //键盘SW按下时 返回-1到1之间的数值 可以控制上下移动 print(Input.GetAxis("Vertical")); //鼠标移动 //左右移动 print(Input.GetAxis("Mouse X")); //上下移动 print(Input.GetAxis("Mouse Y")); //GetAxisRaw 方法不存在渐变数值 只会返回 -1 0 1仅仅是表示移动的个方向 print(Input.GetAxisRaw("Horizontal")); ``` 其它按键以及陀螺仪相关 ```C# //-------------------------- //是否右任意键按下 或者长按 if (Input.anyKey) { print("有键在按下ing"); } if (Input.anyKeyDown) { print("有键按下"); print(Input.inputString);//按下的键盘名字 } //某一个手柄的按下 if (Input.GetButtonDown("Jump")) { } //某一个手柄键抬起 if (Input.GetButtonUp("Jump")) { } //某一个手柄键盘长按 if (Input.GetButtonDown("Jump")) { } //移动设备触摸相关 //有触摸点 if (Input.touchCount > 0) { Touch point1 = Input.touches[0]; //位置 print(point1.position); //触摸点的滑动位置 print(point1.deltaPosition); } //是否启用多点触控 Input.multiTouchEnabled = true; //陀螺仪(重力感应)开启 Input.gyro.enabled = true; //重力加速度向量 print(Input.gyro.gravity); //旋转速度 print(Input.gyro.rotationRate); //陀螺仪 当前旋转的四元数 //可以使物体根据手机旋转角度 做出相应的旋转 print(Input.gyro.attitude); ``` #### Screen ```C# //常用的屏幕相关的属性 //屏幕 //获取当前屏幕的分辨率 Resolution r = Screen.currentResolution; Debug.Log("当前分辨率宽"+r.width+",分辨率的高"+r.height); //Game窗口的宽和高 非设备的宽和高 print(Screen.width); print(Screen.height); //屏幕休眠模式 //永远不息屏 Screen.sleepTimeout = SleepTimeout.NeverSleep; //运行时候全屏 Screen.fullScreen = true; //独占全屏 Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen; //全屏窗口 Screen.fullScreenMode = FullScreenMode.FullScreenWindow; //最大化窗口 Screen.fullScreenMode = FullScreenMode.MaximizedWindow; //窗口模式 Screen.fullScreenMode = FullScreenMode.Windowed; //移动设备--------------等了解 //允许左横向 Screen.autorotateToLandscapeLeft = true; //允许右横向 Screen.autorotateToLandscapeRight = true; //允许自动转换到纵向 Screen.autorotateToPortrait = true; //允许自动旋转到颠倒纵向 Screen.autorotateToPortraitUpsideDown = true; //指定屏幕显示方向 Screen.orientation = ScreenOrientation.Landscape;//横屏 //静态设置方法 //设置分辨率 分辨率宽 分辨率高 不全屏 Screen.SetResolution(1920,1080,false); ``` #### Camera 面板知识![摄像机参数说明](https://file.jishuzhan.net/article/1718506563825569794/d2ba1c006014d4bfff6a1e81318e3151.webp) ![摄像机参数说明2](https://file.jishuzhan.net/article/1718506563825569794/3827f3f050e712873dc5f84e809cca34.webp) ```C# //摄像机相关 //Camera //获取主摄像机(注意设置摄像机tag为mainCamera) print(Camera.main.name); //获取摄像机的数量 print(Camera.allCamerasCount); //得到所有的摄像机 Camera[] allCameras = Camera.allCameras; //摄像机相关委托 Camera.onPreCull += (c) => { //当某些被剔除时候调用的函数 }; Camera.onPreRender += (pre) => { //渲染前执行的函数 }; Camera.onPostRender += (post) => { //摄像机选然后处理的委托 }; //界面上的参数都可以从Camera获取 //eg:获取深度 print(Camera.main.depth); //世界坐标系转屏幕坐标 // x,y为屏幕坐标 z表示点到摄像机的距离 Camera.main.WorldToScreenPoint(transform.position); //屏幕坐标转世界坐标 //注意对Z的处理 z到底距离摄像机右多远近 //确保屏幕坐标 投射到 距离摄像机多远的横切面上 Vector3 poistion = Input.mousePosition; poistion.z = 10; Camera.main.ScreenToWorldPoint(poistion); ``` ### 核心系统 #### 光源系统 1.光源类型 * 点光源(灯泡) * 环境光(太阳光) * 区域光 (烘焙时候使用,面光源) * 聚光灯(手电筒) 2.颜色Color 3.光源模式 * 实时光源Realtime * 烘焙光源Baked * 混合光源Mixed 4. 光源亮度 Intensity 5. 阴影类型Shadow Type 1. NoShadows 无阴影 2. HardShadows 硬阴影 3. SoftShadows 柔和阴影 --效果更逼真 6. Cookie 投影遮罩 一般使用在聚光灯下,进行阴影设置,(可以想想皮影) 7. Draw Halo 球形光环 8. Flare 耀斑资源(摄像机上挂载Flare脚本才可以渲染到Game窗口中) 9. Culling Mask (剔除遮罩层,选择光源的影响层级) 10. Indirect Multiplier 间接光强度 (\<1 每次反弹后更暗 \>1每次反弹后更亮) 11. RealttimeShadows阴影设置 12. Cookie Size 遮罩光大小 13. Render Mode 渲染优先级 #### 物理系统 ##### 刚体 碰撞发生得必要条件:两个物体都具有碰撞体 至少有一个物体具有刚体 1. Mass 质量 质量越大惯性越大 2. Drag 空气阻力 无空气阻力 3. Angular Drag 根据扭矩旋转对象时影响对象的空气阻力大小(两物体碰撞之后会受到一种扭矩,而扭矩阻力就是**影响它旋转的阻力**) 4. Use Gravity 是否受重力影响 5. isKinematic 开启此项 对象不会受到物理引擎驱动 (相当于一个大山,一堵墙,物体撞击它,物体会反弹,而山和墙壁纹丝不动) 6. Interpolate 插值运算 让刚体移动的更平滑(如果帧更新兼具时间过长,可以考虑使用插值运算,保证物体的运动的连贯) 1. None 不使用插值运算 2. Interpolate 根据前一帧的变换来平滑过度 3. Extrapolate 插值运算 根据下一帧的预估变换来平滑插值 7. Collision Detection 碰撞检测算法 ![image-20231028153529033](https://file.jishuzhan.net/article/1718506563825569794/159534c0e236adbdd35b7e1c7fabf668.webp) 补充:子弹击穿问题!由于子弹的速度过快,在两帧更新的时间间隔中,子弹已经穿过物体 从而检测不到在两帧间隔之间产生的击穿效果。 8. Constraints 约束 Freeze Poistion 冻结位置 Freeze Rotation 冻结旋转 ##### **碰撞器**Collider 碰撞器表示物体的体积,挂有碰撞器的物体有了物理边界,不会被其它物体穿透。 3D碰撞体的种类:盒装 、球状、胶囊状、网格状、轮胎状、地形状。 参数: 1. isTragger 是否是触发器 勾选之后无物理效果,只会用于碰撞触发事件 2. Material 物体材质 3. Center 碰撞体在对象体中的中心点的位置 常用碰撞器: * BoxCollider size 碰撞体在x、y、z方向上大小 * Sphere Collider Radius 碰撞体球半径大小 * Capsule Collider Radius(两端半球状半径大小) Height(高度) Direction(轴向) 不规则形状的物体使用碰撞体组合(主要在父对象上挂载使用碰撞体) 不常用碰撞器: * Mesh Collider * Convex 勾选之后 才可以发生碰撞 最多可以右255三角形(添加刚体后必须勾选上才可以产生碰撞效果) * Cooking Options * Mesh(网格信息) * Wheel Collider(不会单独使用,汽车加上刚体才可以给轮胎添加使用 ) * Terrain Collider (地形系统中使用) ##### 物理材质 根据材质来决定碰撞的具体效果。 参数: Dynamic Friction 移动时候的摩擦力 Static Friction 静止摩擦力 Bounciness 弹性 0不会反弹 1时候完全碰撞 无能量损失 Fiction Combine 两个碰撞对象的**摩擦力**的组合方式 * Average 二者的平均值 * Minimun 二者中较小的 * Maximum 二者中较大的 * Mulitipy 二者相乘 unce Combine 两个物体的**弹性**组合方式 * Average 二者的平均值 * Minimun 二者中较小的 * Maximum 二者中较大的 * Mulitipy 二者相乘 ##### 碰撞检测函数 碰撞、触发检测函数也属于生命周期函数,在FixedUpdate之后反应,调用之后又会执行FixedUpdate。 ```C# //碰撞触发接触时候执行 private void OnCollisionEnter(Collision other) { print(transform.name+"和"+other.name+"碰撞接触了"); } //碰撞接触完成之后执行 private void OnCollisionExit(Collision other) { print(transform.name+"和"+other.name+"接触结束了"); } //相互接过程中调用 private void OnCollisionStay(Collision other) { print(transform.name+"和"+other.name+"接触中ing"); } //注:可以从接触到的碰撞器获取物体的所有信息 //触发器函数 //注意参数是Collider private void OnTriggerEnter(Collider other) { } private void OnTriggerExit(Collider other) { } private void OnTriggerStay(Collider other) { } ``` ##### 刚体添加力 ```C# //获取刚体 rigidbody = GetComponent(); //添加力作用 (相对于世界坐标系) rigidbody.AddForce(Vector3.forward*10); //添加力作用 相对本地坐标系 rigidbody.AddRelativeForce(Vector3.forward*10); //添加扭矩 使其旋转(绕着y轴转) rigidbody.AddTorque(Vector3.up); //相对于本地坐标转 rigidbody.AddRelativeForce(Vector3.up); //直接改变速度 速度方向相对于世界坐标系 rigidbody.velocity = Vector3.forward*5; //模拟被爆炸冲击力冲击(物体受到在某位置(1,1,1)产生的爆炸力 10,爆炸半径是10 ) rigidbody.AddExplosionForce(10,Vector3.one, 10); //力的几种模式 //动量定理 Ft=mv v=Ft/m //加速度模式 给物体添加一个持续的加速度 忽略其质量 //v = a * t rigidbody.AddForce(Vector3.forward*10,ForceMode.Acceleration); //力的模式 给物体添加持续一个力 考虑质量 rigidbody.AddForce(Vector3.forward*10,ForceMode.Force); //ForceMode.Impulse ----给物体添加一个瞬间的力 与物体的质量有关 忽略时间 // //ForceMode.VelocityChange---给物体添加一个瞬时速度 忽略质量 ``` Constant Force 添加该compont之后可以持续地给该物体提供力的影响。 补:刚体的休眠,刚体会在某些状态下进行休眠. ```C# //休眠唤醒 if (rigidbody.IsSleeping()) { rigidbody.WakeUp(); } ``` 问:让一个物体产生位移? 1. Update 里 修改`transform poistion` 2. Update 里使用`Transform`提供的`TransLate` 方法 3. 添加刚体力 `rigidBody.AddForce` 4. 刚体改变速度 `rigidBody.velocity =Vector3.forward*10` #### 音效系统 1.面板参数内容 ![image-20231028175204442](https://file.jishuzhan.net/article/1718506563825569794/41007dd8288b18d4983d051d8908d3ae.webp) ![image-20231028175229444](https://file.jishuzhan.net/article/1718506563825569794/642a4f14ee3f9609bdb45afc3c88e12a.webp) ![音效源参数1](https://file.jishuzhan.net/article/1718506563825569794/943ef39c4277024b5c07a933e6dacc80.webp) ![音效源参数2](https://file.jishuzhan.net/article/1718506563825569794/b4a7683fb9ce64a9dd27033864bb329f.webp) **代码控制音频源:** ```C# //音频源 audioSource = GetComponent(); //播放 audioSource.Play(); //停止 audioSource.Stop(); //暂停 audioSource.Pause(); //继续播放 audioSource.UnPause(); //延迟5s播放 audioSource.PlayDelayed(5); ``` 思考:如何动态的进行音效播放? 1. 给需要播放音效的物体上挂载AudioSource脚本控制 2. 制作挂载AudioSource组件的预制体,需要播放音效时候实例化播放 3. 用一个AudioSource来控制不同的音效 ```C# //3.动态加载Clip音效文件 AudioSource audioSource=gameObject.AddComponent(); audioSource.clip=clip;//挂载对应的音效片段 audioSource.Play(); ``` #### 麦克风输入 语音输入... ```C# //麦克风 public AudioClip clip; //设备名字(null表示默认设备) 超出时长之后是否覆盖 录制时长 采样率 clip = Microphone.Start(null, false, 10, 44100); Microphone.End(null);//录制结束 ``` *** ** * ** *** *对于API的学习和了解,在使用时收多查看官方文档,不妨是个不错的学习方法!*