現場レベルのゲーム制作が、すべてここで学べます。
この記事は3Dダンジョン探索型RPGの作り方講座の第15回目です。
前回は、武器/防具/消費アイテムの3種類を実装し、部位別装備(右手/頭/胴/脚/左手)を実現しました。
装備による能力値変動や職業別に装備できる武器の制限、属性耐性システムも構築しました。
前回の記事:

今回も引き続きアイテム周りの処理を追加していきます。
まずは装備品の効果を確認できるようにダメージ計算式を拡張を行い、ショップ画面の実装によって武器、防具、消耗アイテムなどを売買できるようにします。
さらに、武器や防具はダンジョンで手に入れた素材を提供することでお店に並ぶようにしていきます。近年のRPGではおなじみの素材システムですね。
素材を収集して新しい武器や防具を購入してキャラクターを強化して冒険を進めていく。そんな王道のRPGの機能実装をここでマスターしていきましょう。
まずはダメージ計算式に装備品の効果を組み込むところから始めます。
戦闘ダメージ計算式の変更 面白いバランスになる式を考えてみよう
攻撃スキルの効果処理(DamageEffectクラス)を編集し、キャラクターの攻撃力や防御力を基にしたダメージ計算を行うよう式を設計します。
アクターの武器や防具による攻撃力・防御力の増減もここで反映します。
また、併せて状態異常によるダメージ量への影響もここで組み込んでしまいます。
SkillEffect.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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; /// <summary> /// スキル効果の基底クラス /// </summary> [Serializable] public class SkillEffect { /// <summary> /// スキル使用時の対象への効果 /// </summary> /// <param name="battleManager">バトルマネージャークラスへの参照(Battleシーン以外から呼出ならnull)</param> /// <param name="casterData">スキル使用者のCharacterData</param> /// <param name="targetData">スキル使用対象のCharacterData</param> /// <param name="effectValue">スキル効果量倍率</param> public virtual void ApplyEffectToTarget (BattleManager battleManager, CharacterData casterData, CharacterData targetData, float effectValue) { } } /// <summary> /// スキル効果:ダメージ /// </summary> [Serializable] public class DamageEffect : SkillEffect { [Header ("攻撃スキル設定")] public DamageType damageType; // 攻撃の種類 public float damagePower; // ダメージ倍率 public ElementType elementType; // 属性 public bool isRanged; // 遠距離フラグ // 攻撃の種類定義 public enum DamageType { Physical, // 物理ダメージ Magical, // 魔法ダメージ Physical_True, // 物理貫通ダメージ Magical_True, // 物理貫通ダメージ Fixed, // 固定ダメージ } /// <summary> /// スキル使用時の効果(戦闘スキル) /// </summary> public override void ApplyEffectToTarget (BattleManager battleManager, CharacterData casterData, CharacterData targetData, float effectValue) { float skillPower = damagePower * effectValue; // スキル効果量 float attackPoint = 0.0f; // 計算時caster攻撃力 float defensePoint = 0.0f; // 計算時target防御力 int damageValue = 0; // ダメージ量 // ダメージ計算に使用するパラメータ取得 switch (damageType) { case DamageType.Physical: // 物理ダメージ attackPoint = casterData.GetCalculatedAtk (); defensePoint = targetData.GetCalculatedDef (); break; case DamageType.Magical: // 魔法ダメージ attackPoint = casterData.GetCalculatedMAtk (); defensePoint = targetData.GetCalculatedMDef (); break; case DamageType.Physical_True: // 物理貫通ダメージ attackPoint = casterData.GetCalculatedAtk (); break; case DamageType.Magical_True: // 物理貫通ダメージ attackPoint = casterData.GetCalculatedMAtk (); break; } // (アクター攻撃時)武器攻撃力適用 if (casterData is ActorData casterActorData && casterActorData.equipItem_Weapon != null) { attackPoint += ((WeaponItemData)casterActorData.equipItem_Weapon).weaponPower; } // (アクター防御時)防具防御力適用 if (targetData is ActorData targetActorData) { defensePoint += targetActorData.GetArmorParamators ().armorPower; } // スキル効果倍率適用 attackPoint *= skillPower; // ダメージ計算 damageValue = (int)((attackPoint * attackPoint) / (attackPoint + defensePoint)); // 属性耐性値をダメージに適用 damageValue = (int)(damageValue * targetData.GetElementDamage (elementType)); // 乱数 damageValue = (int)(damageValue * UnityEngine.Random.Range (0.85f, 1.15f)); // 固定ダメージ処理 if (damageType == DamageType.Fixed) damageValue = (int)skillPower; // 状態異常「標的」処理(ダメージ量増加) if (targetData.currentStates.ContainsKey (CharacterState.Mark)) { damageValue = (int)(damageValue * 1.3f); } // 状態異常「ガード」処理(ダメージ量減少) if (targetData.currentStates.ContainsKey (CharacterState.Guard)) { damageValue = (int)(damageValue * 0.7f); } // 状態異常「魔法壁」処理(魔法ダメージ半減・「魔法壁」消失) if (targetData.currentStates.ContainsKey (CharacterState.MagicWall) && damageType == DamageType.Magical) { damageValue = (int)(damageValue * 0.5f); targetData.RemoveState (CharacterState.MagicWall); } // ダメージ量の最低値 if (damageValue <= 0) damageValue = 1; // ダメージ量計算結果をログ出力 Debug.Log ("(スキルダメージ) Atk:" + (attackPoint) + " Def:" + (defensePoint) + " Dmg:" + damageValue); // ダメージを対象に適用 if (battleManager != null) { battleManager.ChangeCharacterHP (targetData, -damageValue); // ダメージポップアップ表示 battleManager.SpawnDamagePopupByData (targetData, damageValue, DamagePopupSpawner.DamagePopupType.HP_Damage); } } } |
根幹となるダメージ計算式については以下のようにしました。この部分を変更することでより異なったゲームバランスにすることが可能です。
|
1 |
(攻撃キャラクターの攻撃力)^2 / (攻撃キャラクターの攻撃力 + 防御キャラクターの防御力) |
攻撃力については、この攻撃スキルが物理属性であれば物理攻撃力から、魔法属性であれば魔法攻撃力から取得しています。
防御力についても同様、物理か魔法かで区別しています。
ちなみに貫通ダメージ設定にすれば防御力が無視されます。
そして攻撃力には追加でスキルに設定されたダメージ量が乗算されます。ここの数値はスキルレベル(から取得できる効果量倍率)によって増減します。
計算されたダメージ量は乱数によって0.85倍~1.15倍に変化します。
これで通常攻撃を行った時にダメージ計算が行われるようになりました。
まずは装備なしでテストプレイし、

