Volume 框架

Volume 框架是HDRP最重要的组成部分之一,其作用如下:

  • 为场景设置来自天空盒的环境光照

  • 设置各种阴影效果,包括场景中的Contact Shadow 和 Micro Shadow

  • 设置场景中的雾效

  • 设置基于屏幕空间的反射(SSRelection)和折射(SSRefraction)、环境光遮蔽(Ambient Occlusion)

  • 设置后处理效果

  • 设置实时光线追踪

在同一个场景中可以添加多个带Volume组件的GameObject。每个Volume的模式可以按照需要设置成Global 或是 Local

Global意味着这个Volumn针对整个场景生效。不管相机朝向哪里,Global模式的Volumn都会被计算在内。

Local模式的Volumn则只能在它的碰撞体作用区域内生效。一般Box Collider即可满足碰撞体需要。

Exposure

曝光指的是相机感光元器件接受的光线强度。曝光的数值越高,相机接受的光线强度越大。

参数说明

Exposure重载包括四种模式:Fixed、Automatic、Curve Mapping 和 Use Physical Camera

Fixed 固定曝光

用于曝光变化不大的场景,也就是场景中的各个区域有类似的曝光强度。

可以使用多个被设置为Local模式的Volume,通过Blend Distance(混合距离)来融合切换不同的Volume的曝光。

由于HDRP的光照系统是完全基于物理的,所以可以参考物理世界真实的曝光值。

5cfd281ebd4bad3543fc5a737f12e351.png

Automatic 自动曝光

根据当前相机的位置信息和相关场景信息来自动调节曝光。非常适用于曝光随位置不同而发生变化的场景,例如从光照强烈的户外进入阴暗的室内或者从山洞中移动到山洞外

可以模拟现实中人眼适应不同曝光的情况。

Limit Min & Limit Max

分别用于控制场景自动曝光的最小值和最大值。可以用这两个值分别调节当相机处于场景的暗部和亮部时整体的曝光。

99b37196985c18f0e7285b3578469b4e.png

当画面靠近场景中的亮部,周围的环境会变暗,但是环境也许不应该“那么”暗。此时可以将Limit Max 调低,以提升整个画面的曝光。f688c683062bbe0c79c0bb2e09b5814d.png

当画面处于场景的暗部,比如在黑暗的角落朝外看,那么外部环境会变亮,可能导致画面亮部曝光过度(月光即使很亮也不应该像白天)此时可以将LImit Min 调高,降低整个画面的曝光。

Curve Mapping 曲线控制曝光

X轴代表目前场景的曝光

Y轴代表我们想要的曝光

专家使用。

Use Physical Camera 物理相机控制曝光

通过物理相机的Iso、Shutter Speed 和 Aperture 参数来控制曝光。

Volume 界面中只剩下Compensation(补偿)这一项。

Metering Mode

用于控制如何采样用于曝光的计算场景中的灯光。

  1. Average 针对整个画面进行采样,结果稳定

  2. Post 针对画面中央区域采样。如果画面中央的采样区域较暗,整个画面可能曝光过度。

  3. Center Weighted

Visual Environment

Intro to HDRP & DXR

从2018版本开始,Unity Editor中共有三套渲染管线。

HD Render Pipeline Asset

HDRP配置文件(HD Render Pipeline Asset)管理HDRP项目的所有渲染功能。HDRP会用这个配置文件生成一个HDRP渲染管线的实例,而这个渲染管线的实例包含用于渲染的中间资源。

为了满足项目开发中不同的需求,HDRP还提供了另外两种渲染方式。

针对不同平台使用不同的HDRP配置文件

在同一个HDRP项目中可以创建多个HDRP配置文件,针对不同的计算平台应用不同的HDRP配置文件。

要针对不同的平台使用不同的设置,需要将对应的HDRP配置文件关联到不同的质量等级上。

质量设置里面的HDRP配置文件会覆盖默认的配置文件。

Frame Settings

帧设置针对的是场景中的Camera、Baked or Custom Reflection 和 Realtime Reflection 的相关设置。后面两个反射相关的设置应用在Reflection Probe上

帧设置的优先级低于HDRP配置文件,也就是说,如果在HDRP配置文件中没有打开某项功能,那么帧设置中对应的功能就会自动被禁用。

帧设置可以让我们为不同的相机和反射探针启用/禁用不同的HDRP功能组合。

Volume 框架

Volume 的作用是通过调整各项HDRP功能的参数,影响相机所看到画面的最终渲染效果。

