본문 바로가기
코딩(개발)/Flutter

플러피 게임 제작

by 플랜데버 2021. 3. 4.

코딩파파 모바일게임 따라하기 

 

 

파일구조

 

※ pubspec.yaml

dependencies:

  flame^0.28.0

 

  assets:

    - assets/images/

    - assets/audio/

 

main.dart

import 'package:flame/flame.dart';

import 'package:flutter/material.dart';

import 'my_game.dart';

 

Size size;

var spriteSheet;

 

void main() async {

  WidgetsFlutterBinding.ensureInitialized();

 

  size = await Flame.util.initialDimensions();

  spriteSheet = await Flame.images.load("sprites.png");

 

  runApp(MyGame().widget);

}

 

 game_state.dart

enum GameState { pause, play, gameover }

GameState gameState = GameState.pause;

 

 option.dart

double
 
GAME_SPEED = 50;

 

 components\bird.dart

import 'package:flame/anchor.dart';
import 'package:flame/components/animation_component.dart';
import 'package:flame/flame.dart';
import 'package:flappy_game/game_state.dart';
import 'package:flappy_game/main.dart';
import 'package:flappy_game/options.dart';

final double BIRD_W = 52;
final double BIRD_H = 36.7;
final double GRAVITY = 1200;

class Bird extends AnimationComponent {
  Bird()
      : super.sequenced(BIRD_W, BIRD_H, 'bird.png', 3,
            textureWidth: 17, textureHeight: 12);

  double speedY = 0.0;

  @override
  void update(double t) {
    super.update(t);
    this.anchor = Anchor.center;

    switch (gameState) {
      case GameState.pause:
        this.y = size.height * 0.4;
        this.x = size.width / 2;
        break;
      case GameState.play:
        speedY += (GRAVITY + GAME_SPEED) * t;
        this.y += (speedY * t) / 2;
        this.x = size.width / 2;
        break;
      case GameState.gameover:
        break;
    }
  }

  void onTap() {
    switch (gameState) {
      case GameState.pause:
        this.speedY = -500;
        Flame.audio.play("bubble_pop.mp3", volume: 0.9);
        break;
      case GameState.play:
        this.speedY = -500;
        Flame.audio.play("bubble_pop.mp3", volume: 0.9);
        break;
      case GameState.gameover:
        break;
    }
  }
}

 

 

 components\floor.dart

import 'dart:ui';
import 'package:flame/components/component.dart';
import 'package:flame/position.dart';
import 'package:flame/sprite.dart';
import 'package:flappy_game/game_state.dart';
import 'package:flappy_game/main.dart';
import 'package:flappy_game/options.dart';

 

class Floor extends Component {

  Sprite _floorSprite = Sprite("floor.png");

 

  double xSize = 0;

  double ySize = 0;

  double xPos = 0;

 

  @override

  void render(Canvas c) {

    xSize = size.width * 2;

    ySize = xSize / 168 * 56;

 

    _floorSprite.renderPosition(c, Position(xPos, size.height - ySize * 0.3),

        size: Position(xSize, ySize));

  }

 

  @override

  void update(double t) {

    switch (gameState) {

      case GameState.pause:

      case GameState.play:

        xPos -= t * (30 + GAME_SPEED);

        if (xPos.abs() >= size.width) {

          xPos = 0;

        }

        break;

      case GameState.gameover:

        break;

    }

  }

 

  Rect toRect() {

    return Rect.fromLTWH(0, size.height - ySize * 0.3, xSize, ySize);

  }

}

 

 components\pipe_set.dart

import 'dart:math';
import 'dart:ui';

import 'package:flame/components/component.dart';
import 'package:flame/position.dart';
import 'package:flame/sprite.dart';
import 'package:flappy_game/game_state.dart';
import 'package:flappy_game/options.dart';

import '../main.dart';

class PipeSet extends Component {
  static double pipeH = size.height / 12 * 7;
  static double pipeW = pipeH / 160 * 26;
  static double pipeGap = 4;

  Sprite _pipUp = Sprite("pipe_up.png");
  Sprite _pipDown = Sprite("pipe_down.png");

  double pipePos = size.width;
  int pipeLevel = 1;
  bool hadScored = false;

  @override
  void render(Canvas c) {
    _pipUp.renderPosition(c, Position(pipePos, pipeH / 7 * (pipeLevel - 7)),
        size: Position(pipeW, pipeH));
    _pipDown.renderPosition(
        c, Position(pipePos, pipeH / 7 * (pipeLevel + pipeGap)),
        size: Position(pipeW, pipeH));
  }

