唐老狮Unity入门笔记

目录

Unity脚本基础

生命周期函数 知识点

Inspector窗口可编辑变量 知识点

MonoBehavior中的重要内容

Unity重要组件和Api

GameObject知识点

GameObject的成员变量

GameObject的静态方法

GameObject中的成员方法

时间相关 Time

Transform

Vector3基础和Transform位置

Transform位移

Transform角度和旋转

Transform 缩放和看向

Transform父子关系

Transform坐标转换

Input鼠标键盘输入

Input触摸手柄陀螺仪

屏幕相关Screen               

Camera可编辑参数说明

​编辑Camera代码相关

核心系统

光源组件

面板参数

代码控制

光相关面板

​编辑

碰撞检测

刚体

碰撞器

物理材质

碰撞检测函数

刚体加力

音效系统

音频文件导入

音频源和音频监听脚本

代码控制音频源

麦克风输入相关


Unity脚本基础

生命周期函数 知识点

Inspector窗口可编辑变量 知识点

  1. 私有和保护是无法显示编辑的
  2. 让私有和保护可以显示编辑
    加上强制序列化字段特性 [SerializeField] (序列化就是把一个对象保存到一个文件或数据库字段中)
    如:
    [SerializeField]
    private string privateStr
  3. 公共的可以显示编辑
  4. 公共让其不能显示编辑
    加上特性 [HideInInspector]
  5. 大部分类型都能显示编辑
    //可以被显示
    public int[] array;
    public List<int> list;
    public E_TestEnum type;
    public GameObject gameObj;
    
    //不能被显示(字典,自定义类型)
    public Dictionary<int,string> dic;
    public MyStruct myStruct;
    public MyClass myClass;
    
  6. 让自定义类型可以显示编辑
    加上序列化特性 [System.Serializable]
    [System.Serializable]
    public struct MyStruct
    {
        public int age;
        public bool sex;
    }
  7. 一些辅助特性​​​​​​
    //1. 分组特性 Header
    [Header("基础属性")]
    public int age;
    public bool sex;
    [Header("战斗属性")]
    public int atk;
    public int def;
    
    //2. 悬停注释 Tooltip
    // 为变量添加说明
    [Tooltip("闪避")]
    public int miss;
    
    //3. 间隔特性 Space();
    [Space()]
    public int crit;
    
    //4. 修饰数值的滑条范围 Range
    [Range(min, max)]
    public float luck;
    
    //5. 多行显示字符串,默认不写参数显示3行
    //先参数对应行
    [Multiline()]
    public string tips;
    
    //6. 滚动条显示字符串
    //默认不写为3行
    [TextArea(min, max)]
    public string myLife;
    
    //7. 为变量添加快捷方法 ContextMenuItem
    //方法名不能有参数
    [ContextMenuItem("显示按钮名", "方法名")]
    public int money;
    
    //8. 为方法增加特性能够在Inspector中执行
    [ContextMenu("哈哈哈哈")]
    private void TestFun()
    {
        print("测试方法");
    }

注意      

1. Inspector窗口中的变量关联的就是对象的成员变量,运行时改变他们就是在改变成员变量

2.拖拽到GameObject对象后,再改变脚本中变量默认值,界面上不会改变

3.运行中修改的信息不会保存(可以copy后paste)

MonoBehavior中的重要内容

1. 重要成员

        1.获取依附的GameObject

//gameObject方法
print(this.gameObject.name);

        2.获取依附的GameObject的位置信息

print(this.transform.position);
print(this.transform.eulerAngles);
print(this.transform.lossyScale);
//与上面效果相同
//this.gameObject.transform

        3.获取脚本是否激活

this.enable = true;

        4.获取别的脚本对象依附的gameObject的信息

public Lesson3 otherLesson3;
//...
print(otherLesson3.gameObject.name);
print(otherLesson3.transform.position);

2. 重要方法              

        1.得到自己挂载的单个脚本

//根据脚本名获取,获取失败返回Null
Lesson3_Test t = this.GetComponent("Lesson3_Test") as Lesson3_Test;
//返回的基类,可以用as 转换为子类
print(t);

//根据Type获取
t = this.GetComponent(typeof(Lesson3_Test)) as Lesson3_Test;
print(t);

//根据泛型获取(不用二次转换)
t = this.GetComponent<Lesson3_Test>();

//只要能得到场景中别的对象或其依附的脚本,则能得到它的所有信息
//若挂载多个同一类型脚本,无法确定得到的是哪个(故一般不会挂载多个同一类型脚本)

        2.得到自己挂载的多个脚本

//1
Lesson3[] array = this.GetComponents<Lesson3>();

//2
List<Lesson3> list = new list<Lesson3>();
this.GetComponents<Lesson3>(list);

      3.得到子对象(子的子对象也行)挂载的脚本(默认也会找自己身上是否挂载该脚本)

t = this.GetComponentInChildren<Lesson3_Test>();
//参数默认False,失活时不找,若参数为True,失活也找