次に装備ありでテストプレイすればより変化を確認できます。

ダンジョンRPGのショップシステムの構築 アイテム購入/売却/合成システムを実装
このゲームにおけるアイテム入手手段の1つとして、お金(Gold)を消費して商品としてアイテムを獲得できるショップを作っていきます。
ショップ画面は以下の機能を持ちます。
- 表示モードを「武器を購入」「防具を購入」「消費アイテム(消耗品)を購入」「所持品の売却」の4種類用意し、それぞれボタンで切り替え可能
- 購入モード:
- 商品リストから商品UIを選択し、購入ボタンを押下でGoldと引き換えにそのアイテムを獲得
- 武器購入モードでは画面下部に編成中のアクターUIを前衛・後衛でそれぞれ表示し、選択中の武器が装備できない場合は半透明で表示
- 商品となるアイテムには合成素材を設定可能。そのアイテムは要合成状態になり、合成素材となるアイテムを全て消費しなければ購入できない。(一度購入できるようにすれば以降は永続的に購入可能)
- 合成素材が複数個設定されている場合、その内の1個でも所持しているなら商品リストの中に商品が表示されて確認が可能
- 売却モード:
- 商品リストには商品の代わりに所持アイテム一覧が表示される
- アイテムを選択し売却ボタン押下でアイテムと引き換えにGoldを獲得
これらを踏まえ、まずは街シーンにショップ画面UIを追加しましょう。
ショップ画面UI作成
FacilityUI_EquipをコピーしてFacilityUI_Shopを作成する
まずは施設UIとしてFacilityUI_Shopオブジェクトを用意します。
商品UIリストは使いまわすため、FacilityUI_Equipオブジェクトをコピーして作成すると楽です。