dd003dc04f47c381deb98503587eb575.png

  • 每个HDRP项目中可以有多个HDRP配置文件

    每个配置文件对应不同的画质或目标平台。但是HDRP项目每次只能使用一个HDRP配置文件,也无法在运行时切换HDRP配置文件。

  • HDRP会为Camera(相机),Backed or Custom Reflection(烘培或自定义反射)和Realtime Reflection(实时反射)提供一套默认的帧设置。如果在HDRP配置中没有被启用,就会在帧设置里被禁用。

  • 注意,在HDRP配置文件中已经启用的功能,也要确保默认帧设置中启用了相关功能。即在HDRP配置文件中启用了某个功能,但是没有在帧设置中启用它,那么在项目中也是无法使用它。

  • 可以为场景中一个(或者多个)相机和反射探针自定义帧设置。 如果在这些自定义帧设置中启用某个功能(前提是在HDRP配置文件中已经启用),那么自定义帧设置中的配置信息会覆盖(Override)默认帧设置中的配置信息。

  • 可以为同一个场景这创建多个Volume。这些Volume的模式可能是全局(Global)的或者是本地(Local)的。如果在当前活跃相机的帧设置(如果没有启用自定义帧设置,就使用默认帧设置)中没有启用某个功能,比如Fog,那么在与此相机相关的Volume中调整Fog参数值就没有意义。

HD Render Pipeline Asset中的7类参数

Rendering

  1. Color Buffer Format

    出于对性能的考虑,HDRP默认使用R11G11B10格式(不包含Alpha通道)

    如果要把HDRP渲染的画面合成到另外的图片上,就需要包含Alpha通道,这时就要选择R16G16B16A16格式。带有Alpha通道的格式会对性能造成一定的影响。

    如果需要将R16G16B16A16格式作为最终渲染输出的格式,那么在Post Processing的Buffer Format中也要选择相同的格式。

  2. Lit Shader Mode

    Lit Shader 是HDRP材质使用的默认着色器。可以选择以下三种模式:

    1. Forward:Lit Shader仅使用前向渲染

    2. Deferred:Lit Shader会使用延迟渲染,一些高级材质还会使用前向渲染。

    3. Both:延迟和前向渲染都可用

      选择Both模式可以通过自定义帧设置为相机选择Deffered或者Forward渲染。不过此模式会让HDRP为两种渲染方式都编译相关的着色器变体。

      如果选择前向渲染或Both模式,则可以选择MSAA

  3. Motion Vector

    HDRP可以在屏幕空间反射和运动模糊中使用运动矢量。通过Camera组件启用的TAA必须使用运动矢量才能工作。如果禁用此选项,则运动模糊和TAA功能将不会正常工作,屏幕空间反射则会用低质量渲染模式。

  4. Runtime Debug Display

    启用该选项后可以在运行时显示灯光和材质的属性信息。正式出包时建议禁用。

  5. Dithering Cross-fade 平滑转换

    这是与Game Object的LOD转换相关的功能。启用该选项后可以让HDRP在做LOD转换时进行平滑的转换。

  6. Terrain Hole 地形洞

    启用该选项后可以显示地形上的凹陷孔洞。如果禁用此选项,则地形上的孔洞不会显示。

  7. 如果你的场景中没有使用透明材质或者没有在Lit材质中使用相关选项,则可以禁用以下选项以减少构建时间。

    1. Transparent Backface 透明背面
    2. Transparent Depth Prepass 透明深度预处理
    3. Transparent Depth Postpass 透明深度后处理

    4e7db5f02099684052b94ad28a7b96ec.png

  8. Custom Pass 自定义通道

    如果没有使用Custom Pass 功能,禁用此功能可以节约内存。

  9. Realtime Raytracing 实时光线追踪

    如果要在HDRP项目中使用实时光线追踪功能,则需要先启用此选项。

  10. LOD Bias LOD 偏差

    场景中的相机会使用此数值来计算LOD偏差。

  11. Maximum LOD Level 最大LOD级别

    用于设置相机支持的最大LOD级别。

  12. Decals 贴花

    1. Draw Distance 用于定义相机离开物体多远以后不再渲染贴花。

    2. Atlas Width和Atlas Height 用于设置纹理图集的宽度和高度。这个纹理图集用于保存场景中所有投射在透明表面上的贴花。

    3. Metal and Ambient Occlusion Properties 启用该选项后,贴花能够影响材质上的金属高光和环境光遮蔽。

    4. Maximum Clustered Decals on Screen 屏幕上能够同时显示的贴花数量(这些贴花影响的是透明表面)。
      e9609527dd29619d4590b419e98c8923.png

  13. Dynamic Resolution 动态分辨率

  14. Low res Transparency 低分辨率透明

    启用该选项后使用低分辨率的透明效果。