//多个脚本
Lesson3_Test[] lts = this.GetComponentsInChildren<Lesson3_Test>(true);

List<Lesson3_Test> list2 = new List<Lesson3_Test>;
this.GetComponentsInChildren<Lesson3_Test>(true, list2);

        4.得到父对象(父的父亲也行)挂载的脚本(默认也会找自己身上是否挂载该脚本)

t = this.GetComponentInParent<Lesson3_Test>();

//多个
lts = this.GetComponentsInParent<Lesson3_Test>();

//也有List的

        5.尝试获取脚本

Lesson3_Test l3t;
//提供了更安全的获取单个脚本的方法,得到了返回true
if(this.TryGetComponent<Lesson3_Test>(out l3t))
{
    ...
}

Unity重要组件和Api

GameObject知识点

GameObject的成员变量

//名字
print(this.gameObject.name);
this.gameObject.name = "唐老狮";

//是否激活
print(this.gameObject.activeSelf); // bool

//是否静态
print(this.gameObject.isStatic);

//层级
print(this.gameObject.layer); //int

//标签
print(this.gameObject.tag); //string

//transform
//this.transform  上一知识点
//得到信息相同
print(this.gameObject.transform.position);

GameObject的静态方法

//创建自带几何体
//只要的到了一个GameObject对象,就可以得到它挂载的任何脚本信息
//通过obj.GetComponent来得到
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = "我创建的立方体";

//查找对象的相关知识
string tip = "无法找到失活对象"; //注意
string tip1 = "若场景中存在多个满足条件的对象,无法确定找到的是谁";
//1. 查找单个对象
// 通过对象名查找(效率低,因为会在场景中所有对象去查找)
GameObject obj2 = GameObject.Find("唐老狮");
if(obj2 != null)
{
    print(obj2.name);
}
else {...}

//通过tag查找对象
//GameObject obj3 = GameObject.FindWithTag("Player");
//该方法与上述效果相同
GameObject obj3 = GameObject.FindGameObjectWithTag("Player");

目前有两种方式得到某一单个对象:

1. 是public从外部面板拖,进行关联

2. 通过API来找

// 2. 查找多个对象
string tip3 = "同样只能找激活对象";
//找多个对象的API,只能通过tag
GameObject[] objs = GameObject.FindGameObjectsWithTag("Player");
print("找到tag为Player对象的个数" + objs.Length);

//用的比较少的方法,GameObject父类 Object类的方法
//Unity里的Object是命名空间在UnityEngine中的Object类,也是集成万物之父的一个自定义类
//C#中的Object命名空间在System中

//可以找到场景中挂载的某一脚本对象
//效率更低,要遍历对象,还要遍历它挂载的脚本
Lesson4 o = GameObject.FindObjectofType<Lesson4>();
print(o.gameObject.name);

实例化对象(克隆对象)的方法

实例化(克隆)对象 ,作用是根据一个GameObject对象,克隆一个

//准备克隆的对象
//可以是一个场景对象
//也可以是一个预设体对象
public GameObject myObj;
...

//克隆
GameObject obj5 = GameObject.Instantiate(myObj);
//以后学了更多知识点,就可以操作obj5
//如果继承了MonoBehavior,则不用GameObject
//该方法是 Unity中的Object基类提供的
Instantiate(myObj);

删除对象的方法

GameObject.Destroy(obj5);
//第二个参数 代表延迟几秒钟删除
GameObject.Destroy(obj5, 5);
//Destroy 还可以删除脚本
GameObject.Destroy(this); //删除自己这个脚本

删除对象有两个作用

1. 删除指定的一个游戏对象

2. 删除一个指定的脚本对象

注意:Destroy方法不会马上移除对象,只给对象加一个移除标识,一般情况下会在下一帧时移除并从内存中移除

//没特殊需求建议上一方法,因为异步 降低卡顿几率
GameObject.DestroyImmediate(myObj);

该方法会马上删除对象

如果是继承MonoBehavior类,不用写GameObject

Destroy(myObj2);
DestroyImmediate(myObj);

过场景不移除

默认情况下过场景会移除场景中的对象

//自己依附的GameObject对象过场景不被移除
GameObject.DontDestroyOnLoad(this.gameObject);

//如果继承MonoBehavior也可以写
DontDestroyOnLoad(this.gameObject);

GameObject中的成员方法

创建空物体

//new一个GameObject对象就是创建空物体
GameObject obj6 = new GameObject();
GameObject obj7 = new GameObject("创建的空物体");
GameObject obj8 = new GameObject("顺便加脚本的空物体", typeof(Lesson2), typeof(Lesson1)); //脚本数可变

为对象添加脚本

//继承MonoBehavior的脚本 不能够去new
//如果想要动态的添加继承MonoBehavior的脚本 在某一对象上
//直接用GameObject提供的方法
Lesson1 les1 = obj6.AddComponent(typeof(Lesson1)) as Lesson1;
obj6.AddComponent<Lesson2>();

