feat: Weather

This commit is contained in:
2026-09-21 15:05:47 +02:00
parent 1181e5417f
commit d32fb78ff0
28 changed files with 8478 additions and 2354 deletions

View File

@@ -24,8 +24,10 @@ Material:
m_Shader: {fileID: -6465566751694194690, guid: d96e5a7ca40db7b42bcaea87a6ea493c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_ValidKeywords:
- _WEATHERMAPPING_WORLD
m_InvalidKeywords:
- _RAINMAPPING_WORLD
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
@@ -61,6 +63,9 @@ Material:
- _Ripples: 1
- _RipplesSize: 3
- _Smoothness: 1
- _Snow: 0
- _SnowHeight: 0.15
- _WEATHERMAPPING: 0
- _Wet: 1
m_Colors:
- _Color: {r: 0.49056602, g: 0.49056602, b: 0.49056602, a: 1}

View File

@@ -1,69 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-982736398028994105
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Object
m_Shader: {fileID: -6465566751694194690, guid: d96e5a7ca40db7b42bcaea87a6ea493c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Metallic: 0
- _QueueControl: 0
- _QueueOffset: 0
- _RAINMAPPING: 1
- _RAINTYPE: 0
- _RainMapping: 0
- _RainSize: 0.4
- _RippleSize: 1
- _Ripples: 1
- _RipplesSize: 0.55
- _Smoothness: 0
- _Wet: 1
m_Colors:
- _Color: {r: 0.3584906, g: 0.31442308, b: 0.25533998, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: dd15888282b350e4fa5724465bacecc2
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -7,11 +7,12 @@ Material:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Rain_UV
m_Name: M_Object_UV
m_Shader: {fileID: -6465566751694194690, guid: d96e5a7ca40db7b42bcaea87a6ea493c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_ValidKeywords:
- _WEATHERMAPPING_UV
m_InvalidKeywords: []
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
@@ -38,16 +39,18 @@ Material:
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Metallic: 0
- _Metallic: 0.01
- _QueueControl: 0
- _QueueOffset: 0
- _RainMapping: 1
- _RippleSize: 0.1
- _Ripples: 1
- _Smoothness: 0
- _Smoothness: 0.3
- _SnowHeight: 0.29
- _WEATHERMAPPING: 1
- _Wet: 1
m_Colors:
- _Color: {r: 0.51584196, g: 0.5208259, b: 0.5283019, a: 1}
- _Color: {r: 0.37254903, g: 0.5176471, b: 0.8117647, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1161501858776640379

View File

@@ -7,11 +7,12 @@ Material:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Rain_WS
m_Name: M_Object_WS
m_Shader: {fileID: -6465566751694194690, guid: d96e5a7ca40db7b42bcaea87a6ea493c, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_ValidKeywords:
- _WEATHERMAPPING_WORLD
m_InvalidKeywords: []
m_LightmapFlags: 2
m_EnableInstancingVariants: 0
@@ -38,16 +39,18 @@ Material:
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Metallic: 0
- _Metallic: 0.1
- _QueueControl: 0
- _QueueOffset: 0
- _RainMapping: 0
- _RippleSize: 1
- _RippleSize: 0.3
- _Ripples: 1
- _Smoothness: 0
- _Smoothness: 0.3
- _SnowHeight: 0.15
- _WEATHERMAPPING: 0
- _Wet: 1
m_Colors:
- _Color: {r: 0.51584196, g: 0.5208259, b: 0.5283019, a: 1}
- _Color: {r: 0.112451054, g: 0.134517, b: 0.1792453, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &1161501858776640379

View File

@@ -1,6 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-1017934160256109788
--- !u!114 &-8687615387883125597
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
@@ -21,7 +21,7 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Ripple
m_Shader: {fileID: -6465566751694194690, guid: a0c3c651fff45254c8baca85cfac8aae, type: 3}
m_Shader: {fileID: -6465566751694194690, guid: 3645cd664ced1ec4eb3616f3f2b4193e, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
@@ -51,9 +51,10 @@ Material:
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Fill: 1
- _Fill: 0.369
- _QueueControl: 0
- _QueueOffset: 0
- _Thickness: 0.3
m_Colors: []
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 61205383e03923748898670d8a39bdbd
guid: 50491758ef297114da9c7d4c2095a402
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000

View File

@@ -1,60 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8687615387883125597
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Ripple2
m_Shader: {fileID: -6465566751694194690, guid: 3645cd664ced1ec4eb3616f3f2b4193e, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Fill: 0.369
- _QueueControl: 0
- _QueueOffset: 0
- _Thickness: 0.3
m_Colors: []
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 50491758ef297114da9c7d4c2095a402
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,61 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8043963299266757485
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: M_Snow
m_Shader: {fileID: -6465566751694194690, guid: a8fa828d4bba6dc46b032181eea47409, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Amount: 0.6
- _Float: 4.71
- _QueueControl: 0
- _QueueOffset: 0
- _Smoothness: 0.1
m_Colors: []
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 83270e932c0448b40b9b2ceb6fff32e0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -33,7 +33,7 @@ ModelImporter:
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
globalScale: 100
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1

BIN
Assets/_Content/Models/quad.fbx LFS Normal file

Binary file not shown.

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: 85e9657e4cc463c46b36dab0cd41759f
ModelImporter:
serializedVersion: 24200
internalIDToNameTable: []
externalObjects: {}
materials:
materialImportMode: 2
materialName: 0
materialSearch: 1
materialLocation: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
removeConstantScaleCurves: 0
motionNodeName:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
extraUserProperties: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importPhysicalCameras: 1
importVisibility: 1
importBlendShapes: 1
importCameras: 1
importLights: 1
nodeNameCollisionStrategy: 1
fileIdsGeneration: 2
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 1
bakeAxisConversion: 0
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
optimizeBones: 1
generateMeshLods: 0
meshLodGenerationFlags: 0
maximumMeshLod: -1
meshOptimizationFlags: -1
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVMarginMethod: 1
secondaryUVMinLightmapResolution: 40
secondaryUVMinObjectScale: 1
secondaryUVPackMargin: 4
useFileScale: 1
strictVertexDataChecks: 0
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 3
normalCalculationMode: 4
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
blendShapeNormalImportMode: 1
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
humanDescription:
serializedVersion: 3
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
globalScale: 1
rootMotionBoneName:
hasTranslationDoF: 0
hasExtraRoot: 0
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
humanoidOversampling: 1
avatarSetup: 0
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
importBlendShapeDeformPercent: 1
remapMaterialsIfMaterialImportModeIsNone: 0
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,192 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
[ExecuteAlways]
public class WeatherController : MonoBehaviour
{
public enum WeatherType
{
Clear,
Rain,
Snow
}
[System.Serializable]
public class Automatic
{
public bool Enabled = false;
[Tooltip("Minimum weather duration in seconds, including transitions.")]
[Min(0.1f)] public float MinDuration = 10f;
[Tooltip("Maximum weather duration in seconds, including transitions.")]
[Min(0.1f)] public float MaxDuration = 20f;
}
[System.Serializable]
public class Events
{
public UnityEvent OnStart = new UnityEvent();
public UnityEvent OnStop = new UnityEvent();
}
[System.Serializable]
public class Rain
{
[Min(0f)] public float RainSpeed = 1f;
[Min(0f)] public float WetSpeed = 0.1f;
public Events Events = new Events();
}
[System.Serializable]
public class Snow
{
[FormerlySerializedAs("SnowSpeed")]
[Min(0f)] public float Speed = 0.1f;
[Tooltip("Delay in seconds before snow starts accumulating.")]
[Min(0f)] public float Delay = 1f;
public Events Events = new Events();
}
[SerializeField] private WeatherType currentWeather = WeatherType.Clear;
[SerializeField] private Automatic _automatic = new Automatic();
[SerializeField] private Rain _rain = new Rain();
[SerializeField] private Snow _snow = new Snow();
private WeatherType previousWeather = WeatherType.Clear;
private float snowDelayRemaining = 0f;
private bool automaticWasEnabled = false;
private float automaticTimeRemaining = 0f;
// Current values sent to global shader properties.
private float currentRain = 0f;
private float currentWet = 0f;
private float currentSnow = 0f;
// Cached shader property IDs for efficient access.
private static readonly int RainID = Shader.PropertyToID("_Rain");
private static readonly int WetID = Shader.PropertyToID("_Wet");
private static readonly int SnowID = Shader.PropertyToID("_Snow");
private void OnEnable()
{
previousWeather = WeatherType.Clear;
snowDelayRemaining = 0f;
automaticWasEnabled = false;
automaticTimeRemaining = 0f;
}
private void Update()
{
if (Application.isPlaying) UpdateAutomaticWeather();
// Set target values based on the selected weather.
float targetRain = 0f;
float targetWet = 0f;
float targetSnow = 0f;
switch (currentWeather)
{
case WeatherType.Clear:
targetRain = 0f;
targetWet = 0f; // Dries in clear weather.
targetSnow = 0f;
break;
case WeatherType.Rain:
targetRain = 1f;
targetWet = 1f; // Gets wetter while raining.
targetSnow = 0f;
break;
case WeatherType.Snow:
targetRain = 0f;
targetWet = 0f; // Dries in snowy weather.
targetSnow = 1f;
break;
}
// Handle editor and runtime updates.
if (Application.isPlaying)
{
if (currentWeather != previousWeather)
snowDelayRemaining = currentWeather == WeatherType.Snow ? Mathf.Max(0f, _snow.Delay) : 0f;
// Delay snow accumulation without delaying rain, wetness, or snow melting.
float snowDeltaTime = Time.deltaTime;
if (currentWeather == WeatherType.Snow && snowDelayRemaining > 0f)
{
float waitingTime = Mathf.Min(snowDelayRemaining, snowDeltaTime);
snowDelayRemaining -= waitingTime;
snowDeltaTime -= waitingTime;
}
// Apply gradual transitions at runtime.
currentRain = Mathf.MoveTowards(currentRain, targetRain, Mathf.Max(0f, _rain.RainSpeed) * Time.deltaTime);
currentWet = Mathf.MoveTowards(currentWet, targetWet, Mathf.Max(0f, _rain.WetSpeed) * Time.deltaTime);
currentSnow = Mathf.MoveTowards(currentSnow, targetSnow, Mathf.Max(0f, _snow.Speed) * snowDeltaTime);
}
else
{
// Apply values immediately in the editor.
currentRain = targetRain;
currentWet = targetWet;
currentSnow = targetSnow;
}
// Apply global shader properties (URP).
Shader.SetGlobalFloat(RainID, currentRain);
Shader.SetGlobalFloat(WetID, currentWet);
Shader.SetGlobalFloat(SnowID, currentSnow);
if (Application.isPlaying) UpdateWeatherEvents();
}
private void UpdateAutomaticWeather()
{
if (!_automatic.Enabled)
{
automaticWasEnabled = false;
automaticTimeRemaining = 0f;
return;
}
// Keep the current weather for a full interval when automatic mode starts.
if (!automaticWasEnabled || currentWeather != previousWeather)
{
automaticWasEnabled = true;
automaticTimeRemaining = GetAutomaticDuration();
return;
}
automaticTimeRemaining -= Time.deltaTime;
if (automaticTimeRemaining > 0f) return;
// Select one of the other two weather types to avoid consecutive repeats.
currentWeather = (WeatherType)(((int)currentWeather + Random.Range(1, 3)) % 3);
automaticTimeRemaining = GetAutomaticDuration();
}
private float GetAutomaticDuration()
{
float minDuration = Mathf.Max(0.1f, Mathf.Min(_automatic.MinDuration, _automatic.MaxDuration));
float maxDuration = Mathf.Max(minDuration, Mathf.Max(_automatic.MinDuration, _automatic.MaxDuration));
return Random.Range(minDuration, maxDuration);
}
private void UpdateWeatherEvents()
{
WeatherType nextWeather = currentWeather;
if (nextWeather == previousWeather) return;
WeatherType stoppedWeather = previousWeather;
previousWeather = nextWeather;
// Stop the previous weather before starting the next one.
if (stoppedWeather == WeatherType.Rain) _rain.Events.OnStop?.Invoke();
else if (stoppedWeather == WeatherType.Snow) _snow.Events.OnStop?.Invoke();
if (nextWeather == WeatherType.Rain) _rain.Events.OnStart?.Invoke();
else if (nextWeather == WeatherType.Snow) _snow.Events.OnStart?.Invoke();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 71ef1c7c5389fc949abc68860de5b6e8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff