【Unity1】Unity入门笔记
【Unity1】Unity入门笔记
理理目录
perspective透视模式(近大远小,一般用于3D游戏)
orthographic 正交摄像机(一般用于2D游戏制作)
生命周期函数
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson1 : MonoBehaviour
{
//当前类对象被创建时才会调用
//类似构造函数 创建时进行一些初始化操作
void Awake()
{
print("Awake");
}
//当对象被激活时调用
void OnEnable()
{
print("Onenable");
}
//用于初始化信息 但比Awake要晚一点
//在对象进行第一次帧更新之前执行
void Start()
{
print("Start");
}
//它主要是用于进行物理更新
//它的时间间隔是可在project setting中的 Time里去设置的
void FixedUpdate()
{
print("FixedUpdate");
}
//处理游戏核心逻辑性更新的函数,每次循环执行一次
void Update()
{
print("Update");
}
//一般用来更新摄像机位置
//update和Lateupdate之间 Unity进了一些处理处理动画相关的更新
void LateUpdate()
{
print("LateUpdate");
}
//对象失活时调用
void OnDisable()
{
print("Ondisable");
}
//对象被删除时调用
void OnDestroy()
{
print("OnDestroy");
}
}
Inspector窗口可编辑的变量
1.私有和保护类无法显示编辑
2.可以使私有和保护类在窗口中显示编辑,要加上强制序列化字段:
[SerializeField]
private int i1;
3.公共类可以显示编辑
4.公共类也可以使其不能在窗口中显示编辑,要加上隐藏字段:
[HideInInspector]
public string s;
5.大部分类型都能显示编辑:
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using UnityEngine;
public enum E_TestEnum
{
Normal,
Player,
}
public struct MyStruct
{
public int x;
public int y;
}
public class MyClass
{
public int age;
public string name;
}
public class Lesson2 : MonoBehaviour
{
//可以被显示
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 x;
public int y;
}
7.其它补充:
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using UnityEngine;
public class Lesson2 : MonoBehaviour
{
//文字标签分组
[Header("基础属性")]
public int age=10;
public string name;
[Header("战斗属性")]
public int atk;
public int def;
//悬停注释
[Tooltip("闪避")]
public int miss;
//间隔
[Space()]
public int crit;
//显示滑条范围
[Range(0,10)]
public float luck;
//多行显示字符串,默认为三行
[Multiline()]
public string s1;
[TextArea(3,4)]
public string s2;
//添加快捷方法,第一个参数为按钮名,第二个为方法名(不能带参数)
[ContextMenuItem("重置","Test")]
public int Money;
private void Test()
{
Money = 1;
}
//为方法添加特性,使其能在Inspector中执行
[ContextMenu("测试")]
private void testFun()
{
print("测试成功");
}
}
注意:
1. Inspector窗口中的变量关联的就是对象的成员变量,运行时改变他们就是在改变成员变量
2.脚本部署完毕后,再改变脚本中变量的默认值,窗口中的值也不会改变,可以先移除脚本在拖拽上去
3.如果需要使用运行中的值可以先copy再paste
MonoBehavior中的重要内容
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Lesson3 : MonoBehaviour
{
void Start()
{
//1.获取依附的Gameobject
print(this.gameObject.name);//this可省略,可直接用gameObject获取
//2.获取依附的Gameobject的位置信息
//得到对象位置信息
print(this.transform.position);//位置
print(this.transform.eulerAngles);//角度
print(this.transform.lossyScale);//缩放大小
//this.gameObject.transform 与 this.transform相同,故使用后者,this也可省
//控制脚本是否激活:
this.enabled = true;
this.enabled = false;
//得到依附对象上挂载的其它脚本
public Lesson2 Other;
print(Other.gameObject.name);
//1.得到自己挂载的单个脚本
//根据脚本名获取,若没有对应脚本则获取失败返回空值
Lesson3_Test t = this.GetComponent("Lesson3_Test") as Lesson3_Test;//父类装到子类需转换类型
print(t);
//根据Type获取
t = this.GetComponent(typeof(Lesson3_Test)) as Lesson3_Test;
print(t);
//根据泛型获取 建议使用泛型获取 因为不用二次转换
t = this.GetComponent<Lesson3_Test>();
print(t);
//2.得到自己挂载的同种类型的多个脚本
Lesson3[] arr = this.GetComponents<Lesson3>();
List<Lesson3> l = new List<Lesson3>();
this.GetComponents<Lesson3>(l);
//3.得到子对象挂载的脚本,它默认也会找自己身上是否有挂载了该脚本
t = this.GetComponentInChildren<Lesson3_Test>();//圆弧括号内的参数为true/false,若为true则子对象失活也能查找到该脚本,默认为false
//得子对象 挂载脚本 多个
Lesson3_Test[] lts = this.GetComponentsInChildren<Lesson3_Test>(true);
print(lts.Length);
List < Lesson3_Test > list2 = new List<Lesson3_Test>(); this.GetComponentsInChildren<Lesson3_Test>(true, list2);
print(list2.Count);
//4.得到父对象挂载的脚本(它默认也会找自己身上是否挂载该脚本)
t = this.GetComponentInParent<Lesson3_Test>();
print(t);
lts = this.GetComponentsInParent < Lesson3_Test > (); print(lts.Length);
//5.尝试获取脚本
if(this.TryGetComponent<Lesson3_Test>(out t))
{
//相关处理
}
}
}
GameObject知识点
成员变量
//名字
print(this.gameObject.name);
this.gameObject.name = "改名";
print(this.gameObject.name);
//是否激活
print(this.gameObject.activeSelf);
//是否为静态
print(this.gameObject.isStatic);
//层级(int类型)
print(this.gameObject.layer);
//标签(string类型)
print(this.gameObject.tag);
//transform
print(this.gameObject.transform);//GameObject中也有transform
//但Mono中也有所以与this.transform效果相同
静态方法
创建对象
可以通过GameObject.CreatePrimitive方法创建已有几何体,并且可对其内容进行修改:
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = "创建的立方体";
查找对象
通过名字查找单个对象
使用GameObject.Find()方法,但是效率低下,会遍历场景中所有对象
GameObject obj2 = GameObject.Find("创建");
if (obj2 != null)
{
print("未找到对应对象");
}
else
{
print(obj2.name);
}
通过Tag来查找单个对象
使用GameObject.FindGameObjectsWithTag("Tagname")或者GameObject.FindWithTag("Tagname")来查找,二者相同
GameObject obj3 = GameObject.FindWithTag("Player");
if (obj3 != null)
{
print("根据tag找到的对象" + obj3.name);
}
else
{
print("根据tag未找到对象");
}
以上两种查找方式都找不到失活的对象,而且若查找到多个符合条件的对象无法确定返回的是哪一个
通过Tag查找多个对象
使用GameObject.FindGameObjectsWithTag("Tagname"),也找不到失活的对象
GameObject[] objs = GameObject.FindGameObjectsWithTag("Player");
print(objs.Length);
找到场景中挂载某个脚本
使用GameObject.FindObjectOfType<>(),效率更加低下,遍历场景中的对象还要遍历对象上的所有脚本。
Lesson4 o = GameObject.FindObjectOfType<Lesson4>();
print(o.gameObject.name);
实例化对象(克隆对象)的方法
作用是根据已有的GameObject对象,创建出一个与之一样的对象。
一种是克隆场景上已有的对象,另一种是克隆预设体,将对象或者预设体拖拽过去即可。
使用GameObject.Instantiate()函数,该函数属于Object类中,故如果继承了MonoBehavior类使用时可以不加上GameObject直接使用
GameObject obj5 = GameObject.Instantiate(pre);
删除对象的方法
使用GameObject.Destroy("要删除的对象名称",延迟删除时间)方法,该函数也是在Object类中,如果继承了MonoBehavior类也可以直接使用。
GameObject.Destroy(obj5,5);
Destroy还可以删除指定的脚本对象。
Destroy(this);
注意:这个Destroy方法 不会马上移除对象 只是给这个对象加了一个移除标识
般情况下 它会在下一帧时把这个对象移除并从内存中移除
若想立即删除可以使用GameObject.DestroyImmediate()函数,如果没有特殊需求就不需要使用这个方法,由于Destroy方法是异步的可以减少游戏的卡顿
过场景不移除
默认情况下,切换场景时,场景上的所有对象都会被自动删除,若希望某个对象在过场景时不被删除,使用GameObject.DontDestroyOnLoad();
GameObject.DontDestroyOnLoad(this.gameObject);
//当前脚本所依附的对象过场景不删除
成员方法
创建空物体
new一个GameObject就是在创建一个空物体
GameObject obj6 = new GameObject();
GameObject obj7 = new GameObject("创建的空物体");
GameObject obj8 = new GameObject("创建挂脚本的空物体", typeof(Lesson1), typeof(Lesson2));
为对象添加脚本
继承了MonoBehavior的脚本,是不能够new出来的,若要动态添加继承了MonoBehavior的脚本在某一个对象上则直接使用AddComponent:
Lesson2 l2 = obj6.AddComponent(typeof(Lesson2)) as Lesson2;
Lesson1 l1 = obj6.AddComponent<Lesson1>();//使用泛型更方便
得到脚本的成员方法和继承MonoBehavior的类得到脚本的方法相同,故不再赘述
标签比较
if (this.gameObject.CompareTag("Player"))//括号内也可以写成this.gameObject.tag == "Player"
{
print("该对象的标签是Player");
}
else
{
print("该对象的标签不是Player");
}
设置失活/激活
参数传false失活,true激活
obj6.SetActive(false);
obj7.SetActive(false);
obj8.SetActive(false);
次要的成员方法
了解即可,不建议使用,效率低
this.gameObject.SendMessage("函数名");//自己查找执行该函数
this.gameObject.BroadcastMessage("函数名");//自己和自己的子对象都查找执行该函数
this.gameObject.SendMessageUpwards("函数名");//自己和自己的父对象都查找执行该函数
这些要查找执行的函数都可以带参数,参数写在函数名后面即可
时间相关_Time
时间缩放比
//时间停止
Time.timeScale =0;
//回复正常
Time.timeScale = 1;
//2倍速
Time.timeScale=2;
帧间隔时间
最近的一帧用了多长时间
受scale影响
print("受scale影响的时间"+Time.deltaTime);
不受scale影响
print("不受scale影响的时间" + Time.unscaledDeltaTime);
帧间隔时间,主要用来计算位移,要根据需求选择计算的时间间隔,如果希望游戏暂停时就不动的就用前者,如果希望不受暂停影响就用后者。
游戏开始到现在的时间
受scale影响
print("游戏开始到现在的"+Time.time);
不受scale影响
print("不受scale影响的游戏开始到现在的时间"+Time.unscaledTime);
物理帧时间间隔
受scale影响
print(Time.fixedDeltaTime);
不受scale影响
print(Time.fixedUnscaledDeltaTime);
帧数
从开始到现在游戏跑了多少帧,即多少次循环
print(Time.frameCount);
Transform
游戏对象(Gameobject)位移、旋转、缩放、父子关系、坐标转换等相关操作都由它处理
是Unity提供的极其重要的类
Vector3基础和Transform位置
Vector3主要是用来表示三维坐标系中的一个点或者一个向量
申明变量
Vector3 v = new Vector3();
v.x = 10;
v.y = 10;
v.z = 10;
Vector3 v2 = new Vector3(10,10);//默认z为0
Vector3 v3 = new Vector3(10,10,10);
Vector3 v4;
v4.x = 10;
v4.y = 10;
v4.z = 10;
基本运算
print(v - v3);
print(v + v2);
print(v * 10);
print(v / 10);
常用
print(Vector3.zero);//000
print(Vector3.right);//100
print(Vector3.left);//-100
print(Vector3.forward);//001
print(Vector3.back);//00-1
print(Vector3.up);//010
print(Vector3.down);//0-10
常用的一个方法:计算距离:
print(Vector3.Distance(v2,v*2));
位置
绝对位置
相对于世界坐标
print(this.transform.position);
相对位置
相对于父对象,没有父对象时与position相同
print(this.transform.localPosition);
transform的xyz是不能直接单独修改的,但是可以用vector给他赋值
print(this.transform.position);
print(this.transform.localPosition);
this.transform.position = new Vector3(10, 10, 10);
this.transform.localPosition = Vector3.up * 10;
//如果只想改一个值xy和z要保持原有坐标一致
//1.直接赋值
this.transform.position = new Vector3(19, this.transform.position.y, this.transform.position.z);
//2.先取出来 再赋值
//虽然不能直接改 transform的 xyz 但是 Vector3是可以直接改 xyz的//所以可以先取出来改vector3 再重新赋值
Vector3 vPos = this.transform.localPosition;
vPos.x = 10;
this.transform.localPosition = vPos;
对象当前的朝向
由于对象可以旋转,对象的朝向会与世界坐标朝向不同
print(this.transform.forward);
//对象当前的各朝向
//对象当前的面朝向
print(this.transform.forward);
//对象当前的头顶朝向
print(this.transform.up);
//对象当前的右手边
print(this.transform.right);
Transform位移
自己计算移动
用当前所在位置加上我要移动的位移得到我所在的最终位置
this.transform.position += this.transform.forward*1*Time.deltaTime;//对象面朝向移动
this.transform.position += Vector3.forward*1*Time.deltaTime;//世界坐标系移动
API移动
使用this.transform.Translate()
//参数一:表示位移多少 路程 = 方向 * 速度 * 时间
//参数二:表示相对坐标系默认该参数是相对于自己坐标系的
//相对于世界坐标系的 z轴 动
this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World);
//相对于世界坐标的自己的面朝向去动
this.transform.Translate(this.transform.forward 1 Time.deltaTime, Space.World);
//相对于自己的坐标系下 ,再加上旋转的方向
this.transform.Translate(this.transform.forward * 1 * Time.deltaTime, Space.Self);
//相对于自己的坐标系下的 z轴正方向移动
this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.Self);
一般用API来位移
Transform角度和旋转
角度相关
//相对世界坐标角度
print(this.transform.eulerAngles);
//相对父对象角度
print(this.transform.localEulerAngles);
//注意:设置角度和设置位置一样 不能单独设置xyz 要一起设置
//若要改变面板角度则要设置相对角度
this.transform.localEulerAngles = new Vector3(10,10,10);
this.transform.eulerAngles = new Vector3(10, 10, 10);
print(this.transform.localEulerAngles);
旋转相关
//API计算
//自转
//参数一 每一帧旋转的角度 每个轴具体转多少度
//参数二 默认相对于自己坐标系进行的旋转,Space.World为相对世界坐标系旋转
this.transform.Rotate(new Vector3(0,10,0)* Time.deltaTime);
this.transform.Rotate(new Vector3(0, 10, 0) * Time.deltaTime, Space.World);
//相对于某个轴 转多少度
//参数一:是相对哪个轴进行转动
//参数二:转动的角度
//参数三:默认相对于自己的坐标系 Space.World为相对世界坐标系旋转
this.transform.Rotate(Vector3.right,10* Time.deltaTime);
this.transform.Rotate(Vector3.right, 10 * Time.deltaTime,Space.World);
//相对于某一个点转
//参数一:相当于哪一个点转
//参数二:相对于该点哪一个轴转
//参数三:转的度数旋转速度*时间
this.transform.RotateAround(Vector3.zero, Vector3.right, 10 * Time.deltaTime);
Transform缩放和看向
缩放
缩放不能单独改xyz 只能一起改(并且相对于世界坐标系的缩放大小不能修改)
故更改缩放大小都是相对父对象进行修改
//相对世界坐标系
print(this.transform.lossyScale);
//相对本地坐标系(父对象)
print(this.transform.localScale);
this.transform.localScale=new Vector3(3,3,3);
Unity没有提供关于缩放的API,只能自己算
this.transform.localScale += Vector3.one * Time.deltaTime;
看向
让一个对象一直面向某一个点或者某一个对象
//看向一个点 相对于世界坐标系的
this.transform.LookAt(Vector3.zero);
//看向一个对象 就传入一个对象的 Transform信息
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 会保留 世界坐标下的状态和父对象进行计算得到本地坐标系的信息,实际位置(世界坐标中)不变
//false不会保留 使变成字对象后界面显示的内容和之前相同(在世界坐标中会变化)
this.transform.SetParent(GameObject.Find("Father3").transform, false);
this.transform.SetParent(GameObject.Find("Father3").transform, true);
抛妻弃子
删除不了儿子与孙子之间的父子关系
this.transform.DetachChildren();
获取子对象
可以找到失活的子对象,找不到子对象的子对象
print(this.transform.Find("cube(1)").name);
print(this.transform.childCount);//输出子对象的个数,失活的也算
this.transform.GetChild(0);//通过索引得到子对象,超出个数会报错
子对象操作
public Transform son;
...
if(son.IsChildOf(this.transform))
print("是我的儿子");
//得到自己作为儿子的编号
print(son.GetSiblingIndex());
//把自己设置为第一个儿子
son.SetAsFirstSibling();
//把自己设置为最后一个儿子
son.SetAsLastSibling();
//把自己设置为指定个儿子,超出范围就直接设置成最后一个索引
son.SetSiblingIndex(15);
Transform坐标转换
世界坐标转本地坐标
print(Vector3.forward);
//世界坐标系的点转换为相对本地坐标系的点
//受到缩放影响
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));
Input鼠标键盘输入
鼠标在屏幕的位置
//屏幕左下角为坐标原点 往右是x轴正方向 往上时v轴正方向
//返回值时vector3 但是只有 x和y有值 z一直是0 是因为屏幕本来就是2D的 不存在z轴
print(Input.mousePosition);
检测鼠标输入
可以发射子弹
可以控制摄像机转动
//鼠标按下一瞬间 进入
//只要按下的这一瞬间 进入一次
//0左键 1右键 2中键
if (Input.GetMouseButtonDown(1))
{
print("鼠标某一个键按下了");
}
//鼠标抬起一瞬间 进入
if (Input.GetMouseButtonUp(0))
{
print("鼠标某一个键抬起了");
}
//鼠标长按按下抬起都会进入
//就是 当按住按键不放时 会一直进入 这个判断
if(Input.GetMouseButton(1))
{
print("右键按下");
}
//中键滚动
//返回值的y -1下 0不动 1上
//返回值为Vector2的值 鼠标滚轮滚动会改变其中的y值
print(Input.mouseScrollDelta);
检测键盘输入
//键盘按下
if (Input.GetKeyDown(KeyCode.W))
{
print("w键按下");
}
KeyCode是枚举类型
//传入字符串的重载
//传入的字符串不能是大写的不然会报错
//但是键盘的大小写都能识别
if(Input.GetKeyDown("q"))
{
print("键按下");
}
检测默认轴输入
unity提供了 更方便的方法 来帮助我们控制对象的位移和旋转
//参数名可以看 project setting 中的 input manager
//键盘AD按下时 返回 -1到1之间的变换
//该值就是左右方向-1到0代表左,0到1代表右 我们可以通过它来控制 对象左右移动 或者左右旋转
print(Input.GetAxis("Horizontal"));
//键盘SW按下时 返回 -1到1之间的变换
//该值就是前后方向-1到0代表后,0到1代表前 我们可以通过它来控制 对象上下移动 或者上下旋转
print(Input.GetAxis("Vertical"));
//鼠标横向移动时 -1 到 1 左 右
print(Input .GetAxis("Mouse X"));
//鼠标竖向移动时-1到1下上
print(Input.GetAxis("Mouse Y"));
//GetAxisRaw方法和 GetAxis使用方式相同
//只不过 它的返回值 只会是-101不会有中间值 而GetAxis方法返回的值是-1到0到1的渐变值
Input触摸手柄陀螺仪
检测任意键盘按键
//是否有任意键或鼠标长按
if (Input.anyKey)
print("有一个键长按");
//是否有任意键或鼠标按下
if (Input.anyKeyDown)
print("有一个键 按下");
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);
//屏幕窗口当前宽高
//需要用窗口宽高做计算时使用
print(Screen.width);
print(Screen.height);
//屏幕休眠模式
Screen.sleepTimeout = SleepTimeout.NeverSleep;
Screen.fullScreen = true;
//窗口模式
//独占全屏Fu11screenMode.ExclusiveFullscreen
//全屏窗口FullScreenMode.FullscreenWindow
//最大化窗口FullscreenMode.MaximizedWindow
//窗口模式Fu1lscreenMode.Windowed
Screen.fullScreenMode = FullScreenMode.Windowed;
//移动设备屏幕转向相关
//允许自动旋转为左横向 Home键在左
Screen.autorotateToLandscapeLeft =true;
//允许自动旋转为右横向 Home键在右
Screen.autorotateToLandscapeRight =true;
//允许自动旋转到纵问 Home键在下
Screen.autorotateToPortrait =true;
//允许自动旋转到纵向倒着看 Home键在上
Screen.autorotateToPortraitUpsideDown=true;
//指定屏幕显示方向
Screen.orientation = ScreenOrientation.AutoRotation;
//设置分辨率 一般移动设备不使用
Screen.SetResolution(1920,1080,false);
Camera可编辑参数说明
Clear Flags
skybox天空盒:模拟天空背景,用于3D
Solid Color颜色填充:填充背景颜色,一般用于2D
Depth only只画该层,背景透明:多个摄像机叠加渲染与Depth叠加使用
Don't Clear不移除,覆盖渲染:一般不用
Culling Mask
选择渲染层级,可以指定渲染或不渲染某一层级的对象
Projection
perspective透视模式(近大远小,一般用于3D游戏)
Fov Axis:决定光学仪器的视野范围
Field of view:视口大小
Physical Camera(物理摄像机):摄影相关参数,如Focal Length(焦距)、Sensor Type(传感器类型)、Sensor Size(传感器尺寸)等
orthographic 正交摄像机(一般用于2D游戏制作)
size为大小
Clipping Planes
裁剪平面距离,最近能看多近,最远能看多远
Depth
渲染顺序上的深度,深度大的Camera后被渲染,小的先被渲染,后渲染的会把前渲染的挡住
Redering path
渲染纹理,可以把摄像机画面渲染到一张图上,在Project右键创建Render Texture,主要用于制作小地图
Occlusion Culling
是否启用剔除遮挡,被挡住的模型不被渲染
Viewport Rect
视口范围,屏幕上将绘制该摄像机的视图位置,用于双摄像机游戏,0-1相当于宽高百分比
Rendering path
渲染路径
只做了解
Allow HDR 是否允许高动态范围渲染
Allow MSAA 是否允许抗锯齿
Allow DYnamic Resolution 是否允许动态分辨路呈现
Target Display 用于那个显示器,主要用来开发有多个屏幕的平台游戏
Camera代码相关
重要静态成员
//获取摄像机
//如果用之前的知识 public一个对象然后将camera拖进去 来获取摄像机
//主摄像机的获取
//如果想通过这种方式 快速获取摄像机 那么场景上必须有一个 tag为Maincamera的摄像机
print(Camera.main.name);
//获取摄像机的数量
print(Camera.allCamerasCount);
//得到所有摄像机
Camera[] AllCamera = Camera.allCameras;
print(AllCamera.Length);
//渲染相关委托
//摄像机剔除前处理的委托函数
Camera.onPreCull += (c) =>
{
};
//摄像机 渲染前处理的委托
Camera.onPreRender += (c) =>
{
};
//摄像机 渲染后 处理的委托
Camera.onPostRender += (c) =>
{
};
重要成员
public class Lesson14 : MonoBehaviour
{
public Transform obj;
// Start is called before the first frame update
void Start()
{
//世界坐标转屏幕坐标
//转换过后x和y对应的就是屏幕坐标 z对应的是该物体离摄像机有多远,两者的z之差
//我们会用这个来做的功能 最多的 就是头顶血条相关的功能
Vector3 v = Camera.main.WorldToScreenPoint(this.transform.position);
print(v);
}
// Update is called once per frame
void Update()
{
//屏幕坐标转世界坐标
Vector3 v1 = Input.mousePosition;
//只所以改变z轴 是因为 如果不改 z默认为0
//转换过去的世界坐标系的点 永远都是一个点 可以理解为 视口 相交的焦点
//改变z才能在视野中显示出来
v1.z = 10;
obj.position = Camera.main.ScreenToWorldPoint(v1);
// print(Camera.main.ScreenToWorldPoint(v1));
}
}
光源系统基础
光源组件
Type光源类型
Point:点光源,参数:Range(照射范围)
Spot:聚光灯,方向光(环境光),参数:Range(照射范围)Spot Angle(光锥角度)
Area:面光源(仅在烘焙状态下有用,节约性能,提前将光源效果计算好再将算好的图贴上去,并非实施计算,但是有物体移动到该光源下时,光影效果不受该光源影响)
Color光源颜色
Mode光源模式
Realtime:实时光源,实时渲染,效果好,性能消耗大
Baked:烘焙光源,提前计算好,无法动态变化
Mixed:混合光源,预先计算+实时运算
Intensity光源亮度
Shadow Type
Noshadows:关闭阴影
HardShadows:生硬阴影,性能消耗较低
SoftShadows:柔和阴影,性能消耗较高
Cookie
投影遮罩
Draw Halo
球形光环开关,光源周围产生光晕
Flare
耀斑,若希望摄像机能看到耀斑效果,需要在摄像机上加上Flare Layer脚本,不加脚本摄像机中看不到
Culling Mask
剔除遮罩层 ,决定哪些层的光源对象收到该光源的影响,和Camera中的效果类似
以下了解即可
Indirect Multiplier
调整间接光的强度,大于1每次反射会使光更亮,小于1每次反射会使光更暗
RealtimeShadows
strength:阴影暗度 0-1之间,越大阴影越暗
Resolution:阴影贴图渲染分辨率,越高越逼真、消耗越高
Bias:阴影推理光源的距离
Normal Bias:阴影投射面延法线收缩距离
Near Panel:渲染阴影的近裁剪面
Cookie Size
方向光源(Spot)才有,但一般不设置,用来调整遮罩大小
Render Mode
渲染优先级
auto:运行时确定
Important:以像素质量为单位进行渲染,效果逼真,消耗大
Not important:以快速模式进行渲染
光面板相关
环境相关设置
Skybox Meterial:改变天空和材质
Sun Source:太阳来源,不设置会默认使用场景中最亮的方向光为太阳
Environment Lighting:环境光设置{
Source:环境光光源颜色,Skybox(以天空和材质作为环境光颜色),Gradient(可以为天空、地面、地平线单独选择颜色,得到混合的效果)
Intensity Multiplier:环境光亮度
Ambient Mode:全局光照模式,只有启用了实时全局和全局烘焙时才有用,Realtime(已弃用),Baked(烘焙)
}
其他设置
碰撞检测
碰撞产生的必要条件:两个物体都有碰撞器且至少其中一个是刚体
刚体
Mass:质量(默认为千克),质量越大惯性越大
Drag:空气阻力,0表示没有空气阻力
Angular Drag:扭矩阻力,影响旋转,0表示没有空气阻力
Use Gravity:是否受重力影响
Is Kinematic:若启用则不会被物理引擎影响,只能通过transform对其进行移动
Interpolate:插值运算,让刚体运动更平滑(None(不应用插值运算),Interpolate(根据前一帧的改变平滑变换),Extrapolate(差值运算,根据下一帧的变换来平滑变换))
Collision Detection(碰撞检测模式,用于防止快速移动的物体穿过其他物体而不检测碰撞,一帧移动距离过大直接穿过碰撞对象)
Constraint:约束对刚体运动的限制(Freeze Position:有选择地停止刚体沿着世界xyz轴移动;Freeze Rotation:有选择地停止刚体沿着世界xyz轴旋转)
碰撞器
种类:
盒状、球状、胶囊、网格、轮胎、地形(前三种性能高,后三者性能低)
共同参数:
Is Trigger:是否为触发器,启用该属性则该物体将用于触发事件、并被物理引擎忽略,主要用于没有物理效果的碰撞检测
Material:物理材质,可以确定碰撞体和其他碰撞对象的交互方式
Center:碰撞体在局部控件对象中的中心点位置
常用碰撞器:
BoxCollider(盒状)
Size参数:碰撞体在XYZ轴上的大小
Sphere Collider(球状)
Radius参数:碰撞体的半径大小
Capsule Collider(胶囊)
Radius参数:胶囊体的半径
Height参数:胶囊体的高度
Direction参数:胶囊体在对象局部空间中的轴向
异形物体使用多种碰撞器组合
刚体对象的子对象碰撞器信息参与碰撞检测,给父对象加上刚体脚本,子对象也会参与碰撞
不常用的碰撞器
Mesh Collider(网格碰撞器)、Wheel Collider(环状碰撞器)、Terrain Collider(地形碰撞器)
网格碰撞器:
轮胎:
和刚体父对象一起使用,父对象质量要足够大,不然会被轮胎弹飞
地形碰撞器:
物理材质
创建物理材质
右键create Physic Material后拖拽给碰撞体脚本中的Material栏
物理材质参数
碰撞检测函数
物理碰撞检测响应函数
只要得到了碰撞到的对象的任意信息就可以得到他的所有信息
Collision中包含的碰撞到自己的对象的相关信息
相关参数
碰撞自己的对象的碰撞器的信息
collision.collider
碰撞对象(GameObject)
collision.gameObject
碰撞对象的位置信息
collision.transform
触碰点数相关
collision.contactCount
接触点的具体坐标
ContactPoint[] pos= collision.contacts
函数:
private void OnCollisionEnter(Collision collision)
{
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 + "触发");
}
明确何时响应函数
只要挂载对象和其他物体能产生碰撞或者触发,就能执行对应的碰撞/触发函数
只在子对象上挂载该脚本检测碰撞,如果该子对象没有刚体组件不能执行,需要挂载到他的具有刚体组件的父对象上,挂载到父对象上若只有子对象碰撞到了也会触发
根据需求来写这6个函数
要明确物理碰撞和触发器响应的区别
碰撞和触发器函数都可以写成虚函数,在子类中重写逻辑
一般会把想要重写的碰撞和触发函数写成保护类型的没有必要写成public,因为我们不会手动调用该函数,都是unity通过反射帮助我们调用的
刚体加力
首先要获取刚体
Rigidbody body;
void Start()
{
body = this.GetComponent<Rigidbody>();
}
刚体自带添加力的方法
//添加力
//相对世界坐标系
body.AddForce(Vector3.forward * 10);
//相对本地坐标系
body.AddRelativeForce(Vector3.forward * 10);//body.AddForce(this.transform.forward * 10);效果相同
//添加扭矩力
//相对世界坐标系
body.AddTorque(Vector3.up * 10);
//相对本地坐标系
body.AddRelativeTorque(Vector3.up * 10);
//直接改变速度
//该改动的速度方向是相对于世界坐标系的
body.velocity = Vector3.forward*10;
//模拟爆炸的效果,该爆炸效果只对挂载了该脚本的对象生效
body.AddExplosionForce(10, Vector3.zero, 10);
力的几种模式
//计算方式不同,故最终移动的速度不同
//第二个参数 力的模式 主要的作用 就是 计算方式不同
//由于4种计算方式不同 最终移动速度不同
body.AddForce(Vector3.forward * 10,ForceMode.Force);
//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
//给物体添加一个瞬时速度 忽略质量 忽略时间 默认质量1kg时间1s
//v = 10 * 1 / 1 = 10m/s
//每物理帧移动 10m/s * 0.02s = 0.2m
力场脚本
unity提供了一个常量力的组件:Constant Force 脚本
刚体的休眠
Unity为了节约性能,会在某些情况刚体休眠
//获取刚体是否处于休眠状态
if(rigidBody.IsSleeping())
{
//若是 则唤醒
rigidBody.WakeUp();
}
音效系统
音频文件导入
常用格式
wav、mp3、ogg、aiff
音频文件属性设置
音频源和音频监听脚本
音频源
音频监听脚本
AudioListener只能有一个,一般放在主摄像机上,有该脚本才能听到声音
代码控制音频源
代码控制播放停止
if(Input.GetKeyDown(KeyCode.P))
{
//播放音效
audioSource.Play();
}
if (Input.GetKeyUp(KeyCode.S))
{
//停止音效
audioSource.Stop();
}
if(Input.GetKeyDown(KeyCode.Space))
{
//暂停播放
audioSource.Pause();
}
检测音效是否播放完毕
if(audioSource.isPlaying)
{
print("播放中");
}
else
{
print("播放结束");
}
动态控制音效播放
直接在要播放音效的对象上挂载脚本 控制播放
如同上文的控制音效播放暂停等
实例化挂载了音效源脚本的对象
new一个GameObject,将自动播放音效的的预设体拖到里面,在相应条件下实例化该预设体 ,实际使用较少。
用一个Audiosource来控制播放不同的音效
将对应的音效拖入clip中
public AudioClip clip;
...
AudioSource aud = this.gameObject.AddComponent<AudioSource>();
aud.clip = clip;
aud.Play();
一个Gameobiect可以挂载多个音效源脚本Audiosource
使用时要注意 管理多个音效 控制他们的播放 停止
麦克风输入相关
获取设备麦克风信息
string[] str = Microphone.devices;
for(int i = 0; i < str.Length; i++)
{
print(str[i]);
}
开始录制
//参数一:设备名 传空使用默认设备
//参数二:i超过录制长度后 是否重头录制
//参数三:录制时长
//参数四:采样率
if (Input.GetKeyDown(KeyCode.Space))
{
clip = Microphone.Start(null, false, 10, 44100);
}
结束录制
if (Input.GetKeyUp(KeyCode.Space))
{
Microphone.End(null);
AudioSource s = this.gameObject.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);
场景转换
SceneManager.LoadScene("场景名字");完结