string tip = "使用泛型更方便";

//通过返回值可以得到加入的脚本信息
//得到脚本的成员方法和继承Mono类得到脚本的方法一样

标签比较

if(this.gameObject.CompareTag("Player"))
{
    print("对象的标签是 Player");
}
if(this.gameObject.tag == "Player")
{
    print("对象的标签是 Player");
}

设置激活/失活

//false 失活
//true 激活
obj6.SetActive(false);

次要的成员方法(了解即可,不建议使用,因为效率低)

//通知自己 执行什么行为
//命令自己 去执行TestFun这个函数,会在自己身上挂载的所有脚本中去找该名字(可能重复名字)的函数
this.gameObject.SendMessage("TestFun", 参数);

//广播行为,让自己和自己的子对象执行
this.gameObject.BroadcastMessage("函数名");

//向父对象和自己发送消息并执行
this.gameObject.SendMessageUpwards("函数名");

时间相关 Time

时间缩放比例

//时间停止
Time.timeScale = 0;
//恢复正常
Time.timeScale = 1;
//2倍速
Time.timeScale = 2;

帧间隔时间(最近的一帧用了多长时间(秒))

//受scale影响
print(Time.deltaTime);

//不受scale影响的帧间隔时间
print(Time.unscaledDeltaTime);

帧间隔时间 主要用来计算位移, 路程 = 时间 * 速度

根据需求选择帧间隔时间

deltaTime = unscaledDeltaTime * timeScale

游戏开始到现在的时间

//它主要用来 在单机游戏中计时  ,网络游戏用服务器时间
//收scale影响
print(Time.time);
//不受scale影响
print(Time.unscaledTime);

物理帧间隔时间 用于FixedUpdate

//收scale影响
print(Time.fixedDeltaTime);
//不受scale影响
print(Time.fixedUnscaledDeltaTime);

帧数

//从开始到现在游戏跑了多少帧(多少次循环)
print(Time.frameCount);

Transform

游戏对象(GameObject) 位移、旋转、缩放、父子关系、坐标转换等相关操作都由它处理

它是Unity提供的极其重要的类

Vector3基础和Transform位置

Vector3主要用来表示三维坐标系中的 一个点 或 一个向量

Vector3声明

Vector3 v = new Vector3();
v.x = 10;
v.y = 10;
v.z = 10;

//只传x,y 默认z是0
Vector3 v2 = new Vector3(10, 10);
//一步到位
Vector3 v3 = new Vector3(10,10,10);

//Vector3 为结构体,可以不new,默认值均为0
Vector3 v4;

Vector的基本运算

//  + - * / 
//+ - 就是对应加减

print(v1 * 10);
print(v1 / 2); //xyz均乘除

Vector3常用向量

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 

Vector3常用方法

//计算两个点的距离
print(Vector3.Distance(v1, v12));

位置

//相对世界坐标系
//this.gameObject.transform
//通过position得到的位置 是相对于 世界坐标系 原点的位置
//可能和面板上显示不同
//因为如果对象有父子关系,可能是相对父对象的坐标
print(this.transform.position);

//相对父对象
//如果想要以面板坐标为准则使用该方法
print(this.transform.localPosition);

两种方法得到的坐标可能一样

1. 父对象的坐标就是 世界坐标系原点 0,0,0

2. 对象没有父对象

注意:位置的赋值不能直接改变x, y, z 只能整体改变

//this.transform.position.x = 10;
//上述方法不行

this.transform.position = new Vector3(10, 10, 10);
this.transform.localPosition = Vector3.up * 10;

//如果只想改一个值x
//1.直接赋值
this.transform.position = new Vector3(10, this.transform.position.y, this.transform.position.z);
//2.先取出来,再赋值
Vector3 vPos = this.transform.localPosition;
vPos.x = 101;
this.transform.localPosition = vPos.x;

对象当前的各朝向

//如果想得到对象当前的 一个朝向
//那么就是通过 transform.出来的
//对象当前的面朝向
print(this.transform.forward);
//对象当前的头顶朝向
print(this.transform.up);
//对象当前的右手边
print(this.transform.right);

Transform位移

理解坐标系下的位移计算公式

路程 = 方向 * 速度 * 时间

//方式一 自己计算
//用当前的位置 + 我要动多长距离 得出最终位置
//this.transform.forward 相对于自己的面朝向
//Vector3.forward 世界坐标系的正朝向 0 0 1
this.transform.position += this.transform.forward * 1 * Time.deltaTime;

//方式二 API
//参数一: 表示位移多少
//参数二: 表示 相对坐标系 默认相对于自己坐标系

//相对于世界坐标系的 Z轴 动
this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World);

//相对于世界坐标系的 自己面朝方向 动
this.transform.Translate(this.transform.forward * 1 * Time.deltaTime, Space.World);

//相对于自己坐标系的 自己面朝方向 动 (一定不会这样让物体移动!) 
//this.transform.forward 得到的是相对于世界坐标系的方向, 而相对于自己坐标系 会进一步偏移
this.transform.Translate(this.transform.forward * 1 * Time.deltaTime, Space.Self);

