Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Flutter始めて1ヶ月のビギナーが週末にコツコツとスマブラのキャラ対策メモアプリを作っている話

はじめに

Flutter #2 Advent Calendar 2019の5日目の記事です。

株式会社Tenxia のいまくるすです。

この記事では、10月半ばから転職を機にFlutterを書き始めた私がスマブラのキャラ対策メモ用アプリを作っている話を、採用している技術・アーキテクチャ・ライブラリなどに絡めて書きます。具体的には、Firestore, BLoC/RxDart, json_annotationによるモデルのシリアライズなどについて書きます。

アプリの作りとしては初学者が作るToDoアプリに毛が生えたようなものなので、これからFlutterを初めるような方達の参考になればと思っています。

まだリリースはしていません(最近はポケモンで忙しいので)

スマブラとは?


大乱闘スマッシュブラザーズ SPECIAL | Nintendo Switch | 任天堂 より
https://www.smashbros.com/ja_JP/

人気キャラクターをふっとばして遊ぶ愉快なアクションゲームです
格闘ゲームとして競技(いわゆるeSports)の面でも盛り上がっており、強くなるためにプレイするいわゆるガチプレイヤーも世界中にたくさんいます。私もその一人です(ピーチメイン)。

開発の動機

格闘ゲームではそれぞれのキャラクターのそれぞれの技に特徴があり、対戦で勝つためにはそれらを理解して闘うことが重要になります。その理解のことをキャラ対策と呼びます。(スマブラに限らないことですが)自分の考えをしっかりと言語化して意識しながらPDCAを回すことは上達のために重要です。

現状、多くのプレイヤーはキャラ対策を普通のメモアプリや手書きノートにまとめています。しかし、技の発生の速さ1・後隙2・ガード硬直差3などを確認しながらキャラ毎にまとめてメモを作成したほうが対策が深まるのではないかと思い、開発に着手しました。

作っているアプリの概要

相手キャラクターを選択し、それについてメモを作成できます。
メモの作成ページでは選んだキャラクターのフレームデータが閲覧できます。フレームデータは開発途中なので少しガバガバです。


採用技術

BLoC/RxDart

状態管理にはBLoCパターンを採用しています。採用理由は、見通しを良くするためにビューとロジックを分離したく、かつSwiftでのRx+MVVMの経験があるためすんなり受け入れられたためです。(てかBLoC以外の状態管理方法知らない)

異なるWidget間で同じBLoCを共有するためにbloc_providerを使いました。

モデルの保存、シリアライズ・デシリアラズ

json_annotationjson_serializableを使って、モデルクラスのエンコード・デコードを行いました。

キャラクターのデータはFirestore上に保存し、アプリ起動時にフェッチしてFighterという自前のクラスにデコードして使っています。
Firestoreの導入については、公式のまんまやるのが一番早いと思います。(バージョン最新にしようとしてつまづいた)
全体のコードは載せませんが、クラス定義の雰囲気は以下のようになっています。fighter.g.dartはライブラリによって自動生成された実際のエンコード・デコード処理が書かれたファイルです。

import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';

part 'fighter.g.dart';

@JsonSerializable()
class Fighter {
  final String id;
  @JsonKey(name: 'jp_name')
  final String jpName;

  const Fighter({
    @required this.id,
    @required this.jpName,
  }) : super();

  factory Fighter.fromJson(Map<String, dynamic> json) =>
      _$FighterFromJson(json);

  Map<String, dynamic> toJson() => _$FighterToJson(this);
}

Firestoreから受け取る部分は以下のようになります

    final stream =
        Firestore.instance.collection('fighters').snapshots().map((snapshot) {
      final result = snapshot.documents
          .map((document) => Fighter.fromJson(document.data)) // 先程定義したfromJsonでMap<String, dynamic>のdocument.dataをよしなに変換してくれる
          .toList()
            ..sort((a, b) => a.id.compareTo(b.id));

      return result;
    });

キャラ対策メモについては、Noteというクラスを作成しshared_preferencesを使ってローカルでJson形式文字列として保存しています。
あるページでメモの作成・編集・削除などを行った際に他のページにもその更新が反映されてほしいため、シングルトンを作成してその中でのみメモすべてのデータのStreamを管理する方法を取りました。雰囲気としては以下のようになっています。

class NoteRepository {
  factory NoteRepository() => _instance ??= NoteRepository._();
  static NoteRepository _instance;

  // メモの作成・編集・削除などがあれば、SharedPreferencesを更新するとともにここに変更後のデータを流す
  final _notesSubject = BehaviorSubject<List<Note>>();

  // Widget内ではこれorこれを変形したものを使う
  ValObservable<List<Note>> get notes => _notesSubject;

  // 以下にCRUD処理を定義

今後について

  • リリースまではもう少しかかります(多分二週間くらい?)。キャラクターのデータを整形してFirestoreに保存する作業がボトルネックです
    • 権利関係のことについても調べる必要あり
  • リリースまでに追加する機能としては、自キャラの選択、反確4表示などを考えています

  1. 技の発生の速さ: ボタンを押してから実際に技が出るまでのフレーム数 

  2. 後隙: 技を出してから動けるようになるまでのフレーム数 

  3. ガード硬直差: 技をガードされた時に相手が動けて自分が動けないフレーム数 

  4. 反確: 相手の攻撃をガードしたときにこちらが相手に返せる技。技のデータが分かれば計算可能 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?