質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

意見交換

クローズ

6回答

2300閲覧

ゲームで職業を作るときの実装方法について

hogehoge23

総合スコア14

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

2クリップ

投稿2023/02/10 02:48

0

2

テーマ、知りたいこと

こんにちは。

いわゆる、RPGジャンルのゲームをいずれ作りたいなと考えており
その中で職業(クラス、ロール、ジョブ)を実装する際の仕組みを考えていました。
(職業ごとにステータスに差異があり、使えるスキルや特性が異なるようなイメージ。例えばドラクエの勇者や魔法使い、戦士など・・・)

まず思い浮かんだのが抽象クラスを使って
基底クラスから各職業(戦士、魔法使い、弓使いなど)のそれぞれのクラスを実装していくというものでした。

イメージ説明
※画像引用:https://xr-hub.com/archives/19842

ただ、私がまずこれを思いつくという点でこれが最上の案ではないように思え
もっと良い方法があるのではないかと考えるようになりました。

なんと呼ぶのかわからないのですが
Unityのコンポーネントのように、「魔法のメラが使えるコンポーネント」みたいなものを
その人ができることとしてを1つずつ組み立てていく? など
他にも、何かしら良いアプローチが取れるような気がしています。

考え方の参考になるパターンや、キーワード、実装例、こういう機能も使えるなどがあれば意見をいただきたいです
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

回答6

#1

fana

総合スコア11673

投稿2023/02/10 03:59

編集2023/02/10 04:02
enum CLASS{ Brave, Mage, Monk, Warrior } class Character { CLASS m_Class; }

まず,コレだと 具体的に 何が/どのように 問題となるのか? というのを明確にされると良いのではないでしょうか.
(何が必要か? 何が適しているか? というような話は,実際のゲームのルールに大きく依存すると思うので.)

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#2

yambejp

総合スコア114968

投稿2023/02/10 04:04

まずは全ジョブのアビリティを設定して、ジョブごとにどのアビリティをどのような成長速度でどのくらいの上限まで向上できるか設定すればよいのでは?

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#3

Zuishin

総合スコア28662

投稿2023/02/10 04:35

職業やモンスターをそれぞれのクラスで実装するのは大抵の場合良くありません。

たとえば、戦士と魔術師の両方の能力を持つ上位職の魔法戦士というものを作りたくなったとしましょう。
これは戦士から派生するのが良いでしょうか?
それとも魔術師から派生するのが良いでしょうか?

正解は、戦士のスキルと魔術師のスキルをそれぞれクラスとして実装し、それを戦士、魔術師、魔法戦士のスキルとしてそれぞれのインスタンスに持たせることです。
クラスではなくプロパティにすることで、設定ファイルから読み込むことができるようになり、同じスキルを複数の職業に持たせることができるようになり、戦闘中にいちいち「もし魔術師もしくは魔法戦士もしくは賢者なら〜」と条件分岐しなくても良くなります。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#4

UnchFullburst

総合スコア663

投稿2023/02/11 21:50

編集2023/02/11 22:14

(職業ごとにステータスに差異があり、使えるスキルや特性が異なるようなイメージ。例えばドラクエの勇者や魔法使い、戦士など・・・)

Unityのコンポーネントのように、「魔法のメラが使えるコンポーネント」みたいなものを
その人ができることとしてを1つずつ組み立てていく? など

ここらへんの仕様でZuishinさんの「スキルをクラスとして実装する」というアイデアを使いドラクエワードでUnityの機能使って考えてみましょうか。

スキルについて

まずスキル(特技・呪文)をコンポーネントっぽく…というかインスペクターにドラッグ&ドロップで抜き差しできるように作ります。
具体的にはScriptable Objectを使います。

スキルのinterface、ISkillを考えます。
戦闘などでスキル使うときはこのinterfaceを使う感じです。
とりあえずめっちゃ簡略化してこんな感じで。

C#

