最近项目因为保存场景问题时unity主线程资源被占用导致unity本身的UI(自认为,也可能是以为要储存场景导致场景的UI无法正常使用)无法展示进度条,在无法解决这个问题的情况下想到折中的方式使用winform的进度条来显示进度问题。
在unity本身UI进度条无法正常显示的时候,使用EditorUtility.DisplayProgressBar()这种方式测试自己的数据是正常传输过来的,但是众所周知UnityEditor这个只能在编辑器下使用,打包是无法使用的。所以讨论了很久最后决定另建一个winform窗口和进度条,在Unity里使用Process类来唤醒winform程序的进程,然后使用Socket传输进度值,然后winform程序接受数据更新进度条的显示。
Process主要代码:
#region 进度条的显示隐藏
/// <summary>
/// 唤醒进度条
/// </summary>
public void ShowProgressbar()
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
// return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = true;
string PathBar = ProgressBarPath + "/MyClient.exe";
//string PathBar = "C:/Users/Administrator/Desktop/MyClient/MyClient/bin/Debug/MyClient.exe";//TODO 地址待定
System.Diagnostics.Process.Start(PathBar);
}
/// <summary>
/// 关闭进度条
/// </summary>
public IEnumerator ClosePrrogressbar()
{
LoadBar(1);
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
//Debug.Log("")
yield return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = false;
}
/// <summary>
/// 关闭进度条
/// </summary>
public IEnumerator CloseLoadPrrogressbar()
{
LoadBar(1);
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
//Debug.Log("")
//yield return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = false;
yield return null;
}
#endregion
这里要注意一下不知道为什么进程关闭的时候在加载场景的时候使用会导致进程无法关闭,所以在下面另外写了一个进度条关闭方法,同时ProgressBarExeplore这个 string类型变量是winform的进程名,PathBar是winform程序的exe地址,这个exe只要winform程序生成解决方案就会存在。
Socket通信Unity 端主要代码:
#region Socket通信
/// <summary>
/// Socket通信初始化即搭建服务器
/// </summary>
void InitSocket()
{
//定义侦听端口,侦听任何IP
ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8848);
//定义套接字类型,在主线程中定义
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//服务端需要绑定ip
socket.Bind(ipEnd);
//定义客户端
//IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
//clientEnd = (EndPoint)sender;
IPEndPoint ProgressBar = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8849);
ProgressBarIPEnd = (EndPoint)ProgressBar;
//Debug.Log("waiting for UDP dgram");
开启一个线程连接,必须的,否则主线程卡死
//connectThread = new Thread(new ThreadStart(SocketReceive));
//connectThread.Start();
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sendStr">发送的字符串</param>
public static void SocketSend(string sendStr)
{
//清空发送缓存
sendData = new byte[1024];
//数据类型转换
sendData = Encoding.ASCII.GetBytes(sendStr);
//发送给指定客户端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ProgressBarIPEnd);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
SocketSend("0.02");
}
}
#region 注释
/ <summary>
/ 服务器接收消息,暂时用不上,防止以后用
/ </summary>
//void SocketReceive()
//{
// //进入接收循环
// while (true)
// {
// //对data清零
// recvData = new byte[1024];
// //获取客户端,获取客户端数据,用引用给客户端赋值
// recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
// Debug.Log("message from: " + clientEnd.ToString()); //打印客户端信息
// //输出接收到的数据
// recvStr = Encoding.ASCII.GetString(recvData, 0, recvLen);
// Debug.Log(recvStr);
// //将接收到的数据经过处理再发送出去
// //sendStr = "From Server: " + recvStr;
// // SocketSend(sendStr);
// }
//}
#endregion
/// <summary>
/// 发送进度消息
/// </summary>
/// <param name="progress">进度值</param>
public static void LoadBar(float progress)
{
int num = (int)(progress * 1000);
string msg = num.ToString();
SocketSend(msg);
}
/// <summary>
/// 连接关闭
/// </summary>
public void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
Debug.Log("disconnect");
}
// Use this for initialization
void OnApplicationQuit()
{
SocketQuit();
}
private void OnDisable()
{
//SocketQuit();
}
#endregion
这里使用UDP的原因是自己使用TCP的时候因为传输数据同时到达导致进度正确,我偷懒直接使用UDP,如果有TCP写出来的可以发个链接让我学习一下。
完整代码:
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
/**
*Copyright(C) 2019 by 殊未
*All rights reserved.
*FileName:
*Author: 丁冉
*Version: BVP3.0
*UnityVersion: 2018.4.0
*Date: 2020-03-03
*Description:
*History:
*/
public class SocketConnect : MonoBehaviour
{
public static SocketConnect Instance;
/// <summary>
/// 目标socket
/// </summary>
private static Socket socket;
/// <summary>
/// 客户端
/// </summary>
private static EndPoint clientEnd;
/// <summary>
/// 侦听端口
/// </summary>
IPEndPoint ipEnd;
/// <summary>
/// 接收的字符串
/// </summary>
public static string recvStr;
/// <summary>
/// 发送的字符串
/// </summary>
string sendStr;
/// <summary>
/// 接收的数据,必须为字节
/// </summary>
byte[] recvData = new byte[1024];
/// <summary>
/// 发送的数据,必须为字节
/// </summary>
private static byte[] sendData = new byte[1024];
/// <summary>
/// 接收的数据长度
/// </summary>
private int recvLen;
/// <summary>
/// 连接线程
/// </summary>
private Thread connectThread;
/// <summary>
/// progress端口信息
/// </summary>
private static EndPoint ProgressBarIPEnd;
private string ProgressBarPath = Application.streamingAssetsPath;
private string ProgressBarExeplore = "MyClient";
/// <summary>
/// 是否正在保存
/// </summary>
public bool isSave = false;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
InitSocket();
DontDestroyOnLoad(this.gameObject);
}
#region Socket通信
/// <summary>
/// Socket通信初始化即搭建服务器
/// </summary>
void InitSocket()
{
//定义侦听端口,侦听任何IP
ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8848);
//定义套接字类型,在主线程中定义
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//服务端需要绑定ip
socket.Bind(ipEnd);
//定义客户端
//IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
//clientEnd = (EndPoint)sender;
IPEndPoint ProgressBar = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8849);
ProgressBarIPEnd = (EndPoint)ProgressBar;
//Debug.Log("waiting for UDP dgram");
开启一个线程连接,必须的,否则主线程卡死
//connectThread = new Thread(new ThreadStart(SocketReceive));
//connectThread.Start();
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sendStr">发送的字符串</param>
public static void SocketSend(string sendStr)
{
//清空发送缓存
sendData = new byte[1024];
//数据类型转换
sendData = Encoding.ASCII.GetBytes(sendStr);
//发送给指定客户端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ProgressBarIPEnd);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
SocketSend("0.02");
}
}
#region 注释
/ <summary>
/ 服务器接收消息,暂时用不上,防止以后用
/ </summary>
//void SocketReceive()
//{
// //进入接收循环
// while (true)
// {
// //对data清零
// recvData = new byte[1024];
// //获取客户端,获取客户端数据,用引用给客户端赋值
// recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
// Debug.Log("message from: " + clientEnd.ToString()); //打印客户端信息
// //输出接收到的数据
// recvStr = Encoding.ASCII.GetString(recvData, 0, recvLen);
// Debug.Log(recvStr);
// //将接收到的数据经过处理再发送出去
// //sendStr = "From Server: " + recvStr;
// // SocketSend(sendStr);
// }
//}
#endregion
/// <summary>
/// 发送进度消息
/// </summary>
/// <param name="progress">进度值</param>
public static void LoadBar(float progress)
{
int num = (int)(progress * 1000);
string msg = num.ToString();
SocketSend(msg);
}
/// <summary>
/// 连接关闭
/// </summary>
public void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
Debug.Log("disconnect");
}
// Use this for initialization
void OnApplicationQuit()
{
SocketQuit();
}
private void OnDisable()
{
//SocketQuit();
}
#endregion
#region 进度条的显示隐藏
/// <summary>
/// 唤醒进度条
/// </summary>
public void ShowProgressbar()
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
// return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = true;
string PathBar = ProgressBarPath + "/MyClient.exe";
//string PathBar = "C:/Users/Administrator/Desktop/MyClient/MyClient/bin/Debug/MyClient.exe";//TODO 地址待定
System.Diagnostics.Process.Start(PathBar);
}
/// <summary>
/// 关闭进度条
/// </summary>
public IEnumerator ClosePrrogressbar()
{
LoadBar(1);
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
//Debug.Log("")
yield return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = false;
}
/// <summary>
/// 关闭进度条
/// </summary>
public IEnumerator CloseLoadPrrogressbar()
{
LoadBar(1);
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(ProgressBarExeplore);
//Debug.Log("")
//yield return new WaitForSeconds(0.5f);
for (int i = 0; i < process.Length; i++)
{
process[i].Kill();
}
isSave = false;
yield return null;
}
#endregion
}
这里没有唤醒进程函数的调用,需要自己另外写调用ShowProgressbar()这个方法。
winform程序代码不多主要,就是建立Socket连接以及接受消息更新进度条的显示
winform程序Socket代码:
string recvStr;
string UDPClientIP;
//string editString = "hello wolrd";
Socket socket;
EndPoint serverEnd;
IPEndPoint ipEnd;
//string sendStr;
byte[] recvData = new byte[1024];
byte[] sendData = new byte[1024];
int recvLen = 0;
Thread connectThread;
void InitSocket()
{
try
{
ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8849);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
serverEnd = (EndPoint)sender;
socket.Bind(ipEnd);
//ShowMsg("等待连接");
//SocketSend("hello");
//ShowMsg("连接");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
catch (Exception)
{
//ShowMsg("服务器未开启...");
//throw;
}
}
void SocketSend(string sendStr)
{
//清空
sendData = new byte[1024];
//数据转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定服务端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd);
}
//服务器接收
void SocketReceive()
{
while (true)
{
recvData = new byte[1024];
try
{
recvLen = socket.ReceiveFrom(recvData, ref serverEnd);
}
catch (Exception e)
{
ShowMsg(e.ToString());
//Debug.LogException(e);
}
if (recvLen > 0)
{
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
ShowMsg(recvStr);
LoadBar(recvStr);
}
}
}
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
}
void OnApplicationQuit()
{
SocketQuit();
}
进度条ProgressBar更新代码:
/// <summary>
/// 进度条更新
/// </summary>
/// <param name="msg"></param>
void LoadBar(string msg)
{
int num;
if (int.TryParse(msg, out num))
{
progressBar1.Maximum = 1000;
progressBar1.Value = num;
//textBox1.AppendText("当前进度:" + (((float)num / 10)).ToString() + "%\r\n");
label1.Text = (((float)num / 10)).ToString() + "%";
this.Update();
}
}
完整代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace MyClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitSocket();//启动UDPServer
//InitTCPServer();//启动TCPServer
//LoadBar("500");
}
/// <summary>
/// 禁用Alt F4关闭窗口功能
/// </summary>
/// <param name="e"></param>
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
//e.SuppressKeyPress = (e.Alt && e.KeyCode == Keys.F4);
e.SuppressKeyPress = (e.KeyData == (Keys.Alt | Keys.F4)); // 阻止当前控件接收按键。
}
/// <summary>
/// 控制台显示消息,已关闭
/// </summary>
/// <param name="str"></param>
void ShowMsg(string str)
{
//textBox1.AppendText(str + "\r\n");
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 进度条更新
/// </summary>
/// <param name="msg"></param>
void LoadBar(string msg)
{
int num;
if (int.TryParse(msg, out num))
{
progressBar1.Maximum = 1000;
progressBar1.Value = num;
//textBox1.AppendText("当前进度:" + (((float)num / 10)).ToString() + "%\r\n");
label1.Text = (((float)num / 10)).ToString() + "%";
this.Update();
}
}
string recvStr;
string UDPClientIP;
//string editString = "hello wolrd";
Socket socket;
EndPoint serverEnd;
IPEndPoint ipEnd;
//string sendStr;
byte[] recvData = new byte[1024];
byte[] sendData = new byte[1024];
int recvLen = 0;
Thread connectThread;
void InitSocket()
{
try
{
ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8849);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
serverEnd = (EndPoint)sender;
socket.Bind(ipEnd);
//ShowMsg("等待连接");
//SocketSend("hello");
//ShowMsg("连接");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
catch (Exception)
{
//ShowMsg("服务器未开启...");
//throw;
}
}
void SocketSend(string sendStr)
{
//清空
sendData = new byte[1024];
//数据转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定服务端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd);
}
//服务器接收
void SocketReceive()
{
while (true)
{
recvData = new byte[1024];
try
{
recvLen = socket.ReceiveFrom(recvData, ref serverEnd);
}
catch (Exception e)
{
ShowMsg(e.ToString());
//Debug.LogException(e);
}
if (recvLen > 0)
{
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
ShowMsg(recvStr);
LoadBar(recvStr);
}
}
}
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
}
void OnApplicationQuit()
{
SocketQuit();
}
}
}
winform窗体的属性设置可以参考:https://www.cnblogs.com/fuze/p/6180047.html
winform程序Demo:MyClient