今回はUnityでハクスラローグライク×デッキ構築型カードバトルRPG「呪術迷宮」を作る講座の第18回です。
前回は経験値を消費してプレイヤーのパラメータを強化するシステムと転職・ジョブチェンジシステムを実装しました。
前回の記事:
今回はお金(金貨)を消費してカードを購入できるカードショップ「術札屋」のシステムを作っていきましょう。
カードゲームにはカードショップシステム!ローグライクカードバトルに限らずあらゆるカードゲームの実装に使える実装スキルになります。
また、この章ではゲームの演出を向上させるパーティクルの機能についても触れていきます。
Unityでカードショップシステムを実装しよう
Titleシーンを編集し、術札屋ボタンをタップした時に開かれる術札屋ウィンドウを作成していきます。
術札屋は金貨を消費することでカードパックを購入できます。
パック内に含まれているカードをランダムに複数枚獲得できる機能を作ります。
訓練場ウィンドウで用意したスクロールビューを使用し、まずはカードのパックが複数表示されるところまで作っていきましょう。
術札屋ウィンドウオブジェクト
大部分が訓練場ウィンドウと同じなので、まずTrainingWindowオブジェクトをコピー&ペーストしてShoppingWindowオブジェクトに改名します。
ShoppingWindowオブジェクトにアタッチされているTrainingWindowコンポーネントは削除しておきます。
その後、子オブジェクトUIの表示内容に対して以下の変更を行います。
LogoText | (日本語)術札屋 (英語)Card shop |
StatusLogo | Simple Translation Textをアタッチ (日本語)金貨 (英語)Gold |
StatusIcon | アイコン画像を金貨に変更(lists_141.png) |
商品項目プレハブの作成
スクロールビュー上に表示する項目についても訓練場で使った物とほぼ同等です。
まずはShoppingWindow/ItemRowsSV/Viewport/Contentオブジェクト以下にTrainingItemRowプレハブをドラッグ&ドロップします。
これを編集して商品項目オブジェクトに変えていきますが、まだこのオブジェクトはTrainingItemRowプレハブのインスタンスのままであるので先に紐づけを解除しておきましょう。
Hierarchyビューから青色になっているオブジェクトを右クリック→Prefab→Unpackを選択すると、そのオブジェクトはプレハブとの紐づけが解除され独立したゲームオブジェクトへと戻ります。
アタッチされているTrainingItemコンポーネントは削除しておきます。
それでは商品項目の表示・操作に対応できるよう変更を加えていきます。
オブジェクト名をShoppingItemRowに変更し、子オブジェクトに以下の変更を加えていきます。
ItemIcon | カードパックアイコン(Textures/PackIcons以下)画像の大きさに変更 Width:45 Height:72 |
ItemNameText | 商品名・商品説明テキストが縦に並ぶようなレイアウトに変更します。 PosX:-140 PosY:16 Width:180 Height:40 左寄せに変更 |
ItemExplainText | PosX:-30 PosY:-20 Width:400 Height:32 左寄せに変更 |
(ObtainButton以下) PriceIcon |
アイコン画像を金貨に変更(lists_141.png) |
(ObtainButton以下) ObtainLogo |
(日本語)購 入 (英語)Buy |
商品項目スクリプト仮作成&アタッチ&プレハブ化
そのまま前回と同様の流れで商品項目オブジェクトにアタッチするスクリプト、ShoppingItemを仮作成しておきます。
こちらもShoppingWindowオブジェクトに対してopenPackEffectへの参照をセットしておきます。
これでパック購入時に獲得したカードが何かがわかるようになりました。
カードパック購入時の演出を強化 Unityパーティクルの実装
現在の仕様ではパックを購入した時にただカードが表示されるだけですが、このタイミングで光の粒が飛び散るような演出を加えて見栄えを強化したいと考えます。
こんな時に便利なのがUnityに標準で備え付けられているParticle Systemの機能です。小さな粒の集まりをパーティクルとして生成・制御し、パラメータを変えることで目当ての表現を実現できます。
作成したパーティクルをスクリプトから任意のタイミングで再生したり、ループさせて画面上で常に再生し続けることもできます。
それではカードパック開封時のパーティクルを作成してみましょう。
ShoppingWindowオブジェクト以下に新規ParticleSystemオブジェクトを作成します(右クリック→Effects→Particle System)。名前はOpenPackParticleとしました。
Inspectorを確認してわかるように、Particle Systemコンポーネントがパーティクルの設定を管理しています。Duration~Ring Buffer Moderパラメータ間が基本的な設定を行う領域であり、その下にいくつもの詳細設定タブが配置されています。
タブは名前をクリックすると開閉できます。名前欄の左側にチェックボックスがあり、そこをオンにするとタブ内の設定が全て有効状態となり編集も可能になります。
Main Module (基本設定)
まずは基本設定から確認していきましょう。あまり重要でない設定は割愛します。
Duration | パーティクル実行時間(秒) |
Looping | ループ設定(ONでループする) |
Start Lifetime | パーティクル内の各粒子の生存時間(秒) 入力欄右側のメニューから[Random Between Two Constants]を指定する事で、乱数の幅を指定してのランダム数値にする事も出来る。(他の数値入力系の設定でも同様) |
Start Speed | 各粒子の初期速度 |
Start Size | 各粒子の初期サイズ(1が基準) |
Start Rotation | 各粒子の初期角度(移動する方向ではない。0~360で指定) |
Start Color | 各粒子の初期表示色 |
Play on Awake | ONにするとシーン開始時またはオブジェクト生成時に自動的にパーティクルを再生開始する |
Stop Action | パーティクル再生完了時に行う処理。Destroyを指定するとそのパーティクルのオブジェクトごと消去する。 |
その他Module
他の詳細設定タブについても重要なもののみ一部触れていきます。
Emission
粒子の発生条件について設定します。
Rate over Time | 毎秒の粒子生成量 (0で時間経過による生成を行わない) |
Bursts | 特定のタイミングで一気に複数の粒子を生成する設定。 Time:生成タイミング(再生開始からの秒) Count:生成量 |
Shape
粒子を発生させる範囲や粒子の進む方向などを設定します。
Shapeパラメータにて発生範囲の形状の種類を指定します。この設定によってその他の設定すべきパラメータが変化します。
例えばCircle(円形)を指定すると粒子は円の範囲内で生成されるようになり、その範囲をRadiusパラメータにて数値指定する事になります。
Color over Lifetime
粒子の表示色を寿命までの時間によって変化させます。透明度(alpha)も変化可能です。
Colorパラメータ右側の矩形をクリックすると編集できます。左側のツマミ(キー)を編集すると再生開始時の色を、右側のツマミでは終了時の色を変更可能です。
Renderer
パーティクル全体の表示設定を行います。
Materialパラメータからパーティクルに使用するマテリアル(素材データ)を変更できます(今回は触りません)。
Order in LayerパラメータからはUI上の表示順を変更できます。現在の設定(0)では各種UIの後ろに粒子が隠れてしまっているので、1に変更する必要があります。
Sceneビューでの編集時にパーティクルのテスト再生が可能なので、表示の具合を見つつ数値の調整を行っていくと良いでしょう。
まずOrder in Layerを1に変更して正しく表示されるようにしています。
パック開封演出用の設定
デフォルトの設定でも粒が周囲に飛び散るという動きは出来ているのですが、より使いやすく・見やすくするために以下の変更を行います。
Main Module(基本設定)
パーティクルの時間や粒子の速度などを変更します。
また、このパーティクルはパック開封時に1回再生して終了にするのでループ設定はオフにします。Play On Awakeも必要ではありません。
Emission
Rate over Timeは0にします。代わりにBurst設定を追加し、再生開始時(0秒目)に60の粒子を発生させるようにします。
Shape
初期のShape設定がConeとなっており、このままでも問題なく表示は可能ですがConeというのは円錐を意味するので2Dゲームにはやや不適格です。
円形を表すCircleに変更しておきましょう。その他の設定は変えなくても良いです。
Color over Lifetime
時間経過と共に粒子を徐々に透明にしていきたいのでこの設定を有効にします。
右上のツマミを押すと再生終了時の位置に下矢印カーソルが移動します。alpha値を変更し、0にすれば再生終了時に透明になる処理ができあがります。
これでパーティクル側の設定は完了です。スクリプトからこれを呼び出してみましょう。
OpenPackEffect.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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using DG.Tweening; /// <summary> /// パック開封演出処理クラス /// </summary> public class OpenPackEffect : MonoBehaviour { // 術札屋ウィンドウクラス private ShoppingWindow shoppingWindow; // 開封パーティクル [SerializeField] ParticleSystem unpackParticle = null; // UIオブジェクト [SerializeField] private CanvasGroup canvasGroup = null; // カード関連参照 [SerializeField] private GameObject cardPrefab = null; // カードプレハブ [SerializeField] private Transform cardsParent = null;// カードオブジェクトの親Transform // カード表示演出Sequence private Sequence effectSequence; // 初期化関数(ShoppingWindow.csから呼出) public void Init (ShoppingWindow _shoppingWindow) { // 参照取得 shoppingWindow = _shoppingWindow; // UI初期化 gameObject.SetActive (true); canvasGroup.alpha = 0.0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; } /// <summary> /// 演出を開始する /// </summary> public void StartEffect (List<CardDataSO> cardDatas) { effectSequence = DOTween.Sequence (); // 背景画像表示演出 effectSequence.Append (canvasGroup.DOFade (1.0f, 1.0f)); canvasGroup.interactable = true; canvasGroup.blocksRaycasts = true; // カードを順番に表示する演出 foreach (var cardData in cardDatas) { // オブジェクト作成 var obj = Instantiate (cardPrefab, cardsParent); Card objCard = obj.GetComponent<Card> (); // カード初期設定 objCard.Init (null, Vector2.zero); objCard.SetInitialCardData (cardData, Card.CharaID_Player); } // パーティクル再生 unpackParticle.Play (); } /// <summary> /// 画面を非表示にする /// </summary> public void ClosePanel () { // UI非表示 canvasGroup.alpha = 0.0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; // 演出終了 if (effectSequence != null) effectSequence.Kill (); // 生成されたカードを消去 for (int i = 0; i < cardsParent.childCount; i++) { var cardObject = cardsParent.GetChild (i); Destroy (cardObject.gameObject); } // パーティクル停止 unpackParticle.Stop (); } } |
- (パーティクル).Playメソッドで再生開始、Stopメソッドで再生終了できます。
編集後、OpenPackEffectオブジェクトにパーティクルへの参照をセット。その後、再生ボタンを押して確認してみましょう。
カードパックを買った瞬間に粒が飛び散るアニメーションが見られれば成功です。
(戦闘画面)敵へのダメージ演出もUnity Particleシステムで作ろう
練習も兼ねてもう一種類ほどパーティクルを作ってみましょう。
Battleシーンの編集に切り替え、敵にダメージを与えた時に発生するパーティクルを実装します。
敵の位置を中心として先ほどのような光の粒が飛び出す演出にしますが、少し変化を加えてみましょう。
Stage Area以下にParticleSystemオブジェクトを新規作成し、名前をDamageParticleとします。
敵と同じ位置に表示させるので座標はEnemyPictureParentオブジェクトと同じにします。
パーティクル設定ですが粒子を非表示化する場合に前回は[Color over Lifetime]で透明度を上げるという手段をとりましたが、今回は[Size over Lifetime]を使用してみます。これは残りの寿命に応じて粒の大きさを変更するという設定です。
有効化してこのタブを開くとSizeパラメータに大きさの変化を指定するグラフが表示されています。まずはこれをクリックして選択します。
するとInspectorビュー下端にグラフ編集画面が開かれます。開かれない場合は隠れている状態にあるので、[Particle System Curves]と書かれている部分を上側にドラッグして開きます。
初期設定では時間経過とともに粒が大きくなる設定になっているので、逆にしてグラフが右下がりになるように変更します。下側にグラフの形のプリセットが並んでいるので、どれでも構いませんので右下がりになっている形のものをクリックして適用します。
これでサイズ変更の設定は完了です。
その他の設定項目の設定例については以下です。[Renderer]内のOrder in Layerパラメータを1にする事は忘れないでください。
最後にこれをスクリプトから実行します。敵のHP変更時に呼び出すのでCharacterManagerクラスを編集します。
CharacterManager.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 |
// オブジェクト・コンポーネント [HideInInspector] public BattleManager battleManager; // 戦闘画面マネージャクラス // 敵画像オブジェクト関連 [SerializeField] private Transform enemyPictureParent = null; // 生成した敵オブジェクトの親Transform [SerializeField] private GameObject enemyPicturePrefab = null; // 敵キャラクター画像Prefab private EnemyPicture enemyPicture; // 出現中の敵オブジェクト処理クラス // ステータスUI処理クラス [SerializeField] private StatusUI playerStatusUI = null;// プレイヤーステータス [SerializeField] private StatusUI enemyStatusUI = null; // 敵ステータス // エネミーデータ [HideInInspector] public EnemyStatusSO enemyData; // 敵定義データ(戦闘中ここは変更しない) // 敵側ダメージパーティクル [SerializeField] private ParticleSystem enemyDamageParticle = null; // キャラクターID別ステータスデータ // 現在HPデータ public int[] nowHP; // 最大HPデータ public int[] maxHP; // 各状態異常ポイント [キャラクターID, 状態異常ID] public int[,] statusEffectsPoints; |
- ChangeStatus_NowHPメソッド
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 |
/// <summary> /// 指定キャラクターの現在HPを変更する /// </summary> /// <param name="value">変化量(+で回復)(-でダメージ)</param> public void ChangeStatus_NowHP (int charaID, int value) { // 既に現在HP0なら処理しない if (nowHP[charaID] <= 0) return; // 職業「僧侶」時最大HP追加 if (charaID == Card.CharaID_Player && Data.instance.playerJob == JobDataDefine.JobType.Healer && value > 0) { ChangeStatus_MaxHP (Card.CharaID_Player, 2); } // 現在HP変更 nowHP[charaID] += value; // 最大HPを越えないようにする処理 if (nowHP[charaID] > maxHP[charaID]) nowHP[charaID] = maxHP[charaID]; // UI反映 if (charaID == Card.CharaID_Player) playerStatusUI.SetHPView (nowHP[charaID], maxHP[charaID]); else enemyStatusUI.SetHPView (nowHP[charaID], maxHP[charaID]); // (敵専用)ダメージ演出 if (charaID == Card.CharaID_Enemy) { // パーティクル再生 if (value < 0) enemyDamageParticle.Play (); // 撃破演出 if (IsEnemyDefeated ()) enemyPicture.DefeatAnimation (); // 被ダメージ演出 else if (value < 0) enemyPicture.DamageAnimation (); } } |
編集後、Managerオブジェクト内CharacterManagerコンポーネントにパーティクルへの参照をセットしたらテストプレイをし、敵にダメージを与えてみましょう。
自分で設定した演出が再生されていれば成功です。
まとめ
術札屋のシステムを実装し、ステージで稼いだ金貨から新たなカードを入手できるようになりました。カードをどんどん増やしていってプレイヤーの戦略の幅が広がるという仕組みになっています。
またParticleSystemによってパーティクルの実装も行いました。ParticleSystemは非常に奥が深く、少しパラメータを変更しただけで見た目に大きな変化が起こるので色々こだわって試してみるのも面白いかもしれません。
そろそろ完成が見えてきました。残りの作業も頑張っていきましょう!
次の記事:
コメント
執筆ご苦労様です。読んでいて混乱した点があったのでご報告します。
【商品項目スクリプト仮作成&アタッチ&プレハブ化】の段の、「ShoppingItemオブジェクトにこのスクリプトをアタッチ。」との表記ですが、正しくは「ShoppingItemRowオブジェクトにこのスクリプトをアタッチ。」だと思います。
記事の質の向上に役立てば幸いです。これからも頑張ってください。
ご指摘箇所、Row抜けてましたね汗
修正しておきました。ありがとうございます。