//相对于自己坐标系下的 Z轴 正方向移动
this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.Self);

Transform角度和旋转

角度相关

//相对世界坐标的角度
print(this.transform.eulerAngles);

//相对于父对象的角度
print(this.transform.localEulerAngles);

//注意:设置角度和设置位置一样,不能单独设置xyz,要一起设置
this.transform.localEulerAngles = new Vector3(10, 10, 10);
//如果希望改变的 角度 是面板上的内容, 那一定是改变 相对父对象的角度

旋转相关

//API计算
//自转
//每个轴 具体转多少度
//参数一 相当于 每一帧 旋转的角度
//参数二 默认 相对于自己坐标系
this.transform.Rotate(new Vector3(0,10,0) * Time.deltaTime);
this.transform.Rotate(new Vector3(0,10,0) * Time.deltaTime, Space.World);

//相对于某个轴 转多少度
//参数一 相对于哪个轴进行转动
//参数二 转动的 角度 是多少
//参数三 默认相对于自己坐标系
this.transform.Rotate(Vector3.up, 10 * Time.deltaTime);

//相对于某一个点旋转
//参数一 相对于哪个轴进行转动
//参数二 转动的 角度 是多少
//参数三 默认相对于自己坐标系
this.transform.RotateAround(Vector3.zero, Vector3.up, 10 * Time.deltaTime);

Transform 缩放和看向

缩放

//相对于世界坐标系
print(this.transform.lossyScale);

//相对于本地坐标系(父对象)
print(this.transform.localScale);

注意:1.同样缩放不能只改xyz,要一起改(相对于世界坐标系的大小,只能得不能改)

            所以一般改缩放大小 都是改 相对于父对象的

this.transform.localScale = new Vector3(3,3,3);

           2.Unity没有提供关于缩放的API,只能自己写

this.transform.localScale += Vector3.one * Time.deltaTime;

看向

让一个对象的面朝向, 可以一直看某一点或某一对象

//看向一个点 相对于世界坐标系
this.transform.LookAt(Vector3.zero);

//看向一个对象
public Transform lookAtObj;
...
this.transform.LookAt(lookAtObj);

Transform父子关系

获取和设置父对象

//获取父对象
print(this.transform.parent.name);
//设置父对象,断绝父子关系
this.transform.parent = null;
//设置父对象,认爸爸
this.transform.parent = GameObject.Find("father2").transform;

//通过API来进行父子关系设置
this.transform.SetParent(null);
this.transform.SetParent(GameObject.Find("father2").transform);

//参数一:我的父亲
//参数二:是否保留世界坐标的 位置 角度 缩放 信息
//true 会保留 世界坐标下的状态 和 父对象 进行计算 得到本地坐标系的信息
//    子对象position*父对象scale + 父对象 position = 原世界坐标position 大概?
//    子对象scale * 父对象 scale = 原scale
//其实就是子对象的信息转换到以父对象为标准的坐标系下
//false 不会保留 会直接把世界坐标系下的 位置 角度 缩放 直接赋值到本地坐标系下

this.transform.SetParent(GameObject.Find("father2").transform, true);

抛妻弃子

//就是和所有儿子断绝关系 , 但儿子和孙子之间的关系不影响
this.transform.DetachChildren();

获取子对象

//按名字查找儿子
//找到儿子的 transform信息
//Find能找到失活对象,GameObject相关的不能找到
print(this.transform.Find("Cube(1)").name);
//只能找到自己的儿子,不能找到孙子
//虽然效率比 GameObject.Find相关 要高, 但前提需要知道父对象

//遍历儿子
//如何得到有多少个儿子
//1.失活的儿子也算数量
//2.找不到孙子,孙子不会算数量
print(this.transform.childCount);
//通过索引号 去得到相应的儿子 [0,this.transform.childCount)
//如果编号 超出了儿子数量 会报错
//返回值是 transform 
this.transform.GetChile(0); 

儿子的操作

//判断自己的爸爸是谁
//一个对象 判断自己是不是 另一个对象 的儿子
public Transform son;
...
//son.transform.IsChildOf(this.transform)
if(son.IsChildOf(this.transform)){...}

//得到自己作为儿子的编号
print(son.GetSiblingIndex());
//把自己设置为第一个儿子
son.SetAsFirstSibling();
//把自己设置为最后一个儿子
son.SetAsLastSibling();
//把自己设置为指定个儿子
//就算 填的数量 超出范围(负数 或 更大的数) 也会直接放到最后
son.SetSiblingIndex(15);

Transform坐标转换

世界坐标转本地坐标

//世界坐标系 转 本地坐标系 可以帮助我们大概判断一个相对位置

//世界坐标系的点 转换为 相对本地坐标系的点
//受到缩放影响
print("转换后的点" + this.transform.InverseTransformPoint(Vector3.forward));

