8

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?

pbalaga
  • 2,018
  • 1
  • 21
  • 33
CodingBeagle
  • 1,888
  • 2
  • 24
  • 52
  • 2
    There's absolutely nothing wrong with this approach. If you have to transfer data as a string, then encode it into a string. That said, UTF8 is not a good choice for this; UTF8 is meant to encode strings into binary, not vice versa. I'd use base64. – millimoose Sep 05 '12 at 13:44
  • 1
    Alternately, instead of Protocol Buffers, use a serialisation mechanism that produces text, like JSON or XML. – millimoose Sep 05 '12 at 13:44
  • aha interesting :) I'll try to use the Base64 encoding. Thank you for the advice :D I was unsure if this was an acceptable way of doing it. – CodingBeagle Sep 05 '12 at 14:15
  • An excellent question, but shouldin't it be on **UnityAnswers** instead of stack overflow? – awiebe Sep 05 '12 at 15:28

2 Answers2

4

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);
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Yes, after the hints from above comments I tried to switch to Base64 and found it did wonders to the functionality in my code :) It helped remove unexplainable behaviours. Now I can finally make my functionality for reading in a collection of protobuf messages under the same buffer to reduce the amount of unmanaged-to-managed calls. – CodingBeagle Sep 05 '12 at 15:10
  • Aha interesting! Awesome if I can reduce the number of buffers used :) Thank you for the additional tip! – CodingBeagle Sep 05 '12 at 15:13
4

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);
}
masha
  • 316
  • 3
  • 9