Unity3d 实时天气系统基于UniStorm插件和xx天气API实现(含源码)

news2025/1/17 22:38:42

前言

实时天气在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

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

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

相关文章

Linux命令行工具-使用方法

参考资料 Linux网络命令&#xff1a;网络工具socat详解-CSDN博客 arm-linux-gnueabihf、aarch64-linux-gnu等ARM交叉编译GCC的区别_aarch64-elf-gcc aarch64-linux-gnu-CSDN博客 解决Linux内核问题实用技巧之-dev/mem的新玩法-腾讯云开发者社区-腾讯云 热爱学习地派大星-CS…

【Flink系列】9. Flink容错机制

9. 容错机制 在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 9.1 检查点&#xff08;Checkpoint&#xff09; 9.1.1 检查点的保存 1&#xff09;周期性的触发保存 “随时存档”确实恢复起来方便&#xff0c;可是需要我…

【深度学习】关键技术-激活函数(Activation Functions)

激活函数&#xff08;Activation Functions&#xff09; 激活函数是神经网络的重要组成部分&#xff0c;它的作用是将神经元的输入信号映射到输出信号&#xff0c;同时引入非线性特性&#xff0c;使神经网络能够处理复杂问题。以下是常见激活函数的种类、公式、图形特点及其应…

20.<Spring图书管理系统①(登录+添加图书)>

PS&#xff1a;关于接口定义 接口定义&#xff0c;通常由服务器提供方来定义。 1.路径&#xff1a;自己定义 2.参数&#xff1a;根据需求考虑&#xff0c;我们这个接口功能完成需要哪些信息。 3.返回结果&#xff1a;考虑我们能为对方提供什么。站在对方角度考虑。 我们使用到的…

【Sql递归查询】Mysql、Oracle、SQL Server、PostgreSQL 实现递归查询的区别与案例(详解)

文章目录 Mysql 5.7 递归查询Mysql 8 实现递归查询Oracle递归示例SQL Server 递归查询示例PostgreSQL 递归查询示例 更多相关内容可查看 Mysql 5.7 递归查询 MySQL 5.7 本身不直接支持标准 SQL 中的递归查询语法&#xff08;如 WITH RECURSIVE 这种常见的递归查询方式&#xf…

vue2修改表单只提交被修改的数据的字段传给后端接口

效果&#xff1a; 步骤一、 vue2修改表单提交的时候&#xff0c;只将修改的数据的字段传给后端接口&#xff0c;没有修改得数据不传参给接口。 在 data 对象中添加一个新的属性&#xff0c;用于存储初始表单数据的副本&#xff0c;与当前表单数据进行比较&#xff0c;找出哪些…

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程(配套案例数据)》专栏上线了

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程》全新上线了&#xff0c;欢迎广大GISer朋友关注&#xff0c;一起探索GIS奥秘&#xff0c;分享GIS价值&#xff01; 本专栏以实战案例的形式&#xff0c;深入浅出地介绍了GRASS GIS的基本使用方法&#xff0c;用一个个实例讲…

纯命令 git使用

首先我们到一个新的公司 要添加一个新的git仓库的权限 我们应该现拉去代码 配置git的仓库信息 第一 先添加权限 第二 如果不是自己电脑 需要配置信息 配置基础信息 查看本地git账号git config --global user.name git config --global user.email修改本地账号git co…

Linux系统编程:深入理解计算机软硬件体系和架构

一、硬件体系 首先我们要知道&#xff0c;我们最常见的计算机&#xff08;笔记本&#xff09;以及我们不常见的计算机&#xff08;服务器&#xff09;其实本质上都是一堆硬件的结合&#xff1a;cpu、网卡、显卡、内存、磁盘、显示器、键盘…… 但他们并不是毫无章法地放在一起…

微信小程序-Docker+Nginx环境配置业务域名验证文件

在实际开发或运维工作中&#xff0c;我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件&#xff0c;用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx&#xff0c;就需要将该验证文件正确地映射&#xff08;挂载&#xff09;到容器中&#xff0c;并…

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (五、电影详情页的设计实现)

在上一篇文章中&#xff0c;完成了电影列表页的开发。接下来&#xff0c;将进入电影详情页的设计实现阶段。这个页面将展示电影的详细信息&#xff0c;包括电影海报、评分、简介以及相关影人等。将使用 HarmonyOS 提供的常用组件&#xff0c;并结合第三方库 nutpi/axios 来实现…

在 macOS 上,用命令行连接 MySQL(/usr/local/mysql/bin/mysql -u root -p)

根据你提供的文件内容&#xff0c;MySQL 的安装路径是 /usr/local/mysql。要直接使用 mysql 命令&#xff0c;你需要找到 mysql 可执行文件的路径。 在 macOS 上&#xff0c;mysql 客户端通常位于 MySQL 安装目录的 bin 子目录中。因此&#xff0c;完整的路径应该是&#xff1…

使用docker-compose安装ELK(elasticsearch,logstash,kibana)并简单使用

首先服务器上需要安装docker已经docker-compose&#xff0c;如果没有&#xff0c;可以参考我之前写的文章进行安装。 https://blog.csdn.net/a_lllk/article/details/143382884?spm1001.2014.3001.5502 1.下载并启动elk容器 先创建一个网关&#xff0c;让所有的容器共用此网…

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题 问题描述 本人在使用zotero中的zotero one&#xff08;青柠学术插件&#xff09;的时候&#xff0c;使用插件跳转obsidian中的对应笔记&#xff0c;出现上图情况。&#xff08;错误中提到的…

《小迪安全》学习笔记05

目录 读取&#xff1a; 写入&#xff1a; &#xff08;其中的读取和写入时我认为比较重要的&#xff0c;所以单独做成了目录&#xff0c;这里的读取和写入是指在进行sql注入的时候与本地文件进行的交互&#xff09; 好久没发博客了。。。从这篇开始的小迪安全学习笔记就开始…

Jupyter notebook中运行dos指令运行方法

Jupyter notebook中运行dos指令运行方法 目录 Jupyter notebook中运行dos指令运行方法一、DOS(磁盘操作系统&#xff09;指令介绍1.1 DOS介绍1.2 DOS指令1.2.1 DIR - 显示当前目录下的文件和子目录列表。1.2.2 CD 或 CHDIR - 改变当前目录1.2.3 使用 CD .. 可以返回上一级目录1…

excel 判断某个单元格的日期,如果超过3天,则在另一个单元格显示超过三天的公式

excel 判断某个单元格的日期&#xff0c;如果超过3天&#xff0c;则在另一个单元格显示超过三天的公式&#xff0c;公式如下&#xff1a; IF(DATEDIF(C627,TODAY(),"d")<4,"3天以内","超过三天") IF(D627"超过3天","文件赶紧…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

eBay账号安全攻略:巧妙应对风险

在跨境电商的浪潮中&#xff0c;eBay宛如一座璀璨的灯塔&#xff0c;照亮了无数买卖双方的交易之路。但别忘了&#xff0c;网络安全的阴霾也在悄然蔓延&#xff0c;让eBay账号时刻处于黑客攻击、数据泄露、钓鱼诈骗等风险的阴影之下。别担心&#xff0c;今天就来为你支支招&…

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…