日期季节控制
public class TimeManager : MonoBehaviour { public static TimeManager Instance { get; private set; } // 定义一个事件,在每天过去时触发 public UnityEvent OnDayPass = new UnityEvent(); // 季节枚举类型 public enum Season { Spring, Summer, Fall, Winter } // 当前季节 public Season currentSeason = Season.Spring; private int daysPerSeason = 30;//每季天数 private int daysInCurrentSeason = 1;//当前季节天数 //日期枚举类型 public enum DayOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } public DayOfWeek currentDayOfWeek = DayOfWeek.Monday; public int dayInGame = 1;//游戏中的天数 public int yearInGame = 0;//游戏中的年数 public TextMeshProUGUI dayUIText; private void Awake() { Instance = this; } private void Start() { UpdateUI(); } // 触发过渡到下一天的方法 public void TriggerNextDay() { // 增加游戏中的天数和当前季节的天数 dayInGame += 1; daysInCurrentSeason += 1; currentDayOfWeek = (DayOfWeek)(((int)currentDayOfWeek + 1) % 7); // 检查是否当前季节天数已达到季节总天数 if (daysInCurrentSeason > daysPerSeason) { // 切换到下一个季节,并重置季节天数计数 daysInCurrentSeason = 1; currentSeason = GetNextSeason(); } // 更新UI显示,触发当天过去事件 UpdateUI(); OnDayPass.Invoke(); } // 获取下一个季节的方法 private Season GetNextSeason() { // 计算当前季节索引和下一个季节索引 int currentSeasonIndex = (int)currentSeason; // 获取当前季节索引 int nextSeasonIndex = (currentSeasonIndex + 1) % 4; // 计算下一个季节索引 // 如果下一个季节索引为0(春季),增加游戏年份 if (nextSeasonIndex == 0) { yearInGame += 1; } // 返回下一个季节 return (Season)nextSeasonIndex; } // 更新UI显示的方法 private void UpdateUI() { string currentDayOfWeekChinese = getCurrentDayOfWeekChinese(currentDayOfWeek); string currentSeasonChinese = getCurrentSeasonChinese(currentSeason); dayUIText.text = $"{currentDayOfWeekChinese} 第 {daysInCurrentSeason} 天,{currentSeasonChinese}"; } //获取中文日期 private string getCurrentDayOfWeekChinese(DayOfWeek currentDayOfWeek) { string currentDayOfWeekChinese = ""; switch (currentDayOfWeek) { case DayOfWeek.Monday: currentDayOfWeekChinese = "星期一"; break; case DayOfWeek.Tuesday: currentDayOfWeekChinese = "星期二"; break; case DayOfWeek.Wednesday: currentDayOfWeekChinese = "星期三"; break; case DayOfWeek.Thursday: currentDayOfWeekChinese = "星期四"; break; case DayOfWeek.Friday: currentDayOfWeekChinese = "星期五"; break; case DayOfWeek.Saturday: currentDayOfWeekChinese = "星期六"; break; case DayOfWeek.Sunday: currentDayOfWeekChinese = "星期日"; break; default: break; } return currentDayOfWeekChinese; } //获取中文季节 private string getCurrentSeasonChinese(Season currentSeason) { string currentSeasonChinese = ""; switch (currentSeason) { case Season.Spring: currentSeasonChinese = "春"; break; case Season.Summer: currentSeasonChinese = "夏"; break; case Season.Fall: currentSeasonChinese = "秋"; break; case Season.Winter: currentSeasonChinese = "冬"; break; default: break; } return currentSeasonChinese; } }
配置
效果
时间昼夜交替
素材
https://assetstore.unity.com/packages/2d/textures-materials/sky/fantasy-skybox-free-18353
如果没有天空盒,需要自己配置
新增SkyboxBlendingShader.shader,控制天空盒平滑过渡交替变化
Shader "Custom/SkyboxTransition" { Properties { _TransitionFactor("Transition Factor", Range(0, 1)) = 0.0 _AtmosphereTex("Atmosphere CubeMap", Cube) = "" {} _SpaceTex("Space CubeMap", Cube) = "" {} } SubShader { Tags { "Queue"="Background" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; }; struct v2f { float3 pos : TEXCOORD0; float4 vertex : SV_POSITION; }; float _TransitionFactor; samplerCUBE _AtmosphereTex; samplerCUBE _SpaceTex; v2f vert(appdata_t v) { v2f o; o.pos = v.vertex.xyz; o.vertex = UnityObjectToClipPos(v.vertex); return o; } half4 frag(v2f i) : SV_Target { // Use the transition factor to blend between the two skybox cube maps half4 atmosphereColor = texCUBE(_AtmosphereTex, i.pos); half4 spaceColor = texCUBE(_SpaceTex, i.pos); half4 finalColor = lerp(atmosphereColor, spaceColor, _TransitionFactor); return finalColor; } ENDCG } } FallBack "Skybox/Cubemap" }
配置不同时间过渡材质
新增DayNightSystem,负责管理游戏的昼夜系统
public class DayNightSystem : MonoBehaviour { // 控制方向光的引用 public Light directionalLight; // 一整天的持续时间(以秒为单位) public float dayDurationInSeconds = 24.0f; // 调整一整天的持续时间(以秒为单位) // 当前的小时 public int currentHour; // 当前的分钟 public int currentMinute; // 当前时间在一天中所占比例(范围在0到1之间) float currentTimeOfDay = 0.35f; // 存储不同时间段对应的天空盒 public List<SkyboxTimeMapping> timeMappings; // 用于插值的值(范围在0到1之间) float blendedValue = 0.0f; // 是否锁定下一个白天触发 bool lockNextDayTrigger = false; // 用于显示时间的UI元素 public TextMeshProUGUI timeUI; // 在每帧更新 void Update() { // 根据游戏时间计算当前的时间 currentTimeOfDay += Time.deltaTime / dayDurationInSeconds; currentTimeOfDay = currentTimeOfDay % 1; // 确保值在0到1之间 // 计算当前的小时 currentHour = Mathf.FloorToInt(currentTimeOfDay * 24); // 计算当前的分钟数 currentMinute = Mathf.FloorToInt(currentTimeOfDay * 1440) % 60; // 一天有 24 小时 * 60 分钟 = 1440 分钟 //更新时间UI timeUI.text = $"{currentHour:D2}:{currentMinute:D2}"; // 更新方向光的旋转 directionalLight.transform.rotation = Quaternion.Euler(new Vector3((currentTimeOfDay * 360) - 90, 170, 0)); // 根据时间更新天空盒材质 UpdateSkybox(); } // 更新天空盒材质的方法 private void UpdateSkybox() { // 寻找当前小时对应的天空盒材质 Material currentSkybox = null; foreach (SkyboxTimeMapping mapping in timeMappings) { if (currentHour == mapping.hour) { currentSkybox = mapping.skyboxMaterial; if (currentSkybox.shader != null) { if (currentSkybox.shader.name == "Custom/SkyboxTransition") { blendedValue += Time.deltaTime; blendedValue = Mathf.Clamp01(blendedValue); currentSkybox.SetFloat("_TransitionFactor", blendedValue); } else { blendedValue = 0; } } break; } } if (currentHour == 0 && lockNextDayTrigger == false) { TimeManager.Instance.TriggerNextDay(); lockNextDayTrigger = true; } if (currentHour != 0) { lockNextDayTrigger = false; } // 如果找到了对应的天空盒材质,则更新RenderSettings的skybox if (currentSkybox != null) { RenderSettings.skybox = currentSkybox; } } } // 存储时间和对应天空盒材质的类 [System.Serializable] public class SkyboxTimeMapping { public string phaseName; public int hour; // 小时 public Material skyboxMaterial; // 对应的天空盒材质 }
配置
效果
下雨
下雨粒子效果
这里只做个简单的,想要更复杂的下雨效果,可以看我之前的文章:【实现100个unity特效之7】unity 3d实现各种粒子效果
默认禁用雨
控制雨一直跟随玩家,但是旋转不跟随
//跟随玩家 public class FollowPlayer : MonoBehaviour { public Transform player; public Vector3 offset; private void LateUpdate() { if (player != null) { Vector3 targetPosition = player.position + offset; transform.position = targetPosition; } } }
配置
控制不同天气
新增WeatherSystem
public class WeatherSystem : MonoBehaviour { [Range(0f, 1f)] public float chanceToRainSpring = 0.3f; // 春季下雨概率(30%) [Range(0f, 1f)] public float chanceToRainSummer = 0.7f; // 夏季下雨概率(70%) [Range(0f, 1f)] public float chanceToRainFall = 0.4f; // 秋季下雨概率(40%) [Range(0f, 1f)] public float chanceToRainWinter = 0f; // 冬季下雨概率(0%) public GameObject rainEffect; // 下雨特效 public Material rainSkyBox; // 下雨天气的天空盒材质 public bool isSpecialWeather; // 是否是特殊天气 public AudioSource rainChannel; // 下雨音效的音频源 public AudioClip rainSound; // 下雨音效 public enum WeatherCondition { Sunny, // 晴天 Rainy, // 下雨 Snowy // 下雪(未在代码中实现) } private WeatherCondition currentWeather = WeatherCondition.Sunny; // 当前天气默认为晴天 private void Start() { // 监听一天的流逝事件,用于生成随机天气 TimeManager.Instance.OnDayPass.AddListener(GenerateRandomWeather); } private void GenerateRandomWeather() { TimeManager.Season currentSeason = TimeManager.Instance.currentSeason; float chanceToRain = 0f; // 根据当前季节设定下雨概率 switch (currentSeason) { case TimeManager.Season.Spring: chanceToRain = chanceToRainSpring; break; case TimeManager.Season.Summer: chanceToRain = chanceToRainSummer; break; case TimeManager.Season.Fall: chanceToRain = chanceToRainFall; break; case TimeManager.Season.Winter: chanceToRain = chanceToRainWinter; break; } // 生成一个随机数,用于判断是否下雨 if (Random.value < chanceToRain) { currentWeather = WeatherCondition.Rainy; // 设置为下雨天气 isSpecialWeather = true; // 标记为特殊天气 Invoke("StartRain", 1f); // 延迟1秒开始下雨效果 } else { currentWeather = WeatherCondition.Sunny; // 设置为晴天 isSpecialWeather = false; // 标记为非特殊天气 StopRain(); // 停止下雨效果 } } private void StartRain() { if (rainChannel.isPlaying == false) { rainChannel.clip = rainSound; rainChannel.loop = true; rainChannel.Play(); } RenderSettings.skybox = rainSkyBox; // 切换天空盒为下雨天气材质 rainEffect.SetActive(true); // 激活下雨特效 } private void StopRain() { if (rainChannel.isPlaying) { rainChannel.Stop(); // 停止下雨音效 } rainEffect.SetActive(false); // 关闭下雨特效 } }
修改DayNightSystem
public WeatherSystem weatherSystem; void Update() { // 。。。 if (currentHour == 0 && lockNextDayTrigger == false) { TimeManager.Instance.TriggerNextDay(); lockNextDayTrigger = true; } if (currentHour != 0) { lockNextDayTrigger = false; } // 根据时间更新天空盒材质 if(weatherSystem.isSpecialWeather == false) UpdateSkybox(); } // 更新天空盒材质的方法 private void UpdateSkybox() { // 寻找当前小时对应的天空盒材质 Material currentSkybox = null; foreach (SkyboxTimeMapping mapping in timeMappings) { if (currentHour == mapping.hour) { currentSkybox = mapping.skyboxMaterial; if (currentSkybox.shader != null) { if (currentSkybox.shader.name == "Custom/SkyboxTransition") { blendedValue += Time.deltaTime; blendedValue = Mathf.Clamp01(blendedValue); currentSkybox.SetFloat("_TransitionFactor", blendedValue); } else { blendedValue = 0; } } break; } } // 如果找到了对应的天空盒材质,则更新RenderSettings的skybox if (currentSkybox != null) { RenderSettings.skybox = currentSkybox; } }
配置,雨声我这里就不做配置了
效果
源码
整理好了我会放上来