現場レベルのゲーム制作が、すべてここで学べます。
この記事は3DダンジョンRPGの作り方講座の第13回になります。
前回まででキャラクターの作成からダンジョンの探索、戦闘までの流れを一通り実装しました。
前回の記事:

今回は街シーンで職業ごとに決められたリストの中からアクターがスキルを選んで習得できるシステムを実装します。
世界樹の迷宮などでも採用されているスキルツリー形式にすることで、〇〇を習得済みでないと次のスキルを習得できない、といった前提スキルの作成もできます。
本格的なスキルツリー型の成長システムやスキルツリーゲームの作り方を解説してるサイトは日本初だと思います。ぜひ楽しんでいってください!
Unityでスキルツリーシステムを実装する
本作のスキルシステムの仕様について
このゲームのスキル・スキル習得の大まかな仕様は以下のようになっています。
- 職業ごとに固有のスキルツリーが存在している
(JobDataクラスにスキルツリーの情報を作成できる) - 街シーンでスキルツリーのウィンドウを開くことができ、スキルポイントを割り振ってスキルの習得が可能
- 習得済みのスキルに追加でスキルポイントを割り振るとスキルのレベルアップが可能(最大レベルは個別に設定)
- キャラクターは初期状態でスキルポイントを一定数所持しており、
さらにレベルアップの度に1ポイント獲得する - 各スキルは前提スキルを1つまで設定可能
前提スキルのスキルレベルが指定値に達していないとそのスキルは習得不可
これらの内容をもとに、まずはスクリプトを編集してスキルツリーをJobData内に設定できるようにします。
スキルツリーとスキルノードをUnity C#で作成する
スキルツリー上の各スキルの情報はSkillNodeとして作成します。このクラスをもとに、職業データのScriptableObjectでInspectorからスキルツリー内の情報を設定できるようにしていきます。
ActorData.cs
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
using System.Collections.Generic; using UnityEngine; /// <summary> /// アクターデータクラス(CharacterDataから派生) /// </summary> public class ActorData : CharacterData { // 職業データ public int jobID; // 立ち絵ID public int pictureID; // 習得済みスキル・スキルレベル public Dictionary<SkillData, int> ownedSkillLevels; // 残りスキルポイント public int remainSkillPoint; // 現在レベル public int nowLevel; // 現在経験値 public int nowEXP; // 最大経験値 public int maxEXP; // 内部処理用 [HideInInspector] public int displayNowExp; // 表示用現在経験値 [HideInInspector] public int displayMaxExp; // 表示用最大経験値 /// <summary> /// 職業を取得する /// </summary> public JobData GetNowJob () { return Data.instance.jobDatas[jobID]; } /// <summary> /// 経験値を加算する /// </summary> public void GainExp (int value) { // 経験値加算 nowEXP += value; displayNowExp += value; // 現在経験値が最大値以上ならレベルアップ処理 if (nowEXP >= maxEXP) CheckLevelUp (); } /// <summary> /// レベルを1つ上昇させる /// </summary> public void CheckLevelUp () { // 表示に用いる現在経験値量をもとの値に戻す displayNowExp -= displayMaxExp; // レベル増加 nowLevel++; // 次の最大経験値を適用 maxEXP = GetMaxExp (nowLevel); displayMaxExp = maxEXP - GetMaxExp (nowLevel - 1); // スキルポイント取得 remainSkillPoint++; // ステータス上昇 baseMaxHP += GetNowJob ().st_HP; baseMaxTP += GetNowJob ().st_TP; baseAtk += GetNowJob ().st_Atk; baseDef += GetNowJob ().st_Def; baseMAtk += GetNowJob ().st_MAtk; baseMDef += GetNowJob ().st_MDef; baseLuc += GetNowJob ().st_Luc; } /// <summary> /// 指定レベルにおける最大Exp(次のレベルまでに必要なExp)を返す /// </summary> public int GetMaxExp (int targetLevel) { // 必要経験値の基礎量 int baseNeedExp = targetLevel * 100; // 高いレベルほど追加の経験値を必要とする計算 int additionalNeedExp = (targetLevel - 1) * (targetLevel - 1) * 10; return baseNeedExp + additionalNeedExp; } } |
キャラクターが習得したスキルの情報を管理できるようにしました。
Dictionaryを用いることでスキルデータとスキルレベルを紐づけて同時に扱えます。
また、残りスキルポイントの情報も管理してレベルアップ時には1ポイント追加されるようにします。
SkillTreeNode.cs (新規)
Scripts > Definesフォルダに新しいC#スクリプトを作成します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
using UnityEngine; using System; using System.Collections.Generic; /// <summary> /// スキルツリーノード定義クラス /// </summary> [Serializable] public class SkillTreeNode { [Header ("スキルデータ")] public SkillData skillData; [Space (10)] [Header ("前提スキルデータ")] public SkillData requireSkill; [Header ("前提スキルの要求レベル")] public int requireSkillLevel = 1; /// <summary> /// スキルを習得可能か返す /// </summary> public bool CanLearnSkill (ActorData actorData) { // スキルポイント不十分なら不可 if (actorData.remainSkillPoint <= 0) return false; // 既に最大レベルなら不可 if (actorData.ownedSkillLevels.ContainsKey (skillData)) { int ownedTargetSkillLevel = actorData.ownedSkillLevels[skillData]; if (ownedTargetSkillLevel + 1 >= skillData.GetMaxLevel ()) return false; } return true; } /// <summary> /// スキルを習得する /// </summary> public bool LearnSkill (ActorData actorData) { if (CanLearnSkill (actorData)) {// スキル習得可能 // スキル習得処理 // (既に習得済みであるスキルならレベルアップ処理) if (!actorData.ownedSkillLevels.ContainsKey (skillData)) {// 新規習得 actorData.ownedSkillLevels.Add (skillData, 0); } else {// スキルレベルアップ actorData.ownedSkillLevels[skillData]++; } // 残りスキルポイント減少 actorData.remainSkillPoint--; return true; } // 習得できなかった場合falseを返す return false; } } |
まだこの段階では何をしているかわかりづらいかもしれないですが、スキルツリーシステムで並んでいるスキルノードの定義をしています。
スキルをレベル制で習得・強化できるか判定し、実際に習得処理を行う最小単位といったイメージです。
今はまだ途中段階です。「スキルポイントを消費してスキルを取得するロジック」の中核部分だけが書かれています。
JobDataのScriptableObjectのInspector内で編集することを想定した設計です。
スキルデータのScriptableObjectを各パラメータにドラッグ&ドロップで指定可能できるようにしていきます。
ちなみにMonoBehaviourは継承しません。(継承するとInspectorからのパラメータの編集が出来なくなります。)
Data.cs内 CreateCharacterDataメソッド
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
/// <summary> /// アクターデータを新規作成する /// </summary> public ActorData CreateCharacterData (int jobID, int pictureID) { // ActorDataインスタンス作成 ActorData actorData = ScriptableObject.CreateInstance<ActorData> (); // 職業データ取得 JobData jobData = jobDatas[jobID]; // 立ち絵画像ID設定 actorData.pictureID = pictureID; // 基礎ステータス設定 actorData.baseMaxHP = jobData.st_HP * Character_BaseStatus_Multi; actorData.currentHP = jobData.st_HP * Character_BaseStatus_Multi; actorData.baseMaxTP = jobData.st_TP * Character_BaseStatus_Multi; actorData.currentTP = jobData.st_TP * Character_BaseStatus_Multi; actorData.baseAtk = jobData.st_Atk * Character_BaseStatus_Multi; actorData.baseDef = jobData.st_Def * Character_BaseStatus_Multi; actorData.baseMAtk = jobData.st_MAtk * Character_BaseStatus_Multi; actorData.baseMDef = jobData.st_MDef * Character_BaseStatus_Multi; actorData.baseLuc = jobData.st_Luc * Character_BaseStatus_Multi; actorData.baseSpeed = jobData.st_Speed; // 状態異常リスト初期化 actorData.currentStates = new Dictionary<CharacterState, int> (); // 耐性値設定 actorData.resist_Fire = 0.0f; actorData.resist_Ice = 0.0f; actorData.resist_Light = 0.0f; actorData.resist_Dark = 0.0f; // スキル関連 actorData.remainSkillPoint = 3; // 初期スキルポイント actorData.ownedSkillLevels = new Dictionary<SkillData, int> (); // その他パラメータ設定 actorData.nowLevel = 1; actorData.nowEXP = 0; actorData.maxEXP = actorData.GetMaxExp (actorData.nowLevel); actorData.displayNowExp = actorData.nowEXP; actorData.displayMaxExp = actorData.maxEXP; return actorData; } |
キャラクターの初期化時に習得済みスキルリストのインスタンスを作成し、初期獲得スキルポイントをセットしています。
JobData.cs
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using UnityEngine; using System; using System.Collections.Generic; /// <summary> /// 職業データを定義するScriptableObject /// </summary> [CreateAssetMenu (fileName = "JobData", menuName = "Dungeon/JobDataSO")] public class JobData : ScriptableObject { [Header ("職業基本情報")] public string jobName_JP; // 職業名(JP) public string jobName_EN; // 職業名(EN) [Multiline (2)] public string description_JP; // 職業説明(JP) [Multiline (2)] public string description_EN; // 職業説明(EN) [Header ("初期ステータス")] public int st_HP; // 最大HP public int st_TP; // 最大TP public int st_Atk; // 物理攻撃力 public int st_Def; // 物理防御力 public int st_MAtk; // 魔法攻撃力 public int st_MDef; // 魔法防御力 public int st_Luc; // 運 public int st_Speed;// 速度 [Header ("スキルツリー")] public List<SkillTreeNode> skillTree = new List<SkillTreeNode> (); /// <summary> /// 職業名を取得する /// </summary> public string GetJobName () { // 設定言語が日本語なら日本語の職業名、英語なら英語の職業名を返す return SimpleTranslationText.GetTranslatedString (jobName_JP, jobName_EN); } /// <summary> /// 職業説明を取得する /// </summary> public string GetJobDescription () { // 設定言語が日本語なら日本語の職業説明、英語なら英語の職業説明を返す return SimpleTranslationText.GetTranslatedString (description_JP, description_EN); } } |
職業データがスキルツリー用データをリストで設定・管理できるようにすれば準備完了です。
Inspectorから各スクリプトを設定
アクタースキル作成
まずは適当なアクター用スキルを複数作成します(3~5つ程度でOK)
現在はダメージ効果以外は未実装ですが、あとでスキルツリーの確認が出来れば良いので気にせず適当に用意しましょう。

