[flutter] 플러터 RxDart로 게임 개발하기

플러터는 모바일 애플리케이션 개발을 위한 크로스 플랫폼 프레임워크로, 쉽고 간편한 개발을 지원합니다. RxDart는 플러터와 함께 사용할 수 있는 반응형 프로그래밍 라이브러리로, 비동기 작업을 더욱 편리하게 처리할 수 있게 해줍니다. 이번 글에서는 RxDart를 사용하여 간단한 게임을 개발하는 방법을 살펴보겠습니다.

게임 개요

우리의 게임은 간단한 숫자 맞추기 게임입니다. 게임 화면에는 1부터 100까지의 숫자 중 하나가 랜덤으로 표시되며, 사용자는 입력 창에 숫자를 입력하여 맞춰야 합니다. 맞추기 총 시도 횟수가 제한되어 있으며, 정답을 맞추거나 시도 횟수를 모두 사용하면 게임이 종료됩니다.

프로젝트 설정

먼저, Flutter 프로젝트를 생성하고 RxDart 라이브러리를 추가해야 합니다. pubspec.yaml 파일에 아래 내용을 추가해주세요.

dependencies:
  rxdart: ^0.26.0

그리고 해당 프로젝트를 업데이트하기 위해 터미널에서 아래 명령어를 실행합니다.

flutter packages get

주요 클래스 및 함수

이번 프로젝트에서는 RxDartPublishSubject 클래스와 BehaviorSubject 클래스, 그리고 StreamStreamController를 사용합니다.

게임 개발

  1. main.dart 파일 생성 및 기본 코드 작성

    먼저, main.dart 파일을 생성하고 기본 코드를 작성합니다. 이 코드는 앱을 실행하고 화면을 구성하는 역할을 합니다.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(GameApp());
    }
    
    class GameApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Number Guessing Game',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: GameScreen(),
        );
      }
    }
    
    class GameScreen extends StatefulWidget {
      @override
      _GameScreenState createState() => _GameScreenState();
    }
    
    class _GameScreenState extends State<GameScreen> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Number Guessing Game'),
          ),
          body: Center(
            child: Text('Game Screen'),
          ),
        );
      }
    }
    
  2. 게임 로직 구현

    이제 게임 로직을 구현해보겠습니다. 먼저, 게임 화면에서 사용자가 입력하는 TextField 위젯을 추가합니다. 그리고 사용자가 입력한 값을 구독하고, 게임 로직에 따라 상태를 업데이트하도록 코드를 작성합니다.

    import 'package:rxdart/rxdart.dart';
    
    class _GameScreenState extends State<GameScreen> {
      TextEditingController _inputController = TextEditingController();
      PublishSubject<int>? _guessSubject;
      BehaviorSubject<bool>? _gameOverSubject;
    
      // 게임 로직 초기화
      void _initializeGame() {
        _guessSubject = PublishSubject<int>();
        _gameOverSubject = BehaviorSubject<bool>();
    
        // 게임 오버 상태를 확인하여 처리
        _gameOverSubject?.listen((gameOver) {
          if (gameOver) {
            _guessSubject?.close();
            _gameOverSubject?.close();
          }
        });
      }
    
      @override
      void initState() {
        super.initState();
        _initializeGame();
      }
    
      @override
      void dispose() {
        _inputController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Number Guessing Game'),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextField(
                controller: _inputController,
                keyboardType: TextInputType.number,
                onChanged: (value) {
                  // 사용자 입력을 발행
                  _guessSubject?.add(int.parse(value));
                },
              ),
              SizedBox(height: 16.0),
              Text('Guess Result'),
            ],
          ),
        );
      }
    }
    

    이제 사용자가 입력한 값을 게임 로직에서 처리할 수 있도록 준비가 됐습니다.

  3. 게임 로직 완성

    지금까지 작성한 코드에 게임 로직을 추가해보겠습니다. 게임 화면에 숫자를 랜덤으로 표시하고, 사용자가 입력한 값을 비교하여 결과를 표시합니다. 또한, 게임 오버 상태를 체크하여 게임 종료를 처리합니다.

    class _GameScreenState extends State<GameScreen> {
      // ...
    
      int _targetNumber = 0;
      int _attemptCount = 0;
      bool _gameOver = false;
    
      void _startGame() {
        _gameOver = false;
        _attemptCount = 0;
        _targetNumber = _generateTargetNumber();
    
        // 사용자 입력을 구독
        _guessSubject?.listen((guessNumber) {
          if (_gameOver) return; // 이미 게임 종료 상태일 경우 종료
    
          _attemptCount++;
    
          if (guessNumber == _targetNumber) {
            // 정답일 경우 게임 종료 처리
            _gameOverSubject?.add(true);
          } else if (_attemptCount >= 5) {
            // 시도 횟수 초과 시 게임 종료 처리
            _gameOverSubject?.add(true);
          } else {
            // 오답일 경우 결과 표시
            setState(() {
              _resultText = guessNumber < _targetNumber
                  ? 'Higher'
                  : 'Lower';
            });
          }
        });
      }
    
      int _generateTargetNumber() {
        // 1부터 100까지의 랜덤한 숫자 생성
        return Random().nextInt(100) + 1;
      }
    
      @override
      void initState() {
        super.initState();
        _initializeGame();
        _startGame();
      }
    
      @override
      Widget build(BuildContext context) {
        // ...
      }
    }
    

    이제 게임이 정상적으로 동작하도록 준비가 되었습니다.

실행 결과

위의 코드를 실행하면 다음과 같이 숫자를 맞추는 게임이 화면에 표시됩니다.

Screenshot

사용자는 입력 창에 숫자를 입력하고 제출할 수 있습니다. 맞춘 숫자가 맞으면 정답으로 표시되고, 시도 횟수 초과 시 게임 종료 메시지가 표시됩니다.

이처럼 RxDart를 활용하면 플러터에서 비동기 처리를 더욱 효율적으로 할 수 있습니다. 게임 개발뿐만 아니라 여러 다른 애플리케이션에서도 RxDart를 적극 활용하여 개발할 수 있습니다.

결론

이번 글에서는 플러터와 RxDart를 사용하여 간단한 게임을 개발하는 방법을 살펴보았습니다. RxDart를 사용하면 비동기 작업을 간단하게 처리할 수 있으며, 반응형 프로그래밍의 장점을 활용할 수 있습니다. 플러터로 다양한 애플리케이션을 개발할 때 RxDart를 함께 활용해보세요!

참고 자료