本文最后更新于:10 months ago

不知道该采用什么格式……目前是跟着《Unity3D游戏开发》学,书没看几页版本换了三个。

就按照每天日志来写好了,正好记录下每天看了几页。

补充!而非摘抄!书上的内容不记!

被跳过的部分

第三章

P105

2.5 P97-100

Time.timeScale & Time.deltaTime:时间缩放比例 与 每帧所需时间

Time.timeScale:

  • =1,游戏正常进行,deltaTime = 0.01
  • =0,游戏暂停,deltaTime = 0
  • =0.5,游戏慢放,deltaTime = 0.005,意味着每帧时间缩短,

其影响:

影响 不影响
FixedUpdate Update, LateUpdate(频率取决于帧内计算量,本就没有固定速度)
Time.deltaTime Time.unscaledDeltaTime, Time.fixedDeltatime
Time.time的流逝速率 Time.unscaledTime的流逝速率

其中:

Update和FixedUpdate:每一帧调用一次,帧时间不固定,通常用于监听输入、处理脚本事件

FixedUpdate:固定更新,每fixedTimestep=0.02s调用一次,独立于帧速率(frame rate=FPS),通常用于执行物理计算

deltaTime:上一帧完成的时间,是游戏内时间

fixedDeltatime:是物理计算及fixedUpdate等固定帧更新的时间间隔,是游戏内时间

Unity使用固定时间间隔(fixedTimestep)来执行模拟,fixedUpdate的执行速率完全取决于fixedTimeStep。fixedTimeStep越短,物理更新(physics updates)越频繁,系统负荷越大。

:如果游戏的帧率比fixedTimestep要快,那么就会有一些帧不执行fixedUpdate(fixedTimeStep=0.02,fps=60,则60帧里只有50帧执行fixedUpdate)。

:如果游戏的帧率比fixedTimestep要慢,那么就会每帧会执行>=1次fixedUpdate(fixedTimeStep=0.01,fps=25,则每帧执行4次fixedUpdate)。

img

maximumDeltaTime是用于限制deltaTime,确保在经过很慢的一帧过后过高的deltaTime不会带来问题。

如果有一次帧很慢,而帧更新必须被挤在physics updates之间,故而可能一帧后需要进行大量次数的physics updates,而画面没有更新,画面就与物理系统不同步了。故而Unity提供maximum allowed timestep来限制用于进行物理更新的时间,从而限制一帧内FixedUpdate被调用的次数。

timeScale是用来表示游戏时间相对于现实时间的流逝比例,>1是加速,<1是减速,=0停滞。timeScale不会影响执行速度,而是改变Update和FixedUpdate接收到的deltaTime和fixedDeltaTime的值。(文档内部似乎有冲突)

:deltaTime *= timeScale。

:fixedDeltaTime不变,永远与游戏时间成比例。从而假设timeScale=0.5,那么fixedUpdate调用速度实际变慢。(若fixedDeltaTime *= timeScale,则fixedUpdate的实际调用速度不变)

time: 从程序开始运行的时间,是游戏内时间

fixedTime:从程序开始到上次FixedUpdate的时间,是游戏内时间

unscaledTime:从程序开始的时间,是实际时间

通常的移动代码:

void Update() {
    cube.Translate(Vector3.forward * Time.deltaTime);
}

从而做到每秒速度均衡。

设置目标FPS:

Application.targetFrameRate = 30;

2.7 P100-105

Awake & Start

Unity会同时处理所有脚本:先执行所有脚本的Awake(若有),再执行Start。

Awake用于初始化,Start可访问其它脚本数据。

序列化

把游戏数据写入文件,从而更新/调整只需要更改文件,不需要重新编译游戏。

数据写入文件,数据接口写入类,再写一个文件用于读写。

显示在Inspector中的属性都具有Serialize功能(再次读取Unity时是有值的,不需要再次赋值)。

private需要用[SerializeField]来设置序列化,就会显示在面板上。外部类依旧无法访问。

public可以用[HideInspector]来取消显示。

还可以通过Range(0, 1)来设置范围,int/float都适用。

[Range(0, 1)][SerializeField] private int private_id;

可以通过如下代码来获得、修改、存储序列化数据:

// 更新数据
serializedObject.Update();
// 获取数据信息
SerializedProperty property = serializedObject.FindProperty("private_id");
// 赋值数据
property.intValue = EditorGUILayout.IntField("ID", property.intValue);
// 保存数据
serializedObject.ApplyModifiedProperties();

Asset & Object

Asset:硬盘上文件,包含一到多个Object。(如:材质 引用1~多个 纹理)

Object:序列化数据,描述了资源实例,是UnityEngine.Object的子类。

序列化时,包括:

:文件的GUID,标记Asset文件,存储在.meta中。

:Local ID,标记Object,是局部唯一的。

2.8 P44-

MenuItem

MenuItem(string itemName, bool isValidateFunction, int priority)

itemName:路径

priority:小的显示靠上,相差>=11默认为不同组。默认1000。

isValidateFunction:bool,是验证按钮是否显示的函数

[MenuItem("Assets/My Tools/Tools 1", true, 1)] // 函数返回bool,表示是否显示该菜单按钮
static bool Show()
{
    return Selection.activeObject != null;
}
[MenuItem("Assets/My Tools/Tools 1", false, 1)] // 函数为被点击时执行的函数
static void MyTools1()
{
    Debug.Log(Selection.activeObject.name);
}

InitializeOnLoadMethod

// 第一次打开编辑器运行一次,之后每次进入Play都运行一次
[InitializeOnLoadMethod]
// 在每次进入 Play 模式时运行一次,还可以通过参数确定在加载场景之前还是之后调用方法。
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]

委托 Delegate

观察者模式:主题Subject,Observer可以订阅和取消订阅,Observer会收到消息(当Subject内数据改变)

C#用委托实现回调函数。

public class DelegateScript: MonoBehaviour
{
    // 声明一个委托类型,实例引用一个方法
    internal delegate void MyDelegate(int num);
    MyDelegate myDelegate;
    
    void Start()
    {
        // 实例myDelegate引用方法PrintNum
        myDelegate = PrintNum; // 赋值时,PrintNum可以有多种参数,编译器会自动选择合适的重载版本
        myDelegate(50);
    }
    
    void PrintNum(int num) {
        Debug.Log("Print Num: " + num);
    }
}

internal:在同一个程序集(一个完整的.exe/.dll)的文件,可以跨类。内部类型/成员。

protected:继承子类可以访问,可以跨程序集。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Diary: 2022/10 Previous
Diary: 2021/12 Next