↑攻撃スキル「スマッシュ」を作成します。スキル発動時効果はAdd Damage Effectを選択してから数値を入力します。



まだ攻撃スキルの効果実装しかしていないのでひとまずここでは4つの攻撃スキルを作成しました。
スキルツリーのデータ設定の仕様
スキルツリーを設定するJobDataを1つ選択してInspectorを確認します。
下部にSkillTreeパラメータが追加されているので割り当てたいスキルの数だけ要素を追加しましょう。
各要素(ノードと呼称)の1個目のパラメータ[Skill Data]にはそのノードが対応するスキルデータのScriptableObjectを指定します。
[Require Skill]には前提スキルのスキルデータを同様に指定します。前提スキルが無いなら空欄(null)のままにしておきます。
前提スキルがスキルツリーに存在しない場合や、2つのスキルが相互に前提スキルとして指定し合っている場合は後々エラーが出るので注意してください。
また、「基礎スキル→派生スキル→更に派生のスキル」という三階層まで前提スキルは指定可能です。
言い換えるなら「前提の前提スキル・前提スキル・スキル」という構造は可能です。
[Require Skill Level]は前提スキルが指定されている場合、その前提スキルに必要なスキルレベルの数値を入力します。1以下ならスキルレベル不問で習得しているだけで条件クリアです。3と入力したならスキルレベルを3まで上げていないとこのスキルは習得できません。
実際にスキルツリーデータを構築してみる
それでは試しにスキルツリーを作ってみましょう。今回はウォーリアのジョブにスキルツリーを構築していきます。JobDataのScriptable Objectでスキルツリーを設定していきます。


