【unity实战】时间控制 昼夜交替 四季变化 天气变化效果
【unity小技巧】时间控制 昼夜交替 四季变化 天气变化效果
最终效果
文章目录
日期季节控制
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;
}
}
配置,雨声我这里就不做配置了
效果
源码
整理好了我会放上来
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)