【unity实战】时间控制 昼夜交替 四季变化 天气变化效果

news2024/11/13 11:09:57

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 日期季节控制
  • 时间昼夜交替
    • 素材
    • 如果没有天空盒,需要自己配置
    • 新增SkyboxBlendingShader.shader,控制天空盒平滑过渡交替变化
  • 下雨
    • 下雨粒子效果
    • 控制雨一直跟随玩家,但是旋转不跟随
    • 控制不同天气
  • 源码
  • 完结

日期季节控制

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是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1533250.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ —— 内存管理

目录 1. C内存分布 2. C 内存管理方式 2.1 new 和 delete 操作内置类型 2.2 new 和 delete 操作自定义类型 3. operator new与operator delete函数 4. new和delete的实现原理 5. malloc/free 和 new/delete 的区别 1. C内存分布 首先看一段代码&#xff1a; int globalV…

短视频矩阵系统源头技术开发--每一次技术迭代

短视频矩阵系统源头开发3年的我们&#xff0c;肯定是需求不断的迭代更新的&#xff0c;目前我们已经迭代了3年之久&#xff0c;写技术文章已经写了短视频矩阵系统&#xff0c;写了3年了&#xff0c;开发了3年了 短视频矩阵获客系统是一种基于短视频平台的获客游戏。短视频矩阵系…

18个惊艳的可视化大屏(第29辑):机械自动化设备仪器

当涉及到机械自动化生产管理时&#xff0c;可视化大屏可以提供以下九个价值&#xff1a; 实时监控 可视化大屏可以实时显示生产线上的各个环节和设备的运行状态。运营人员可以通过大屏实时监控生产线的生产效率、设备运行状况等关键指标&#xff0c;及时发现并解决问题&#…

面试笔记——Redis(集群方案:主从复制、哨兵模式和分片集群)

主从复制 在 Redis 主从集群中&#xff0c;一个主节点&#xff08;Master&#xff09;负责处理客户端的读写请求&#xff0c;而多个从节点&#xff08;Slave&#xff09;则负责复制主节点的数据&#xff0c;并对外提供读取服务——解决高并发问题。 主节点&#xff08;Master&…

echart多折线图堆叠 y轴和实际数据不对应

当使用 ECharts 绘制堆叠折线图时&#xff0c;有时会遇到 y 轴与实际数据不对应的问题。 比如明明值是50&#xff0c;但折线点在y轴的对应点却飙升到了二百多 解决办法&#xff1a; 查看了前端代码发现在echart的图表中有一个‘stack’的属性&#xff0c;尝试把他删除之后y轴的…

带大家深入理解redis的主从复制

为何需要主从复制 Redis作为开发过程中经常用到的缓存数据库&#xff0c;我们在开发过程中可能会遇到各种各样的问题&#xff0c;比如&#xff1a; Redis的读并发量太大怎么办&#xff1f; 单机版的Redis挂掉怎么办&#xff1f; 我们不仅需要并发&#xff0c;更需要安全&…

力扣热门算法题 52. N 皇后 II,53. 最大子数组和,54. 螺旋矩阵

52. N 皇后 II&#xff0c;53. 最大子数组和&#xff0c;54. 螺旋矩阵&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.20 可通过leetcode所有测试用例。 目录 52. N 皇后 II 解题思路 完整代码 Python Java 53. 最大子数组…

【消息队列开发】 虚拟主机设计——操作绑定

文章目录 &#x1f343;前言&#x1f332;添加绑定&#x1f333;删除绑定⭕总结 &#x1f343;前言 本次开发任务&#xff1a; 实现对绑定的添加与绑定 &#x1f332;添加绑定 对于绑定的操作相较于前面对交换机和队列的操作就会麻烦一点了 我们分为以下七步来实现&#x…

UE4 Json事件设置Asset值

通过Json事件来设置&#xff0c;比如骨骼网格体&#xff08;换皮&#xff09;等等

用Python直接获取Word文档页数、字数、段落数、节数等信息

计算 Word 文档的页数、字数等信息是出版、学术和内容管理等领域的一项基本任务。准确的页数和字数对于评估文档长度、估算印刷成本、分析文本复杂性以及确保符合格式化指南至关重要。逐个预览文档查看相关信息是非常麻烦的事情&#xff0c;我们可以在不预览文档的情况下&#…

