Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any proper way of sending byte data to Unity3D from a C plugin?

Just a question of curiosity here.

When you write plugins for Unity on the iOS platform, the plugins have a limited native-to-managed callback functionality (from the plugin and then to Unity). Basically this documentation: iOS plugin Unity documentation

states that the function signature you are able to call back to is this:

Only script methods that correspond to the following signature can be called from native code: function MethodName(message:string)

The signature defined in C looks like this:

void UnitySendMessage(const char* obj, const char* method, const char* msg);

So this pretty much means I can only send strings back to Unity.

Now in my plugin I'm using protobuf-net to serialize objects and send them back to unity to be deserialized. I have gotten this to work, but by a solution I feel is quite ugly and not very elegant at all:

Person* person = [[[[[Person builder] setId:123]
                    setName:@"Bob"]
                   setEmail:@"[email protected]"] build];
NSData* data = [person data];

NSString *rawTest = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

UnitySendMessage("GameObject", "ReceiveProductRequestResponse", [rawTest cStringUsingEncoding:NSUTF8StringEncoding]);

Basically I simply encode the bytestream into a string. In Unity I then get the bytes of the string and deserialize from there:

System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
Byte[] bytes = encoding.GetBytes(message);

This does work. But is there really no other way of doing it? Perhaps someone have an idea of how it could be done in some alternative way?

like image 823
CodingBeagle Avatar asked Sep 05 '12 13:09

CodingBeagle


2 Answers

Base-64 (or another similar base) is the correct way to do this; you cannot use an encoding here (such as UTF8) - an encoding is intended to transform:

arbitrary string <===encoding===> structured bytes

i.e. where the bytes have a defined structure; this is not the case with protobuf; what you want is:

arbitrary bytes <===transform===> structured string

and base-64 is the most convenient implementation of that in most cases. Strictly speaking, you can sometimes go a bit higher than 64, but you'd probably have to roll it manually - not pretty. Base-64 is well-understood and well-supported, making it a good choice. I don't know how you do that in C, but in Unity it should be just:

string s = Convert.ToBase64String(bytes);

Often, you can also avoid an extra buffer here, assuming you are serializing in-memory to a MemoryStream:

string s;
using(var ms = new MemoryStream()) {
    // not shown: serialization steps

    s = Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
}
like image 106
Marc Gravell Avatar answered Nov 15 '22 15:11

Marc Gravell


Example based on Marc Gravell's answer:

On the ios side:

-(void)sendData:(NSData*)data
{
    NSString* base64String = [data base64Encoding];
    const char* utf8String = [base64String cStringUsingEncoding:NSUTF8StringEncoding];
    UnitySendMessage("iOSNativeCommunicationManager", "dataReceived", utf8String);
}

and on the unity side:

public delegate void didReceivedData( byte[] data );
public static event didReceivedData didReceivedDataEvent;

public void dataReceived( string bytesString )
{
    byte[] data = System.Convert.FromBase64String(bytesString);

    if( didReceivedDataEvent != null )
        didReceivedDataEvent(data);
}
like image 25
masha Avatar answered Nov 15 '22 16:11

masha