Unity3d 实时天气系统基于UniStorm插件和xx天气API实现(含源码)
前言
实时天气在Unity3d三维数字沙盘中的作用非常重要,它能够增强虚拟环境的真实感和互动性,实时天气数据的应用可以提供更为精准和直观的天气信息支持,如果真实的数据加上特效、声音和模型反馈会提高产品档次,提高真实感。
目前Unity3d虽然没有自带天气系统,不过天气关联的插件也是琳琅满目,这里个人推荐,项目中使用过的UniStorm插件,其提供了天气系统、时间系统(日夜切换),天气过度(动画)、物理效果、音效、多平台支持(pc/移动端/ARVR等),而且它还具有高度可扩展性和自定义性。
有了UniStorm插件的天气系统作为外壳支撑,真实的天气数据使用彩云天气API接入实现,它提供实况数据、分钟级降水、小时级和天级数据、还有天气预警数据。目前我只需要其实际数据接口,注册即送10000/日调用量,这里作为测试和技术研究完全够用。
关注并私信 U3D实时天气 免费获取测试程序(底部公众号)。
效果
实时效果:
大雨
雷雨
雾霾
其他效果:
萤火虫
火暴雨
雷暴雪
晴转阴
实现
这里的实现思路很简单,就是通过天气API接口获取实时的天气数据,通过数据接口返回数据解析出需要的实时数据,将数据显示在UI上,并通过UniStorm插件的UniStormManager管理类直接调用变更天气类型,并同步了三维地图上的天实时气系统效果。本工程基于Unity2019.4.22f1c1实现
UI搭建
这里的UI系统主要是展示一些文字信息:天气、温度、湿度等,接口提供了很多数据,可以按需解析显示。这里的UI简单搭建:
即用于文字显示天气、温度、湿度等,图标显示天气图标,按钮点击同步天气。
经纬度数据
天气数据的获取需要位置信息,数字沙盘大部分都是运行在PC平台上,获取经纬度直接通过设备的GPS并不适用,因为PC通常没有内建的GPS模块,使用外部API或服务来获取基于IP地址的地理位置。这边采用的代码如下(仅供参考,该接口偶尔会请求异常):
//获取ip地址信息
IEnumerator GetIPLocation()
{UnityWebRequest request = UnityWebRequest.Get("https://ipinfo.io/json");yield return request.SendWebRequest();// 检查网络错误或 HTTP 错误if (request.isNetworkError || request.isHttpError)Debug.LogError("请求失败: " + request.error);else{// 解析 JSON 数据string jsonResponse = request.downloadHandler.text;LocationData locationData = JsonUtility.FromJson<LocationData>(jsonResponse);string[] latLong = locationData.loc.Split(',');if (latLong.Length == 2){Latitude = latLong[0];Longitude = latLong[1];Debug.Log("Latitude: " + Latitude + ", Longitude: " + Longitude);}else{Debug.LogWarning("经纬度解析失败!");}}
}
其中返回的json数据类如下:
[System.Serializable]
public class LocationData
{public string ip;public string city;public string region;public string country;public string loc; // "latitude,longitude"
}
天气数据
数据通过接口直接前往api开放平台注册登录,然后创建应用:
创建应用后选择接口类型(天气),应用场景(随便选),填写应用名称后点击创建即可,创建后在应用管理列表能看到Token和用量等信息,Token后续需要使用,还需注意QPS为1,无法支撑请求频繁情况。
请求接口:
curl "https://api.caiyunapp.com/v2.6/TAkhjf8d1nlSlspN/101.6656,39.2072/realtime"
请求代码如下:
IEnumerator GetWeatherInfo(){string url = $"{BaseUrl}/{ApiKey}/{Longitude},{Latitude}/realtime";//Debug.Log("请求地址: " + url);using (UnityWebRequest request = UnityWebRequest.Get(url)){yield return request.SendWebRequest();// 检查网络错误或 HTTP 错误if (request.isNetworkError || request.isHttpError)Debug.LogError("请求失败: " + request.error);else{Debug.Log("请求成功: " + request.downloadHandler.text);}}}
请求返回实时数据结构:
{"status": "ok", // 返回状态 "api_version": "v2.6", // API 版本"api_status": "active", // API 服务状态"lang": "zh_CN", // 返回语言"unit": "metric", // 单位"tzshift": 28800, // 时区偏移"timezone": "Asia/Shanghai", // 时区"server_time": 1640745758,"location": [39.2072, 101.6656],"result": {"realtime": {"status": "ok","temperature": -7, // 地表 2 米气温"humidity": 0.58, // 地表 2 米湿度相对湿度(%)"cloudrate": 0, // 总云量(0.0-1.0)"skycon": "CLEAR_DAY", // 天气现象"visibility": 7.8, // 地表水平能见度"dswrf": 47.7, // 向下短波辐射通量(W/M2)"wind": {"speed": 1.8, // 地表 10 米风速"direction": 22 // 地表 10 米风向},"pressure": 85583.47, // 地面气压"apparent_temperature": -9.9, // 体感温度"precipitation": {"local": {"status": "ok","datasource": "radar","intensity": 0 // 本地降水强度},"nearest": {"status": "ok","distance": 10000, // 最近降水带与本地的距离"intensity": 0 // 最近降水处的降水强度}},"air_quality": {"pm25": 45, // PM25 浓度(μg/m3)"pm10": 49, // PM10 浓度(μg/m3)"o3": 6, // 臭氧浓度(μg/m3)"so2": 8, // 二氧化硫浓度(μg/m3)"no2": 42, // 二氧化氮浓度(μg/m3)"co": 1.1, // 一氧化碳浓度(mg/m3)"aqi": {"chn": 63, // 国标 AQI"usa": 124},"description": {"chn": "良","usa": "轻度污染"}},"life_index": {"ultraviolet": {"index": 3,"desc": "弱" // 参见 [生活指数](tables/lifeindex)},"comfort": {"index": 12,"desc": "湿冷" // 参见 [生活指数](tables/lifeindex)}}},"primary": 0}
}
按这个请求格式,先根据需要的数据新建需要解析的数据结构:
// 定义数据结构(根据彩云天气 API 文档)
[System.Serializable]
public class WeatherData
{public WeatherResult result;
}[System.Serializable]
public class WeatherResult
{public RealtimeWeather realtime;
}[System.Serializable]
public class RealtimeWeather
{public string skycon; // 天气状况public float temperature; // 温度public float humidity; // 湿度public float pressure; // 地面气压
}
天气显示
上一步,通过接口获取了天气实时数据并进行了解析,这一步将通过数据的更新UI和场景中的天气类型。
UI的刷新Text内容直接数据赋值显示即可,图标和天气现象文字需要根据内容做个转换映射。天气现象代码和文字如下:
这里直接将其转为字典(Dictionary)存储,方便天气现象文字获取:
// 天气现象 映射表
private static readonly Dictionary<string, string> SkyconMap = new Dictionary<string, string>{{"CLEAR_DAY", "晴(白天)"},{"CLEAR_NIGHT", "晴(夜间)"},{"PARTLY_CLOUDY_DAY", "多云(白天)"},{"PARTLY_CLOUDY_NIGHT", "多云(夜间)"},{"CLOUDY", "阴"},{"LIGHT_HAZE", "轻度雾霾"},{"MODERATE_HAZE", "中度雾霾"},{"HEAVY_HAZE", "重度雾霾"},{"LIGHT_RAIN", "小雨"},{"MODERATE_RAIN", "中雨"},{"HEAVY_RAIN", "大雨"},{"STORM_RAIN", "暴雨"},{"FOG", "雾"},{"LIGHT_SNOW", "小雪"},{"MODERATE_SNOW", "中雪"},{"HEAVY_SNOW", "大雪"},{"STORM_SNOW", "暴雪"},{"DUST", "浮尘"},{"SAND", "沙尘"},{"WIND", "大风"}
};
关于天气的图标使用的是UniStorm插件内置的图标,正式项目可以让美术搞一套完整的,我这里使用的就是很精简的:
这里的图标没法和天气接口的天气现象一一对应,所以这些图标也需要中间一次转换:
//获取图标类型
string GetIconType(string skycon)
{string icon = "";switch (skycon){case "CLEAR_DAY":case "CLEAR_NIGHT":icon = "clear";break;case "PARTLY_CLOUDY_DAY":case "PARTLY_CLOUDY_NIGHT":icon = "partly_cloudy";break;case "CLOUDY":case "FOG":icon = "cloudy";break;case "LIGHT_HAZE":case "MODERATE_HAZE":case "HEAVY_HAZE":icon = "haze";break;case "LIGHT_RAIN":case "MODERATE_RAIN":icon = "light_rain";break;case "HEAVY_RAIN":case "STORM_RAIN":icon = "heavy_rain";break;case "LIGHT_SNOW":case "MODERATE_SNOW":icon = "light_snow";break;case "HEAVY_SNOW":case "STORM_SNOW":icon = "heavy_snow";break;case "DUST":case "SAND":icon = "sand";break;default:icon = "clear";break;}return icon;
}
如上处理映射了天气现象和需要动态加载的天气图标,更新天气UI的完整代码如下:
//更新天气UI
void UpdateWeatherUI(string jsonData)
{try{// 使用 Unity 内置的 JsonUtility 或第三方 JSON 解析工具(如 Newtonsoft.Json)解析数据WeatherData weatherData = JsonUtility.FromJson<WeatherData>(jsonData);if (weatherData != null && weatherData.result != null){text_skycon.text = "天气:<color=#14FF00>" + (SkyconMap.ContainsKey(weatherData.result.realtime.skycon) ?SkyconMap[weatherData.result.realtime.skycon] : weatherData.result.realtime.skycon) + "</color>";text_temp.text = ($"温度:<color=#14FF00>{weatherData.result.realtime.temperature}℃</color>");text_humidity.text = ($"湿度:<color=#14FF00>{weatherData.result.realtime.humidity * 100}%</color>");text_pressure.text = ($"气压:<color=#14FF00>{weatherData.result.realtime.pressure} Pa</color>");Icon.sprite = Resources.Load<Sprite>("Weather Icons/" + GetIconType(weatherData.result.realtime.skycon));SwitchWeatherType(weatherData.result.realtime.skycon);}else{Debug.LogError("解析天气数据失败");}}catch (Exception e){Debug.LogError($"解析天气数据失败:" + e);}
}
接下来处理三维场景中的天气系统,即将接口返回的天气现象转换到UniStorm插件的天气类型,并进行切换的操作,这里也存在两者之间没法直接一一对应,如下是插件的内置天气类型:
UniStorm插件内置天气类型说明
0 Clear //晴朗
1 Mostly Clear //大部分晴朗
2 Mostly Cloudy //大部分多云
3 Partly Cloudy //局部多云
4 Cloudy //多云
5 Lightning Bugs //萤火虫
6 Blowing Pollen //吹花粉
7 Blowing Leaves //吹落叶
8 Blowing Pine Needles //吹松针
9 Blowing Snow //飞雪
10 Foggy //雾
11 Overcast //阴
12 Hail //冰雹
13 Heavy Rain //暴雨
14 Rain //雨
15 Light Rain //小雨
16 Drizzle //毛毛雨
17 Heavy Snow //大雪
18 Snow //雪
19 Light Snow //小雪
20 Thunderstorm //雷雨
21 Thunder Snow //雷雪
22 Dust Storm //尘暴
23 Fire Rain //火星雨
24 Fire Storm //火星暴雨
这些是在UniStorm System节点上的AllWeatherTypes列表,上面的序号和类型是默认的,它新增了很多创意类型如Fire Rain、Fire Storm和Lightning Bugs等,默认如下:
当然您也可以去新增/修改自己的天气类型,但是如果修改过AllWeatherTypes列表,则没法直接套用。这里的天气映射和切换代码如下:
//切换场景中天气
void SwitchWeatherType(string skycon)
{int idx = 0;switch (skycon){case "CLEAR_DAY":case "CLEAR_NIGHT":idx = 0;break;case "PARTLY_CLOUDY_DAY":case "PARTLY_CLOUDY_NIGHT":idx = 3;break;case "CLOUDY":idx = 2;break;case "FOG":idx = 10;break;case "LIGHT_HAZE":case "MODERATE_HAZE":case "HEAVY_HAZE":idx = 22;break;case "LIGHT_RAIN":idx = 15;break;case "MODERATE_RAIN":idx = 14;break;case "HEAVY_RAIN":case "STORM_RAIN":idx = 13;break;case "LIGHT_SNOW":idx = 19;break;case "MODERATE_SNOW":idx = 18;break;case "HEAVY_SNOW":case "STORM_SNOW":idx = 17;break;case "DUST":case "SAND":idx = 22;break;default:idx = 0;break;}if (UniStormSystem.Instance != null && idx >= 0 && idx < UniStormSystem.Instance.AllWeatherTypes.Count)UniStormManager.Instance?.ChangeWeatherInstantly(UniStormSystem.Instance.AllWeatherTypes[idx]);
}
其中ChangeWeatherInstantly方法是直接切换,没有过度动画等效果,当然您也可以采用ChangeWeatherWithTransition方法,这个带个过度,如雨转晴中间有个丝滑的过度效果:
源码工程
https://download.csdn.net/download/qq_33789001/90272787
相关文章:

Unity3d 实时天气系统基于UniStorm插件和xx天气API实现(含源码)
前言 实时天气在Unity3d三维数字沙盘中的作用非常重要,它能够增强虚拟环境的真实感和互动性,实时天气数据的应用可以提供更为精准和直观的天气信息支持,如果真实的数据加上特效、声音和模型反馈会提高产品档次,提高真实感。 目前…...

年后找工作需要注意的事项
大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | A…...

模拟器多开窗口单IP与代理IP关系
模拟器多开窗口同IP背后出现的问题 在游戏世界中,模拟器多开窗口是玩家们提升体验的常见做法。通过在同一设备上开启多个模拟器窗口,玩家可以同时运营多个游戏账号,增加游戏的趣味性和效率。 一旦检测到一个IP地址下登录了过多的账号&#x…...
Android ScrollView嵌套X5WebView大片空白问题
scrollview嵌套后webview的高度不可控。留有大片空白。 注:官方不建议scrollview嵌套webview 最好让webview自身滚动 解决方案: act_news_detail_wv.setWebViewClient(new WebViewClient() {Overridepublic void onPageFinished(WebView webView, Str…...

Java Web开发进阶——WebSocket与实时通信
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,广泛应用于需要实时数据交换的应用程序中。它能够实现服务器与客户端之间的双向通信,避免了传统 HTTP 请求/响应的延迟。结合 Spring Boot,开发实时通信应用变得更加高效与简便。 1. …...

zerotier搭建虚拟局域网,自建planet
基于该开源项目 自建planet节点,更快速,更安全 本教程依据docker-zerotier-planet 项目文档书写,并以linux(centos 7)和windows作为示例,需要其他系统配置方法,可移步项目文档 一. 前置资源 具有外网ip的服务器 后面…...

SQL面试题1:连续登陆问题
引言 场景介绍: 许多互联网平台为了提高用户的参与度和忠诚度,会推出各种连续登录奖励机制。例如,游戏平台会给连续登录的玩家发放游戏道具、金币等奖励;学习类 APP 会为连续登录学习的用户提供积分,积分可兑换课程或…...

2Spark Core
2Spark Core 1.RDD 详解1) 为什么要有 RDD?2) RDD 是什么?3) RDD 主要属性 2.RDD-API1) RDD 的创建方式2) RDD 的算子分类3) Transformation 转换算子4) Action 动作算子 3. RDD 的持久化/缓存4. RDD 容错机制 Checkpoint5. RDD 依赖关系1) 宽窄依赖2) 为什么要设计宽窄依赖 …...