Lighting

  1. SSAO(Screen Space Ambient Occlusion)启用此选项后可以为场景添加基于屏幕空间计算的环境光遮蔽效果。可以在Volume中的Ambient Occlusion Override中对效果进行调整。

  2. Volumetrics 体积光

    为场景中的灯光和雾效增加体积光效果。

  3. Light Layers 光照层可以

    在这里启用/禁用光照层(Light Layers)功能。此功能可以让场景中的光源只照亮指定的物体,忽略无关的物体。在HDRP Global Settings -> Layers Names 中可以见到对应层

  4. Cookies 光线遮罩

  5. Reflections 反射
    9494534745278471e7e3a3217ea91803.png

  6. Sky 天空
    2524c9d6c97cc60d8bb2ac333a2fe060.png

    1. Reflection 当场景中没有如何反射探针可以用于计算物体表面的反射信息时,HDRP会使用天空盒Cubemap来计算反射信息。Reflection Size可以控制用于计算反射信息的天空盒Cubemap的分辨率。此分辨率斌不会影响天空盒本身的质量。

    2. Lighting Override Mask 可以让环境光照和天空背景进行分离。如果在此指定了一个Layer而不是使用默认的Nothing,那么HDRP会在场景中寻找与此Layer相关联的Game Object,如果找到的GameObject中包含Volume组件而且可以对当前相机产生影响,那么HDRP就会使用这些Volumn中的信息来计算环境光照。

  7. Shadow 阴影
    d1b2413c2a758ae18a76e6a00215c0a2.png

    1. Shadowmask

      控制Shadowmask光照模式(Shadowmask Lighting Mode)的启用/禁用。

    2. Maximum Shadows on Screen 同屏显示最大阴影数量

      超过这里设定的阴影数量之外的阴影将不被渲染。

    3. Filtering Quality 过滤质量

      选择高质量可以提升阴影质量,减少阴影边缘的锯齿。在Forward模式和Both模式下,可以选择Low,Medium和High三档质量。在Deferred模式下,只能使用Medium质量。

    4. Screen Space Shadows

      启用该选项后,HDRP会在一个单独的通道中计算基于屏幕空间的阴影。

    5. Use Contact Shadows 使用接触阴影

      此处可以选择Low、Medium、High质量。然后在Light组件中可以选择可用的接触阴影质量。

      如果在HDRP配置文件中不勾选上述任何一项,那么只能在Light组件中选择Custom选项。

      要使用接触阴影,需要在Default Frame Settings中启用Contact Shadows选项。

Lighting Quality Settings

光照质量的相关设置,配合Lighting中的参数使用,调整相应数值以达到质量与性能的平衡。

Material

  1. Available Material Quality Levels 可用材质质量等级

    默认所有材质质量都可以使用

  2. Default Material Quality Level 默认材质质量等级

  3. Distortion 变形

  4. Subsurface Scattering 次表面散射

    可以很好的表示光在材质内多次反弹折射等等效果,玉石、翡翠等应用此效果明显。

  5. Fabric BSDF Convolution                             

    若启用此选项,则在使用Fabric材质时,HDRP会单独为织物着色器(Fabric Shader)计算一份反射探针数据,用于生成更准确的光照效果。不过这样做会导致项目中存在两份光照数据,也会导致目前可见的反射探针数量减少一半。

  6. Diffusion Profile List 漫射配置文件列表

    在此保存用于控制次表面散射效果和半透明效果的Diffusion Profile。一个HDRP配置文件最多可以保存15个Diffusion Profile。

Post-processing

  1. Grading LUT Size & Grading LUT Format

    Size用于控制颜色分级时所用的LUT(Lookup Texture)的大小。默认数值32提供了比较平衡的速度和质量表现。

    Format用于设置LUT的编码格式。可以选择R11G11B10R16G16B16A16或者R32G32B32A32格式。精度越高的格式颜色越精准。

Post-processing Quality Settings 后处理质量设置

与光照质量设置类似,可以为后处理效果设置采样值。

e21c555d3500b554ee4bec37f8590f04.png

