前言:只是自己的一点理解,自己在项目实践中没出现问题,如果有不足欢迎指正。主打听劝!
一:什么是MVC模式
MVC是一种经典的软件架构模式,用于实现用户界面与业务逻辑的分离。它分为三层:
Model:用于数据和业务逻辑层,表示应用的核心结构或数据结构。
View:表现层,负责显示数据,不直接处理业务逻辑。
Controller:控制器层,接收用户输入或数据更新,处理逻辑,通知 Model 和 View。
作用:管理模块分离数据、表现和逻辑
二:什么是观察者模式
观察者模式(Observer Pattern)是一种设计模式,用于实现“发布-订阅”机制: 一个对象(发布者)状态改变时,会通知所有订阅者(观察者); 被广泛应用于事件驱动系统、UI 更新、数据推送场景。
Subject(被观察者)保存观察者列表,状态改变时通知他们。
Observer(观察者)定义回调接口,接收状态变化。
作用:实现模块间的解耦和通知机制
三:如何协同工作和他们的作用
协同工作:
Model
或 Controller
可以作为 Subject(被观察者);
View
作为 Observer(观察者),接收数据变化通知,自动更新界面;
SocketManager(数据源)
↓
DevjosnController(Controller + Subject)
↓ ↘
View1(Observer) View2(Observer)
作用:
使用 MVC,把数据处理和 UI 逻辑解耦;
使用观察者模式,让多个 UI 界面响应不同控制器的数据变化;
易于拓展:你以后要加新的设备、UI 组件,不用改核心数据处理逻辑;
易于维护:UI 问题和通信问题可以独立排查。
四:具体的应用
1.工具类:底层数据接收
这个类负责通讯的连接管理与数据接收,案例是wenbsocket
using System;
using System.Text;
using UnityEngine;
using NativeWebSocket;
using System.Threading.Tasks;
using System.IO;
/// <summary>
/// 负责 WebSocket 的连接管理与数据接收,不处理业务数据逻辑
/// </summary>
public class SocketManager : MonoBehaviour
{
private WebSocket websocket;
private int reconnectAttempts = 0;
private const int maxReconnectAttempts = 10;
private bool isReconnecting = false;
private async void Start()
{
await ConnectWebSocket();
}
/// <summary>
/// 连接 WebSocket,并注册事件回调
/// </summary>
private async Task ConnectWebSocket()
{
if (websocket != null)
{
await websocket.Close();
websocket = null;
}
websocket = new WebSocket("ws://localhost:5260/ws");
websocket.OnOpen += () =>
{
Debug.Log("✅ WebSocket 连接成功!");
reconnectAttempts = 0;
isReconnecting = false;
};
websocket.OnError += (e) =>
{
Debug.LogError("❌ WebSocket 错误: " + e);
TryReconnect();
};
websocket.OnClose += (e) =>
{
Debug.LogWarning("🔌 WebSocket 连接关闭!");
TryReconnect();
};
websocket.OnMessage += (bytes) =>
{
string json = Encoding.UTF8.GetString(bytes);
// 可选调试写入桌面文件
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string path = Path.Combine(desktopPath, "jsonData.txt");
File.WriteAllText(path, json);
// 转交给 DevjosnController 进行解析和数据分发
DevjosnController.ProcessJsonData(json);
};
try
{
Debug.Log("🚀 正在连接 WebSocket...");
await websocket.Connect();
}
catch (Exception ex)
{
Debug.LogError("🚫 WebSocket 连接失败:" + ex.Message);
TryReconnect();
}
}
/// <summary>
/// 断线重连机制,最多尝试10次
/// </summary>
private async void TryReconnect()
{
if (isReconnecting || reconnectAttempts >= maxReconnectAttempts)
return;
isReconnecting = true;
reconnectAttempts++;
Debug.Log($"🔄 正在尝试第 {reconnectAttempts} 次重连...");
await Task.Delay(3000);
await ConnectWebSocket();
}
void Update()
{
#if !UNITY_WEBGL || UNITY_EDITOR
websocket?.DispatchMessageQueue();
#endif
}
private async void OnApplicationQuit()
{
if (websocket != null)
{
await websocket.Close();
}
}
}
2.Model:数据结构
这个数据实体类封装了业务中需要使用的数据字段
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public class Device
{
public string deviceId;
public string deviceCode;
public string name;
public int deviceType;
public bool isOnline;
public int? status;
public string load;
public float? runningHours;
public string lastOnlineTime;
public string lastOfflineTime;
public string ipAddress;
public int port;
public string description;
public string locationX;
public string locationY;
public string locationZ;
public string manufacturer;
public string model;
public string serialNumber;
public string firmwareVersion;
public string softwareVersion;
public bool isEnabled;
public string createdTime;
public string updatedTime;
public string communicationStationId;
public string faultCode;
public string faultMessage;
}
[Serializable]
public class DevController
{
public string id;
public string code;
public string name;
public string type;
public string location;
public int status;
public string ipAddress;
public int port;
public string protocol;
public int sendBufferLength;
public string receiveBuffer;
public int receiveBufferLength;
public int queueCount;
public bool isActive;
public bool isOnline;
public bool isAvailable;
public string parameters;
public string sendBuffer;
public bool isAutoConnect;
public string createdAt;
public string updatedAt;
public string lastHeartbeat;
public List<Device> devices;
}
[Serializable]
public class ControllerList
{
public DevController[] Devcontrollers;
}
3.Controller:控制层 同时这里还是Subject(被观察者)
这个类是数据处理和推送(具体解析的什么数据我给删除了,解析逻辑还在)
using LitJson;
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// DevjosnController 用于接收设备控制器 JSON 数据,解析后进行分发,
/// 实现全局与局部观察者的事件通知机制(观察者模式)。
/// 单例结构确保全局唯一数据管理点。
/// </summary>
public class DevjosnController : MonoBehaviour
{
private static Dictionary<string, List<IDeviceDataObserver>> controllerObservers = new Dictionary<string, List<IDeviceDataObserver>>(); // 存储按控制器 ID 注册的观察者列表
private static List<IDeviceDataObserver> globalObservers = new List<IDeviceDataObserver>(); // 存储所有注册的全局观察
// 上一次接收到的控制器数据,用于做数据变更判断,防止重复刷新
private List<DevController> previousData = new List<DevController>();
// 单例引用,方便全局访问本类
private static DevjosnController instance;
/// <summary>
/// Mono Awake 生命周期中初始化单例引用
/// </summary>
void Awake()
{
instance = this;
}
#region 注册与注销观察者(观察者模式核心逻辑)
/// <summary>
/// 注册全局观察者,会接收到所有控制器的数据变动
/// </summary>
public static void RegisterObserver(IDeviceDataObserver observer)
{
if (!globalObservers.Contains(observer))
globalObservers.Add(observer);
}
/// <summary>
/// 注销全局观察者
/// </summary>
public static void UnregisterObserver(IDeviceDataObserver observer)
{
globalObservers.Remove(observer);
}
/// <summary>
/// 注册指定控制器 ID 的观察者,仅接收该控制器的数据变动
/// </summary>
public static void RegisterObserver(string controllerId, IDeviceDataObserver observer)
{
if (!controllerObservers.ContainsKey(controllerId))
controllerObservers[controllerId] = new List<IDeviceDataObserver>();
if (!controllerObservers[controllerId].Contains(observer))
controllerObservers[controllerId].Add(observer);
}
/// <summary>
/// 注销指定控制器 ID 的观察者
/// </summary>
public static void UnregisterObserver(string controllerId, IDeviceDataObserver observer)
{
if (controllerObservers.ContainsKey(controllerId))
controllerObservers[controllerId].Remove(observer);
}
#endregion
/// <summary>
/// 内部方法:从 SocketManager 推送的 JSON 数据中解析后的 DevController 列表,进行事件分发
/// </summary>
private void OnControllersFromSocket(List<DevController> newControllers)
{
// 只有在数据真正变化时才触发通知,避免 UI 或逻辑频繁刷新
if (!IsSameData(newControllers, previousData))
{
previousData = new List<DevController>(newControllers);
// 推送给全局观察者
foreach (var observer in globalObservers)
observer.OnDeviceDataUpdated(previousData);
// 推送给按 controller.id 注册的局部观察者
foreach (var controller in previousData)
{
if (controllerObservers.TryGetValue(controller.id, out var observers))
{
foreach (var observer in observers)
observer.OnControllerDataUpdated(controller);
}
}
}
}
/// <summary>
/// 比较两组控制器数据是否完全一致(仅检查 ID 和在线状态),避免重复推送
/// </summary>
private bool IsSameData(List<DevController> a, List<DevController> b)
{
if (a.Count != b.Count) return false;
for (int i = 0; i < a.Count; i++)
{
if (a[i].id != b[i].id || a[i].isOnline != b[i].isOnline)
return false;
}
return true;
}
/// <summary>
/// 接收原始 JSON 字符串数据,进行解析并转换为 DevController 列表,然后触发数据推送
/// </summary>
public static void ProcessJsonData(string json)
{
try
{
JsonData jsonData = JsonMapper.ToObject(json);
List<DevController> parsedControllers = new List<DevController>();
foreach (JsonData controllerData in jsonData["Data"])
{
// 解析控制器基本信息
DevController controller = new DevController
{
};
// 解析该控制器下属设备信息
if (controllerData["devices"] != null)
{
foreach (JsonData deviceData in controllerData["devices"])
{
Device device = new Device
{
};
controller.devices.Add(device);
}
}
parsedControllers.Add(controller);
}
// 将解析后的控制器列表传递给事件分发函数
instance.OnControllersFromSocket(parsedControllers);
}
catch (Exception ex)
{
Debug.LogError("❌ JSON 解析失败:" + ex.Message);
}
}
}
4.view作为Observer(被观察者)
这个类接收观察者数据推送通知做对应逻辑处理(具体后续操作删除接收数据逻辑还在)
接收多个控制器
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSXanimatormini : MonoBehaviour, IDeviceDataObserver
{
// ✅ 支持多个目标控制器ID(可以同时监听多个控制器)08小库和连廊 09大库
private HashSet<string> targetControllerIds = new() {
"0d4fe201-a2d3-40db-9972-4e5194c25108"
};
/// <summary>
/// ✅ 组件启用时:注册为数据中心的观察者
/// </summary>
private void Start()
{
}
/// <summary>
/// ✅ 组件启用时:确保注册
/// </summary>
private void OnEnable()
{
DevjosnController.RegisterObserver(this);
}
/// <summary>
/// ✅ 组件禁用时:取消注册,避免内存泄漏
/// </summary>
private void OnDisable()
{
DevjosnController.UnregisterObserver(this);
}
/// <summary>
/// ✅ 接收单个控制器数据(当前未使用)
/// </summary>
void IDeviceDataObserver.OnControllerDataUpdated(DevController controller) { }
/// <summary>
/// ✅ 接收所有控制器推送的设备数据,筛选目标控制器,更新输送线状态
/// </summary>
public void OnDeviceDataUpdated(List<DevController> controllers)
{
foreach (var controller in controllers)
{
if (!targetControllerIds.Contains(controller.id))
continue;
foreach (var device in controller.devices)
{
string load = device.load;
string deviceCode = device.deviceCode;
if (string.IsNullOrEmpty(load) || string.IsNullOrEmpty(deviceCode))
continue; // 跳过无效数据
if (int.TryParse(device.status.ToString(), out int status))
{
statusDict[deviceCode] = status;
loadDict[deviceCode] = load;
GoodsMove();
}
}
}
}
}
接收单个控制器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using DG.Tweening;
public class Fourwaycar2 : MonoBehaviour, IDeviceDataObserver
{
public void OnDeviceDataUpdated(List<DevController> controllers) { } // 取多个控制器留空
/// <summary>
/// 观察者事件注册
/// </summary>
private void OnEnable()
{
DevjosnController.RegisterObserver("22222222-2222-2222-2222-222222222222", this);
}
private void OnDisable()
{
DevjosnController.UnregisterObserver("22222222-2222-2222-2222-222222222222", this);
}
/// <param name="controllers"></param>
/// <summary>
/// 接收到控制器ID=1的设备数据后触发
/// </summary>
public void OnControllerDataUpdated(DevController controller)
{
//接收控制器数据
foreach (var device in controller.devices)
{
}
}
}
5:观察者模式接口管理
using System.Collections.Generic;
/// <summary>
/// 观察者模式接口
/// </summary>
public interface IDeviceDataObserver
{
// 接收所有控制器的更新
void OnDeviceDataUpdated(List<DevController> controllers);
接收特定控制器的设备列表带上控制器数据
void OnControllerDataUpdated(DevController controller);
}