linux之进程信号(初识信号,信号的产生)
目录 引入一、初识信号(信号预备知识)1.生活中的信号2.Linux中的信号3.信号进程得出的初步结论 二、信号的产生1.通过终端输入产生信号拓展: 硬件中断2.调用系统函数向进程发信号3.硬件异常产生信号4.软件条件产生信号拓展: 核心转储技术总结一下: 引入 一、初识信…...
基于nginx实现正向代理(linux版本)
介绍 在企业开发环境中,局域网内的设备通常需要通过正向代理服务器访问互联网。正向代理服务器充当中介,帮助客户端请求外部资源并返回结果。局域网内也就是俗称的内网,局域网外的互联网就是外网,在一些特殊场景内,例…...

【蓝牙】win11 笔记本电脑连接 hc-06
文章目录 前言步骤 前言 使用电脑通过蓝牙添加串口 步骤 设置 -> 蓝牙和其他设备 点击 显示更多设备 更多蓝牙设置 COM 端口 -> 添加 有可能出现卡顿,等待一会 传出 -> 浏览 点击添加 hc-06,如果没有则点击 再次搜索 确定 添加成…...

小程序组件 —— 31 事件系统 - 事件绑定和事件对象
小程序中绑定事件和网页开发中绑定事件几乎一致,只不过在小程序不能通过 on 的方式绑定事件,也没有 click 等事件,小程序中绑定事件使用 bind 方法,click 事件也需要使用 tap 事件来进行代替,绑定事件的方式有两种&…...

