將Unity的控制檯輸出在移動端
阿新 • • 發佈:2018-12-15
Unity輸出在移動端
描述:將Unity的控制檯輸出的資訊輸出到手機端 條件:有一個公網IP,或者一臺伺服器 原理:服務端程式在服務端執行,主要負責把訊息轉發給其他客戶端; 客戶端程式將訊息傳送給服務端,同時將其封裝為一個dll用於在unity上引用,unity上還需要一個控制檯介面,一個控制指令碼。服務端邏輯
服務端主程式.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net. Sockets;
using System.Net;
using System.Threading;
/// <summary>
/// 使用socket_tcp編寫Unity聊天室的伺服器端
/// </summary>
namespace SyncServer
{
class Program
{
/// <summary>
/// 伺服器計算機所在的ip和埠
/// </summary>
//public const string m_ipAddress = "公網ip";
public const int m_port = 2233;
/// <summary>
/// 伺服器Socket物件
/// </summary>
static Socket m_tcpServerServer;
/// <summary>
/// 儲存連線上伺服器的客戶端
/// </summary>
static List<Client> m_clientList = new List<Client>();
/// <summary>
/// 執行緒:專門用來處理客戶端的連線
/// </summary>
static Thread m_ServerThread;
static void Main(string[] args)
{
Console.WriteLine("是否啟動伺服器,y/n");
if (Console.ReadLine() == "y")
{
StartServerProgram();
}
}
/// <summary>
/// 啟動伺服器程式
/// </summary>
static void StartServerProgram()
{
m_tcpServerServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//關於IPAddress.Any,本來是想直接用外網ip的,不過我這裡報錯“在其上下文中,該請求的地址無效”
IPEndPoint ipEndServer = new IPEndPoint(IPAddress.Any, m_port);
try
{
m_tcpServerServer.Bind(ipEndServer);
m_tcpServerServer.Listen(10);
Console.WriteLine("伺服器已經啟動");
Console.WriteLine("當前的IP:" + ipEndServer.Address.ToString() + "\n" + "埠號:" + ipEndServer.Port);
}
catch (Exception ex)
{
Console.WriteLine("異常:"+ex);
}
//建立一個執行緒專門用來監聽客戶端的連線從而不影響下面要執行的程式碼
m_ServerThread = new Thread(AcceptClientConnect);
//監聽
m_ServerThread.Start();
}
static void AcceptClientConnect()
{
while (true)
{
//監聽客戶端連線
Console.WriteLine("等待客戶端的連線!");
Socket tcpClientSocket = m_tcpServerServer.Accept();
IPEndPoint ipEndClient = (IPEndPoint)tcpClientSocket.RemoteEndPoint;
//輸出客戶端的IP和埠
Console.WriteLine(ipEndClient.Address.ToString() + ":" + ipEndClient.Port.ToString());
//客戶端通訊邏輯放在Client裡處理
Client client = new Client(tcpClientSocket);
m_clientList.Add(client);
}
}
/// <summary>
/// 將接收的訊息廣播出去
/// </summary>
/// <param name="message"></param>
public static void BroadCastMessage(string message)
{
if (m_clientList == null || m_clientList.Count == 0)
return;
byte[] data = Encoding.UTF8.GetBytes(message);
//用來儲存已經斷開連線的客戶端
List<Client> notConnected = new List<Client>();
foreach (var client in m_clientList)
{
//廣播
if (client.IsConnected)
{
client.SendMessageToClient(data);
}
else
{
notConnected.Add(client);
}
}
//從連線列表中刪除已經斷開的客戶端
foreach (var item in notConnected)
{
m_clientList.Remove(item);
}
}
}
}
Client.cs 這個類還是在服務端的,用來處理通訊邏輯
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
namespace SyncServer
{
class Client
{
/// <summary>
/// 儲存與連線上來的客戶端對應的Socket物件
/// </summary>
private Socket m_clientSocket;
/// <summary>
/// 獲取Socket是否連線
/// </summary>
public bool IsConnected
{
get { return m_clientSocket.Connected; }
}
private Thread m_curThread;
private byte[] m_data = new byte[1024];
public Client(Socket s)
{
m_clientSocket = s;
m_curThread = new Thread(ReceiveMessage);
m_curThread.Start();
}
void ReceiveMessage()
{
while (true)
{
//在接收訊息之前要判斷Socket是否連線
if (m_clientSocket.Poll(10, SelectMode.SelectRead))//試圖讀取客戶端資料,如果10毫秒內讀取不到,那麼判斷已經斷開連線,返回true
{
m_clientSocket.Shutdown(SocketShutdown.Both);
m_clientSocket.Close();
break;
}
//接收訊息
int length = m_clientSocket.Receive(m_data);//如果沒有接收到訊息,程式會一直在這裡等待
//Console.WriteLine(m_data.Length);//會輸出1024
string message = Encoding.UTF8.GetString(m_data, 0, length);
//獲取客戶端的IP和埠
IPEndPoint ipEndClient = (IPEndPoint)m_clientSocket.RemoteEndPoint;
//輸出客戶端的IP和埠
if (!string.IsNullOrEmpty(message))
{
Console.WriteLine(ipEndClient.Address.ToString() + ":" + "接收到的訊息:" + message);
}
//將接收的訊息廣播出去,將訊息顯示在每個客戶端的聊天螢幕上
Program.BroadCastMessage(message);
}
}
/// <summary>
/// 將上面的函式再做一次優化,將字串轉換為byte陣列的過程移到廣播那邊去,這樣局只需要轉換一次就能傳送給所有的客戶端了
/// </summary>
/// <param name="message"></param>
public void SendMessageToClient(byte[] messageData)
{
m_clientSocket.Send(messageData);
}
}
}
服務端這邊就算完成了,在本地先用127.0.0.1測試之後在部署到伺服器上,並執行程式等待客戶端的到來。
客戶端邏輯
客戶端邏輯
/// <summary>
/// 這裡我們不需要顯示訊息,只負責傳送和接收
/// </summary>
public class ZyMessage
{
/// <summary>
/// 伺服器計算機所在的ip和埠
/// </summary>
public const string m_ipAddress = "公網ip";
public const int m_port = 2233;
private static Socket m_clientSocket;
private static Thread m_reCeiveThread;
private static byte[] m_msgData = new byte[1024];//訊息資料容器
public static string m_message;//儲存訊息
/// <summary>
/// 啟動客戶端程式
/// </summary>
public static void Start()
{
ConnentToServer();
}
/// <summary>
/// 供外部引用,將訊息傳送給伺服器
/// </summary>
/// <param name="message"></param>
public static void RemotePrint(object message)
{
if (string.IsNullOrEmpty(message.ToString()))
{
return;
}
else
{
byte[] data = Encoding.UTF8.GetBytes(message.ToString());
m_clientSocket.Send(data);
}
}
/// <summary>
/// 接收資料方法,在新執行緒中執行
/// </summary>
private static void Receive()
{
while (true)
{
//在接受訊息之前判斷Socket是否連線
if (m_clientSocket.Connected == false)
break;
int length = m_clientSocket.Receive(m_msgData); //這裡會等待接收訊息,程式暫停,只有接收到訊息後才會繼續執行
m_message = Encoding.UTF8.GetString(m_msgData, 0, length);
}
}
/// <summary>
/// 接收訊息
/// </summary>
public static void ReceiveMessage()
{
//開啟一個執行緒專門用於接收訊息
m_reCeiveThread = new Thread(Receive);
m_reCeiveThread.Start();
}
/// <summary>
/// 自定義的函式,連線到伺服器
/// </summary>
public static void ConnentToServer()
{
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//跟伺服器建立連線
//clientSocket.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.108"), 2345));
m_clientSocket.Connect(new IPEndPoint(IPAddress.Parse(m_ipAddress), m_port));
}
/// <summary>
/// 清空訊息
/// </summary>
public static void Clear()
{
m_message = "";
}
/// <summary>
/// 關閉功能,斷開連線
/// </summary>
public static void Shutdown()
{
//禁用Socket的傳送和接收功能
m_clientSocket.Shutdown(SocketShutdown.Both);
//關閉Socket
m_clientSocket.Close();
}
}
這個類我封裝為dll,在unity上直接使用。
MobileClient指令碼
using UnityEngine;
using UnityEngine.UI;
using ZY;
/// <summary>
/// 這個指令碼一定要掛在一個元件上,同時ZY.dll記得引用
/// 可以想象成Debug.log(),只不過你需要MobileClient._instance.Print(內容);這樣來使用
/// 公司的網路可能無法使用
/// </summary>
public class MobileClient : MonoBehaviour {
public Text content; //展示訊息的文字
public static MobileClient _instance; //當前的例項
private void Awake()
{
if (_instance == null)
{
_instance = this;
}
else return;
}
void Start ()
{
ZyMessage.Start(); //啟動客戶端
ZyMessage.ReceiveMessage(); //開啟訊息的接收
}
[ContextMenu("hh")] //這個方法供給單例去外部使用
public void Print(object obj)
{
ZyMessage.RemotePrint(obj); //列印到移動端,類比Debug.log(obj);
}
void Update ()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
ZyMessage.Shutdown();
Application.Quit();
}
if (!string.IsNullOrEmpty(ZyMessage.m_message))
{
content.text += "\n" + ZyMessage.m_message;
ZyMessage.Clear(); //顯示出來後,將訊息清空一次
}
}
private void OnDestroy()
{
ZyMessage.Shutdown();
}
}
這個類完成後就可以使用了,使用方法MobileClient._instance.Print(內容);
移動端的介面搭建
一般只需要一個文字框就行了,但不能上下移動,參考(https://blog.csdn.net/wsc122181582/article/details/64438093 ),這篇部落格,為了讓控制檯更具有cmd風格,你可以和我一樣設定 然後把MobileClient指令碼掛上,Text元件外部賦給指令碼的content。部署到你的手機上,接下來建立一個測試場景Test,在攝像頭元件新增MobileClient指令碼,content不賦值,建立測試指令碼
移動端的顯示如上。至此,通過MobileClient._instance.Print(),你就可以在同一個場景裡將訊息列印到移動端,以上可以優化的地方很多,但目前於我來說夠用了,如果有不足或其他問題,願意交流。