Day74:WEB攻防-机制验证篇重定向发送响应状态码跳过步骤验证码回传枚举

目录 验证码突破-回传显示&规律爆破 某目标回显显示 某APP验证码爆破 验证目标-重定向用户&重定向发送 某CMS重定向用户 某CMS重定向发送 验证逻辑-修改响应包&跳过步骤URL 某APP修改响应包 某APP跳过步骤URL 实战SRC验证逻辑挖掘分享案例 短信验证码回…

集成学习 | 集成学习思想:Boosting

目录 二. Boosting思想1. Adaboost 算法1.1 Adaboost算法构建流程1.2 sklearn库参数说明 2. Gradient Boosting 算法2.1 Gradient Boosting算法构建流程2.2 Gradient Boosting算法的回归与分类问题2.2.1 Gradient Boosting回归算法均方差损失函数绝对误差损失函数 2.2.2 Gradie…

使用 Dify 和 AWS Bedrock 玩转 Anthropic Claude 3

本篇文章&#xff0c;聊聊怎么比较稳定的使用 Anthropic Claude 3&#xff0c;以及基于目前表现非常好的模型&#xff0c;来做一些有趣的 AI Native 小工具。 写在前面 在实际体验了半个多月&#xff0c;月初上线的 Anthropic Claude Pro 后&#xff0c;发现 Claude 3 系列模…

不同的Git仓库单独设置用户名和邮件地址

最近使用公司电脑将自己的一个私人项目推送到远程仓库&#xff0c;仓库显示的公司邮箱地址。因为设置了全局的username和usermail&#xff0c;这样就比较尴尬了。但是又不能频繁来回改用户信息&#xff0c;那么请看下面如何单独设置仓库的用户信息&#xff0c;让不同的仓库展示…

怎么轻松制作证件照?推荐这三款制作工具!

在日常生活中&#xff0c;我们经常需要制作各种证件照&#xff0c;如身份证、护照、驾驶证等。为了帮助大家快速、便捷地制作证件照&#xff0c;我将在本文中推荐三款优秀的证件照制作工具&#xff0c;包括国内外的软件&#xff0c;满足不同用户的需求。1.水印云 水印云是一款国…

自动推送个人站点到百度收录

自动推送个人站点到百度收录 准备 验证站点 访问百度收录官网注册帐号选择用户中心-站点管理 在“站点管理”里面点击“添加站点”&#xff0c;填写你的站点地址&#xff08;支持子域名&#xff09; 根据你的站点的内容、类型勾选站点属性 点击“验证站点”。 两种方式都可以…

学点儿Java_Day6_面向对象:类、封装、构造方法

1 类 1.1 定义 类&#xff1a;对现实世界中事物的抽象。Student 对象&#xff1a;现实世界中具体的个体。张三、李四 这些具体的学生 面向对象的特征&#xff1a;抽象、封装、继承、多态 OOP: Object Oriented Programming 类和对象的总结&#xff1a; 1、现实世界都是由很多…

GO-初识包管理

初识包管理&#xff0c;知道项目中文件和文件夹之间的关系 输出&#xff0c;代码&#xff0c;在go编译器运行时会显示在屏幕中 初识数据类型 整型&#xff0c;数字。例如&#xff1a;1、2、3、4 字符串类型&#xff0c;表示文本信息的。例如:“张三”“李四” 布尔类型&#x…

windows docker

写在前面的废话 最近在学习riscv的软件相关内容&#xff0c;倒是有别人的sg2042机器可以通过ssh使用&#xff0c;但是用起来太不方便了&#xff0c;经常断掉&#xff0c;所以想着在自己的机器上跑一跑riscv的操作系统。最常见的有两种方法吧&#xff0c;第一个就是qemu&#xf…

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密 上一篇某招聘软件的sig及sp参数被和谐掉了,所以懂得都懂啊! 因为web的api没有那么全,所以来看了下app的,ios的防护几乎没有,纸糊的一样,android端的有点复杂了,到最后我也没能完整的实现整个加密过程,我也只复现到DF…