1. asa_s

    Posted

    asa_s
Changes in title
+ゲーム開発(C#)で「スキル」や「コマンド」の追加・管理にオススメなFactory Methodパターン
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,143 @@
+#TL; DR
+ゲーム開発において、複数作る必要があり、さらに後からも追加することになる「スキル」や「コマンド」の実装に、GoFのデザインパターンの1つ「Factory Method」パターンを使うことで明快な管理をしよう、という趣旨の記事になります。
+
+#Factory Methodパターン
+Factory Methodパターンは簡単に言うと「物(Instance)を作る工場(Factory)」を用意して、多様なインスタンス生成はFactoryに任せてしまう、という考え方です。
+
+例えばゲーム開発の例でいうと、「Lightning」、「Heal」という2つのスキル(class)があったとして、利用者が各スキルをインスタンス化するのではなく、SkillFactoryというクラスに「Lightning, Healを作ってくれ」といった風にお願いするイメージです。
+
+#サンプルコード
+では実際の上記の例をサンプルコードで見ていきたいと思います。
+
+
+```csharp:SkillFactory.cs
+using System;
+
+/// <summary>
+/// Skillを管理し、作ってくれるFactoryクラス
+/// </summary>
+public class SkillFactory
+{
+ /// <summary>
+ /// SkillKindを引数に、それに応じたスキルを返す
+ /// </summary>
+ public AbstractSkill Create(SkillKind skillKind)
+ {
+ switch(skillKind)
+ {
+ case SkillKind.Lightning:
+ return new LightningSkill();
+ case SkillKind.Heal:
+ return new HealSkill();
+ default:
+ throw new ArgumentException();
+ }
+ }
+
+ /// <summary>
+ /// 現在存在しているSkill一覧
+ /// </summary>
+ public enum SkillKind
+ {
+ Lightning,
+ Heal
+ }
+}
+```
+
+
+```csharp:AbstractSkill.cs
+/// <summary>
+/// スキルの抽象クラス
+/// </summary>
+abstract public class AbstractSkill
+{
+ /// <summary>
+ /// スキル実行の抽象メソッド
+ /// </summary>
+ public abstract void Play();
+}
+```
+
+```csharp:LightningSkill.cs
+using UnityEngine;
+/// <summary>
+/// スキル「ライトニング」の具象クラス
+/// </summary>
+public class LightningSkill : AbstractSkill
+{
+ /// <summary>
+ /// スキル「ライトニング」の実行
+ /// </summary>
+ public override void Play() {
+ Debug.Log("Lightning!");
+ }
+}
+```
+
+```csharp:HealSkill.cs
+using UnityEngine;
+/// <summary>
+/// スキル「ヒール」の具象クラス
+/// </summary>
+public class HealSkill : AbstractSkill
+{
+ /// <summary>
+ /// スキル「ヒール」の実行
+ /// </summary>
+ public override void Play() {
+ Debug.Log("Heal!");
+ }
+}
+```
+
+以上で準備は完了です。
+では最後に、実行です。
+
+プレイヤーはキーボードの「L」を押すことでスキル「ライトニング」を、「H」を押すことでスキル「ヒール」をセットし、「S」で実行する簡易サンプルコードです。
+
+```csharp:Player.cs
+
+...
+
+ // 選択中のスキル
+ private SkillFactory.SkillKind selectedSkillKind;
+
+ private void Update()
+ {
+ if (Input.GetKeyDown(KeyCode.L)) {
+ Debug.Log("Skill [Lightning] Selected!");
+ this.selectedSkillKind = SkillFactory.SkillKind.Lightning;
+ }
+
+ if (Input.GetKeyDown(KeyCode.H)) {
+ Debug.Log("Skill [Heal] Selected!");
+ this.selectedSkillKind = SkillFactory.SkillKind.Heal;
+ }
+
+ if (Input.GetKeyDown(KeyCode.S)) {
+ var skillFactory = new SkillFactory();
+ var skill = skillFactory.Create(this.selectedSkillKind);
+ skill.Play();
+ }
+ }
+
+...
+```
+
+<img width="205" alt="スクリーンショット 2018-09-26 23.09.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/97286/723fc6d7-dc74-c311-a655-bba3a20360b5.png">  実行結果
+
+同じ「S」キーを押した処理でも、this.selectedSkillKindによって実行するスキルが変わっていることがわかります。
+
+要点をまとめると、LightningやHealなどのスキルは全て抽象クラスのAbstractSkillを継承し、継承先のPlay()に実際の実行処理を書くことで、利用者(Player)側は何のスキルかを意識せず実行することができます。そのため新スキルを追加する際は、新スキルのクラスとFactory側の修正、「選択中のスキル」の管理で対応できます。
+
+また、スキルの必要MPやダメージ量、スキル自体のType(攻撃、回復、バフ、デバフなど)のenumなどを抽象クラスで定義しておき実装を強制することで、安全に他の人も新スキルを追加していくことができます。
+
+ 
+
+以上です。
+何かご指摘やもっと良い方法等がありましたらコメントお願いします!
+
+
+ 
+(補足:サンプルコードでは「選択中のスキル」をPlayer.csで管理していますが、別途、スキルを管理するコントローラーがあるとわかりやすいかもしれません。またUpdateにベタでスキル実行を書いていますが、実際にはキャラクターのStateを管理してスキルが連射できないようにするなど、細やかな対応が必要です。あくまでFactory Methodの説明のためのコードです)