⚔️

Flutterのsuperをタクティクスオウガのクラスチェンジをイメージして学んでみた

2021/09/11に公開1

Flutter superをタクティクスオウガのステータス値(超簡易)で学んでみた

分かりそうで、分からないsuperの使い方を学ぶ

本稿、あるいは本シリーズはFlutterの公式ドキュメントを参考に初学者が理解を深めるものである。

superってなんだ。なぜ使うんだろう、という疑問が今も浮かびながら本稿を執筆している。例えば、FlutterのデモコードではStatefulWidgetを継承したMyHomePageに子クラスのコンストラクタの他に、super(key: key)なるものが記述されている。

初学者としてはこのsuperがなんなのか理解を深めなければ、コードが何を指すのか分からない場面が多々出てくる。

...class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title; 

  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  
  Widget build(BuildContext context) {
  ...
 以下略
 

MyHomePageはStatefulWidgetを継承している。ということはMyHomePageクラスはStateful Widgetの子クラスで、StatefulWidgetはMyHomePageクラスの親クラスということになる。そして、superは親クラスのコンストラクタを呼び出すことができるものである。

上記のコードでは、MyHomePageクラスのコンストラクタにはkeyというパラメータがある。ここで受け取ったkeyを親クラス(StatefulWidget)に受け渡しているのだ。しかし、実際には上記コードではkeyはnullであるという(正直Keyの使い方が分からなくて、困っているがその解説は本稿の趣旨から外れるため後日調べる)。とにかく、本稿ではsuperは親クラスのコンストラクに引数を受け渡していると説明する。

superを使ってみる

では、なぜsuperが必要なのか。

例えば、『タクティクスオウガ』の主人公デニムは他のキャラと同様、クラスチェンジができる。デフォルトのソルジャーから、ナイトにクラスチェンジした際、ステータスに変化が生じるが、そのステータスの増減はデニムの元のステータスを参照しなければならない。

つまり、他のキャラがナイトになるのと、デニムがナイトになるのではそれぞれステータスが違って当然だ。今回はそのデニムのステータスをsuperを使って表現してみた。

以下のコードは一応、現在の理解力で記述したもので、2021年9月11日現在、動作を確認している。しかし、もっと良い書き方があるかもしれない。

void main() {
  Knight knight = Knight(speed: -10, power: 20, vital: 6);
  knight.knightStatus();
  knight.denimWords();
}

//親クラスの定義
class Denim {
  int denimSpeed = 3; //デニムの元々のスピード
  int classSpeed; //デニムのクラスによるスピード補正

  int denimPower = 5; //デニムの元々の力
  int classPower; //デニムのクラスによる力補正

  int denimVital = 3; //デニムの元々の体力
  int classVital; //デニムのクラスによる体力補正

  Denim({required this.classSpeed, required this.classPower, required this.classVital}) {}

  void denimWords() {
    print('馬鹿なことはやめるんだッ!');
  }
}

//子クラスの定義
class Knight extends Denim {
  //親クラスのコンストラクタを使用
  Knight({required int speed, required int power, required int vital}) : super(classSpeed: speed, classPower: power, classVital: vital) {}

  void knightStatus() {
    print('デニムのスピードは${super.denimSpeed + super.classSpeed}');
    print('デニムの力は${super.denimPower + super.classPower}');
    print('デニムの体力は${super.denimVital + super.classVital}');
  }

  
  void denimWords(){
    print('……わかっています。');
  }
}

子クラスKnightは親クラスである、Denimを参照しており、自身のコンストラクタで引数として受け取ったspeed等のステータスをsuperを使いDenimクラスのコンストラクタを呼び出している。そして、classSpeed等、各ステータスに渡しているのだ。

そして、knightStatus()では元々のステータスとKnightのステータス増減の合計値を出力させている。

ちなみに、Knightクラス内の @override 以下の denimWords()は親クラスであるDenimのセリフを上書きしている(原作を知っている人からすれば、なんでナイトになったらロールートに行くことになってるのだとツッコミたくなるだろうが)。

これをさらに利用するならば、クラスごとにステータスを変化させることだってできるし、戦闘中のセリフを変えたり、死亡時の処理を変化させることもできる。

親クラスの元々の処理を活かすことで、処理の方向性が散らばらずに済むような気がした(あくまでも最後は親クラスのコンストラクタに引き渡して、インスタンス生成時に処理してもらう)。

参考: https://flutternyumon.com/how-to-use-class-inheritance/

Discussion

akifumiakifumi

この記事を読んだことによりタクティクスゲーをプレイしたくなりました。