いくつかのiOSデバイス間でデータのやりとりをする時に使うGKSessionクラスってあるじゃない。これを使いたくていろいろとテストしてたんだ。
GKTankってすごく参考になるサンプルがアップルにあって、それを使うと実際にデータのやりとりをしているのを確認できたんだよ。
ただ、GKTankでは次のような構造体を送信してるんだ。
typedef struct {
CGPoint tankPreviousPosition;
CGPoint tankPosition;
CGPoint tankMissilePosition;
CGPoint tankDestination;
float tankRotation;
float tankDirection;
float tankMissileDirection;
int tankMissile;
} tankInfo;
他にも次のような整数型のデータを送信してるんだよね。
int gameUniqueID;
GKTankのコードを見ると、これらのデータにヘッダーを付けて送っているんだ。
ヘッダーにはアプリ開始時からカウントされる番号や送信するデータの種類を記述してるんだ。そりゃそうだよね、送信するデータがいったいどういう構成なのかわからないと受信した方でも困っちゃうよね。
で、いざ自分も同じようにやってみようとして困ったのが、NSData型のデータを送信したいんだけど、やり方がよくわからなかったんだ。
GKTankだと上記のように構造体とか数値とかそういうのばかりなので、勝手が違ったんだよね。
送信したいのはNSData型といったけど、別に配列のNSArrayでも、文字列のNSStringでも日時を表すNSDateでもなんでもいいんだ、だってNSCoding使えばそれらはどれもNSData型にすることができるもんね。
何時間もGoogle先生に聞いた結果ようやく動くコードになりましたってのが次のコード。
送信部分が二つに分かれているのはGKTankのコードに倣っているというかそのままというか。
いろんなタイプのデータを送るわけだけど、それらはいろんなメソッドで記述すればいいんだけど、どれも最終的には送信部分2をコールするってイメージなんだ。
あと、GKTankとちがって送信部分2でヘッダーはデータのタイプを示す1つしかないよ(オリジナルは2つあるよね)。
-(void)sendButtonTappedHandler
{
//送信部分1
NSString *message = @"Hello";
NSData *messageData = [message dataUsingEncoding: NSUTF8StringEncoding];
int length = [messageData length];//5
[self sendNetworkPacket:session_ packetID:1 withData:(void*)[messageData bytes] ofLength:length reliable:YES];
}
- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend
{
//送信部分2
static unsigned char networkPacket[kMaxPacketSize];
const unsigned int packetHeaderSize = sizeof(int);
if(length < (kMaxPacketSize - packetHeaderSize)) {
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = 1;//packetID;
// copy data in after the header
memcpy( &networkPacket[packetHeaderSize], data, length );
NSData *packet = [NSData dataWithBytes:networkPacket length:(length+packetHeaderSize)];
[session_ sendData:packet toPeers:[NSArray arrayWithObject:peerId_] withDataMode:GKSendDataReliable error:nil];
}
}
最初データの数え方がよくわからなくてさホント困っちゃったよ。
もともとは'sizeof'とかになってて、整数型とかならそれでいいんだろうけど、NSDataでそれやるとポインタ分(番地を表す数バイト分)だけという間抜けなことになっちゃうよね。
ここは'malloc_size'だなんてGoogle先生が教えてくれたりしたので混乱もしたけど、どいうやら上記のようにNSDataのlengthメソッドで取ればいいみたいよ。
送信したら今度は受信する方だよね。
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context
{
unsigned char *incomingPacket = (unsigned char *)[data bytes];
int *pIntData = (int *)&incomingPacket[0];
int packetID = pIntData[0];
switch( packetID ) {
case 1:
{
NSData *messageData = [NSData dataWithBytes:&incomingPacket[4] length:[data length]-4];
NSString *message = [[NSString alloc] initWithData:messageData encoding:NSUTF8StringEncoding];
NSLog(@"message is %@",message);
}
break;
default:
break;
}
}
NSDataのデータを作るというか戻すのに、最初は次のようなコードを書いてたんだけどうまくいかなかったんだ。
NSData *messageData = (NSData *)&incomingPacket[4];
元々のソースコードを眺めているとこれでいいのかなと思うんだけど、結果的には長さをちゃんと指定しないとうまくいかなかったよ。
そんなわけで、dataWithBytes:length: メソッドを使ってる。length:の部分で4バイト分引いているのはもちろんヘッダー分ね。
えらく苦労しちゃったんだけど、とりあえずデータを送信することが無事できたよ。今日は赤飯だね!