//世界坐标系的方向 转换为 相对本地坐标系的方向
//把方向向量 移到本地原点位置
//不受缩放影响
print("转换后的方向" + this.transform.InverseTransformDirection(Vector3.forward));
//受缩放影响
print("转换后的方向(受缩放影响)" + this.transform.InverseTransformVector(Vector3.forward));

本地坐标转世界坐标(重要)

//本地坐标系点 转换为 相对世界坐标系点 (受缩放影响)
print("本地 转 世界 点" + this.transform.TransformPoint(Vector3.forward));

//本地坐标系的方向 转换为 世界坐标系的方向
//不受缩放影响
print("本地 转 世界 方向" + this.transform.TransformDirection(Vector3.forward));
//受缩放影响
print("本地 转 世界 方向(受缩放影响)" + this.transform.TransformVector(Vector3.forward));

最重要的是 本地坐标系点转换为世界坐标系的点

比如 玩家 再 自己面前 n个单位 放一团火, 就不用关心世界坐标系。

Input鼠标键盘输入

输入相关内容肯定写在Update当中

鼠标在屏幕位置

//屏幕坐标的原点 在 屏幕左下角 往右 是x正方向 往上 是y正方向
//返回值Vector3 但是只有 x 和 y ,z一直是0,因为屏幕是2d的
print(Input.mousePosition);

检测鼠标输入

作用:

1.可以做 发射子弹

2.可以控制摄像机 转动

//鼠标按下一瞬间 进入
//0左键 1右键 2中键
if(Input.GetMouseButtonDown(0)){
print("鼠标某一个键按下了");
}

//鼠标抬起一瞬间
//0左键 1右键 2中键
if(Input.GetMouseButtonUp(0)){
print("鼠标某一个键抬起了");
}

//鼠标长按按下抬起都会进入
if(Input.GetMouseButton(0)){
print("左键按下");
}

//中键滚动
//返回值的 y -1下滚 0无滚 1上滚
//它的返回值 是Vector的值 滚动鼠标中键 会改变y的值
print(Input.mouseScrollDelta);

检测键盘输入

//KeyCode 为枚举
if(Input.GetKeyDown(KeyCode.W))
{
    print("W键按下");
}

传入字符串的重载

//传入的 字符串 不能为大写 不然会报错
//但是大小写都能识别
if(Input.GetKeyDown("q"))
{
    print("Q键按下")
}

键盘抬起和长按

if(Input.GetKeyUp(KeyCode.W))
{
    print("W键抬起");
}

if(Input.GetKey(KeyCode.W))
{
    print("W键长按");
}

检测默认轴输入

我们学习鼠标 键盘输入 主要用于控制玩家 比如 旋转 位移

Unity为此提供了更方便的方法

//具体名可以看 project setting 中的 input manager
//键盘AD按下 返回 -1 到 1 的变换
// 相当于 得到的这个值就是 我们的左右方向 可以用它控制左右移动 旋转
print(Input.GetAxis("Horizontal"));

//键盘SW按下 返回 -1 到 1 的变换
print(Input.GetAxis("Vertical"));

//鼠标横向移动 返回 -1 到 1 的变换
print(Input.GetAxis("Mouse X"));

//鼠标竖向移动 返回 -1 到 1 的变换
print(Input.GetAxis("Mouse Y"));

//GetAxisRaw方法 和 GetAxis使用方式相同
//只不过 它的返回值 只会是 -1 0 1 不会有中间值

Input触摸手柄陀螺仪

//是否有任意键或鼠标长按
if(Input.anyKey)
{
    ...
}

//是否有任意键或鼠标长按
if(Input.anyKeyDown)
{
    ...
}

//这一帧的键盘输入 可有上面2个结合使用
print(Input.inputString); 

//手柄输入相关
//得到连接的手柄的所有按钮名字
string[] strs = Input.GetJoystickNames();

//某一手柄键按下
if(Input.GetButtonDown("Jump")){...}

//某一手柄键抬起
if(Input.GetButtonUp("Jump")){...}

//某一手柄键长按
if(Input.GetButton("Jump")){...}


//移动设备触摸相关
if(Input.touchCount > 0)
{
    Touch t1 = Input.touches[0];

    //位置
    print(t1.position);
    //相对上次位置的变化
    print(t1.deltaPosition);
}

//是否启用多点触控
Input.multiTouchEnabled = false;

//陀螺仪(重力感应)
//开启才能正常使用
Input.gyro.enabled = true;

//重力加速度向量
print(Input.gyro.gravity);

//旋转速度
print(Input.gyro.rotationRate);

//陀螺仪 当前的旋转四元数
//比如 用这个角度信息 来控制 场景上的一个3D物体受重力影响
//手机怎么动 它怎么动
print(Input.gyro.attitude);

屏幕相关Screen               

静态属性

常用

//当前屏幕分辨率
Resolution r = Screen.currentResolution;
print("当前屏幕分辨率的宽" + r.width + "高" + r.height);

