当前位置: 首页 > article >正文

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


http://www.kler.cn/a/506796.html

相关文章:

  • 音视频文件提供流式传输之HTTP Live Streaming (HLS)
  • SUN的J2EE与微软的DNA
  • 【设计模式】6大设计原则和23种设计模式
  • 【Linux】10.Linux基础开发工具使用(3)
  • mysql community server社区版M2 macbook快速安装
  • 安卓与苹果系统安全性之比较
  • ip属地是根据手机号还是位置
  • MySQL中like模糊查询如何优化?
  • 【伪随机数】关于排序算法自测如何生成随机数而引发的……
  • C语言变长嵌套数组常量初始化定义技巧
  • 【排错记录】免密、nginx、cgroup、sshd
  • css 原子化
  • iOS页面设计:UIScrollView布局问题与应对策略
  • 【promethues 9090占用端口】没有网络,如何杀掉9090端口暂用的进程
  • Android 后台线程
  • 4. 使用springboot做一个音乐播放器软件项目【数据库表的创建】
  • 国产linux系统(银河麒麟,统信uos)使用 PageOffice 实现后台批量生成PDF文档
  • Math Reference Notes: 矩阵性质
  • python管理工具:conda部署+使用
  • 《黄金像凶杀案-再起》V1.2.0+Dlcs官方中文学习版