[Flame] Fluter로 게임을 빠르게 만드는 방법.

30354 단어 FlutterDarttech
https://flame-engine.org/

이른바 Flame


Flame은 Fluter로 2D 게임을 만드는 패키지입니다.
Flame Component System(FCS)이라는 구성 요소 기반 구조에 따라 게임을 간단하게 수행할 수 있습니다.
공식 문서여기.

설치하다.


pubspec.yaml의 의존 관계를 기술합니다.
또한 게임에서 그림을 저장하는 폴더를 만들고 assets라고 선언합니다.
pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flame: 1.0.0-rc9

・・・

flutter:
  assets:
    - assets/images/

PositionComponent 제작


구성 요소는 게임에서 업데이트와 그리기 방법을 가진 요소입니다.
Flame에는 PositionComponent라는 기본 구성 요소가 준비되어 있습니다.
다음 코드는 PositionComponent 클래스를 상속하는 EnemySprite 클래스의 예입니다.
onGameResize() 방법은 초기화 및 화면 크기 변경 시 호출됩니다.
화면 크기를 기준으로 위치와 사이즈를 변경할 수 있어 스펀지 모양을 표시할 수 있다.
업데이트 () 방법에서 이동 등 업데이트 처리를 기술합니다.
렌더 () 방법에서 Canvas를 사용할 수 있기 때문에 자신의 속성을 사용하여 자유롭게 그릴 수 있습니다.
Canvas의 데이텀 좌표는 왼쪽 위에 있고 어셈블리 자체의 데이텀 좌표는 anchor 속성에 의해 지정됩니다.
enemy_sprite
class EnemySprite extends PositionComponent {
  EnemySprite({
    required double length,
    Color? color,
  })  : this.length = length,
        this.color = color ?? Colors.greenAccent,
        super(
          anchor: Anchor.center,
          size: Vector2.all(length),
        );
  late double length;
  late Color color;

  
  void onGameResize(Vector2 gameSize) {
    position = Vector2(
      gameSize.x / 2.0,
      gameSize.y / 5.0,
    );
  }

  
  void update(double dt) {
    super.update(dt);
  }

  
  void render(Canvas canvas) {
    super.render(canvas);
    canvas.drawRect(
      Rect.fromLTWH(0, 0, length, length),
      Paint()..color = color,
    );
  }

}

Sprite Component 제작


SpriteComponent는 이미지가 있는 구성 요소입니다.
PositionComponent 클래스를 계승했지만 render () 방법을 생략할 수 있기 때문에 PositionComponent보다 간단하게 처리할 수 있습니다.
player_sprite.dart
class PlayerSprite extends SpriteComponent {
  PlayerSprite({
    required Image image,
    required Vector2 size,
  }) : super(
          sprite: Sprite(image),
          size: size,
        ) {
    anchor = Anchor.center;
  }

  
  void onGameResize(Vector2 gameSize) {
    position = Vector2(
      gameSize.x / 2.0,
      gameSize.y / 5.0 * 5.0,
    );
  }

  
  void update(double dt) {
    super.update(dt);
  }
}

TextComponent 제작


Text Component 클래스는 텍스트가 있는 구성 요소입니다.
PositionComponent의 클래스를 상속하므로 동적으로 이동하고 회전할 수 있습니다.
글꼴 크기 및 색상과 같은 글꼴 정보는 config 속성으로 지정됩니다.
info_sprite
class InfoSprite extends TextComponent {
  InfoSprite({
    String? text,
    Color? color,
    double? fontSize,
  }) : super(
          text ?? "default",
          config: TextConfig(
            color: color ?? Colors.white,
            fontSize: fontSize ?? 32.0,
          ),
        ) {
    anchor = Anchor.center;
  }
}

조합 구성 요소


구성 요소는 addChild() 방법으로 중첩될 수 있습니다.
※ 중첩할 때 부모 구성 요소는 서브 구성 요소의 onGame Resize () 방법과 업데이트 () 방법을 호출해야 합니다.
addChild(component);

게임 사이클


구성 요소가 완성되면 게임 순환에 추가합니다.
Flame은 BaseGame이라는 편리한 게임 순환을 준비했다.
기본 게임 카테고리를 계승한 클래스를 게임위젯이라는 위젯에 전송하면 화면 표시가 완성된다.
게임 위젯은 Stateful Widget이기 때문에 runApp() 함수에 직접 전달하거나 다른 위젯에 추가할 수 있다.
onLoad () 방법은 게임을 초기화할 때 호출되는 비동기식 방법입니다.
그림% 1개의 캡션을 편집했습니다.
이미지 읽기 방법은 여러 가지가 있지만 기본 게임 유형의 이미지 속성을 사용하는 것은 매우 간단하다.
파일 경로는 "assets/images/"폴더의 PNG 파일 이름만 지정합니다.
※ PNG 파일 이외의 파일은 지원되지 않는 것 같습니다.
await images.load('player.png')
구성 요소는dd() 방법을 통해 게임 순환에 추가할 수 있습니다.
추가된 구성 요소의 업데이트 () 방법과render () 방법은 자동으로 호출됩니다.
다음은 게임 순환의 예이다.
main.dart
void main() => runApp(GameWidget(game: MainGame()));

class MainGame extends BaseGame {
  late PlayerSprite player;
  late EnemySprite enemy;

  /// ロード処理
  