//屏幕窗口当前宽高 对应game窗口的宽高
//一般写代码 要用窗口宽高 做计算时 就用他们
print(Screen.width);
print(Screen.height);

//屏幕休眠模式
Screen.sleepTimeout = SleepTimeout.NeverSleep;


不常用

//运行时是否全屏模式
Screen.fullScreen = true;
//窗口模式
//独占全屏 FullScreenMode.ExclusiveFullScreen
//全屏窗口 FullScreenMode.FullScreenWindow
//最大化窗口 FullScreenMode.MaximizedWindow
//窗口模式    FullScreenMode.Windowed
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen

//移动设备屏幕转向相关
//允许自动旋转为左横向 Home键在左
Screen.autorotateToLandscapeLeft = true;
//允许自动旋转为右横向 Home键在右
Screen.autorotateToLandscapeRight = true;
//允许自动旋转为纵向 Home键在下
Screen.autorotateToPortrait = true;
//允许自动旋转为纵向倒着看 Home键在上
Screen.autorotateToPortraitUpsideDown = true;

//指定屏幕显示方向
Screen.orientation = ScreenOrientation.Landscape;

静态方法

//设置分辨率 一般移动设备不使用 true表示全屏
Screen.SetResolution(1920,1080,false);

Camera可编辑参数说明

Clear Flags

skybox 天空盒(3D)       Solid Color 颜色填充(2D)       

Depth only 只画该层,背景透明(于Dept配合使用)        Don‘t Clear 不移除 覆盖渲染

深入讲解:在Unity中使用多个相机 - 及其重要性 - 简书 (jianshu.com)

Culling Mask

选择性渲染部分层级(可以指定只渲染对应层级的对象)

Projection

Clipping Planes

裁剪平面距离

Depth

渲染顺序上的深度 值越小越先被渲染 后渲染的内容可以挡住先渲染的内容

可以做到让场景中的多个摄像机叠加显示(Depth only)

Target Texture

渲染纹理:       

        可以把摄像机画面渲染到一张图上(用于制作小地图)

        在Project右键创建 Render Texture

Occlusion Culling

是否启用剔除遮挡(会让被挡住的不被渲染)

其它

Camera代码相关

重要静态成员

//1.获取摄像机
//如果用之前的知识 来获取摄像机 定义公共变量,拖进来
//主摄像机的获取
//若想要通过这种方式,必须要有MainCamera的tag
print(Camera.main.name);

//获取摄像机的数量
print(Camera.allCamerasCount);

//得到所有摄像机
Camera[] allCamera = Camera.allCameras;
print(allCamera.Length);

//2.渲染相关委托
//摄像机剔除前处理的委托函数
Camera.onPreCull += (c) =>
{

};
//摄像机渲染前处理的委托函数
Camera.onPreRender += (c) =>
{

};
//摄像机渲染后处理的委托函数
Camera.onPostRender += (c) =>
{

};

重要成员

//1.界面上的参数 都可以在Camera中获取到
//比如 下面是得到 主摄像机对象 上的深度
Camera.main.depth = 10;

//2.世界坐标转屏幕坐标
//xy对应屏幕坐标 v对应离摄像机有多远
//我们会用来做的最多的功能 头顶血条
Vector3 v = Camera.main.WorldToScreenPoint(this.transform.position);

//3.屏幕坐标转世界坐标
//之所以改变z轴 是因为 不改 默认为0
//转换过去的世界坐标的点 永远是一个点 可以理解为 视口 相交的焦点
//若改变z 则相当于距摄像机前方多少单位的横截面上的点
Vector3 v = Input.mousePosition;
v.z = 5;
obj.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);

核心系统

光源组件

面板参数

Type

Color & Mode & Intensity

Shadow Type

Cookie

投影遮罩(光图形贴图)

Draw Halo & Flare

Draw halo(光晕效果)  Flare(耀斑效果,摄像机要加上Flare layer脚本)

Culling Mask

剔除遮罩层,决定哪些层会受到光源影响

其他

代码控制

直接改面板上的值就够用

light.intensity = 0.5f;

光相关面板

Window->Rendering->Lighting Settings

Environment

OtherSettings

碰撞检测

刚体

碰撞产生的必要条件:两个物体都有碰撞器 至少一个物体有刚体(加入RigidBody脚本)

注意点:

物体有刚体脚本才能受到力的作用

RigidBody组件信息

碰撞器

碰撞器表示物体的体积(形状)  刚体会利用体积进行碰撞计算 模拟真实的碰撞效果,产生力的作用

3D碰撞器种类

  1. 盒状
  2. 球状
  3. 胶囊
  4. 网格
  5. 轮胎
  6. 地形

共同参数

常用碰撞器

异形物体使用多种碰撞器组合

刚体对象的子对象碰撞器信息 参与碰撞检测

给父对象加上刚体, 子对象也会参与碰撞

不常用碰撞器

物理材质

创建物理材质

Project + -> Physic Material

物理材质参数

碰撞检测函数