目前可以设置Depth of Field、Motion Blur、Bloom、Chromatic Aberration(色差)

XR设置

HDRP在特定的平台上输出VR应用

PC需要DX11支持

Ray Tracing

Unity 支持的光追是基于HDRP的,一个光追的Unity项目,首先是一个HDRP项目

Photon with Unity 随笔

Intro

Photon Unity Networking (PUN) is a Unity package for multiplayer games. Flexible matchmaking gets your players into rooms where objects can be synced over the network. RPCs, Custom Properties or “low level” Photon events are just some of the features. The fast and (optionally) reliable communication is done through dedicated Photon server(s), so clients don’t need to connect one to one.

相关代码可

Refer to the link below to view in the Asset Store (PUN2)

PUN 2 - FREE | 网络 | Unity Asset Store

This note is based on PUN2 Version 2.41 - August 2, 2022

Dev Region

一般的,如果配置中空缺此项,游戏会自动连接延迟低的服务器,可能会导致无法观察到彼此的房间。推荐将Dev Region设置为kr或者jp

General Functions

Common Use Variables

PhotonNetwork.CurrentRoom记录了当前连接的房间的信息

PhotonNetwork.PlayerList记录了连接到当前房间的玩家信息列表

PhotonNetwork.IsMasterClient当前客户端是房主

PhotonNetwork.NickName本地玩家的昵称

General Process

导入Photon的包后,可以使用PUN Wizard完成初始化,需要有自己的AppID。稍后可以通过修改Assets/Photon/PhotonUnityNetworking/Resources/PhotonServerSettings.asset路径下的配置文件进行配置。

一般的,连接的级别大致分为Server(Master)-Lobby-Room

下列代码给出了连接服务器、创建、加入房间的代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//MonoBehaviourPunCallbacks

PhotonNetwork.ConnectUsingSettings();
public override void OnConnectedToMaster(){}

PhotonNetwork.CreateRoom(roomNameInputField.text);
public override void OnCreatedRoom(){}
public override void OnCreateRoomFailed(short returnCode, string message){}

PhotonNetwork.JoinRoom(info.Name);
public override void OnJoinedRoom(){}

PhotonNetwork.LeaveRoom();

public override void OnRoomListUpdate(List<RoomInfo> roomList){}
public override void OnMasterClientSwitched(Player newMasterClient){}
public override void OnPlayerEnteredRoom(Player newPlayer){}

下面给出了实际的代码应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
public class Launcher : MonoBehaviourPunCallbacks
{
void Start()
{
//通过配置连接到服务器
PhotonNetwork.ConnectUsingSettings();
}

public void CreateRoom()
{
//创建房间,需要指定房间名
if (string.IsNullOrEmpty(roomNameInputField.text))
{
return;
}
PhotonNetwork.CreateRoom(roomNameInputField.text);
}

//加入房间,以房间名为参数
public void JoinRoom(RoomInfo info)
{
PhotonNetwork.JoinRoom(info.Name);
MenuManager.Instance.OpenMenu("Loading");
}

//退出房间
public void LeaveRoom()
{
PhotonNetwork.LeaveRoom();
}

//重要的回调方法:
//成功连接到服务器后调用
public override void OnConnectedToMaster()
{
Debug.Log("[Network]Connected to Master");
PhotonNetwork.JoinLobby();

//同步场景
PhotonNetwork.AutomaticallySyncScene = true;
}

//本地客户端连接到房间时调用
//PhotonNet
public override void OnJoinedRoom()
{
roomNamePUN.text = PhotonNetwork.CurrentRoom.Name;

Player[] players = PhotonNetwork.PlayerList;

foreach (Transform trans in playerListContent)
{
Destroy(trans.gameObject);
}

for (int i = 0; i < players.Count(); i++)
{
Instantiate(playerListItemPrefab, playerListContent).GetComponent<PlayerListItem>().SetUp(players[i]);
}

//使得按钮只对创建客户端可见
startGameBtn.SetActive(PhotonNetwork.IsMasterClient);
}

public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
//PhotonNetwork.countOfRooms != PhotonNetwork.GetRoomList();
Debug.Log("[Network]Room List is Updated: " + roomList.Count() + " in total");

//先清理已有的房间列表
foreach (Transform item in roomListContent)
{
Destroy(item.gameObject);
}


for (int i = 0; i < roomList.Count(); i++)
{
if (roomList[i].RemovedFromList)
continue;
Instantiate(roomListItemPrefab, roomListContent).GetComponent<RoomListItem>().SetUp(roomList[i]);
}
}

