基础知识

快捷键


工作原理

反射机制和游戏场景

GameObject类对象存在于游戏场景中的所有对象里,是最最基础的对象;

GameObject对象添加各种各样的组件(其实就是C#脚本),来使各个对象有不同的功能

Unity通过反射new出挂在GameObject上C#脚本的对象,并让脚本对象与GameObject关联

游戏场景本质是YAML配置文件

预设体(预制体)

预先设置好的物体,相当于模板

将场景中,由多个对象组成的单个对象,可以变为预设体

创建方法

直接将此物体(SimpleObject)拖到Project窗口的文件夹内就可以生成预设体

使用

可以直接拖入场景使用

修改

修改预设体后,选中预设体,在Inspector窗口中单击Overrides,点击ApplyApply All就可以完成修改

修改过后,所有预设体实例同步更新

单独编辑

右键open可以单独编辑预设体


脚本基础

MonoBehavior类

  1. 只有继承了这个类,才能挂载在GameObject上;

  2. 继承了这个类的子类不能new

  3. 使用DisallowMultipleComponent特性可以使该脚本只能在同一物体上挂一次

  4. 遵循面向对象继承多态原则;

不继承Mono类

  1. 不能挂在GameObject上;

  2. 使用时需要自己new

  3. 一般为单例模式的类(用于管理)或者数据结构类(用于存储数据);

执行先后顺序

脚本右上角Execution Order...可以管理脚本执行的先后顺序


生命周期函数

  1. 一般为privateprotected

  2. 依附的对象取消激活后,所有的生命周期函数都会暂停

  3. 生命周期函数可以继承,支持多态;

  4. 如果生命周期函数没有写逻辑,就不要写该生命周期函数,会造成额外开销;

Awake

该脚本对象创建时调用,且只调用一次,类似构造函数

void Awake()
{
}

OnEnable

依附的对象激活时调用,当一个对象被激活时,进行逻辑处理

void OnEnable()
{
}

Start

第一次帧更新之前调用,只能调用一次,比Awake晚执行

void Start()
{
}

FixedUpdate

用于物理计算相关的处理,每帧执行,自定义每帧的间隔时间

void FixedUpdate()
{
}

Project Settings中的Time可以更改间隔时间(Fixed Timestep)

Update

游戏核心逻辑帧更新,晚于FixedUpdate

void Update()
{
}

LateUpdate

晚于Update的帧更新

一般用于处理摄像机位置更新相关内容,因为在UpdateLateUpdate之间,Unity会进行渲染相关更新

void LateUpdate()
{
}

OnDisable

依附的对象取消激活时调用,当一个对象被取消激活时,进行逻辑处理

void OnDisable()
{
}

OnDestory

依附的对象销毁时调用

void OnDestroy()
{
}

辅助特性

强制序列化特性

[SerializeField]
[SerializeField] public int id;

编辑器里隐藏特性

[HideInInspector]
[HideInInspector] public string card;

分组特性

为成员分组

[Header("分组说明")]
[Header("Array value")]
public float[] floats;
public List<int> list;
​
[Header("Other value")]
public ETestEnum testEnum;
public GameObject obj;

悬停注释

为变量添加说明

[Tooltip("说明内容")]
[Tooltip("This is a test enum value.")] public ETestEnum testEnum;

间隔特性

让2个字段间出现间隔

[Space]
public float[] floats;
[Space]
public List<int> list;

修饰数值滑条范围

[Range(最大值,最小值)]
[Range(0, 100)]
public float[] floats;

多行显示字符串

默认显示3行,传入参数显示对应行

[Multiline(行数)]
[Multiline]
public string card;

滚动条显示字符串

默认超过3行显示滚动条

[TextArea(最少显示行数,最多显示行数“超过这个数显示滚动条”)]
[TextArea(3, 4)]
public string life;

变量添加快捷方法

为变量的右键菜单添加一个可以执行对应方法的按钮(方法必须无参无返回值,且在同一个脚本文件中

[ContextMenuItem("显示按钮名", "方法名")]
[ContextMenuItem("Add money", "AddMoney")]
public int money;
​
void AddMoney()
{
    money += 100;
}

添加在Inspector执行的方法

为该脚本中的某个函数添加能够在Inspector中执行的方法

[ContextMenu("显示名称")]
[ContextMenu("Debug test")]
void PrintTest()
{
    Debug.Log("Test func.");
}


MonoBehavior重要内容

重要成员

获取依附的对象
gameObject
依附对象的名称
name
依附对象的位置信息
transform.position; // 位置
transform.rotation; // 旋转
transform.eulerAngles; // 角度
transform.lossyScale; // 缩放
设置脚本激活

并不会马上停止运行,还是先运行完,再停止

enabled = true;

重要方法

所有获取组件的函数,都会搜索整个父子层级,即,父对象的父对象,子对象的子对象等等

获取依附对象上的其它脚本

获取当前依附对象上,某个脚本类的单个对象

GetComponent()
根据脚本名称获取
TestEmpty testEmpty = GetComponent("TestEmpty") as TestEmpty;
根据Type获取
TestEmpty testEmpty2 = GetComponent(typeof(TestEmpty)) as TestEmpty;
根据泛型获取(推荐)

主要使用的获取方式

TestEmpty testEmpty3 = GetComponent<TestEmpty>();

获取依附对象上的所有特定脚本

获取某个类的脚本在当前依附物体上所存在的所有对象

GetComponents<>()
TestEmpty[] testEmpties = GetComponents<TestEmpty>();
foreach (var t in testEmpties)
{
    Debug.Log(t);
}

获得子对象特定脚本

获取当前依附对象的子对象挂载的特定脚本(会检测当前依附对象是否挂载)

GetComponentInChildren<>(是否检测取消激活的子对象“默认为false”);
TestEmpty t = GetComponentInChildren<TestEmpty>();


获取子对象所有特定脚本

获取当前依附对象的子对象挂载的所有特定脚本(会检测当前依附对象是否挂载)

GetComponentsInChildren<>(是否检测取消激活的子对象“默认为false”);
var list = new List<TestEmpty>();
GetComponentsInChildren(true, list);();

获取父对象特定脚本

获取当前依附对象的父对象上的特定脚本对象(如果当前依附对象没有父对象,则默认检测当前依附对象是否挂载特定脚本)

GetComponentInParent<>()
TestEmpty t = GetComponentInParent<TestEmpty>();

获取父对象所有特定脚本

获取当前依附对象的父对象上所有的特定脚本对象(如果当前依附对象没有父对象,则默认检测当前依附对象是否挂载特定脚本)

GetComponentsInParent<>()
TestEmpty[] t = GetComponentsInParent<TestEmpty>();

安全获取组件

防止返回值为null,改函数返回值为bool类型,传入需要赋值的变量

TryGetComponent<>()
if (TryGetComponent<TestEmpty>(out var t))
{
    Debug.Log(t);
}

重要组件和API

以下规定:脚本=组件

GameObject

成员变量

名称

可修改

gameObject.name
是否激活

不可修改

gameObject.activeSelf
静态

可修改

gameObject.isStatic
层级

可修改

gameObject.layer


标签

可修改

gameObject.tag
变换

可修改内部变量

gameObject.transform
transform.position = new Vector3(1, 1, 1);

静态方法

创建自带几何体
GameObject.CreatePrimitive(PrimitiveType枚举);
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.name = "Create Cube";
查找对象(不推荐)

无法查找到取消激活的对象

这些方法都会影响性能,查找时会遍历所有对象,不推荐使用

对象名查找单个对象
GameObject.Find("对象名称");
var o = GameObject.Find("Cube");
if (o != null)
{
    Debug.Log(o);
}
标签查找单个对象
GameObject.FindWithTag("标签名称")
GameObject.FindGameObjectWithTag("标签名称")
查找多个对象

只能通过标签查找

GameObject.FindGameObjectsWithTag("标签名称")
实例化(克隆)对象

通过传入预设体场景中对象来进行动态创建物体(克隆一个对象)

GameObject.Instantiate(Object对象)
public Object cloneObj;
Object o = GameObject.Instantiate(cloneObj);

删除对象

不仅可以删除场景物体,还可以删除脚本(组件),资产等等

移除时不会马上移除,而是打上移除标识,在下一帧将对象从内存中移除

GameObject.Destroy(Object对象,延迟几秒删除“默认0s”);
GameObject.DestroyImmediate(Object对象); // 马上移除对象,不推荐使用
public Object cloneObj;
GameObject.Destroy(cloneObj);
过场景不删除

默认下,切换场景时,场景中的对象会被自动删除

使用函数使某个对象过场景不被删除

GameObject.DontDestroyOnLoad(Object对象);
GameObject.DontDestroyOnLoad(this.gameObject);

成员方法

创建空物体
GameObject 对象变量名 = new GameObject();
GameObject 对象变量名 = new GameObject("名称");
GameObject 对象变量名 = new GameObject("名称", typeof(脚本1), typeof(脚本2), ...);
为对象添加脚本(组件)
组件类型 组件变量名 = 对象变量名.AddComponent<组件类型>();
标签比较
对象变量名.CompareTag("标签名称")
设置激活状态
对象变量名.SetActive(是否激活);
让其它对象执行函数(不推荐)
对象变量名.SendMessage("函数名")
对象变量名.SendMessage("函数名", 传入值)
​
// 让自己和自己的子对象执行
对象变量名.BroadcastMessage("函数名")
// 让自己和父对象执行
对象变量名.SendMessageUpwards("函数名")

Time

时间缩放比例

默认为1,大于1加速,小于1减速

Time.timeScale = 1;

帧间隔时间

最近的一帧用了多长时间(秒),不可修改

主要用来计算位移(路程=时间*速度)

时间缩放比例影响
Time.deltaTime
不受时间缩放比例影响
Time.unscaledDeltaTime

游戏开始到现在的时间

主要在单机游戏中计时,不能修改

时间缩放比例影响
Time.time
不受时间缩放比例影响
Time.unscaledTime

物理帧间隔时间

时间缩放比例影响
Time.fixedDeltaTime
不受时间缩放比例影响
Time.fixedUnscaledDeltaTime

帧数

游戏从开始到现在跑了多少帧(执行了几次Update,游戏进行了几次循环)

可以作用于帧同步

Time.frameCount

Transform

Unity坐标系

坐标轴

含义

代码

坐标

+X

Vector3.right

(1,0,0)

-X

Vector3.left

(-1,0,0)

+Y

Vector3.up

(0,1,0)

-Y

Vector3.down

(0,-1,0)

+Z

Vector3.forward

(0,0,1)

-Z

Vector3.back

(0,0,-1)


坐标

世界坐标

相对于世界坐标系的位置

transform.position
本地坐标

相对于父对象坐标系的位置

transform.localPosition
对象朝向
面朝向
transform.forward

蓝色代表Z轴,是表示物体面朝的方向

让蓝色的Z轴围绕绿色的Y轴旋转,就是在改变物体的面朝向

将Rotation的Y设置成90,就是改变了物体的面朝向

由此得出下表:

Rotation

transform.forward

蓝色Z轴朝向

(0,0,0)

(0,0,1)

前面

(0,90,0)

(1,0,0)

左边

(0,180,0)

(0,0,-1)

后面

(0,-90,0)

(-1,0,0)

右边

上下朝向
transform.up
左右朝向
transform.right
位移

假定Unity使用1毫米为一个Unity距离

路程(毫米/帧) = 方向 速度 时间

手动计算

物体根据自身坐标系的方向位移:

transform.position += -transform.right * (1 * Time.deltaTime);

物体根据世界坐标轴的方向(固定)位移:

transform.position += Vector3.left * (1 * Time.deltaTime);

Translate
transform.Translate(方向 * (速度 * 帧间隔), 坐标系)

第二个参数:

默认Space.Self

Space.Self:

基于自身的坐标系,移动的方向直接映射到自身坐标系上。

transform.Translate(Vector3.left * (1 * Time.deltaTime), Space.Self);

比如,

移动方向:长蓝色线段

映射到自身坐标系:短蓝色线段

最终移动方向:长黄色线段

Space.World:

基于世界的坐标系,移动的方向直接映射到世界坐标系上。

transform.Translate(-transform.right * (1 * Time.deltaTime), Space.World);

比如,

移动方向:短蓝色线段

映射到世界坐标系:长黄色线段

最终移动方向:长黄色线段

角度

世界(欧拉)角度
transform.eulerAngles
本地(欧拉)角度
transform.localEulerAngles
旋转
transform.Rotate(new Vector3(围绕X轴每帧转几度, 围绕Y轴每帧转几度, 围绕Z轴每帧转几度) * 帧间隔, 坐标系)
transform.Rotate(围绕坐标轴的哪个轴旋转, 每帧转几度 * 帧间隔, 坐标系)
Space.Self

绕着自身坐标轴旋转

transform.Rotate(new Vector3(0, 100, 0) * Time.deltaTime, Space.Self);
// 围绕物体自身向上的坐标轴旋转
transform.Rotate(Vector3.up, 10 * Time.deltaTime, Space.Self); 
Space.World

绕着世界坐标轴旋转

transform.Rotate(new Vector3(0, 100, 0) * Time.deltaTime, Space.World);
// 围绕向上(Y)的世界坐标轴旋转
transform.Rotate(Vector3.up, 10 * Time.deltaTime, Space.World);
围绕着某点进行旋转

围绕着某点进行环形运动,类似于地球绕着太阳转

transform.RotateAround(围绕的点坐标, 围绕的轴, 每帧转几度 * 帧间隔);
// 围绕原点旋转
transform.RotateAround(Vector3.zero, Vector3.up, 100 * Time.deltaTime);

缩放

世界缩放

不能修改

transform.lossyScale
本地缩放
transform.lossyScale
手动修改
transform.localScale = Vector3.one * Time.deltaTime;
看向

物体的面朝向(+Z轴)朝向某一个点(Vector3)或一个对象(Transform)

transform.LookAt(坐标)
// 看向原点
transform.LookAt(Vector3.zero);

父子关系

父对象
断绝所有子类的父子关系

将与当前对象有子关系的所有对象取消与当前对象的父子关系

transform.DetachChildren();
获取子对象
transform.Find("对象名称") // 可以找到取消激活的对象,不能查找子对象的子对象
transform.GetChild(索引); // 通过索引获取子对象
获取子对象数量
  1. 取消激活的子对象也算;

  2. 查找不到子对象的子对象;

transform.childCount
子对象
获取父对象
transform.parent
设置父对象
  1. 普通赋值

    transform.parent = null; // 取消父子关系
    transform.SetParent(null);
    ​
    //设置父对象
    public GameObject newParent;
    transform.parent = newParent.transform;
    transform.SetParent(newParent.transform);
  2. API设置

    第二个参数:

    如果为true,子对象的变换(Transform)会重新计算,计算为相对于父对象的变换(Transform)。

    在场景中体现为,子对象的变换(Transform)还是原来设置父子关系之前的变换(Transform);

    如果为false,不会改变子对象的变换(Transform)。

    在场景中体现为,子对象的变换(Transform)已经不在原来设置父子关系之前的变换(Transform);

    transform.SetParent(Transform对象);
    transform.SetParent(Transform对象, 子对象的变换是否根据父对象变换变化“默认true”);
    transform.SetParent(null);
    transform.SetParent(newParent.transform, false);
判断是否为某个父类的子对象
子对象Transform.IsChildOf(父对象Transform)
获取做为子对象的编号
transform.GetSiblingIndex()
设置为第一个子对象
transform.SetAsFirstSibling()
设置为最后一个子对象
transform.SetAsLastSibling()
设置指定子对象编号

索引超出当前最大或最小子对象编号时,会自动设置为最后一个子对象

transform.SetSiblingIndex(索引);

坐标转换

世界坐标=>本地坐标

世界坐标轴上的,转化为对象坐标轴上的点的位置不变,只是换了参考坐标系)

会受到缩放影响

Vector3 v = transform.InverseTransformPoint(Vector3.forward);
世界方向=>本地方向

世界坐标轴上的方向向量,转化为对象坐标轴上的方向向量方向向量的位置改变

不受到缩放影响(Direction)
Vector3 v = transform.InverseTransformDirection(Vector3.forward);
会受到缩放影响(Vector)
Vector3 v = transform.InverseTransformVector(Vector3.forward);
本地坐标=>世界坐标

会受到缩放影响

transform.TransformPoint(Vector3.forward);
本地方向=>世界方向
不受到缩放影响(Direction)
Vector3 v = transform.TransformDirection(Vector3.forward);
会受到缩放影响(Vector)
Vector3 v = transform.TransformVector(Vector3.forward);

Input(推荐InputSystem

鼠标在屏幕的位置

屏幕坐标原点在游戏画面左下角,右=+X,上=+Y

Input.mousePosition

检测鼠标输入

鼠标输入的一

左键0右键1中键2

按下

瞬间调用一次

Input.GetMouseButtonDown(鼠标按键)
抬起

瞬间调用一次

Input.GetMouseButtonUp(鼠标按键)
长按(按下和抬起)

从按下开始执行,每一帧都会执行(如果在Update中),抬起结束

Input.GetMouseButton(鼠标按键)
中键滚动

返回(X,Y),Y=-1下滚Y=0不动Y=1上滚

Input.mouseScrollDelta

检测键盘输入

按下
Input.GetKeyDown(KeyCode.W)
传入字符串
Input.GetKeyDown("小写按键名称")
抬起
Input.GetKeyUp(KeyCode.W)
长按(按下和抬起)

从按下开始执行,每一帧都会执行(如果在Update中),抬起结束

Input.GetKey(KeyCode.W)
任意按键

包括鼠标按钮

Input.anyKey()
Input.anyKeyDown()
输入字符

获取每帧按下键盘的字符

Input.inputString

检测默认轴输入

返回值是一个浮点数,在-1~1之间,一般表示方向,没有触发时默认0

Input.GetAxis("热键配置名称");

Project Settings里的Input Manager可以进行热键配置

修改Size即可增添热键

比如,左右水平热键

移动设备触摸

if (Input.touchCount > 0)
{
    Touch touch = Input.touches[0];
​
    // 位置
    Vector2 pos = touch.position;
    // 相对上次位置的变化
    Vector2 dpos = touch.deltaPosition;
}
​
// 是否启用多点触控
Input.multiTouchEnabled = true;
​
// 开启陀螺仪
Input.gyro.enabled = true;
// 重力加速度向量
Vector3 gv = Input.gyro.gravity;
// 旋转速度
Vector3 rv = Input.gyro.rotationRate;
// 当前旋转的四元数
Quaternion rq = Input.gyro.attitude;

InputSystem

新输入系统基于事件,输入设备和动作逻辑互相分离,通过配置映射来处理输入信息。具有易用,多设备多平台协调一致的特点。

安装

  1. WindowPackage Manager选择Packages: Unity Registry搜索Input System即可安装


  1. 设置当前输入系统为Input System,重启编辑器

Project SettingsPlayerOther Settings下修改

Input Actions

用来管理各种按键的集合文件,可以定义按键名称,触发方式等等

右键创建Input Actions

注意要点击Save Assets按钮或者勾选Auto-Save才能保持

Action Maps

一个按键映射的集合,包含自定义按键

Actions

按键操作,使用Binding绑定按键

Value是轴操作类型,Button是按钮操作类型

Interactions是触发条件,如,双击,按住不放等等


Player Input

Player Input脚本用于挂载在需要输入操作的对象上

Actions

选择创建的Input Actions资产,Default Map选择默认的按键映射

Behavior

热键触发后需要执行函数的方式

Invoke Unity Events

Events:

需要Behavior选择Invoke Unity Events才能显示;

会出现Input Actions资产中定义的映射表(如,Player,Monster);

Device Lost Event:表示输入设备连接丢失时触发

Device Regained Event:表示输入设备重新连接时触发

Controls Changed Event:表示换输入设备时触发

自定义的映射类下会出现热键函数绑定设置,选择挂载了包含执行函数组件的对象,右侧可以选择执行函数

比如,当前Sphere类挂载了有Jump函数的InputSystemTest组件

InputSystemTest组件:

没有CallbackContext判断默认会执行3次

public class InputSystemTest : MonoBehaviour
{
    // 该无参函数会默认执行3次
    public void Jump()
    {
        Debug.Log("Jump.");
    }
}

执行顺序:context.started -> context.performed -> context.canceled

public void Jump(InputAction.CallbackContext context) 
{
    // 当按键取消后返回true
    if (context.canceled)
    {
        Debug.Log("Jump.");
    }
}
Invoke C Sharp Events

该方法需要在当前对象上挂载用于定义热键触发函数的组件

如,这里新建的InputSystemTest组件

在组件的生命周期函数中添加热键触发事件

using UnityEngine.InputSystem;
​
public class InputSystemTest : MonoBehaviour
{
    private PlayerInput _playerInput;
​
    private void Awake()
    {
        // 获取Player Input组件对象
        _playerInput = GetComponent<PlayerInput>();
        // 添加热键触发事件(当有任意动作被触发时)
        _playerInput.onActionTriggered += Jump;
    }
​
    private void Jump(InputAction.CallbackContext context)
    {
        if (context.performed)
        {
            Debug.Log("Jump.");
        }
    }

自定义Input Actions(推荐)

用这个方法,一个被操控的对象就不用挂载多个脚本去管理输入,只需挂载一个有热键触发的执行函数组件就可以了

自动生成Input Actions类

自动生成Input Actions类用来管理热键触发函数

不要修改这个自动生成类

在Input Actions资产面板选择Generate C# Class自动生成类(默认在Input Actions资产的当前路径,且生成的文件名称与Input Actions资产相同)

在该生成文件的最底部,有Action的触发函数

使用Input Actions类
  1. 创建一个用来存储热键触发函数的组件(如,InputSystemTest

  1. InputSystemTest的生命周期函数中添加需要的事件

    执行顺序:context.started -> context.performed -> context.canceled

    InputAction类对象.ActionMaps映射.Action动作.执行顺序 += 函数
    private void Awake()
    {
        _sphereRigidbody = GetComponent<Rigidbody>();
    ​
        // 创建一个Input Action类对象
        PlayerInputController inputController = new PlayerInputController();
        // 启用指定映射
        inputController.Player.Enable();
        // 添加跳跃事件
        inputController.Player.Jump.performed += Jump;
    }
    ​
    private void Jump(InputAction.CallbackContext context)
    {
        if (context.performed)
        {
            Debug.Log("Jump.");
        }
    }
  2. 使用前需要启用特定的映射Action Maps

    // 启用指定映射
    InputAction类对象.ActionMaps映射.Enable();
  3. 不需要挂载Player Input组件,只需挂载存储热键触发函数的组件(这里是InputSystemTest)就好了


2D Vector

2D向量输入,当Action TypeValue时才能添加

常用于wasd前进后退移动输入

使用方法

组件绑定热键方法

inputVector.x:左右移动浮点数

inputVector.y:前后移动浮点数

private void Awake()
{
    // 创建一个Input Action类对象
    PlayerInputController inputController = new PlayerInputController();
    // 启用指定映射
    inputController.Player.Enable();
​
    // 添加移动事件
    inputController.Player.Movement.performed += Movement;
}
​
private void Movement(InputAction.CallbackContext context)
{
    // context.ReadValue读取轴值
    var inputVector = context.ReadValue<Vector2>();
    Vector3 v = new Vector3(inputVector.x, 0, inputVector.y);
}

热键映射不同输入设备

每个热键映射都有不同输入设备的按键设置

添加的Control Schems是所有热键映射共享的,即每个热键映射都会有Control Schems的按键绑定界面

使用方式
  1. 添加一个Control Schems

  2. 选择一个/多个输入设备

  3. 勾选现有Control Schems即可绑定输入设备

  4. 也可给同一映射添加多个Control Scheme来进行不同输入设备的按键绑定

显示当前所有绑定按键

All Control Schemes可以显示当前映射所有不同输入设备绑定的按键


热键映射切换

组件绑定热键方法

可以动态切换热键映射,所有热键映射必须显式启用才能被触发

InputAction类对象.映射名称.Enable() // 启用
InputAction类对象.映射名称.Disable() // 禁用
private PlayerInputController _playerInputController;
​
private void Awake()
{
    _sphereRigidbody = GetComponent<Rigidbody>();
​
    // 创建一个Input Action类对象
    _playerInputController = new PlayerInputController();
​
    // 启用指定映射
    _playerInputController.Player.Enable();
    // 添加跳跃事件
    _playerInputController.Player.Jump.performed += Jump;
    // 添加移动事件
    _playerInputController.Player.Movement.performed += Movement;
    // 添加怪物热键事件
    _playerInputController.Monster.Print.performed += MonsterPrint;
}
​
private void Update()
{
    // 当键盘的P键按下时,禁用Player按键映射,启用Monster按键映射
    if (Keyboard.current.pKey.wasPressedThisFrame)
    {
        _playerInputController.Player.Disable();
        _playerInputController.Monster.Enable();
    }
}
​
private void MonsterPrint(InputAction.CallbackContext context)
{
    Debug.Log("I am monster.");
}

查看连接的输入设备

Window->Analysis->Input Debugger可以查看当前连接的所有输入设备

双击设备,可以实时监控设备的输入信息

其中显示了Input System包中相应设备的所有值,比如,当前的Keyboard包下的某些成员变量的值


热键重新绑定

组件绑定热键方法

为热键设置新的触发按键,当调用此方法时,立马开始监听,按下的键就是新的触发按键

重新绑定前需要禁用相应映射

需要手动释放RebindingOperation对象

_playerInputController.Player.Disable();
// 重新为热键分配按键
_playerInputController.Player.Jump.PerformInteractiveRebinding()
    // ...还可以添加其它的修饰函数
    // 无法分配鼠标按键
    .WithControlsExcluding("Mouse")
    // 绑定完成后
    .OnComplete(callback =>
    {
        // 启用相应映射
        _playerInputController.Player.Enable();
        // 需要手动释放RebindingOperation对象
        callback.Dispose();
    })
    .Start();
获取新绑定的按键(覆盖按键的新路径)
callback.action.bindings[0].overridePath

Screen

静态属性

当前屏幕分辨率

当前设备的整个屏幕分辨率

Screen.currentResolution
2560 x 1440 @ 240.002978124323Hz
屏幕窗口当前宽高

当前游戏程序窗口的宽高

// 宽
Screen.width;
// 高
Screen.height;
屏幕休眠模式

当游戏运行时,屏幕的休眠模式

Screen.sleepTimeout = SleepTimeout.NeverSleep; // 永不休眠
Screen.sleepTimeout = SleepTimeout.SystemSetting; // 系统设置
全屏模式

游戏运行时是否为全屏模式

Screen.fullScreen = true;
窗口模式
Screen.fullScreenMode = FullScreenMode.Windowed; // 窗口模式
Screen.fullScreenMode = FullScreenMode.MaximizedWindow; // 最大化窗口
Screen.fullScreenMode = FullScreenMode.FullScreenWindow; // 全屏窗口
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen; // 独占全屏
移动设备屏幕转向
Screen.autorotateToPortrait = true; // 正常竖屏
Screen.autorotateToLandscapeLeft = true; // Home键在左边,摄像头在右边
Screen.autorotateToLandscapeRight = true; // Home键在右边,摄像头在左边
Screen.autorotateToPortraitUpsideDown = true; // 倒立竖屏
指定屏幕显示方向
Screen.orientation = ScreenOrientation.LandscapeLeft; // ScreenOrientation枚举

静态方法

设置分辨率(一般移动设备不使用)

Screen.SetResolution(宽, 高, 是否全屏);
Screen.SetResolution(1920, 1080, false);

Camera

组件变量参数

Clear Flags


Skybox

天空盒

Solid Color

颜色填充,针对于2d游戏,Background可以设置填充颜色

Depth Only

只渲染当前摄像机的Depth,不渲染其它摄像机深度

用途:UI单独一个摄像机,游戏场景单独一个摄像机

示例:

Depth = -1, Clear Flags = Skybox

Depth = 0, Clear Flags = Depth Only

Game游戏场景


Culling Mask

该摄像机能够渲染哪些层级

层级


Projection

摄像机摄影模式

Perspective

透视模式

Orthographic

正交模式


FOV Axis

视场角

Vertical

垂直计算FOV

Horizontal

水平计算FOV


Field of View

视野范围


Physical Camera

物理摄像机,模拟现实摄像机


Clipping Planes

裁剪平面距离

Near

摄像机开始渲染的距离(离摄像机多远的距离)

Far

摄像机结束渲染的距离(最大能看见的距离)


Depth

渲染顺序上的深度

当有多个摄像机时,该值越小越先渲染,即,数字小的,会被数字大的画面遮住

示例

UI单独一个摄像机,游戏场景单独一个摄像机


Viewport Rect

视口范围(主要用于双摄像机游戏)

该摄像机渲染的图像处于屏幕中的位置

0~1相当于宽高百分比


Rendering Path

渲染路径


Target Texture

渲染纹理(主要用来制作小地图)

将摄像机捕捉的图像渲染到Render Texture对象上

创建Render Texture并赋值

Render Texture


Occlusion Culling

是否启用剔除遮挡

如果A物体被B物体遮挡,那被遮挡部分不会被渲染,节省性能


HDR

是否允许高动态范围渲染


MSAA

是否允许抗锯齿


Allow Dynamic Resolution

是否运行多态分辨率呈现


Target Display

显示在哪个显示器上


重要静态成员

获取摄像机
主摄像机

标签必须是MainCamera,否则无效

如果有多个主摄像机,那就会随机返回其中一个

Camera.main

所有摄像机

获取场景中所有摄像机

Camera[] cameras = Camera.allCameras;

渲染委托
摄像机剔除前
Camera.onPreCull += ...
摄像机渲染前
Camera.onPreRender += ...
摄像机渲染后
Camera.onPostRender += ...

重要成员

组件参数获取
Camera.main.depth = 0;
...
世界坐标=>屏幕坐标

返回值为Vector3

X:屏幕坐标X,

Y:屏幕坐标Y,

Z:该世界坐标到摄像机的距离(比如,头顶血条2D显示时,利用Z实现近大远小)

摄像机对象.WorldToScreenPoint(世界坐标)
Vector3 screenPoint = mainCam.WorldToScreenPoint(transform.position);
屏幕坐标=>世界坐标

传入的屏幕坐标只有x,y,Z轴是世界坐标到摄像机的距离

摄像机对象.WorldToScreenPoint(屏幕坐标)
Vector3 worldPoint = mainCam.ScreenToWorldPoint(new Vector3(pos.x, pos.y, 10));

Light

Light组件变量参数

Type

光源类型

Spot

聚光灯


Directional

方向光(环境光)

Point

点光源

Area

面光源(仅烘焙)


Range

发光范围距离


Spot Angle

聚光灯光锥角度


Color

光照颜色


Mode

光照模式

Realtime

实时光源

Mixed

混合光源

Baked

烘焙光源(实时+混合光源)


Intensity

光照强度


Indirect Multipler

改变间接光强度

低于1:每次反弹会使光更暗

大于1:每次反弹会使光更亮


Shadow Type

阴影类型

No Shadows

无阴影

Hard Shadows

硬阴影

Soft Shadows

软阴影


RealtimeShadows

实时阴影

Strength

阴影暗度,0~1,类似于阴影透明度

Resolution

阴影分辨率,越高越逼真,越消耗性能

Bias

阴影推离光源的距离

Normal Bias

阴影投射面沿法线收缩距离

Near Panel

渲染阴影的近裁剪面


烘焙贴图


Draw Halo

光晕


Flare

耀斑

摄像机要添加Flare Layer才能看见耀斑


Render Mode

渲染模式

Auto

运行时确定

Important

以像素为单位进行渲染,效果逼真,消耗大

Not Important

以快速模式进行渲染


Culling Mask

剔除遮罩层,决定哪些层的对象受到该光源影响


组件参数获取

light.intensity = .5f;
...

Light设置

Window->Rednering->Lighting打开光照设置面板


Environment

Skybox Material

天空盒材质球


Sun Source

太阳来源

不设置会默认使用场景中最亮的方向光代表太阳


Enviroment Lighting

环境光设置

Source:环境光光源颜色

Skybox:天空和材质作为环境光颜色

Gradient:可以为天空、地平线、地面单独选择颜色和他们之间混合

Color:指定颜色


Rigbody

刚体,用来模拟物理

组件参数

Mess

质量(默认单位千克


Drag

空气阻力

根据力移动对象时,影响对象的空气阻力大小


Angular Drag

根据扭矩旋转对象时,影响对象旋转的空气阻力大小

扭矩旋转:当无重力时,2个有刚体的物体互相碰撞,弹开之后,物体会飘走并旋转


Use Gravity

是否受重力影响


Is Kinematic

启用此选项,则对象不会被物理引擎驱动,只能通过Transform对其进行操作


Interpolate

插值运算

让刚体物体移动的更平滑

None

不进行插值运算

Interpolate

根据一帧的变换来平滑变换

Extrapolate

差值运算

根据一帧的估计变换来平滑变换


Collision Detection

碰撞检测模式

用于防止快速移动的对象穿过其它对象而不进行碰撞检测

Discrete

离散检测

Continuous

连续检测

Continuous Dynamic

动态连续检测(性能消耗高)

Continuous Speculative

连续推测检测

性能消耗比

Discrete<Continuous<Continuous Speculative<Continuous Dynamic

不同检测模式碰撞选择表


刚体加力

自带添加力方法

刚体加力:就是给物体一个有方向有速度的力(如,向后猛推物体)

获取刚体组件
Rigidbody rigidbody = GetComponent<Rigidbody>();

添加力

世界坐标:

刚体组件对象.AddForce(方向 * 力的大小);
rigidbody.AddForce(Vector3.forward * 10);

本地坐标:

刚体组件对象.AddRelativeForce(方向 * 力的大小);
rigidbody.AddRelativeForce(Vector3.forward * 100);

添加扭矩力

添加扭矩力,让其旋转

世界坐标:

刚体组件对象.AddTorque(方向 * 力的大小);
rigidbody.AddTorque(Vector3.up * 10);

本地坐标:

刚体组件对象.AddRelativeTorque(方向 * 力的大小);
rigidbody.AddRelativeTorque(Vector3.up * 100);

直接改变速度

该速度方向是世界坐标

rigidbody.velocity
rigidbody.velocity = Vector3.forward * 5;

模拟爆炸效果

相当于在一个点发生了爆炸,产生的冲击波使物体移动

刚体组件对象.AddExplosionForce(力的大小, 哪个点发生了爆炸, 爆炸半径);
rigidbody.AddExplosionForce(100, Vector3.zero, 10f);

力的模式(ForceMode)

添加力方法都可以传入ForceMode参数,模式不同,力的计算就不同,导致最终的移速也会不同

Force

持续力模式(最符合现实物理)

给物体添加一个持续的力,与物体质量有关


Impulse

损失力模式

给物体添加一个瞬间的力,与物体质量有关,忽略时间


Acceleration

加速度模式

给物体增加一个持续的加速度忽略其质量


VelocityChange

速度改变模式

给物体一个添加一个瞬时速度忽略其质量和时间


力场组件(Constant Force)

相当于之前加力的可视化操作


刚体休眠

当刚体处于休眠状态时,不会进行物理计算

是否正在休眠
刚体组件对象.IsSleeping()
唤醒刚体
刚体组件对象.WakeUp();
休眠后立马唤醒
if (_rigidbody.IsSleeping())
{
    _rigidbody.WakeUp();
}

Collider

碰撞器

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

碰撞器种类

3D碰撞器中除了Box ColliderCapsule ColliderSphere Collider,其它性能消耗都很高


组件参数

Is Trigger

是否是触发器

触发器:发生碰撞时,会被物理引擎忽略,并触发对应的C#事件。主要用于没有物理效果的碰撞检测。

Material

物理材质

可以确定碰撞体和其它对象碰撞时的交互(表现)方式

Center

碰撞体在对象局部空间中的中心点位置


多碰撞器组合

当一个父对象有多个子对象时,直接在父对象添加刚体,就可以将子对象中所有的碰撞器一起检测碰撞(子对象决定父对象的体积/形状)


Physic Material

物理材质

Dynamic Friction

物体移动中的摩擦力

通常在0~1之间,

值为0时:会像在冰面一样滑行

值为1时:会使物体迅速停止

Static Friction

物体静止时的摩擦力

通常在0~1之间,

值为0时:会让物体像冰块一样

值为1时:会让物体很难被移动

Bounciness

表面弹性,0为不反弹,1会一直弹上弹下

Friction Combine

2个碰撞对象摩擦力的组合方式

Bounce Combine

2个碰撞对象弹性的组合方式


碰撞检测函数

注意

  1. 碰撞和触发响应函数属于特殊的生命周期函数,也是通过反射调用;

  2. 与物理碰撞触发相关的响应函数处于FixedUpdateUpdate生命周期函数之间

  3. 如果是多碰撞器组合物体,检测的组件不能怪在子对象上,应该挂在刚体父对象

  4. 碰撞和触发器函数都快写成虚函数,让子类重写逻辑

物理碰撞检测响应函数

碰撞触发接触时

碰撞触发接触时,自动触发一次

private void OnCollisionEnter(Collision other)
{
}
2个物体相互接触时

2个物体相互摩擦时,每帧调用。如果两物体接触后静止不动,就不会触发

private void OnCollisionStay(Collision other)
{
}
碰撞结束时

碰撞结束(2个物体分离时),自动触发一次

private void OnCollisionExit(Collision other)
{
}
Collision

传入的碰撞对象信息

碰撞到的对象的碰撞器信息
Collision类对象.collider
碰撞对象的依附对象
Collision类对象.gameObject
碰撞接触点
Collision类对象.contactCount
碰撞接触点坐标
ContactPoint[] contactPointos = Collision类对象.contacts;

触发器碰撞触发函数

碰撞触发接触时

碰撞触发接触时,自动触发一次

private void OnTriggerEnter(Collider other)
{
}
2个物体相互接触时

2个物体相互接触时,每帧调用。

private void OnTriggerStay(Collider other)
{
}
碰撞结束时

碰撞结束(2个物体分离时),自动触发一次

private void OnTriggerExit(Collider other)
{
}
Collider组件

具体Collider组件


音效系统

Audio Clip

音频片段,就是具体的声音文件(.mp3等等)

Load In Background

启用此选项,该Clip文件会放在单独的线程中加载,因此不会阻塞主线程


Load Type

加载的类型

Decompress On Load

该文件将在加载后立即解压缩

Compressed In Memory

保持文件的压缩状态至内存中,当播放时,才进行解压缩

Streaming

动态解码声音。

该方法使用最少的内存来缓冲从磁盘中增量读取并动态解码的压缩数据


Preload Audio Data

启用之后,此音频将会在场景加载前被预加载


Compression Format

压缩格式


Audio Source

音频源组件,用以发出声音,是连接Audio Listener和Audio Mixer的通道

Output

将Audio Clip输出到Audio Mixer


Priority

优先级


Volume

音量大小


Pitch

播放速度


Stereo Pan

2D模式下,播放音频的位置


Spatial Blend

设置声音为2D还是3D


Reverb Zone Mix

回声效果


3D Sound Settings

3D音效设置


Audio Listener

音频监听器组件,通常与Audio Source一起工作,用来接收声音,一般放在摄像机上面


Audio Mixer

混音器,游戏具有不同的音轨,使用这个可以分开进行控制

规则,就是用来打破的( ̄へ ̄)!