複数人参加のリアルタイム通信アプリを作る。(クライアント編)

WebSocketってなんだ?」「SocketRocketでiPhoneからWebSocket通信をする。」「Node.jsでWebSocketサーバを立てる」で要素技術は学んだので、さっそくリアルタイム通信なおもちゃの作成に挑みました。

みんなでボールを転がすだけの遊び

完成図がこちら!

画像なので分かりにくいですが、参加者ひとりひとりがボールを操作します。誰かがボールを動かすと、他の人のiPhoneにもリアルタイムに反映されます。それだけです。ここから、対戦要素を入れるなりなんなりすればリアルタイムなゲームなどが作れます。

WebSocketサーバへの接続を確立する。

ここは以前に書いたのと同じです。

- (IBAction)connect:(id)sender {    
    [hostTextField resignFirstResponder];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"ws://%@:8080/",hostTextField.text]];
    socket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:url]];
    socket.delegate = self;
    [socket open];
}
 
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
}

画面を触った時に、その位置座標をサーバに送信する。

- (void)viewDidLoad
{
    [super viewDidLoad];
    worldImageView.delegate = self;
    ballImageViews = [[NSMutableArray alloc] initWithCapacity:10];
}
 
- (void)touchesMoved:(CGPoint)location {
    NSString *requestBody = [NSString stringWithFormat:@"{\"x\":\"%lf\",\"y\":\"%lf\"}", location.x, location.y];
    [socket send:requestBody];
}

誰かがiPhoneの画像を触ると、その座標をサーバへ送信します。ImageViewはタッチされた時にコールバック処理が実行できるように拡張したものを使っています。

[socket send:requestBody];

ポイントはここです!事前にコネクションを確立したSRWebSocketのsendメソッドでサーバへデータを送信します。

- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    [self touchesMoved:CGPointMake(100, 100)];
}

WebSocketのコネクションを確立した時にも、テキトーな座標を触ったのと同じ処理を実行することにしておきます。これで接続した直後に他にユーザーの画面にボールを出せます。

サーバからデータを受信したら描画する。

サーバから何かしらのデータが送られてきたら、それをトリガーに画面に書き換えを行います。

ここでポイントなのは、HTTP通信と違って、サーバからデータが送られてくるのは「自分が何かをした時だけではない」ということです。同時に参加している他のユーザーが操作した時にも、参加者全員へのデータ送信が行われるので、参加者全員の画面が再描画されます。

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
    [self showBalls:[message JSONValue]];
}

ちなみに、次のブログで、サーバ側の実装を書こうとおもいますが、サーバからのレスポンスは、全てのユーザーの座標の配列になっています。ユーザーが3人参加していたら、サーバから座標が3つ入った配列が送られてくるイメージです。

それを以下のようにひとつずつ描画していきます。参加人数は随時変わるので、参加者数が増えた時はボールのUIImageViewを生成して追加します。

- (void)showBalls:(NSArray *)ballLocations{
 
    while([ballImageViews count] < [ballLocations count]){
        UIImageView *view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ball.png"]];
        view.frame = CGRectMake(0, 0, 60, 60);
        view.contentMode = UIViewContentModeScaleAspectFit;
        [worldImageView addSubview:view];
        [ballImageViews addObject:view];
    }
 
    for (int i = 0; i < [ballLocations count]; i++) {
        NSDictionary *location = [ballLocations objectAtIndex:i];
        UIImageView *view = [ballImageViews objectAtIndex:i];
 
        CGRect frame = view.frame;
        frame.origin.x = [[location objectForKey:@"x"] doubleValue];
        frame.origin.y = [[location objectForKey:@"y"] doubleValue];
        view.frame = frame;
    }

これでクライアント側は完成です。あとは、サーバ側で、「誰かがボールを移動した時に、他の全員に、全員の座標をプッシュ通知する」というような処理を書けば良いです。

About katty0324

One comment

Leave a Reply

Scroll To Top