//当房主发生了变化
public override void OnMasterClientSwitched(Player newMasterClient)
{
startGameBtn.SetActive(PhotonNetwork.IsMasterClient);
}

//离开房间时调用
public override void OnLeftRoom()
{

}

//房间消息列表更新时调用(如创建了新房,已有的房间关闭)
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
//PhotonNetwork.countOfRooms != PhotonNetwork.GetRoomList();
Debug.Log("[Network]Room List is Updated: " + roomList.Count() + " in total");

//先清理已有的房间列表
foreach (Transform item in roomListContent)
{
Destroy(item.gameObject);
}


for (int i = 0; i < roomList.Count(); i++)
{
if (roomList[i].RemovedFromList)
continue;
Instantiate(roomListItemPrefab, roomListContent).GetComponent<RoomListItem>().SetUp(roomList[i]);
}
}

//当一个远程玩家进入房间时调用
public override void OnPlayerEnteredRoom(Player newPlayer)
{
Debug.Log("[Network]" + newPlayer.NickName + " connected, in room: " + PhotonNetwork.NickName);
Instantiate(playerListItemPrefab,playerListContent).GetComponent<PlayerListItem>().SetUp(newPlayer);
}
}

当玩家都进入同一个房间时,可以考虑使用来同步场景

1
PhotonNetwork.LoadLevel(1); 

PlayerProperties

PlayerProperties 是一种同步玩家间状态的方法,以下是代码示例,目的为同步玩家手中所持有的武器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//区分C#内置的ashtable与Photon提供的Hashtable
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class PlayerController : MonoBehaviourPunCallbacks
{
PhotonView PV;

private void Awake()
{
PV = GetComponent<PhotonView>();
}

//同步武器的实质是同步所持武器的itemIndex
void EquipItem(int _index)
{
itemIndex = _index;

//当我们装备一个武器时,首先查看是否是localPlayer,若是,将把这个切换到的武器index发出去
if (PV.IsMine)
{
Hashtable hash = new Hashtable();
hash.Add("itemIndex", itemIndex);
PhotonNetwork.LocalPlayer.SetCustomProperties(hash);
}
}

//所有在房间内玩家会收到同步信息,调用此回调。参数中给出了属性被改变的玩家,属性新值
public override void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
//本地已经切换了武器,无需再次同步 && 在客户端看,仅仅改变对应玩家的index,而不是所有玩家
if (changedProps.ContainsKey("itemIndex")&&!PV.IsMine && targetPlayer == PV.Owner)
{
EquipItem((int)changedProps["itemIndex"]);
}
}
}

[PunRPC]

Example

需要Pun远程调用的方法,应当给与PunRPC标签,下面给出造成伤害、收到伤害的流程:

单发枪类,能够开火

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SingleShotGun : Gun
{
//枪开火,进行射线检测
void Shoot()
{
//从摄像机的近剪裁面的中点向着远剪裁面的中点绘制一条射线
Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f));
ray.origin = cam.transform.position;
if (Physics.Raycast(ray, out RaycastHit hit))
{
Debug.Log("[Weapon]Ray cast hit " + hit.collider.gameObject.name);
hit.collider.gameObject.GetComponent<IDamageable>()?.TakeDamage(((GunInfo)itemInfo).damage);

//生成 Bullet Impact
PV.RPC(nameof(RPC_Shoot),RpcTarget.All, hit.point,hit.normal);
}
}
}

PlayerController类,每一个玩家角色GameObject都挂载此类

使用nameof()可以避免可能的拼写错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//本地被击打的物体实现IDamageable接口,调用TakeDamage()方法
public class PlayerController : MonoBehaviourPunCallbacks, IDamageable
{
public void TakeDamage(float damage)
{
//PV.Owner指定了被打击的角色,damage为此次命中的伤害
PV.RPC(nameof(RPC_TakeDamage), PV.Owner, damage);
Debug.Log("[Combat]" + "I(" + PhotonNetwork.NickName + ") Try to hurt " + PV.Owner.NickName + " at damage " + damage);
}

//对于被打击的玩家来说,其RPC方法被调用了
[PunRPC]
void RPC_TakeDamage(float damage, PhotonMessageInfo info)
{
Debug.Log("[RPC][Combat]" + PV.Owner.NickName + " Take damage " + damage);
currentHealth -= damage;

healthbarImage.fillAmount = currentHealth / maxHealth;

if (currentHealth <= 0)
{
Die();
//找到发送信息者,追溯伤害来源
PlayerManager.Find(info.Sender).GetKill();
}
}
}