  @override
  void update(double t) {
    switch (gameState) {
      case GameState.pause:
        pipePos = size.width;
        hadScored = false;
        break;
      case GameState.play:
        if (pipePos < -pipeW) {
          pipePos = size.width;
          hadScored = false;
          pipeLevel = Random().nextInt(5);
          if (pipeLevel == 0) pipeLevel = 6;
        }
        pipePos -= t * (30 + GAME_SPEED);
        break;
      case GameState.gameover:
        break;
    }
  }

  Rect getPipUpRect() {
    return Rect.fromLTWH(pipePos, pipeH / 7 * (pipeLevel - 7), pipeW, pipeH);
  }

  Rect getPipDownRect() {
    return Rect.fromLTWH(
        pipePos, pipeH / 7 * (pipeLevel + pipeGap), pipeW, pipeH);
  }

  void scoreUpdated() {
    hadScored = true;
  }
}

 

 components\score.dart

import 'dart:collection';
import 'dart:ui';

import 'package:flame/components/component.dart';
import 'package:flame/sprite.dart';
import 'package:flappy_game/consts/sprite_dimentions.dart';

import '../main.dart';

class Score extends PositionComponent {
  static final double numW = size.width / 20;
  static final double numH = numW / 12 * 18;
  HashMap<String, Sprite> _digits;

  SpriteComponent _oneDigit;
  SpriteComponent _twoDigit;
  SpriteComponent _threeDigit;

  int _score = 0;

  Score()
      : _digits = HashMap.from({
          "0": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.zeroNumberX,
              y: SpritePositions.zeroNumberY),
          "1": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.firstNumberX,
              y: SpritePositions.firstNumberY),
          "2": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.secondNumberX,
              y: SpritePositions.secondNumberY),
          "3": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.thirdNumberX,
              y: SpritePositions.thirdNumberY),
          "4": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.fourthNumberX,
              y: SpritePositions.fourthNumberY),
          "5": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.fifthNumberX,
              y: SpritePositions.fifthNumberY),
          "6": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.sixthNumberX,
              y: SpritePositions.sixthNumberY),
          "7": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.seventhNumberX,
              y: SpritePositions.seventhNumberY),
          "8": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.eighthNumberX,
              y: SpritePositions.eighthNumberY),
          "9": Sprite.fromImage(spriteSheet,
              width: SpriteDimentions.numberWidth,
              height: SpriteDimentions.numberHeight,
              x: SpritePositions.ninethNumberX,
              y: SpritePositions.ninethNumberY),
        }) {
    _oneDigit = SpriteComponent.fromSprite(numW, numH, _digits["0"]);
    _twoDigit = SpriteComponent.fromSprite(numW, numH, _digits["0"]);
    _threeDigit = SpriteComponent.fromSprite(numW, numH, _digits["0"]);

    _oneDigit.x = numW * 8;
    _twoDigit.x = numW * 1.5;
    _threeDigit.x = numW * 1.5;

    _oneDigit.y = numH * 1.5;
  }

  void addScore() {
    _score++;
  }

  void resetScore() {
    _score = 0;
  }

  @override
  void render(Canvas c) {
    String scoreInStr = _score.toString().padLeft(3, "0");

    _oneDigit.sprite = _digits[scoreInStr[0]];
    _twoDigit.sprite = _digits[scoreInStr[1]];
    _threeDigit.sprite = _digits[scoreInStr[2]];

    _oneDigit.render(c);
    _twoDigit.render(c);
    _threeDigit.render(c);
  }
}

 

 components\titles.dart

import 'dart:ui';

import 'package:flame/components/component.dart';
import 'package:flame/position.dart';
import 'package:flame/sprite.dart';
import 'package:flappy_game/game_state.dart';
import 'package:flappy_game/main.dart';

class Titles extends Component {
  Sprite _getReady =
      Sprite.fromImage(spriteSheet, x: 295, y: 59, width: 92, height: 25);

  Sprite _gameOver =
      Sprite.fromImage(spriteSheet, x: 395, y: 59, width: 96, height: 25);

  double xSize = 0;
  double ySize = 0;

  @override
  void render(Canvas c) {
    switch (gameState) {
      case GameState.pause:
        xSize = size.width * 0.6;
        ySize = xSize / 92 * 25;
        _getReady.renderPosition(
            c, Position(size.width * 0.2, size.height * 0.2),
            size: Position(xSize, ySize));
        break;
      case GameState.play:
        break;
      case GameState.gameover:
        xSize = size.width * 0.6;
        ySize = xSize / 96 * 25;
        _gameOver.renderPosition(
            c, Position(size.width * 0.2, size.height * 0.2),
            size: Position(xSize, ySize));
        break;
    }
  }