↑このように設定してみました。スマッシュは無条件で習得可能。タイタンブローはスマッシュを習得していれば習得可能。アースシャッターはタイタンブローを習得していれば習得可能。そしてリコイルストライクはスマッシュのレベルが3以上であれば習得可能です。
スキルツリー画面の作成
それではここからは実際のスキルツリー画面の作成を行っていきましょう。
スキルツリー画面(スキルの習得とレベルアップを行う画面)は街シーンにおける施設UIの1つとして作成します。
他の施設UIと同様にFacilityUIプレハブからインスタンスを作成します。オブジェクト名はFacilityUI_SkillTreeとします。
ウィンドウサイズは大きく確保しておくと良いでしょう。

WindowオブジェクトでWidthとHeightを調整し、WindowLogoTextの内容をスキル習得に変更しました。
スキルツリーシステムウィンドウのUIパーツ作成
”スキルツリー内のノードはそれぞれUIで表示し、クリック(タップ)で選択することでそのスキルの習得・レベルアップを行える”という設計方針で進めていきます。
そしてツリー上のノードUIは前提スキルと派生スキルを線でつないで表示したいので、線も画像UIとして作成していきます。
アクターリスト表示ScrollView_ActorsScrollView
作成済みの全キャラクターを表示するScrollViewオブジェクトです。
これは編成画面(FacilityUI_Formation)の待機中アクターリストと同一のUI仕様でよいため、WaitingScrollViewオブジェクトを全てコピーして位置調整・名前変更でOKです。
ただし子オブジェクトにある、BoxCollider2Dによる当たり判定をとっているArea_Standbyオブジェクトは削除してOKです。

各種テキストUIオブジェクトを作成する
必要になるのは以下の5種類です。画面中央にはスキルツリーを大きく表示するのでその部分は空け、上下に小さく表示します。
いずれも表示内容はスクリプトから設定するのでSimpleTransitionTextなどは不要です。全てWindowの子オブジェクトとしてTextMeshProで作成していきましょう。
職業名表示テキスト_JobNameText
まとめ

これでスキルツリーの画面とシステムの実装が完了しました。動作確認も兼ねて様々な職業スキルを作成してみても良いでしょう。
今の状態から優先してスキル効果の実装などを行いたい方もいるかもしれませんが、ダメージ計算などの際にはこのあと実装する装備品によって式が影響されるので、先にアイテム関連処理の実装に移りたいと思います。
次の記事:

現場レベルのゲーム制作が、すべてここで学べます。






コメント