走る、狂うまで【Visual C++でゆっくり2Dアクションゲーム作成】Part 16

Поделиться
HTML-код
  • Опубликовано: 1 дек 2024

Комментарии • 7

  • @Vithe-Gaming
    @Vithe-Gaming 14 дней назад +1

    その都度リファクタリングするのは、本当に大事ですね。
    進んでからだとその修正も大変ですし、開発自体に時間が掛かる可能性が高くなりますし。
    私も現在、しばらく放置していた独自変数を少し手直しをしてますが、やはり面倒ですね。出来る限り、書き直す部分は極力少ない組み方をしていますが、使いまわす事も多いものは、それだけで時間と精神を削られます。

    • @SukakiyoLab
      @SukakiyoLab  14 дней назад

      大変ですが、リファクタリングは念入りにやっていきたいですね。ここをいかにわかりやすく作れるかが今後の活動にも響いてくるので。
      ただし時間をかけすぎるとモチベも下がっていくので、システムの根幹部分に関しては次回で決めに行きたいところです。

  • @酢-l2h
    @酢-l2h 11 дней назад +2

    -こんにちは!初めて動画を見させていただいたんですが、-
    -各`Component`の初期化はそれぞれのコンストラクタで行えばいいように思うのですが、`ComponentFactory`の存在意義は何でしょうか?-
    -各`Component`のコンストラクタに初期化を任せて、コンストラクタを直接呼ぶ形にすれば`ComponentFactory`がまるまる不要になる気がするんですが。-
    -何かファクトリを介さないといけない理由はあるのでしょうか?-
    過去動画も見て理解しました。ランタイムで生成する`Component`を切り替えるためにファクトリが挟まってるんですね。

    • @SukakiyoLab
      @SukakiyoLab  11 дней назад +1

      良〜い質問ですね〜
      実はComponentのコンストラクタで初期化の処理をすべて任せるのは、作り方によっては可能だと思います。
      ComponentFactoryを実装している利点は、ComponentやGameObjectの初期化に関わる役割を削減できるという点が挙げられます。ComponentFactoryを実装しておくことで、Componentそのものを登録しておく機能など、比較的「Componentの外部に任せたい作業」を実装することができます。
      要するに「プログラムの構成がわかりやすくなる」というのが、結局のところの利点だと思います。(たぶんComponentですべてを管理する場合、static変数やstaticメソッドを使って同様のことが可能だと思います)
      あとnew演算子やdelete演算子をなるべく直接使いたくなかったり、作ったComponentをまとめて管理できたりすると便利だなーとか考えたりして、作っておいた方がいろいろ便利だなと実装に踏み切りました!
      たぶん他にも実装方法はいろいろあります!

    • @酢-l2h
      @酢-l2h 11 дней назад +2

      @@SukakiyoLab
      返信ありがとうございます!
      たしかにランタイムで設定ファイルからもろもろ読み込むときなんかは結局条件分岐書いてインスタンス化対象を切り替えることになるので
      `Component`生成のもろもろを担うクラスがあった方が楽そうですね。いや~失念してました

    • @SukakiyoLab
      @SukakiyoLab  11 дней назад +1

      いえいえ自分もまだまだプログラミング初心者なので、めちゃくちゃ助かります!
      (やっべ、よくよく考えたら「ちょっと前までめちゃくちゃ読みにくいソースコード書いてた」なんて言えない)

    • @酢-l2h
      @酢-l2h 11 дней назад +2

      @@SukakiyoLab
      (注:長文です。mdエディタに張り付けて読むこと推奨)
      いえいえ私も全然初心者ですので...
      一応最近勉強しているなりの提案を書いておきます
      ファクトリ側に各`Component`ごとの定義を書いちゃってて、具体的すぎる気がするので、その部分をDI的な構造にして外に出すといいんじゃないかと思います。
      `Component`が増える度に`ComponentFactory`の中身をいじりに行くのは面倒ですし。
      C++わからんのでTypescriptで適当に書いた例: (型定義がクソ雑魚なのは許して...)
      ```typescript
      /* Static members */
      interface ComponentConstructor{
      tryCreate(option: unknown): Component | null;
      /* Add definitions below if you want. */
      }
      /* Non-static members */
      interface Component{
      /* Add definitions below if you want. */
      }
      /* おまじない (deep.tacoskingdom.com/blog/181) */
      const Implements = () => (ctor: U) => ctor;
      class ComponentFactory{
      private readonly registered_components: Set = new Set();
      public register(component: ComponentConstructor): void{
      this.registered_components.add(component);
      }
      public createComponent(option: unknown): Component | null{
      for (const component of this.registered_components) {
      const result = component.tryCreate(option);
      if (result) return result;
      }
      return null;
      }
      }
      @Implements()
      class StringComponent{
      private readonly option: string;
      public constructor(option: string) {
      this.option = option;
      }
      public static tryCreate(option: unknown): StringComponent | null{
      return typeof option === "string" ? new StringComponent(option) : null;
      }
      }
      @Implements()
      class NumberComponent{
      private readonly option: number;
      public constructor(option: number) {
      this.option = option;
      }
      public static tryCreate(option: unknown): NumberComponent | null{
      return typeof option === "number" ? new NumberComponent(option) : null;
      }
      }
      const factory = new ComponentFactory();
      factory.register(StringComponent);
      factory.register(NumberComponent);
      console.log(factory.createComponent("Hi!")); // Output: StringComponent { option: 'Hi!' }
      console.log(factory.createComponent(42)); // Output: NumberComponent { option: 42 }
      console.log(factory.createComponent(false)); // Output: null
      ```
      (staticなメンバーをinterfaceに書こうとするとややこしくなるのはJS由来のTSの仕様なので気にしてはいけない)
      これで`ComponentFactory#createComponent(option)`したときに`ComponentFactory`に`register()`した`Component`の`tryCreate(option)`を片っ端から呼び出して、成功したらその結果を返す挙動になります。
      (全て失敗したら`null`を返す。一応雑に動作確認済み。)
      ただ、こういう定義をすると当然型による補完が効かなくなります。
      動的に返すインスタンスを切り替えるのに使うだけなら大した問題にはならないと思いますが、ハードコードした`Component`を使いたい場合にファクトリから呼び出すのが補完が効かなくてうざかったりするかもしれません。
      その場合は`Component`のオプションに補完を効かせるためだけの関数なんかを定義するといいかもしれません。
      (もっといい方法あるかも...)
      具体的な実装方法ですが、多分C++で実装しようと思ったら、
      - `register()`あるいは`new`時に`tryCreate()`相当の関数を関数ポインタで受けて、適当なコレクションに保存
      - C++でできるか知らんが、上のTSコードは(インスタンスではなく)クラス自体を受け取るような処理をしている。
      もしできるならその方が分かりやすいコードになるかも。
      - `createComponent()`時に保存した関数ポインタ達を列挙して片っ端から与えられたオプションと共に呼び出す
      という実装になるんじゃないかと思います。
      ご参考までに。