I'm trying to create a little test app, using unity. I'm building out one instance of the app and setting it to function as a Host and then running another instance within the unity editor as a client. In this current iteration I have a single button that SHOULD, when pressed is take a screenshot and then send that screenshot to all clients. But nothing seems to to be working. The initial issue that I'm hitting is a ReadPixels error:
ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame. UnityEngine.Texture2D:ReadPixels(Rect, Int32, Int32)
UNETChat:OnPostRender() (at Assets/Scripts/UNETChat.cs:50)
<TakeSnapshot>c__Iterator0:MoveNext() (at Assets/Scripts/UNETChat.cs:42)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
.
Beyond that, once solved I'm sure that there is a lot of additional work in here that's off the mark. But I'm just starting here. Thanks for the help!
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.NetworkSystem;
public class MyMsgType
{
public static short texture = MsgType.Highest + 1;
};
//Create a class that holds a variables to send
public class TextureMessage : MessageBase
{
public byte[] textureBytes;
public string message = "Test Received Message"; //Optional
}
public class UNETChat : Chat
{
NetworkClient myClient;
public Texture2D previewTexture;
string messageToSend = "Screen Short Image";
private void Start()
{
//if the client is also the server
if (NetworkServer.active)
{
// Register to connect event
myClient.RegisterHandler(MsgType.Connect, OnConnected);
// Register to texture receive event
myClient.RegisterHandler(MyMsgType.texture, OnTextureReceive);
}
setupClient();
}
public IEnumerator TakeSnapshot(int width, int height)
{
yield return new WaitForSeconds(0.1F);
Texture2D texture = new Texture2D(800, 800, TextureFormat.RGB24, true);
texture.ReadPixels(new Rect(0, 0, 800, 800), 0, 0);
texture.LoadRawTextureData(texture.GetRawTextureData());
texture.Apply();
sendTexture(texture, messageToSend);
// gameObject.renderer.material.mainTexture = TakeSnapshot;
}
public void DoSendTexture()
{
StartCoroutine(TakeSnapshot(Screen.width, Screen.height));
}
// SERVER //////////////////////////
//Call to send the Texture and a simple string message
public void sendTexture(Texture2D texture, string message)
{
TextureMessage msg = new TextureMessage();
//Convert Texture2D to byte array
msg.textureBytes = texture.GetRawTextureData();
msg.message = message;
NetworkServer.SendToAll(MyMsgType.texture, msg);
Debug.Log("Texture Sent!!");
}
// CLIENT //////////////////////////
// Create a client and connect to the server port
public void setupClient()
{
//Create new client
myClient = new NetworkClient();
//Register to connect event
myClient.RegisterHandler(MsgType.Connect, OnConnected);
//Register to texture receive event
myClient.RegisterHandler(MyMsgType.texture, OnTextureReceive);
//Connect to server
myClient.Connect("localhost", 4444);
}
//Called when texture is received
public void OnTextureReceive(NetworkMessage netMsg)
{
TextureMessage msg = netMsg.ReadMessage<TextureMessage>();
//Your Received message
string message = msg.message;
Debug.Log("Texture Messsage " + message);
//Your Received Texture2D
Texture2D receivedtexture = new Texture2D(4, 4);
receivedtexture.LoadRawTextureData(msg.textureBytes);
receivedtexture.Apply();
}
public void OnConnected(NetworkMessage netMsg)
{
Debug.Log("Connected to server");
}
}
Wait until the end of the current frame before taking the screenshot or before calling the Texture2D.ReadPixels
function. This can be done with the WaitForEndOfFrame
class. Notice how I cached it to avoid creating new Object each time the TakeSnapshot
function is called.
WaitForEndOfFrame frameEnd = new WaitForEndOfFrame();
public IEnumerator TakeSnapshot(int width, int height)
{
yield return frameEnd;
Texture2D texture = new Texture2D(800, 800, TextureFormat.RGB24, true);
texture.ReadPixels(new Rect(0, 0, 800, 800), 0, 0);
texture.LoadRawTextureData(texture.GetRawTextureData());
texture.Apply();
sendTexture(texture, messageToSend);
// gameObject.renderer.material.mainTexture = TakeSnapshot;
}
I noticed you have yield return new WaitForSeconds(0.1F)
in your script which waits for 0.2
seconds before taking the screenshot. If you must wait for 0.2
seconds, then first wait for 0.2
seconds and then wait for end of frame before taking the screenshot:
WaitForSeconds waitTime = new WaitForSeconds(0.1F);
WaitForEndOfFrame frameEnd = new WaitForEndOfFrame();
public IEnumerator TakeSnapshot(int width, int height)
{
yield return waitTime;
yield return frameEnd;
Texture2D texture = new Texture2D(800, 800, TextureFormat.RGB24, true);
texture.ReadPixels(new Rect(0, 0, 800, 800), 0, 0);
texture.LoadRawTextureData(texture.GetRawTextureData());
texture.Apply();
sendTexture(texture, messageToSend);
// gameObject.renderer.material.mainTexture = TakeSnapshot;
}