1public interface ISkill 2{ 3 // 使用に必要なコスト 4 int SPCost { get; } 5 // スキルを使用したときの実際の処理を書く。ダメージとか回復とか。 6 void UseSkill(); 7 // スキルを使用可能かどうか 8 bool IsUsable(int currentSP); 9}

そしてこのISkillを継承したスキルの基底クラス、SkillBaseをScriptableObjectで書きます。

C#

1public abstract class SkillBase : ScriptableObject, ISkill 2{ 3 [SerializeField] protected int spCost = 0; 4 public int SPCost => spCost; 5 6 public abstract void UseSkill(); 7 8 public virtual bool IsUsable(int currentSP) 9 { 10 return currentSP >= SPCost; 11 } 12}

で、このSkillBaseを使って、具体的なスキルをそれぞれ別クラスとして書きます。

C#

1// 通常攻撃 2[CreateAssetMenu(fileName = "NormalAttack")] 3public class NormalAttack : SkillBase 4{ 5 public override void UseSkill() 6 { 7 Debug.Log("通常攻撃!"); 8 Debug.Log("50ダメージ!"); 9 } 10}

C#

1// ばくれつけん 2[CreateAssetMenu(fileName = "BakuretsuKen")] 3public class BakuretsuKen : SkillBase 4{ 5 // 攻撃回数 6 [SerializeField] private int attackCount = 4; 7 public override void UseSkill() 8 { 9 Debug.Log("ばくれつけんをはなった!"); 10 for (int i = 0; i < attackCount; i++) 11 { 12 int damage = Random.Range(10, 30); 13 Debug.Log(damage + "ダメージ!"); 14 } 15 } 16}

C#

1// まじんぎり 2[CreateAssetMenu(fileName = "MajinGiri")] 3public class MajinGiri : SkillBase 4{ 5 // 会心の一撃が出る確率 6 [SerializeField] private float criticalRate = 37.5f; 7 8 public override void UseSkill() 9 { 10 Debug.Log("まじんのごとくきりかかった!"); 11 // 会心チェック 12 if (Random.Range(0, 100) < criticalRate) 13 { 14 Debug.Log("かいしんのいちげき!"); 15 Debug.Log("100ダメージ!"); 16 } 17 else 18 { 19 Debug.Log("ミス!"); 20 } 21 } 22}

そうするとこいつらはScriptbaleObjectなので、右クリックで作成することでAssetとして扱うことができます。
バランス調整がコードを変えずともエディターだけで可能です。
イメージ説明
イメージ説明

キャラクターについて

次にキャラクターを考えます。ステータスはHP、SPのみにします
キャラクターは3つのクラスに分けて考えます。

1. Statsクラス
キャラ名、基礎HP、基礎SPを持っている。キャラクターの基礎ステータス設定クラス。

2. Jobクラス
ジョブのHP係数、SP係数、スキル(ScriptableObject[])を持っている。ジョブ設定クラス。

3. Characterクラス
実際のキャラクターのクラス。ゲーム内で戦闘を行う。
HP、SP、スキル(ISkill[])、Statsクラス、Jobクラスを持っている。スキルを使って攻撃を行う。

Characterの中でStats * Job をかけ合わせて、キャラ性能を表現する感じですね。

まずStatsです。
Characterに設定できるように、ScriptableObjectで書きます。

C#

1[CreateAssetMenu(fileName = "Stats")] 2public class Stats : ScriptableObject 3{ 4 [SerializeField] private string characterName; 5 [SerializeField] private int baseHP; 6 [SerializeField] private int baseSP; 7 8 public string CharacterName => characterName; 9 public int BaseHP => baseHP; 10 public int BaseSP => baseSP; 11}

次はJobです。
Unityではinterfaceをインスペクターに登録することができません(シリアライズできないので)。
しかしinterfaceを継承させたオブジェクトをinterfaceにキャストして取り出すことで、実質的にインスペクターで扱うことが出来ます。
この方法を使って、Jobにスキルを持たせます。

C#

1[CreateAssetMenu(menuName = "Job")] 2public class Job : ScriptableObject 3{ 4 // ジョブ名 5 [SerializeField] private string jobName = "Job"; 6 // HP係数 7 [SerializeField] private float hpCoefficient = 1f; 8 // SP係数 9 [SerializeField] private float spCoefficient = 1f; 10 // スキル 11 [SerializeField] private ScriptableObject[] skills; 12 13 public float HPCoefficient => hpCoefficient; 14 public float SPCoefficient => spCoefficient; 15 16 // ScriptableObjectをISkillにキャストする。 17 public ISkill[] Skills 18 { 19 get 20 { 21 ISkill[] skillInterfaces = new ISkill[skills.Length]; 22 for (int i = 0; i < skills.Length; i++) 23 { 24 skillInterfaces[i] = (ISkill)skills[i]; 25 } 26 return skillInterfaces; 27 } 28 } 29}

だいたいこんな感じのScriptableObjectになります。

イメージ説明

で、最後にこれらを使うCharacterクラスです。

C#

1public class Character : MonoBehaviour 2{ 3 // ステータス 4 [SerializeField] private int hp; 5 [SerializeField] private int sp; 6 7 // キャラ設定 8 [SerializeField] private Stats stats; 9 10 // ジョブ 11 [SerializeField] private Job job; 12 13 // 習得スキル 14 private ISkill[] skills; 15 16 // 初期化 17 public void Initialize() 18 { 19 skills = job.Skills; 20 // statsとjobをかけ合わせてステータス決定 21 hp = (int)(stats.BaseHP * job.HPCoefficient); 22 sp = (int)(stats.BaseSP * job.SPCoefficient); 23 } 24 25 // ジョブを変更した時 26 public void SetJob(Job newJob) 27 { 28 job = newJob; 29 Initialize(); 30 } 31 32 public void UseSkill(int skillIndex) 33 { 34 // SPが足りていたら 35 if (skills[skillIndex].IsUsable(hp)) 36 { 37 // スキルを使う 38 skills[skillIndex].UseSkill(); 39 // SPを減らす 40 sp-= skills[skillIndex].SPCost; 41 } 42 } 43}

あとはこいつを使うだけですね。
とりあえずStartで回してみます。

C#

1 private void Start() 2 { 3 Initialize(); 4 for (int i = 0; i < skills.Length; i++) 5 { 6 Debug.Log("◆スキル" + i); 7 UseSkill(i); 8 } 9 }

イメージ説明
イメージ説明

キャラクターに対して、スキル、キャラ設定、ジョブがそれぞれ別のScriptable Objectになっているので、既存コードに手を入れずに簡単に新要素の追加を行うことができます。
自分の場合はひとまずこんな感じです。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#5

この回答は、運営により削除されました。

#6

hogehoge23

総合スコア14

投稿2023/02/17 11:02

個別回答やベストアンサー的な機能がない?ようでしたので、回答経由にて失礼いたします。

#1
ご意見ありがとうございます。
抽象クラス以外の良いアプローチがあれば例を知りたかったので投稿させていただいた次第でした。
投稿内容が分かりづらいものとなっていたようで、申し訳ありません。

#2
ご意見ありがとうございます。
魔法が5種類あるとしたらAさんはどのレベルでどれを覚えるor覚えないかを個別に設定するようなイメージでしょうか。たしかにそれであればコントロールが効く部分もありそうに思います。

#3 /#4
まとめてのご返信となり恐れ入ります。
非常にわかりやすい考え方を含め、回答をありがとうございます。
たしかに抽象クラスで作っていくと「似ているけどちょっと違う」みたいなものを作るときに頭をひねることになりそうですね・・・。

掲載いただいたサンプルも非常にわかりやすく、手元で写経のように組んでみたところなんとなくですが全体像が掴めたような気がします。これを拡張していく方向で考えを膨らませていきたいと思います。

#5
Thanks for replay.
I wasn't sure about the reference examples, but I'll play it.

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#7

fana

総合スコア11673

投稿2023/02/18 02:15

投稿内容が分かりづらいものとなっていたようで、申し訳ありません。

別にそういうことを指摘しているみたいなことではないです.

…を明確にされると良いのではないでしょうか.

という言葉通りに受け取ってもらえれば.
要は,最も単純な方法で特に何も問題が無いならばそれでいいわけで,逆に言えば「どうすればいいのか?」と考えている際には,単純な方法だと やれない/面倒になる/etc な事柄が存在しているハズ.
そういった点がぼんやりしているのであれば,まずそれを明らかにしないと,「抽象クラス」が良い設計なのかどうかを判断することはできないでしょう.

例えば,他の方が「魔法戦士」という例の話をされていますが,もしもあなたが作る物には「戦士と魔法使いしか出てこない(魔法戦士は作らない)」のであれば,「この方法だと魔法戦士を作ろうとしたら苦労するよね」というその設計の欠点は,今回は度外視しても良いのかもしれないわけで.

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

最新の回答から1ヶ月経過したため この意見交換はクローズされました

意見をやりとりしたい話題がある場合は質問してみましょう!

質問する

関連した質問