Bullet Impact

通过Photon同步大量生成的GameObject是昂贵的,一般使用RPC调用对应方法,生成物体。

1
2
3
4
5
6
7
8
9
10
11
12
[PunRPC]
void RPC_Shoot(Vector3 hitPosition,Vector3 hitNormal)
{
Collider[] colliders = Physics.OverlapSphere(hitPosition, 0.3f);
Debug.Log("[RPC][Combat]" + hitPosition);
if (colliders.Length != 0)
{
GameObject bulletImpactObj = Instantiate(bulletImpactPrefab, hitPosition + hitNormal * 0.005f, Quaternion.LookRotation(hitNormal, Vector3.up) * bulletImpactPrefab.transform.rotation);
Destroy(bulletImpactObj,10f);
bulletImpactObj.transform.SetParent(colliders[0].transform);
}
}

Photon View

View ID[1..999] 对于每个Photon View 应当是 unique 的

Photon.Instantiate

Instantiate prefabs from Photon is not the same as instantiate a prefab from Unity normally

为了能够在多种平台上成功的加载预制体,尤其是此挂载了PhotonView的预制体(而不是仅仅在Editor中),

PhotonPrefabs must be in the resources folder.

Because Unity automatically excludes any file not referenced in the editor from the final build, and we don’t reference PhotonPrefabs. We use strings.

1
PhotonNetwork.Instantiate(Path.ComPhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", nameof(PlayerManager)), Vector3.zero, Quaternion.identity);bine("PhotonPrefabs", "PlayerManager"), Vector3.zero, Quaternion.identity);

为了防止可能的拼写错误,可以使用nameof()

Unity 能够将在Resources文件夹中的文件正常打包

可以在Instantiate的时候为对应GameObject的PV赋值

以创建PlayerController为例,将需要传递的参数封装到最后一个参数object[] 种

1
2
3
4
5
6
7
8
9
10
11
12
13
void CreateController() 
{
Transform spawnpoint = SpawnManager.Instance.GetSpawnpoint();
Debug.Log("[Player]Instantiated Player Controller: " + PhotonNetwork.NickName);
controller = PhotonNetwork.Instantiate(System.IO.Path.Combine("PhotonPrefabs",nameof(PlayerController)),spawnpoint.position, spawnpoint.rotation, 0, new object[] {PV.ViewID});
}


void GetParam()
{
//拆箱时,需要指定cast的类型
int id = (int)PV.InstantiationData[0];
}

Photon Transform View

需要通过Photon同步Transform信息的物体,应当在Photon View之外额外挂在此组件。通过Photon同步时,不应当继续使用本地的物理等等逻辑计算的Transform信息,否则会导致GameObject的抖动等不稳定现象。

在初始化之初关闭即可

1
2
3
4
if(!PV.isMine)
{
Destroy(rb);
}

PUN(Not recommanded)

Photon Unity Networking Classic - FREE | 网络 | Unity Asset Store

Photon.MonoBehaviour vs. Photon.PunBehaviour

For PunBehaviour:

This class provides a .photonView and all callbacks/events that PUN can call. Override the events/methods you want to use.

多与回调相关时候,应当使用 Photon.PunBehaviour

利用await/async 与 Task 关键词替换协程功能

Intro

参考视频:# Unity async / await: Coroutine’s Hot Sister [C# & Unity]

协程是Unity提供的异步解决方法,但是实际应用时有诸多不便与不优雅之处。本文将用几个例子对比参照协程与await/async关键字。当然,Unity指出,在Unity中写多线程代码应当参考Unity - Manual: C# Job System

旧例

利用协程转动三个物体,并使得这三个物体逐个停止。

给出下列协程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;
using UnityEngine;

public class Shape: MonoBehaviour()
{
public IEnumerator RotateForSeconds(float duration)
    {
    var end = Time.time + duration;
    while(Time.time < end)
    {
    transform.Rotate(new Vector3(1,1)*Time.deltaTime*150;
    yield return null;
    }
    }
}

给出下列调用协程的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;
using UnityEngine;

public class ShapeManager: MonoBehaviour()
{
[SerializeField] private Shape[] _shapes;

public void BeginTest()
{
for(var i = 0; i<_shapes.Length;i++)
{
StartCoroutine(_shapes[i].RotateForSeconds(1+1*i);
}
}
}