  @override
  void update(double t) {
    // TODO: implement update
  }
}

 

 

 contents\sprite_dimentions.dart

 

class SpriteDimentions {
  static double numberWidth = 12.0;
  static double numberHeight = 18.0;
}

class SpritePositions {
  static double zeroNumberX = 496.0;
  static double zeroNumberY = 60.0;
  static double firstNumberX = 136.0;
  static double firstNumberY = 455.0;
  static double secondNumberX = 292.0;
  static double secondNumberY = 160.0;
  static double thirdNumberX = 306.0;
  static double thirdNumberY = 160.0;
  static double fourthNumberX = 320.0;
  static double fourthNumberY = 160.0;
  static double fifthNumberX = 334.0;
  static double fifthNumberY = 160.0;
  static double sixthNumberX = 292.0;
  static double sixthNumberY = 184.0;
  static double seventhNumberX = 306.0;
  static double seventhNumberY = 184.0;
  static double eighthNumberX = 320.0;
  static double eighthNumberY = 184.0;
  static double ninethNumberX = 334.0;
  static double ninethNumberY = 184.0;
}

 

my_game.dart

import 'dart:ui';

import 'package:flame/components/component.dart';
import 'package:flame/flame.dart';
import 'package:flame/gestures.dart';
import 'package:flame/sprite.dart';
import 'package:flappy_game/components/bird.dart';
import 'package:flappy_game/components/floor.dart';
import 'package:flappy_game/components/pipe_set.dart';
import 'package:flappy_game/components/score.dart';
import 'package:flappy_game/components/titles.dart';
import 'package:flappy_game/game_state.dart';
import 'package:flappy_game/main.dart';
import 'package:flame/game/base_game.dart';

class MyGame extends BaseGame with TapDetector {
  Bird _bird;
  Floor _floor;
  Titles _titles;
  PipeSet _pipeSet;
  Score _score;

  MyGame() {
    _bird = Bird();
    _floor = Floor();
    _titles = Titles();
    _pipeSet = PipeSet();
    _score = Score();

    this
      ..add(
          SpriteComponent.fromSprite(size.width, size.height, Sprite("bg.png")))
      ..add(_pipeSet)
      ..add(_bird)
      ..add(_floor)
      ..add(_titles)
      ..add(_score);
  }

  @override
  void update(double t) {
    super.update(t);

    if (gameState == GameState.play) {
      //새가 바닥에 부딪혔을때
      if (checkIf2CompoCollision(_bird.toRect(), _floor.toRect())) {
        setGameOver();
      }

      //새가 위 파이프에 부딪혔을때
      if (checkIf2CompoCollision(_bird.toRect(), _pipeSet.getPipUpRect())) {
        setGameOver();
      }

      //새가 아래 파이프에 부딪혔을때
      if (checkIf2CompoCollision(_bird.toRect(), _pipeSet.getPipDownRect())) {
        setGameOver();
      }

      checkIfBirdPassedPipe();
    }
  }

  void setGameOver() {
    Flame.audio.play("hit.wav");
    Flame.audio.play("die.mp3");
    gameState = GameState.gameover;
  }

  @override
  void onTap() {
    _bird.onTap();

    switch (gameState) {
      case GameState.pause:
        gameState = GameState.play;
        break;
      case GameState.play:
        break;
      case GameState.gameover:
        gameState = GameState.pause;
        _score.resetScore();
        break;
    }
  }

  bool checkIf2CompoCollision(Rect item1, Rect item2) {
    var intersectedRect = item1.intersect(item2);
    return intersectedRect.width > 2 && intersectedRect.height > 2;
  }

  //새가 파이프를 통과했을때
  void checkIfBirdPassedPipe() {
    if (_pipeSet.hadScored) return;

    if (_pipeSet.getPipUpRect().right < _bird.toRect().left) {
      Flame.audio.play("point.mp3");
      _score.addScore();
      _pipeSet.scoreUpdated();
    }
  }
}

 

 

 

 

 

 

 

코딩파파

www.youtube.com/watch?v=CVdGjaFI5Bw

 

'코딩(개발) > Flutter' 카테고리의 다른 글

ListView / AnimateList  (0) 2021.03.04
TextField  (0) 2021.03.04
체크박스 있는 리스트뷰  (0) 2021.02.09
DatePicker  (0) 2021.02.09
webview  (0) 2021.02.04

댓글