A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEngine.Serialization;
4
5namespace UnityEngine.Rendering
6{
7 /// <summary>
8 /// A generic Volume component holding a <see cref="VolumeProfile"/>.
9 /// </summary>
10 [CurrentPipelineHelpURL("Volumes")]
11 [ExecuteAlways]
12 [AddComponentMenu("Miscellaneous/Volume")]
13 public class Volume : MonoBehaviour, IVolume
14 {
15 [SerializeField, FormerlySerializedAs("isGlobal")]
16 bool m_IsGlobal = true;
17
18 /// <summary>
19 /// Specifies whether to apply the Volume to the entire Scene or not.
20 /// </summary>
21 public bool isGlobal
22 {
23 get => m_IsGlobal;
24 set => m_IsGlobal = value;
25 }
26
27 /// <summary>
28 /// A value which determines which Volume is being used when Volumes have an equal amount of influence on the Scene. Volumes with a higher priority will override lower ones.
29 /// </summary>
30 [Delayed]
31 public float priority = 0f;
32
33 /// <summary>
34 /// The outer distance to start blending from. A value of 0 means no blending and Unity applies
35 /// the Volume overrides immediately upon entry.
36 /// </summary>
37 public float blendDistance = 0f;
38
39 /// <summary>
40 /// The total weight of this volume in the Scene. 0 means no effect and 1 means full effect.
41 /// </summary>
42 [Range(0f, 1f)]
43 public float weight = 1f;
44
45 /// <summary>
46 /// The shared Profile that this Volume uses.
47 /// Modifying <c>sharedProfile</c> changes every Volumes that uses this Profile and also changes
48 /// the Profile settings stored in the Project.
49 /// </summary>
50 /// <remarks>
51 /// You should not modify Profiles that <c>sharedProfile</c> returns. If you want
52 /// to modify the Profile of a Volume, use <see cref="profile"/> instead.
53 /// </remarks>
54 /// <seealso cref="profile"/>
55 public VolumeProfile sharedProfile = null;
56
57 /// <summary>
58 /// Gets the first instantiated <see cref="VolumeProfile"/> assigned to the Volume.
59 /// Modifying <c>profile</c> changes the Profile for this Volume only. If another Volume
60 /// uses the same Profile, this clones the shared Profile and starts using it from now on.
61 /// </summary>
62 /// <remarks>
63 /// This property automatically instantiates the Profile and make it unique to this Volume
64 /// so you can safely edit it via scripting at runtime without changing the original Asset
65 /// in the Project.
66 /// Note that if you pass your own Profile, you must destroy it when you finish using it.
67 /// </remarks>
68 /// <seealso cref="sharedProfile"/>
69 public VolumeProfile profile
70 {
71 get
72 {
73 if (m_InternalProfile == null)
74 {
75 m_InternalProfile = ScriptableObject.CreateInstance<VolumeProfile>();
76
77 if (sharedProfile != null)
78 {
79 m_InternalProfile.name = sharedProfile.name;
80
81 foreach (var item in sharedProfile.components)
82 {
83 var itemCopy = Instantiate(item);
84 m_InternalProfile.components.Add(itemCopy);
85 }
86 }
87 }
88
89 return m_InternalProfile;
90 }
91 set => m_InternalProfile = value;
92 }
93
94 internal List<Collider> m_Colliders = new List<Collider>();
95
96 /// <summary>
97 /// The colliders of the volume if <see cref="isGlobal"/> is false
98 /// </summary>
99 public List<Collider> colliders => m_Colliders;
100
101 internal VolumeProfile profileRef => m_InternalProfile == null ? sharedProfile : m_InternalProfile;
102
103 /// <summary>
104 /// Checks if the Volume has an instantiated Profile or if it uses a shared Profile.
105 /// </summary>
106 /// <returns><c>true</c> if the profile has been instantiated.</returns>
107 /// <seealso cref="profile"/>
108 /// <seealso cref="sharedProfile"/>
109 public bool HasInstantiatedProfile() => m_InternalProfile != null;
110
111 // Needed for state tracking (see the comments in Update)
112 int m_PreviousLayer;
113 float m_PreviousPriority;
114 VolumeProfile m_InternalProfile;
115
116 void OnEnable()
117 {
118 m_PreviousLayer = gameObject.layer;
119 VolumeManager.instance.Register(this);
120 GetComponents(m_Colliders);
121 }
122
123 void OnDisable()
124 {
125 VolumeManager.instance.Unregister(this);
126 }
127
128 void Update()
129 {
130 UpdateLayer();
131 UpdatePriority();
132
133#if UNITY_EDITOR
134 // In the editor, we refresh the list of colliders at every frame because it's frequent to add/remove them
135 GetComponents(m_Colliders);
136#endif
137 }
138
139 internal void UpdateLayer()
140 {
141 // Unfortunately we need to track the current layer to update the volume manager in
142 // real-time as the user could change it at any time in the editor or at runtime.
143 // Because no event is raised when the layer changes, we have to track it on every
144 // frame :/
145
146 int layer = gameObject.layer;
147 if (layer == m_PreviousLayer)
148 return;
149
150 VolumeManager.instance.UpdateVolumeLayer(this, m_PreviousLayer, layer);
151 m_PreviousLayer = layer;
152 }
153
154 internal void UpdatePriority()
155 {
156 if (!(Math.Abs(priority - m_PreviousPriority) > Mathf.Epsilon))
157 return;
158
159 // Same for priority. We could use a property instead, but it doesn't play nice with the
160 // serialization system. Using a custom Attribute/PropertyDrawer for a property is
161 // possible but it doesn't work with Undo/Redo in the editor, which makes it useless for
162 // our case.
163 VolumeManager.instance.SetLayerDirty(gameObject.layer);
164 m_PreviousPriority = priority;
165 }
166
167 void OnValidate()
168 {
169 blendDistance = Mathf.Max(blendDistance, 0f);
170 }
171 }
172}