コピー元のUIはItemsScrollView以外は削除し、Windowの子オブジェクトとしてはWindowLogoText、CloseButton、ItemsScrollViewにします。
ItemsScrollViewは左下辺りに縦に大きく表示させます。

ショップの選択肢カテゴリボタンUI ー武器屋/防具屋/アイテム屋/売却ーを設定する
購入・売却モード等の切り替えはカテゴリボタンと呼称するボタンUIによって行います。
画面上部の空いた所にボタンUIを4つ横並びで配置しましょう。配置するボタン自体はTownButtonプレハブからの複製でOKです。
まずはWindowの子にCategoryButtonsは空のオブジェクトで作成します。RectTransformの位置を調整してHorizontal Layout Groupをコンポーネントで追加します。

次にボタンを設置していきます。CategoryButtonsの子オブジェクトとして4つのボタンオブジェクトを作成します。TownButtonプレハブからボタンを作り位置とTextMeshProの内容を調整します。


TextMeshProの内容を調整して武器屋ボタンができました。
同様の調整を3つのボタンについても行います。まずはCategoryButton_Weaponsを複製して名称を変更します。

TextMeshProの内容をそれぞれのボタンについて変更していきます。



全体としては以下のようになります。

選択アイテム説明テキストUI
次に、商品UIをクリックして選択したときにその商品アイテムの名前と説明文を表示するUI群を作成します。
必要なのはアイテムアイコン画像、アイテム名テキスト、Goldのアイコン画像、費用Goldテキスト、アイテム説明文テキストの5つです。画像は全てImageオブジェクトで作成し、テキストはTextMeshProオブジェクトで作成します。
これらをSelectItemDetailUIsオブジェクトの子オブジェクトとして作成します。
また、テキストオブジェクトではスクリプトから表示内容を変更するのでSimple Translation Textは不要です。
まずはWindowの子オブジェクトとして空のオブジェクトを作成し、名称をSelectItemDetailUIsとします。
SelectItemDetailUIs
空のオブジェクトで作成します。

次に、5つのオブジェクトをSelectItemDetailUIsの子オブジェクトとして作成していきます。
SelectItemIcon
Imageオブジェクトで作成します。
![]()
SelectItemNameText
TextMeshProオブジェクトで作成します。

SelectItemDescriptionText
TextMeshProオブジェクトで作成します。

CostGoldIcon
Imageオブジェクトで作成します。

CostGoldText
TextMeshProオブジェクトで作成します。
![]()
上記画像を参考にしながら5つのオブジェクトを作成できればOKです。
合成素材アイテム表示UI
商品となるアイテムのScriptableObjectに合成素材が設定されている場合、その合成素材アイテムをアイコン画像・名前テキストでそれぞれ表示する必要が出てきます。
ここでは合成素材アイテムを表示するためのUIを作成します。
合成素材は1アイテムにつき最大で4種まで設定できるようにします。なのでアイコン画像と名前テキストのセットを4個作成すると良いでしょう。
親オブジェクトとなる背景画像オブジェクトにはCanvas Groupをアタッチしておきます。
CraftingRequirementsArea
まずはImageオブジェクトで親オブジェクトとなる背景オブジェクトを作成します。Windowの子オブジェクトとして作成しCraftingRequirementsAreaとします。

CanvasGroupのInteractibleとBlocksRayCastsはオフにします。
SelectItemNameText
CraftingRequirementsAreaの子オブジェクトとしてTextMeshProで作成します。