力扣cf补题-1【算法学习day.94】
前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向(例如想要掌握基础用法,该刷哪些题?建议灵神的题单和代码随想录)和记录自己的学习过程,我的解析也不会做的非常详细,只会提供思路和一些关…...

系统学习算法:专题四 前缀和
题目一: 算法原理: 这道题是一维前缀和的模板题,通过这道题我们可以了解什么是前缀和 题意很简单,就是先输入数组个数和查询次数,然后将数组的值放进数组,每次查询给2个数,第一个是起点&#x…...
java 迪米特法则,原理、思想、工作流程、实现细节、稳定性、优缺点、应用场景等
迪米特法则(Law of Demeter,LoD),也被称为“最少知识原则”,是一种指导面向对象设计的原则,旨在减少对象之间的耦合度。以下是对迪米特法则的详细解析。 1. 定义 迪米特法则指出:一个对象应该…...

vue项目引入阿里云svg资源图标
1:生成svg图标 登录阿里云官网 1.1 创建项目组 1.2 从阿里云网站上面获取喜欢的图标加入到已有的项目组 1.3 如果团队有自己的设计师,也可以让设计师上传自己的svg图标到阿里云指定的项目组; 使用的时候,把 资源包下载到本地项…...
存储过程和触发器
目录 1、存储过程 1.1 存储过程的概述 1.2 存储过程的类型 1. 系统存储过程 2. 本地存储过程 3. 临时存储过程 4. 扩展存储过程 1.3 T-SQL创建存储过程 1.4 T-SQL执行存储过程 1.5 T-SQL查看存储过程 1.6 T-SQL修改存储过程 1.7 T-SQL删除存储过程 2、触发器 2.1 …...

《拉依达的嵌入式\驱动面试宝典》—计算机网络篇(二)
《拉依达的嵌入式\驱动面试宝典》—计算机网络篇(二) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《…...

【深度学习实战】kaggle 自动驾驶的假场景分类
本次分享我在kaggle中参与竞赛的历程,这个版本是我的第一版,使用的是vgg。欢迎大家进行建议和交流。 概述 判断自动驾驶场景是真是假,训练神经网络或使用任何算法来分类驾驶场景的图像是真实的还是虚假的。 图像采用 RGB 格式并以 JPEG 格式…...

Spring Boot 和微服务:快速入门指南
💖 欢迎来到我的博客! 非常高兴能在这里与您相遇。在这里,您不仅能获得有趣的技术分享,还能感受到轻松愉快的氛围。无论您是编程新手,还是资深开发者,都能在这里找到属于您的知识宝藏,学习和成长…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...