碰撞和触发响应函数 属于 特殊的生命周期函数(在物理帧更新和逻辑帧更新之间) 也是通过反射调用

物理碰撞检测响应函数

//碰撞触发接触时会 自动执行这个函数
private void OnCollisionEnter(Collision collision)
{
    //Collision类型的参数 包含了 碰到自己的对象的相关信息

    //关键参数
    //1.碰撞到的对象碰撞器的信息
    //collision.collider

    //2.碰撞对象的依附对象(GameObject)
    //collision.gameObject

    //3.碰撞对象的依附对象的位置信息
    //collision.transform
    
    //4.触碰点数相关
    //collision.contactCount
    //接触点 具体的坐标
    ContactPoint[] pos = collision.contacts;

    //只要得到了 碰撞到的对象的 任意一个信息 就可以得到它的所有信息

    print(this.name + "被" + collision.gameObject.name + "撞到了");
}    

//碰撞结束分离时 会自动执行的函数
private void OnCollisionExit(Collision collision)
{
    print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
}

//两个物体相互接触摩擦时 会不停的调用该函数
private void OnCollisionStay(Collision collision)
{
    print(this.name + "一直在和" + collision.gameObject.name + "接触");
}

触发器检测响应函数

//触发开始的函数 当第一次接触时 会自动调用
private void OnTriggerEnter(Collider other)
{
    print(this.name + "被" + other.gameObject.name + "触发了");
}

//触发结束的函数 当水乳相融的状态结束时 会自动调用
private void OnTriggerExit(Collider other)
{
    print(this.name + "和" + other.gameObject.name + "结束水乳相融的状态了");
}

//当两个对象 水乳相融时 会不停调用
private void OnTriggerStay(Collider other)
{
    print(this.name + "和" + other.gameObject.name + "正在水乳相融");
}

要明确什么时候会响应函数

1. 只要挂载的对象 能和别的物体产生碰撞或触发 那么对应的6个函数就能被响应

2. 6个函数一般根据需求来选择书写

3. 如果是一个异形物体,刚体在父对象上, 如果想通过子对象上挂脚本检测碰撞是不行的,必须挂载在这个刚体父对象上

4. 要明确 物理碰撞和触发器响应的区别

碰撞和触发器函数都可以写成虚函数 在子类去重写逻辑

碰撞和触发器函数不需要我们主动调用,所以基本是private或protected

一般会把想要重写的 碰撞和触发函数 写成保护类型的 没有必要写成public 都是通过Unity通过反射帮助我们自动调用的

刚体加力

刚体自带添加力的方法

给刚体加力的目标就是让其具有一个速度 朝向某一个方向移动

Rigidbody rigidBody;
...
//1.首先应该获取刚体组件
rigidBody = this.GetComponent<Rigidbody>();

//2.添加力
//相对世界坐标
//世界坐标 z轴正方向加了一个力
//加力后 对象是否停止移动 是由阻力决定而读(Start中)
//若阻力为0 那么不会停止移动
//若想要一直动 放在Update中一直推
rigidBody.AddForce(Vector3.forward * 10);
//若想要在世界坐标系中 让对象 相对于自己面朝向动
rigidBody.AddForce(this.transform.forward * 10);

//相对本地坐标系
rigidBody.AddRelativeForce(Vector3.forward * 10);

//3.添加扭矩力 让其旋转
//相对世界坐标
rigidBody.AddTorque(Vector3.up * 10);

//相对本地坐标系
rigidBody.AddRelativeTorque(Vector3.up * 10);

//4.直接改变速度
//这个速度方向相对于世界坐标系
rigidBody.velocity = Vector3.forward * 5;

//5.模拟爆炸效果
//只对调用这个方法的对象起作用
//模拟爆炸的力 一定是 所有希望产生爆炸效果影响的对象
//都需要得到他们的刚体 来执行这个方法 才能都有效果
//力 中心 半径
rigidBody.AddExplosionForce(10, Vector3.zero, 10);

力的几种模式

//第二个参数 力的模式 主要的作用 就是 计算方式不同
//由于4种计算方式不同 最终移动速度不同
rigidBody.AddForce(Vector3.forward * 10, ForceMode.Acceleration);

//动量定理
//Ft = mv
// v = Ft/m

//1.Acceleration
//给物体增加一个持续的速度, 忽略其质量
//v = Ft/m
//F:(0,0,10)
//t:0.02s  物体帧时间
//m:默认为1
//v = 10 * 0.02 / 1 = 0.2m/s
//每物理帧移动 0.2m/s * 0.02s = 0.004m

//2.Force
//给物体添加一个持续的力, 与物体的质量有关
//m:2kg
//v = 10 * 0.02 / 2 = 0.1m/s
//每物理帧移动 0.1m/s * 0.02s = 0.002m

//3.Impulse
//给物体添加一个瞬间的力 ,与物体质量有关 忽略时间 默认为1
//t:1s
//v = 10 * 1 / 2 = 5m/s
//每物理帧移动 5m/s * 0.02s = 0.1m