SimpleTranslationText.csもアタッチして言語変化に対応させておきます。
CR_ItemIcon
次に素材アイテムを示すアイコンを作成します。CraftingRequirementsAreaの子オブジェクトとしてImageオブジェクトで作成します。
![]()
CR_ItemNameText
次にCR_ItemIconの子オブジェクトとしてTextMeshProオブジェクトを作成します。
これは素材アイテム名を示すためのTextオブジェクトです。

このTextオブジェクトはスクリプトから表示内容を設定するのでSimpleTranslation.csは不要です。
ここまでできたらCR_ItemIconをプレハブ化します。
CR_ItemIconをプレハブ化して4つに複製する
CR_ItemIconをプレハブ化してPrefabs > Townに保存。その後ヒエラルキーで4つに複製し、RectTransformの位置を調整します。
![]()
![]()
![]()
4つの合成素材を表示するUIができました。
前衛・後衛アクターミニUI表示
商品選択で武器アイテムを選んでいるとき、その武器が装備可能かを表示するためにActorMiniUIを並べて配置します。
操作手順としては既に作ってあるFacilityUI_Formation > Windowの子オブジェクトのArea_VanguardオブジェクトとArea_RearguardオブジェクトをコピーしてFacilityUI_Shop > Windowの子オブジェクトにペーストしてきます。
ここではパーティーメンバーのオブジェクトを移動させる必要はなく、当たり判定は不要なのでBox Collider 2Dは削除しておきます。
その後プレハブからActorMiniUIを3つずつArea_VanguardとArea_Rearguardの子オブジェクトに設定します。

(↑最後にArea_VanguardとArea_RearguardをWindowの子オブジェクトに入れておいてください。よく見るとこの動画段階ではFacilityUI_Shopの子オブジェクトになってることがわかります。ありがちなミスなので注意です)
前衛・後衛ごとにActorMiniUIは3つ配置しますが、編成画面で使ったときよりこのUIは小さく表示したいため親オブジェクトのScaleごと小さくしておきます。RectTransformの位置も微調整しておきます。その結果が以下の図です。


商品購入ボタンと素材合成ボタンを作成
画面下部に「商品を購入するボタン(Button_Buying)」と「商品の合成素材を支払って合成するボタン(Button_Crafting)」の2つを配置します。
TownButtonプレハブからの複製で問題ありません。
なお購入ボタン側のTextはスクリプトから編集するためSimple Translation Textのアタッチは不要です。(既にある場合はRemoveしておきます。)
設置後のオブジェクトの設定は以下のようになります。




↑Button_Craftingの方はSimple Translationを残しておきましょう。
所持Gold表示UIを作成
Goldを扱う画面なので、画面内に常に所持Goldを表示するようにしておくと親切です。
画面右上端にGoldを示すアイコンと、所持量を表すテキストUIをそれぞれ作成します。
まずはGoldsIconをWindowオブジェクトの子オブジェクトとしてImageで作成。
![]()
続いてGoldsTextオブジェクトをWindowオブジェクトの子オブジェクトとしてTextMeshProで作成します。

ここまででショップのUIオブジェクト作成は完了です。お疲れ様でした。ここからは実際にC#スクリプトで機能実装をしていきます。
ショップ機能を実現するUnity C#スクリプトを作成
それではショップ画面を動作させるためのスクリプトを作成していきます。
ItemDataクラスも編集して合成素材を設定できるようにもします。
まとめ

今回はアイテムショップと素材システムを完成させました。
ここまででRPGでよく作りたくなるアイテム周りのシステムの数多くを実装できたかと思います。
次はやりこみ要素やゲームの中毒性を高める図鑑機能を作っていきます。
ゲーム内に登場するアイテムやモンスター図鑑をコンプリートしたくなる。次回はそんなコレクター要素のあるゲームを作るスキルを習得します。
次の記事:

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






コメント