  Future<void> onLoad() async {
    // 敵の初期化
    enemy = EnemySprite(length: 100.0);
    add(enemy);

    // プレイヤーの初期化
    player = PlayerSprite(
      image: await images.load('player.png'),
      size: Vector2(100.0, 100.0),
    );
    add(player);
  }
}

카메라 설정


BaseGame 클래스에는 camera 속성이 있습니다.
카메라를 사용하면 게이머를 따라가거나 큰 배경의 일부분을 표시할 수 있다.
예를 들어 다음 줄만 추가하면 카메라가 유저 부품을 따라간다.
 camera.followComponent(player);

입력 이벤트 처리


플라임에는 화면 클릭, 끌기, 길게 누르기 등 각종 입력 이벤트를 받는 믹슨이 준비돼 있다.
사용 방법은 매우 간단하다. 모든 입력 이벤트의 Mixin을 게임 순환 클래스에 추가하고 각 방법에 대해 다시 쓰기만 하면 입력 이벤트를 받을 수 있다.
main.dart
class MainGame extends BaseGame
    with
        TapDetector,
        VerticalDragDetector,
        HorizontalDragDetector {

・・・

  /// 垂直方向にドラッグしたときの処理
  /// tokino syori
  
  void onVerticalDragUpdate(DragUpdateDetails details) {
    // プレイヤーを移動
    player.position += Vector2(
      details.delta.dx,
      details.delta.dy,
    );
  }

  /// 水平方向にドラッグしたときの処理
  
  void onHorizontalDragUpdate(DragUpdateDetails details) {
    // プレイヤーを移動
    player.position += Vector2(
      details.delta.dx,
      details.delta.dy,
    );
  }

  /// タップ or クリックしたときの処理
  
  void onTap() {
    //
  }
}

판정 결과


각 구성 요소에 판결을 추가하려면 구성 요소에 Hitbox Mixin 및 Collidable Mixin이 추가됩니다.
이름처럼 Hitbox는 판정된 형태(Shape)를 나타냅니다.
안전 타격기의 모양은 히트박스 렉탱글(사각형)과 히트박스 서클(원형) 등이다.
※ 단위는 픽셀 수가 아니라 중심에서 나오는 배율입니다.
그리고ddShape () 방법으로 구성 요소에 안전 테두리 모양을 추가합니다.
여러 개의 안전 테두리를 추가할 수 있기 때문에 조합하면 복잡한 모양에 대응할 수 있다.
final hitbox = HitboxRectangle(relation: Vector2(1.0, 1.0));
addShape(hitbox);
다른 구성 요소와 접촉할 때의 처리는 onCollision () 방법에 기술되어 있습니다.
이 때 "is"키워드를 사용하여 어떤 구성 요소와 접촉했는지 판단할 수 있습니다.
또한 각 구성 요소의 판정을 검사하기 위해 게임 링 클래스에HasCollidables Mixin을 추가해야 한다.
class MainGame extends BaseGame with HasCollidables {
  ・・・
}
다음 예에서 접촉한 Enemy Sprite를 게임 순환에서 삭제했다.
구성 요소의 shouldRemove 속성이 진짜일 때, 게임 순환은 자동으로 게임에서 이 구성 요소를 삭제합니다.
또한 접촉을 위해 Enemy Sprite 측도 addShape () 방법으로 자신의 안전 테두리를 정의해야 한다.
player_sprite.dart
class PlayerSprite extends SpriteComponent with Hitbox, Collidable {
  PlayerSprite({
    required Image image,
    required Vector2 size,
  }) : super(
          sprite: Sprite(image),
          size: size,
        ) {
    // ヒットボックスを定義 
    hitbox = HitboxRectangle(relation: Vector2(1.0, 1.0));
    addShape(hitbox);
  }
  late HitboxRectangle hitbox;

  
  void onGameResize(Vector2 gameSize) {
    // ヒットボックスとコンポーネントの位置を合わせる
    hitbox.position = position;
  }
  
  ・・・

  /// 接触したときの処理
  
  void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
    if (other is EnemySprite) {
      // 敵を消す
      other.shouldRemove = true;
    }
  }
}

애니메이션


Flame에서 애니메이션 처리를 Effect라고 합니다.
구성 요소의 이동과 회전 처리 등은 업데이트 () 방법에도 쓰일 수 있지만, 구성 요소의ddEffect () 방법에 효과 정보를 전달하면 Flame는 자동으로 애니메이션 처리를 합니다.
다음은 5초가량 움직이는 무브이펙트의 예다.
플라임은 회전 처리를 하는 로테이트 Effect와 확대 축소를 하는 스캐럴 Effect 등도 준비했다.
addEffect(MoveEffect(
      path: [
        Vector2(-200.0, 0.0),
        Vector2(400.0, 0.0),
        Vector2(-200.0, 0.0),
      ],
      duration: 5.0, 
      curve: Curves.linear,
      isInfinite: true,  // ループするかどうか
      isAlternating: true,  // 反転するかどうか
      isRelative: true,  // 相対座標にするかどうか
    ));

총결산


image_1
많이 길어졌지만 여기에 소개된 기능은 단지 하나의 예일 뿐이다.
플레임은 간단하지만 다기능으로 진짜 게임을 만들 수 있어 재미있다.
이번에 제작된 샘플 코드는 제 Giithub에서도 볼 수 있습니다.
https://github.com/7oh2020/flame_example
이 기사를 통해 Flame에 다소 관심이 있었으면 좋겠어요.
그게 다야.

좋은 웹페이지 즐겨찾기