//4.VelocityChange
//给物体添加一个瞬时速度 忽略质量 忽略时间
//v = 10 * 1 / 1 = 10m/s
//每物理帧移动 10m/s * 0.02s = 0.2m

力场脚本

Constant Force 脚本

控制持续的力和旋转

刚体的休眠

Unity为了节约性能,会在某些情况刚体休眠

//获取刚体是否处于休眠状态
if(rigidBody.IsSleeping())
{
    //若是 则唤醒
    rigidBody.WakeUp();
}

音效系统

音频文件导入

音频源和音频监听脚本

添加音频源脚本 Audio Source

音频监听脚本AudioListener

有AudioListener才能听到声音,场景中只用一个该脚本(主视角摄像机)

代码控制音频源

代码控制播放停止

AudioSource audioSource;
...
audioSource = this.GetComponent<AudioSourcce>();
...
if(Input.GetKeyDown(KeyCode.P))
{
    //播放
    audioSouce.Play();
    //延迟播放 5s
    audioSouce.PlayDelayed(5);
}
if(Input.GetKeyDown(KeyCode.S))
{
    //停止
    audioSouce.Stop();
}
if(Input.GetKeyDown(KeyCode.Space))
{
    //暂停
    audioSouce.Pause();
}

if(Input.GetKeyDown(KeyCode.X))
{
    //停止暂停,和暂停后play效果一样
    audioSouce.UnPause();
}

如何检测音效播放完毕

//Update中不断检测
if(audioSource.isPlaying)
{
    print("播放中");
}
else
{
    print("播放结束");
}

如何动态控制音效播放

1.直接在要播放的音效的对象上挂载脚本 控制播放

2.实例化挂载了音效源脚本的对象

        创建一个挂载音效脚本的预设体 playOnWake

        其实用的比较少,因为还要控制销毁对象

public GameObject obj;
Instantiate(obj);

3.用一个AudioSource来控制播放不同的音效

AudioSource audioSource;
publc AudioClip clip;
...
AudioSource aus = this.gameObject.AddComponent<AudioSource>();
aus.clip = clip;
aus.Play();

//潜在知识点
//一个GameObject可以挂载多个 音效源脚本AudioSource
//使用时要注意 如果要挂载多个 一定要管理他们 控制播放 停止
//不然没办法获取谁是谁

麦克风输入相关

获取设备麦克风信息

string [] strs = Microphone.devices;
for(int i = 0; i < strs.Length; i++)
{
    print(strs[i]);
}

开始录制

private AudioClip clip;
//参数一 设备名 传空使用默认设备
//参数二 超过录制长度后 是否从头录制
//参数三 录制时长 s
//参数四 采样率
if(Input.GetKeyDown(KeyCode.Space))
{
    clip = Microphone.Start(null, false, 10, 44100);
}
// 返回 AudioClip

结束录制

if(Input.GetKeyUp(KeyCode.Space))
{
    Microphone.End(null);
    //第一次获取 没有才添加
    AudioSource s = this.GetComponent<AudioSource>();
    if( s == null )
        s = this.gameObject.AddComponent<AudioSource>();
    s.clip = clip;
    s.Play();
}

获取音频数据用于存储或传输

//规则 用于存储数组数据长度 用 声道数 * 剪辑长度
float[] f = new float[clip.channels * clip.samples];
clip.GetData(f,0); //第二参数为偏移位置
//输出采样率
print(f.Length);

完结😊

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity是一款广泛应用于游戏开发的跨平台游戏引擎,它为开发者提供了丰富的工具和资源,帮助他们创建高质量的游戏作品。其中一个非常重要的组件就是天空盒,它用来渲染游戏中的天空和背景。 Unity的天空盒(Skybox)是一个360度全景图片,它被用来模拟游戏场景中的天空。天空盒可以是一个图片或者一组图片,通过将这些图片投射到一个立方体或球体表面上,使得玩家在游戏中观察到的天空整体效果更加真实。 在使用Unity中的天空盒时,开发者可以选择使用预设的天空盒或自定义的天空盒。预设的天空盒包含了一些常见的天气和场景效果,比如晴朗、阴云、夜晚、雨天等。而自定义的天空盒则提供了更多的创作自由,开发者可以根据游戏的需求,使用自己制作的图片或材质来创建独特的天空效果。 为了让天空盒能够更好地适应游戏场景Unity还提供了一些调整选项。开发者可以调整天空盒的亮度、颜色、旋转、纹理过滤等参数,以达到想要的效果。此外,Unity还支持动态天空盒,即可以根据时间、季节或其他事件的改变而实时调整天空盒的显示效果。 总的来说,Unity的天空盒是一项非常重要的功能,它能够为游戏提供逼真的天空背景,为玩家带来更加沉浸式的游戏体验。开发者可以根据自己的需求选择预设的天空盒或自定义的天空盒,并通过调整参数来创造出独特的天空效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值