浮标点和相机停靠点外置,用resources加载图片,删除脚本文件夹分类

This commit is contained in:
XuGaoFeng
2026-07-11 10:42:30 +08:00
parent 9c68a64c5b
commit 2732326b32
63 changed files with 1878 additions and 513 deletions

View File

@@ -0,0 +1,496 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
struct RequiredInformation
{
public string SNWithdeviceName;
public Vector3 localPosition;
public string customName;//这个是浮标上面的文本,自己命的名
public string pictureSerialNumber;//MT开头的图片
public string tag;//用tag来显示sorting Icons
public RequiredInformation(string sn, Vector3 pos, string name, string picSn,string tag)
{
SNWithdeviceName = sn;
localPosition = pos;
customName = name;
pictureSerialNumber = picSn;
this.tag = tag;
}
public RequiredInformation(string sn, string name, string picSn, string tag)
{
SNWithdeviceName = sn;
localPosition = Vector3.zero;
customName = name;
pictureSerialNumber = picSn;
this.tag = tag;
}
}
public class AskForWEBInfomation : MonoBehaviour
{
public GameObject ICONSPosition;//把ICONSPosition拖进来提供我的这些浮标的位置
public Camera cam;
public GameObject buoy;//把BUoy预制体拖进来使用代码自动生成ICON的子物体
// public List<Sprite> pictures = new List<Sprite>();//把图片预制体都拖过来
[Serializable]
public class IdentifierTextBinding
{
public string identifier;
public TMP_Text targetText;
}
[Header("认证")]
[TextArea(2, 4)]
public string authorization = "tenantCode=DEMO&accessToken=225e1a7ffede96cb5adab3aa98775660f4c6a066";
[Header("编辑器测试地址")]
public string editorFullUrl = "https://3d.gzckgc.cn/api.php/Device/deviceDetail";
[Header("设备SN列表")]
public List<string> deviceSnList = new List<string>
{
"ys_2026020420","ys_2026020419","ys_2026020418","ys_2026020417","ys_2026020416","ys_2026020415"
};
List<RequiredInformation> infos = new List<RequiredInformation>()
{ //构造函数现在可以直接加3个我现在已经通过读取场景中的位置来调节位置了这里也不想再删它了,后面读取的时候再覆盖一下这里也不影响.
new RequiredInformation("ys_2026020415_sb_gnss001",new Vector3(10.9530001f,-10.7779989f,-1.75300026f),"GNSS","MT014","沉降监测") ,
new RequiredInformation("ys_2026020416_sb_wuxianqingjiaoji001",new Vector3(-0.25177002f,-0.00340000005f,3.51672745f),"无线倾角计1","倾角图标","倾角监测") ,
new RequiredInformation("ys_2026020416_sb_wuxianqingjiaoji002",new Vector3(-3.22239995f,-0.0055999998f,1.67970002f),"无线倾角计2","倾角图标","倾角监测") ,
new RequiredInformation("ys_2026020416_sb_wuxianqingjiaoji003",new Vector3(-0.254400015f,-0.00449999981f,-0.108400002f),"无线倾角计3","倾角图标","倾角监测") ,
new RequiredInformation("ys_2026020417_sb_fengjizhendong001",new Vector3(1.14961243f,55.1254196f,2.07600784f),"振动加速度1","MT019","振动加速度") ,
new RequiredInformation("ys_2026020417_sb_fengjizhendong002",new Vector3(1.14961243f,55.1785011f,1.38459396f),"振动加速度2","MT019","振动加速度") ,
new RequiredInformation("ys_2026020418_sb_yingbianji001",new Vector3(-1.90900004f,10.3079996f,3.55800009f),"应变计1","MT013","应变监测") ,
new RequiredInformation("ys_2026020418_sb_yingbianji002",new Vector3(-3.17600012f,10.2980003f,1.66199994f),"应变计2","MT013","应变监测") ,
new RequiredInformation("ys_2026020418_sb_yingbianji003",new Vector3(-1.87399995f,10.2880001f,-0.150199994f),"应变计3","MT013","应变监测") ,
new RequiredInformation("ys_2026020418_sb_yingbianji004",new Vector3(0.349900007f,10.2938004f,0.450199991f),"应变计4","MT013","应变监测") ,
new RequiredInformation("ys_2026020418_sb_yingbianji005",new Vector3(0.400999993f,10.2967997f,2.88899994f),"应变计5","MT013","应变监测") ,
new RequiredInformation("ys_2026020419_sb_wenshiya001",new Vector3(-69.6037674f,-33.9762917f,141.763977f),"温湿压1","温湿图标","气象监测") ,
new RequiredInformation("ys_2026020420_sb_yachashi001",new Vector3(10.7563248f,-12.5099993f,-1.32729721f),"静力水准仪1","MT014","沉降监测") ,
new RequiredInformation("ys_2026020420_sb_yachashi002",new Vector3(-1.17034912f,-12.5128984f,4.73325729f),"静力水准仪2","MT014","沉降监测") ,
new RequiredInformation("ys_2026020420_sb_yachashi003",new Vector3(-1.17050004f,-12.5162926f,-1.3204f),"静力水准仪3","MT014","沉降监测") ,
new RequiredInformation("ys_2026020420_sb_yachashi004",new Vector3(1.83947754f,-12.5169001f,1.70901871f),"静力水准仪4","MT014","沉降监测") ,
new RequiredInformation("ys_2026020420_sb_yachashi005",new Vector3(-4.20899963f,-12.5176001f,1.70901871f),"静力水准仪5","MT014","沉降监测") ,
};
/* Dictionary<string, Vector3> deviceName = new Dictionary<string, Vector3>//这个是ICONS下面所有的设备用系统自动实例化
{
{ "ys_2026020415_sb_gnss001",new Vector3(10.9530001f,-10.7779989f,-1.75300026f)} ,
{"ys_2026020416_sb_wuxianqingjiaoji001", new Vector3(-0.25177002f,-0.00340000005f,3.51672745f)},
{"ys_2026020416_sb_wuxianqingjiaoji002",new Vector3(-3.22239995f,-0.0055999998f,1.67970002f)},
{"ys_2026020416_sb_wuxianqingjiaoji003",new Vector3(-0.254400015f,-0.00449999981f,-0.108400002f) },
{"ys_2026020417_sb_fengjizhendong001",new Vector3(1.14961243f,55.1254196f,2.07600784f)},
{"ys_2026020417_sb_fengjizhendong002",new Vector3(1.14961243f,55.1785011f,1.38459396f) },
{"ys_2026020418_sb_yingbianji001" ,new Vector3(-1.90900004f,10.3079996f,3.55800009f)},
{"ys_2026020418_sb_yingbianji002" ,new Vector3(-3.17600012f,10.2980003f,1.66199994f)},
{"ys_2026020418_sb_yingbianji003" ,new Vector3(-1.87399995f,10.2880001f,-0.150199994f)},
{"ys_2026020418_sb_yingbianji004" ,new Vector3(0.349900007f,10.2938004f,0.450199991f)},
{"ys_2026020418_sb_yingbianji005" ,new Vector3(0.400999993f,10.2967997f,2.88899994f)},
{"ys_2026020419_sb_wenshiya001",new Vector3(-69.6037674f,-33.9762917f,141.763977f)},
{"ys_2026020420_sb_yachashi002",new Vector3(-1.17034912f,-12.5128984f,4.73325729f)},
{"ys_2026020420_sb_yachashi003",new Vector3(-1.17050004f,-12.5162926f,-1.3204f)},
{"ys_2026020420_sb_yachashi004",new Vector3(1.83947754f,-12.5169001f,1.70901871f)},
{"ys_2026020420_sb_yachashi005",new Vector3(-4.20899963f,-12.5176001f,1.70901871f)},
};
Dictionary<string, string> customName = new Dictionary<string, string>//这个是浮标上面的文本,自己命的名
{
{ "ys_2026020415_sb_gnss001","GNSS"} ,
{"ys_2026020416_sb_wuxianqingjiaoji001", "无线倾角计1"},
{"ys_2026020416_sb_wuxianqingjiaoji002","无线倾角计2"},
{"ys_2026020416_sb_wuxianqingjiaoji003","无线倾角计3" },
{"ys_2026020417_sb_fengjizhendong001","振动加速度1"},
{"ys_2026020417_sb_fengjizhendong002","振动加速度2" },
{"ys_2026020418_sb_yingbianji001" ,"应变计1"},
{"ys_2026020418_sb_yingbianji002" ,"应变计2"},
{"ys_2026020418_sb_yingbianji003" ,"应变计3"},
{"ys_2026020418_sb_yingbianji004" ,"应变计4"},
{"ys_2026020418_sb_yingbianji005" ,"应变计5"},
{"ys_2026020419_sb_wenshiya001","温湿压1"},
{"ys_2026020420_sb_yachashi002","温湿压2"},
{"ys_2026020420_sb_yachashi003","温湿压3"},
{"ys_2026020420_sb_yachashi004","温湿压4"},
{"ys_2026020420_sb_yachashi005","温湿压5"},
};*/
[Header("手动拖拽绑定identifier -> TMP_Text")]
public List<IdentifierTextBinding> bindings = new List<IdentifierTextBinding>();
[Header("显示设置")]
public string valueFormat = "0.##";
public bool includeUnit = true;
private Dictionary<string, TMP_Text> textMap = new Dictionary<string, TMP_Text>();
public bool _isRunning = false;//这个东西只是用来防止手动连点
private void Awake()
{
cam = Camera.main;
InitiateICONS();
BuildBindingMap();
}
private void InitiateICONS()
{
//使用 info 之前要先用 ICONPosition 来调整一下里面的位置,用 foreach 还不能改,只能用 for 循环慢慢改回去
for (int i = 0; i < infos.Count; i++)
{
RequiredInformation info = infos[i];
info.localPosition = ICONSPosition.transform.Find(info.SNWithdeviceName).localPosition;
infos[i] = info;
}
//开始用修改好的infos来工作
foreach (RequiredInformation info in infos)
{
//生成一个预制体
GameObject obj = Instantiate(buoy);
obj.name = info.SNWithdeviceName;
obj.transform.SetParent(transform);
obj.transform.localPosition = info.localPosition;
obj.tag = info.tag;//添加标签
//开始修理预制体下面的响应函数和text
int first = obj.name.IndexOf("_");
int second = obj.name.IndexOf('_', first + 1);//second 是第二个下划线的索引索引是从0开始的
string deviceSn = obj.name.Substring(0, second);//前半部分比如ys_2026030415,末尾没有下划线,但是这部分不重要,重要的是后半部分。
//这里second表示长度第二个下划线索引是9也就是第10位但是索引做长度正好把第十位丢掉了
string deviceName = obj.name.Substring(second + 1); //设备名,也就是后半部分,从指定索引开始到结尾
//第一个子物体主要负责浮标的响应函数
obj.transform.GetChild(0).name = deviceName + "ICON";
obj.transform.GetChild(0).GetComponent<Button>().onClick.RemoveAllListeners();
obj.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(
() => {
cam.GetComponent<DoubleClickToFocus>().FlyToIconAndConvertCore(obj.transform.GetChild(0).name);
}
);
obj.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(
() => {
ManuallyLoadOneDevice(obj.name);
}
);
/* foreach (Sprite img in pictures)//遍历图片数组,如果找到就结束,现在改成用 Resources 直接加载
{
if (img.name == info.pictureSerialNumber)
{
obj.transform.GetChild(0).GetComponent<Image>().sprite = img;
break;
}
}*/
obj.transform.GetChild(0).GetComponent<Image>().sprite = Resources.Load<Sprite>("Icons/"+info.pictureSerialNumber);
//第二个子物体负责浮标上面的文字描述
obj.transform.GetChild(1).name = deviceName + "Text";
obj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = info.customName;
}
}
private void BuildBindingMap()
{
textMap.Clear();
foreach (var binding in bindings)
{
if (binding == null) continue;
if (string.IsNullOrWhiteSpace(binding.identifier)) continue;
if (binding.targetText == null) continue;
textMap[binding.identifier] = binding.targetText;
}
}
private void Start()
{
StartCoroutine(LoadAllDevices());
}
//这个是和前端交互的时候按下的按钮
public void ManuallyLoadOneDevice(string SnWithDeviceName)
{
if (_isRunning)
return;
StartCoroutine(LoadOneDeviceManually(SnWithDeviceName));
}
IEnumerator DelayPart()
{
yield return new WaitForSeconds(2.0f); // 等待2秒
// 这里写延迟执行的代码
Debug.Log("2秒后执行");
}
private IEnumerator LoadAllDevices()
{
if (string.IsNullOrWhiteSpace(authorization) || authorization.Contains("替换"))
{
Debug.LogError("authorization 还没填。");
yield break;
}
foreach (string sn in deviceSnList)
{
yield return StartCoroutine(LoadOneDevice(sn));
}
Debug.Log("全部设备文本更新完成");
}
private IEnumerator LoadOneDevice(string deviceSn)
{
string url = GetRequestUrl();
WWWForm form = new WWWForm();
form.AddField("device_sn", deviceSn);
using (UnityWebRequest req = UnityWebRequest.Post(url, form))
{
req.SetRequestHeader("Accept", "application/json, text/plain, */*");
req.SetRequestHeader("Authorization", authorization);
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"请求失败 | SN={deviceSn} | {req.error}");
yield break;
}
string json = req.downloadHandler.text;
DeviceDetailResponse resp = null;
try
{
resp = JsonUtility.FromJson<DeviceDetailResponse>(json);
}
catch (Exception e)
{
Debug.LogError($"JSON解析失败 | SN={deviceSn} | {e.Message}");
yield break;
}
if (resp == null || resp.status == null)
{
Debug.LogError($"响应为空 | SN={deviceSn}");
yield break;
}
if (resp.status.code != 0)
{
Debug.LogError($"业务失败 | SN={deviceSn} | code={resp.status.code} | msg={resp.status.msg}");
yield break;
}
if (resp.result == null || resp.result.data_points == null || resp.result.data_points.timeseries == null)
{
Debug.LogWarning($"没有 timeseries 数据 | SN={deviceSn}");
yield break;
}
foreach (DevicePoint point in resp.result.data_points.timeseries)
{
if (point == null) continue;
if (string.IsNullOrWhiteSpace(point.identifier)) continue;
UpdateText(point.identifier, point.value, point.unitName, point.name);
}
}
}
private IEnumerator LoadOneDeviceManually(string deviceSnWithName)//狗日的unity多参数不能用inspector编辑,重载还不能名字相同,烦。。
{
_isRunning = true;
yield return null;
float transitionDuration = Camera.main.transform.GetComponent<DoubleClickToFocus>().transitionDuration;
yield return new WaitForSeconds(transitionDuration + 0.01f); // 等待2秒
// 这里写延迟执行的代码
Debug.Log("2秒后执行");
//为了手动点击传出sn和传感器名字命名ys_2026020427_sb_wuxianqingjiaoji001这样用下划线找sn和名字
//我给这个方法重载了,
//分别是手动点击时和自动跳转
//唯一不同就是最下面判断是不是被点击的设备
int first = deviceSnWithName.IndexOf("_");
int second = deviceSnWithName.IndexOf('_', first + 1);
string deviceSn = deviceSnWithName.Substring(0, second);
string iconName = GameObject.Find("ICONS").transform.Find(deviceSnWithName).transform.Find(deviceSnWithName.Substring(second + 1) + "Text").GetComponent<TextMeshProUGUI>().text;
//传感器的名字。用这个 object 里面的 text。无法用 gameobject. find 直接找到子对象,先找它的父对象,然后用父对象找子对象。
string url = GetRequestUrl();
WWWForm form = new WWWForm();
form.AddField("device_sn", deviceSn);
using (UnityWebRequest req = UnityWebRequest.Post(url, form))
{
req.SetRequestHeader("Accept", "application/json, text/plain, */*");
req.SetRequestHeader("Authorization", authorization);
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"请求失败 | SN={deviceSn} | {req.error}");
yield break;
}
string json = req.downloadHandler.text;
DeviceDetailResponse resp = null;
try
{
resp = JsonUtility.FromJson<DeviceDetailResponse>(json);
}
catch (Exception e)
{
Debug.LogError($"JSON解析失败 | SN={deviceSn} | {e.Message}");
yield break;
}
if (resp == null || resp.status == null)
{
Debug.LogError($"响应为空 | SN={deviceSn}");
yield break;
}
//此部分为手动获取数据和自动获取数据的唯一区别。
SendData sendData = new SendData();
sendData.result = resp.result;
sendData.iconName = iconName;
string CoutJsonData = JsonUtility.ToJson(sendData, true);
Application.ExternalCall("sendDataToVue", CoutJsonData);
//Debug.Log(CoutJsonData);
if (resp.status.code != 0)
{
Debug.LogError($"业务失败 | SN={deviceSn} | code={resp.status.code} | msg={resp.status.msg}");
yield break;
}
if (resp.result == null || resp.result.data_points == null || resp.result.data_points.timeseries == null)
{
Debug.LogWarning($"没有 timeseries 数据 | SN={deviceSn}");
yield break;
}
foreach (DevicePoint point in resp.result.data_points.timeseries)
{
if (point == null) continue;
if (string.IsNullOrWhiteSpace(point.identifier)) continue;
UpdateText(point.identifier, point.value, point.unitName, point.name);
}
}
_isRunning = false;//协程运行完成自动解锁。
Debug.Log("单次查询完成");
}
private void UpdateText(string identifier, string valueStr, string unitName, string itemName)
{
if (!textMap.TryGetValue(identifier, out TMP_Text target) || target == null)
{
Debug.LogWarning($"没有绑定 identifier: {identifier}");
return;
}
string finalText = valueStr;
if (float.TryParse(valueStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float value))
{
finalText = value.ToString(valueFormat, CultureInfo.InvariantCulture);
}
if (includeUnit)//&& !string.IsNullOrWhiteSpace(unitName)
{
finalText += unitName;
finalText = itemName + ": " + finalText;
}
target.text = finalText;
}
private string GetRequestUrl()
{
#if UNITY_WEBGL && !UNITY_EDITOR
return "/api.php/Device/deviceDetail";
#else
return editorFullUrl;
#endif
}
[ContextMenu("重建绑定字典")]
private void RebuildBindings()
{
BuildBindingMap();
Debug.Log("绑定字典已重建");
}
[Serializable]
public class DeviceDetailResponse
{
public ApiStatus status;
public DeviceResult result;
}
[Serializable]
public class ApiStatus
{
public int code;
public string msg;
}
[Serializable]
public class DeviceResult
{
public int device_id;
public string device_name;
public string device_sn;
public string status;
public string online_status;
public string location;
public string address;
public string pic;
public DataPoints data_points;
}
[Serializable]
public class SendData
{
public string iconName;
public DeviceResult result;
}
[Serializable]
public class DataPoints
{
public DevicePoint[] control;
public DevicePoint[] timeseries;
}
[Serializable]
public class DevicePoint
{
public string point_id;
public string name;
public string alias;
public string identifier;
public string unitName;
public string unitIdentifier;
public string accessMode;
public string dataType;
public string value;
public string callback_time;
}
}