简体   繁体   中英

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:@"bob@example.com"] 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?

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

ie 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);
}

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM