본문 바로가기
Flutter/GAME Programming

[플러터 게임] 고급 게임 기술 / 멀티플레이어 기능 추가

by Maccrey Coding 2024. 10. 6.
728x90
반응형

 

멀티플레이어 게임은 친구와 함께 또는 전 세계의 다른 플레이어와 경쟁할 수 있는 재미를 제공합니다.

이러한 기능을 구현하기 위해서는 네트워크 통신의 기초를 이해해야 합니다.

이번 포스트에서는 Dart로 구현한 서버와 Flutter 클라이언트를 통해 REST API와 WebSocket을 사용하여 멀티플레이어 기능을 구현하는 방법을 알아보겠습니다.

1. 네트워크 통신 기초

1.1 REST API

REST API는 클라이언트와 서버 간의 통신을 위한 규칙입니다. Dart에서는 shelf 패키지를 사용하여 REST API를 쉽게 구축할 수 있습니다.

REST API를 사용하면 클라이언트에서 서버에 데이터를 요청하고 응답받을 수 있습니다.

1.2 WebSocket

WebSocket은 클라이언트와 서버 간의 지속적인 연결을 유지하여 실시간 데이터 전송을 가능하게 하는 프로토콜입니다.

Dart에서는 shelf_websocket 패키지를 사용하여 WebSocket 서버를 만들 수 있습니다.

멀티플레이어 게임에서는 빠른 데이터 전송이 필요하므로 WebSocket을 사용하는 것이 좋습니다.

2. 서버 구현

Dart 서버를 구현하기 위해 필요한 패키지를 추가합니다. pubspec.yaml 파일에 다음과 같은 내용을 추가합니다.

2.1 pubspec.yaml

name: multiplayer_game_server
dependencies:
  shelf: ^1.4.0
  shelf_websocket: ^0.2.0

2.2 서버 코드

아래는 Dart로 작성된 서버 코드입니다. REST API와 WebSocket을 모두 설정하는 코드입니다.

// 서버 코드: server.dart
import 'dart:convert';
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf_websocket/shelf_websocket.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

List<Map<String, dynamic>> players = []; // 모든 플레이어 정보를 저장하는 리스트

void main() async {
  final handler = const Pipeline()
      .addMiddleware(logRequests()) // 요청 로깅 미들웨어
      .addHandler(_handleRequest); // 요청 핸들러

  final server = await shelf_io.serve(handler, 'localhost', 3000);
  print('서버가 ${server.address.host}:${server.port}에서 실행 중입니다.');
}

// REST API 핸들러
Future<Response> _handleRequest(Request request) async {
  if (request.method == 'POST' && request.url.path == 'addPlayer') {
    // 플레이어 추가
    final player = {'id': DateTime.now().millisecondsSinceEpoch, 'position': {'x': 0, 'y': 0}};
    players.add(player);
    return Response.ok(json.encode(player), headers: {'Content-Type': 'application/json'});
  }

  if (request.url.path == 'ws') {
    // WebSocket 핸들러
    return webSocketHandler((WebSocketChannel webSocket) {
      webSocket.stream.listen((message) {
        final data = json.decode(message);
        if (data['type'] == 'update') {
          // 플레이어 위치 업데이트
          final player = players.firstWhere((p) => p['id'] == data['id'], orElse: () => null);
          if (player != null) {
            player['position'] = data['position'];
            // 모든 클라이언트에 플레이어 목록 전송
            final jsonPlayers = json.encode(players);
            webSocket.sink.add(jsonPlayers);
          }
        }
      });
    });
  }

  return Response.notFound('Not Found');
}

3. 클라이언트 구현

이제 Flutter 클라이언트를 작성해 보겠습니다. 클라이언트는 REST API를 통해 플레이어를 추가하고, WebSocket을 통해 실시간으로 다른 플레이어의 위치를 업데이트합니다.

3.1 pubspec.yaml

Flutter 프로젝트의 pubspec.yaml 파일에 다음과 같은 의존성을 추가합니다.

dependencies:
  flutter:
    sdk: flutter
  http: ^0.14.0
  web_socket_channel: ^2.1.0

3.2 클라이언트 코드

아래는 Flutter 클라이언트 코드입니다.

// 클라이언트 코드: main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:web_socket_channel/web_socket_channel.dart';

void main() {
  runApp(MyGameApp());
}

class MyGameApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '멀티플레이어 게임',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: GameHome(),
    );
  }
}

class GameHome extends StatefulWidget {
  @override
  _GameHomeState createState() => _GameHomeState();
}

class _GameHomeState extends State<GameHome> {
  final WebSocketChannel channel = WebSocketChannel.connect(
    Uri.parse('ws://localhost:3000/ws'), // WebSocket 서버 주소
  );

  List<dynamic> players = [];
  int playerId = 0;

  @override
  void initState() {
    super.initState();
    addPlayer(); // 플레이어 추가
    channel.stream.listen((message) {
      setState(() {
        players = json.decode(message); // 플레이어 목록 업데이트
      });
    });
  }

  Future<void> addPlayer() async {
    final response = await http.post(
      Uri.parse('http://localhost:3000/addPlayer'),
    );

    if (response.statusCode == 200) {
      final player = json.decode(response.body);
      playerId = player['id'];
    } else {
      throw Exception('플레이어 추가 실패');
    }
  }

  void updatePosition(double x, double y) {
    final message = json.encode({
      'type': 'update',
      'id': playerId,
      'position': {'x': x, 'y': y},
    });
    channel.sink.add(message); // 위치 업데이트 전송
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('멀티플레이어 게임')),
      body: Stack(
        children: players.map((player) {
          return Positioned(
            left: player['position']['x'],
            top: player['position']['y'],
            child: GestureDetector(
              onPanUpdate: (details) {
                final newX = player['position']['x'] + details.delta.dx;
                final newY = player['position']['y'] + details.delta.dy;
                updatePosition(newX, newY); // 위치 업데이트
              },
              child: Container(
                width: 50,
                height: 50,
                color: Colors.blue,
                child: Center(child: Text(player['id'].toString())),
              ),
            ),
          );
        }).toList(),
      ),
    );
  }

  @override
  void dispose() {
    channel.sink.close(); // WebSocket 연결 종료
    super.dispose();
  }
}

코드 설명

  1. 서버 코드
    • Dart의 shelf 패키지를 사용하여 REST API를 설정하고, shelf_websocket을 사용하여 WebSocket 서버를 설정합니다.
    • /addPlayer 엔드포인트를 통해 새로운 플레이어를 추가하고, WebSocket을 통해 실시간으로 플레이어의 위치를 업데이트합니다.
  2. 클라이언트 코드
    • Flutter 앱에서 WebSocket을 연결하여 서버와 실시간으로 통신합니다.
    • addPlayer() 메서드를 사용하여 서버에 플레이어를 추가하고, 서버로부터 받은 데이터를 players 리스트에 저장합니다.
    • 사용자가 공을 드래그할 때마다 updatePosition() 메서드를 호출하여 새로운 위치를 서버에 전송합니다.
    • GestureDetector를 사용하여 플레이어의 움직임을 감지하고, UI에서 플레이어를 화면에 그립니다.

Dart로 구현한 서버와 Flutter 클라이언트를 통해 멀티플레이어 게임의 기능을 추가하는 것은 흥미롭고 도전적인 작업입니다.

REST API와 WebSocket을 활용하여 클라이언트와 서버 간의 실시간 통신을 구현함으로써, 플레이어 간의 상호작용을 가능하게 할 수 있습니다.

위의 예제 코드를 참고하여 자신만의 멀티플레이어 게임을 만들어 보세요!

구독!! 공감과 댓글,

광고 클릭은 저에게 큰 힘이 됩니다.

 

Starting Google Play App Distribution! "Tester Share" for Recruiting 20 Testers for a Closed Test.

 

Tester Share [테스터쉐어] - Google Play 앱

Tester Share로 Google Play 앱 등록을 단순화하세요.

play.google.com

728x90
반응형