前回までの記事で、敵への攻撃まで実装出来ましたね。
やってない人は前回の記事参照↓
今回の記事では、マナコストの実装をしていきます!!
完成形はこんな感じ↓
マナの実装が出来ればかなり完成形に近づくので、張り切っていこう!!
では行くぞ!!!
Contents
【全体概要】全体の流れの説明
ではまず、今回やっていく作業全体の説明をしていきます!
今回の実装の流れはこんな感じ↓
- マナポイントをコードから表示する
- ターン毎にマナポイントを増やす
- カードを出したらマナポイントを減らす
- 出せるカードに印を付ける
- マナポイント以下は出せなくする
ではひとつひとつ実装していこう!!
【実装】マナポイントの実装
①マナポイントをコードから表示する
ではまずマナポイントの表示を設定していくよ!
いま表示されてるマナポイントはあくまで「仮の数字」なので、コードから操作できるようにしていこう!
ではGamaManagerにオレンジ部分を追記していこう↓
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 |
(省略) public class GameManager : MonoBehaviour { [SerializeField] CardController cardPrefab; [SerializeField] Transform playerHand, playerField, enemyField; [SerializeField] Text playerLeaderHPText; [SerializeField] Text enemyLeaderHPText; [SerializeField] Text playerManaPointText; [SerializeField] Text playerDefaultManaPointText; public int playerManaPoint; // 使用すると減るマナポイント public int playerDefaultManaPoint; // 毎ターン増えていくベースのマナポイント (省略) void StartGame() // 初期値の設定 { enemyLeaderHP = 5000; playerLeaderHP = 5000; ShowLeaderHP(); /// マナの初期値設定 /// playerManaPoint = 1; playerDefaultManaPoint = 1; ShowManaPoint(); // 初期手札を配る SetStartHand(); // ターンの決定 TurnCalc(); } void ShowManaPoint() // マナポイントを表示するメソッド { playerManaPointText.text = playerManaPoint.ToString(); playerDefaultManaPointText.text = playerDefaultManaPoint.ToString(); } (省略) } |
特にエラーが出なければアタッチに進もう!!
⓪.まず4つ分ある「ManaCost」を「ManaPoint」に名前を変えよう。
(既に「Point」になってる!って人はスルーで大丈夫。)
※これはただ単に個人的に変えたかっただけなので、「ManaCost」の方がしっくりくる人はそのままでもいいよ!(変えてくれた方が無駄な読み替えが必要ないから、変えた方が楽だとは思うけども)
①.「ManaPoint」を「playerManaPointText」にアタッチする
②.「DefaultManaPoint」を「playerDefaultManaPointText」にアタッチする
では実行してみよう!
マナの数字が1に変わったかな??
変わったならOK!!
②ターン毎にマナを増やす
では次にマナポイントを毎ターン1づつ増やしていく処理を実装するよ!!
GamaManagerの「PlayerTurn」メソッドにオレンジ部分を追記↓
1 2 3 4 5 6 7 8 9 10 11 |
void PlayerTurn() { (省略) /// マナを増やす playerDefaultManaPoint++; playerManaPoint = playerDefaultManaPoint; ShowManaPoint(); DrawCard(playerHand); // 手札を一枚加える } |
追記出来たら実行してみよう!!
ターンエンドボタンを押す度にマナポイントが増えるようになったかな??
無事、実装出来ていたら次へ進もう!
※ちなみに、マナが10以上になった時にもし数字が重なる場合は、左右に表示位置をずらした方がいいかも。こんな感じに↓
③使用時にマナを減らす
では今度は「カードを出す度にマナポイントを減らす処理」を追記していこう!
※ ここから追記するコードが増えるので、間違えないように注意しながら進めてね!!
GameManagerの「ShowManaPointメソッド」の下に下記のメソッドを追記↓
1 2 3 4 5 |
public void ReduceManaPoint(int cost) // コストの分、マナポイントを減らす { playerManaPoint -= cost; ShowManaPoint(); } |
DlopPlaceクラスを下記コードになるように修正↓
※丸々コピペでもOK。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; // フィールドにアタッチするクラス public class DropPlace : MonoBehaviour, IDropHandler { public void OnDrop(PointerEventData eventData) // ドロップされた時に行う処理 { //CardMovement card = eventData.pointerDrag.GetComponent<CardMovement>(); // ドラッグしてきた情報からCardMovementを取得 CardController card = eventData.pointerDrag.GetComponent<CardController>(); // 今回の書き換え部分 if (card != null) // もしカードがあれば、 { //card.cardParent = this.transform; // カードの親要素を自分(アタッチされてるオブジェクト)にする card.movement.cardParent = this.transform; // カードの親要素を自分(アタッチされてるオブジェクト)にする 今回の書き換え部分 card.DropField(); // カードをフィールドに置いた時の処理を行う } } } |
CardControllerにオレンジ部分を追記↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CardController : MonoBehaviour { public CardView view; // データを表示する public CardModel model; // データに関することを処理 public CardMovement movement; // 移動(movement)に関することを操作 private void Awake() { view = GetComponent<CardView>(); movement = GetComponent<CardMovement>(); } (省略) public void DropField() { GameManager.instance.ReduceManaPoint(model.cost); } } |
エラーメッセージが出てないことを確認して、問題なければ実行してみよう!
カードを出した時にマナポイントが減るようになったかな??
無事減ったならOK!!次に進もう!
④使用可能な手札にカードに印を付ける
マナポイントは減るようになったけど、こんな感じで↓
今のままだといくらでもカードを出せちゃう!!
という訳で、まずは「使用可能な手札のカード」に緑の枠を付けるようにしていくよ!!
GameManagerの「ReduceManaPoint」メソッドとその下に追記しよう↓
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 |
public void ReduceManaPoint(int cost) { playerManaPoint -= cost; ShowManaPoint(); SetCanUsePanelHand(); } void SetCanUsePanelHand() // 手札のカードを取得して、使用可能なカードにCanUseパネルを付ける { CardController[] playerHandCardList = playerHand.GetComponentsInChildren<CardController>(); foreach (CardController card in playerHandCardList) { if (card.model.cost <= playerManaPoint) { card.model.canUse = true; card.view.SetCanUsePanel(card.model.canUse); } else { card.model.canUse = false; card.view.SetCanUsePanel(card.model.canUse); } } } |
同じくGameManagerの「DrawCard」メソッドにオレンジ部分を追記しよう↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void DrawCard(Transform hand) // カードを引く { // デッキがないなら引かない if (deck.Count == 0) { return; } CardController[] playerHandCardList = playerHand.GetComponentsInChildren(); if (playerHandCardList.Length < 9) { // デッキの一番上のカードを抜き取り、手札に加える int cardID = deck[0]; deck.RemoveAt(0); CreateCard(cardID, hand); } SetCanUsePanelHand(); } |
CardModelにオレンジ部分を追記↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class CardModel { public int cardId; public string name; public int cost; public int power; public Sprite icon; public bool canUse = false; public bool PlayerCard = false; public bool canAttack = false; (以下、省略) |
CardViewにオレンジ部分を修正、追記↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class CardView : MonoBehaviour { [SerializeField] Text nameText, powerText, costText; [SerializeField] Image iconImage; [SerializeField] GameObject canAttackPanel, canUsePanel; public void Show(CardModel cardModel) // cardModelのデータ取得と反映 { nameText.text = cardModel.name; powerText.text = cardModel.power.ToString(); costText.text = cardModel.cost.ToString(); iconImage.sprite = cardModel.icon; } public void SetCanAttackPanel(bool flag) { canAttackPanel.SetActive(flag); } public void SetCanUsePanel(bool flag) // フラグに合わせてCanUsePanelを付けるor消す { canUsePanel.SetActive(flag); } } |
CardControllerのDropFieldメソッドにオレンジ部分を追記↓
1 2 3 4 5 6 |
public void DropField() { GameManager.instance.ReduceManaPoint(model.cost); model.canUse = false; view.SetCanUsePanel(model.canUse); // 出した時にCanUsePanelを消す } |
コードを張り付けてエラーがないことを確認したら、次はパネルをアタッチをしていくよ!
①②Cardプレハブをクリックして、「OpenPrefab」をクリック
①「CanUsePanel」をアタッチする。
では実行してみよう!!
2コスト以下の手札に枠が付いて、、
1コストのカードを出すと、マナポイントが1になって、コストが1のカードにだけ枠が付いた!!
という感じ!!!!
だいぶゲームっぽくなってきたね!
あともうちょっと頑張ろう!!
⑤マナポイント以上のコストのカードを出せないようにする
とは言えまだマナポイントを下回ってもカードを出せてしまうので、次はマナポイントよりコストが大きいカードは動かせないようにしていくよ!!
ではやっていこう!
CardMovementにオレンジ部分を追記しよう↓
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler { public Transform cardParent; bool canDrag = true; // カードを動かせるかどうかのフラグ public void OnBeginDrag(PointerEventData eventData) // ドラッグを始めるときに行う処理 { CardController card = GetComponent<CardController>(); canDrag = true; if (card.model.canUse == false) // マナコストより少ないカードは動かせない { canDrag = false; } if (canDrag == false) { return; } cardParent = transform.parent; transform.SetParent(cardParent.parent, false); GetComponent<CanvasGroup>().blocksRaycasts = false; // blocksRaycastsをオフにする } public void OnDrag(PointerEventData eventData) // ドラッグした時に起こす処理 { if (canDrag == false) { return; } transform.position = eventData.position; } public void OnEndDrag(PointerEventData eventData) // カードを離したときに行う処理 { if (canDrag == false) { return; } transform.SetParent(cardParent, false); GetComponent<CanvasGroup>().blocksRaycasts = true; // blocksRaycastsをオンにする } } |
では実行してみよう!!
マナポイント以上のコストのカードを引っ張っても動かなくなったかな??
これで大枠は完成!!
多少のバグが残っているけど、一旦コードの解説をしてから後で修正していくよ!!
【コードの解説】マナポイントの実装
今回はかなり色々なクラスをいじったので、
「どこをどう変えて、どうなったのか。」
をよく確認しながら進んで行こう!!
今回の実装の流れはこんな感じだったよね↓
- マナポイントを表示する
- ターン毎にマナポイントを増やす
- カードを出したらマナポイントを減らす
- 出せるカードに印を付ける
- マナポイント以下は出せなくする
ではそれぞれの詳細について説明していくよ!!
①マナポイントの表示
まずはマナポイントを表示するコードの解説をしていくよ!!
ここではまず、
- 「マナポイントを表示するメソッド」を作成する。
- マナポイントに数字を入れる。
- マナポイントを表示する。
ってな流れでマナポイントを表示しているよ。
では実際のコードで見ていこう!
①「マナポイントを表示するメソッド」を作成
まずは「マナポイントを表示するメソッド」を作成するよ!
1 2 3 4 5 6 7 8 |
≪GameManager≫ /// 【解説①】マナポイントを表示する /// void ShowManaPoint() { playerManaPointText.text = playerManaPoint.ToString(); playerDefaultManaPointText.text = playerDefaultManaPoint.ToString(); } |
メソッドの中の処理としては、
「マナポイントテキストって言う文字列に、
マナポイントっていう数字を文字列に変換して入れますよ!!」
ってなことをしているよ。
ただこの説明だと正直、
って思われそうなので、
最強に砕けた説明をすると、
文字しか受け付けない超数学嫌いの「てきすと子」に、「マナポイントはこれだよー」って、「1」を渡すと、「数字とか、まじむり。」って言われるけど、
「1」を文字として、てきすと子に渡す(ToString)と、「1ね。おっけー」って受け取ってくれるわけですよ!
(分かる?この説明??笑)
②③初期値の入力とマナポイントの表示
次は②マナポイントの初期値を設定して、さっき書いた③「ShowManaPoint」メソッドでマナポイントを表示するよ!
1 2 3 4 5 6 7 8 9 |
≪GameManager≫ void StartGame() // 初期値の設定 { /// 【解説②】マナポイントの初期値の設定 /// playerManaPoint = 1; playerDefaultManaPoint = 1; ShowManaPoint(); /// 【解説③】マナポイントを表示 } |
②ターン毎にマナポイントを増やす
ここでは単に、
「自分のターンが来たら、マナポイントを1増やす」
って処理をやっているよ!
なのでターン毎の動きを処理している「PlayerTurn」メソッドに追記をして、マナポイントの増加を実装しているよ↓
1 2 3 4 5 6 7 |
void PlayerTurn() { /// 【解説】マナを増やす処理 /// playerDefaultManaPoint++; // マナポイントを1増やす playerManaPoint = playerDefaultManaPoint; ShowManaPoint(); // マナポイントを表示する } |
③カード使用時にマナポイントを減らす
今回の記事ではここからの処理が肝になるよ!
追記した処理が結構多いので、処理の流れを抑えると、
- カードがフィールドに置かれると、
- 「DropPlace」クラスの「OnDrop」メソッドが起動。
- その中で「CardController」クラスの「DropField」が呼ばれて、
- 「GameManager」クラスの「ReduceManaPoint」を呼んで、
- コスト分マナポイントを減らして、
- 現在のマナポイントを表示する。
こんな感じで色んな処理が連続して実装されてるよ!
ではコードを含めて見ていこう!
①②③フィールドにカードが置かれると、「OnDrop」メソッドが動く
カードをフィールドにドロップすると、Fieldに貼り付けた「DropPlace」クラスの「OnDrop」メソッドが動くよね?
その中で、今回追記した「DropField」メソッドが呼ばれる。
1 2 3 4 5 6 7 8 9 10 11 12 |
≪DropPlace≫ public void OnDrop(PointerEventData eventData) // ドロップされた時に行う処理 { CardController card = eventData.pointerDrag.GetComponent<CardController>(); if (card != null) /// 【解説①②】カードがフィールドに置かれると、 { card.movement.cardParent = this.transform; card.DropField(); // 【解説③】DropFieldメソッドを呼ぶ } } |
④DropFieldメソッドが呼ばれる
「DropFieldメソッド」が呼ばれて、「ReduceManaPoint」(マナポイントを減らす処理)を呼ぶ。
1 2 3 4 5 6 7 |
≪CardController≫ /// 【解説④】カードをフィールドに置いた時の処理 /// public void DropField() { GameManager.instance.ReduceManaPoint(model.cost); /// マナを減らす } |
⑤⑥マナポイントを減らして、表示する
「ReduceManaPoint」メソッドが呼ばれたので、マナポイントからカードのコストの数字を引いて、「ShowManaPoint」メソッドを呼んで、マナポイントを改めて表示する!!って感じ!
1 2 3 4 5 6 7 8 |
≪GameManager≫ /// カード使用時にマナポイントを減らす処理 /////////////////////// public void ReduceManaPoint(int cost) { playerManaPoint -= cost; /// 【解説⑤】「playerManaPoint」をコスト分減らす ShowManaPoint(); /// 【解説⑥】マナポイントを表示する } |
④手札の使用可能カードに印を付ける
ここの処理もややこしいので、まず流れを抑えよう。
- CardModelクラスに「canUse」という、使用可能かどうかを判断するフラグを作る。
- CardViewクラスに「SetCanUsePanel」メソッド(canUseの値によって、カードに緑の枠を付ける or 消す処理をするメソッド)を追記する。
- GameManagerクラスに「SetCanUsePanelHand」メソッド(手札のカードのコストとマナポイントを比較して、マナポイントの方が多いカードには緑の枠を付けるメソッド)を追記する。
- ドローした時に「SetCanUsePanelHand」を実行する様にする。
- カードを使った時に「SetCanUsePanelHand」を実行する様にする。
- カードをフィールドに出した時に、「canUse」の値をfalseにして「CanUsePanel」を外す。(緑の枠を取る)
文字で書くとめっちゃゴチャゴチャしてるけど、
コードなら割とシンプルなのでコードを見ながら説明していくよ!
①「canUse」フラグの作成
カード1枚1枚に「今このカードは使用できるかどうか」のフラグを作る。
初期値は当然、false。
1 2 3 4 5 6 |
≪CardModel≫ public class CardModel { public bool canUse = false; /// 【解説①】「使用できるかどうか」のフラグを作る } |
②フラグに合わせて緑の枠を付ける or 消すメソッドの作成
このカードが使用できるかどうかを表す「CanUse」フラグに合わせて、「CanUsePanel」を付ける or 消すメソッドを作る。
1 2 3 4 5 6 7 8 9 10 |
≪CardView≫ public class CardView : MonoBehaviour { ///【解説②】「CanUsePanel」をフラグに合わせて消すor出すメソッド public void SetCanUsePanel(bool flag) { canUsePanel.SetActive(flag); } } |
③手札のカードに「CanUsePanel」を付ける
手札のカードのリストを取得して、条件に当てはまるカードに「CanUsePanel」を付けていってるよ!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
≪GameManager≫ /// 【解説③】手札の使用可能なカードに枠を付ける処理 void SetCanUsePanelHand() { // 手札のカードを取得して、CanUseパネルを付ける CardController[] playerHandCardList = playerHand.GetComponentsInChildren<CardController>(); foreach (CardController card in playerHandCardList) // 手札のカードを1枚づつ判定する { if (card.model.cost <= playerManaPoint) // マナポイントよりコストが小さい場合 { card.model.canUse = true; card.view.SetCanUsePanel(card.model.canUse); // パネルを付ける } else // マナポイントよりコストが大きい場合 { card.model.canUse = false; card.view.SetCanUsePanel(card.model.canUse); // パネルを消す } } } |
④ドローした時に「SetCanUsePanelHand」を実行する
ドローした時にも改めて「SetCanUsePanelHand」メソッドを実行しよう。
これをしないと、ドローしたカードに対して「CanUse」のフラグが更新されないので、本当なら使えるはずのコストのカードでも使えなくなっちゃうので注意!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
≪GameManager≫ void DrawCard(Transform hand) // カードを引く { if (playerHandCardList.Length < 9) { // デッキの一番上のカードを抜き取り、手札に加える int cardID = deck[0]; deck.RemoveAt(0); CreateCard(cardID, hand); } SetCanUsePanelHand(); ///【解説④】ドローした後に「CanUsePanel」の更新をする」 } |
⑤カードを使用した時に「SetCanUsePanelHand」を実行する
さっきと同じく、マナポイントが変化した時にも「SetCanUsePanelHand」メソッドを実行しよう。
「ReduceManaPoint」の中にこのメソッドを入れることで、マナポイントが減った後に、手札のカードのコストとマナポイントを比較して、「CanUsePanel」を付けてくれるよ!
1 2 3 4 5 6 7 8 9 10 |
≪GameManager≫ public void ReduceManaPoint(int cost) { playerManaPoint -= cost; ShowManaPoint(); /// 【解説⑤】マナポイントを減らした後にも改めて「CanUsePanel」を更新する SetCanUsePanelHand(); } |
⑥フィールドに出した時に「CanUsePanel」を消す
手札の「CanUsePanel」が付いたカードを出すと、パネルが付いたままフィールドに置かれてしまうので、
「DropField」メソッド(カードをフィールドに置いた時に動く処理)に、「CanUsePanel」を消す処理を書き加えてあげよう!!
1 2 3 4 5 6 7 8 |
≪CardController≫ public void DropField() { GameManager.instance.ReduceManaPoint(model.cost); model.canUse = false; view.SetCanUsePanel(model.canUse); ///【解説⑥】フィールドに出した時にCanUsePanelを消す } |
⑤マナ以上のカードを動かせなくする
では最後に使用できないカードを動かせないようにするコードの説明していくよ!
流れとしてはこんな感じ↓
- 「canDrag」フラグ(カードを動かせる or 動かせないのフラグ)を作る。
- 「canUse」の値によって「canDrag」フラグを変更する。
- 「canDrag」がFalseなら動かせない処理を入れる。
コードだとこんな感じ↓
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 |
≪CardMovement≫ public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler { bool canDrag = true; /// 【解説①】カードを動かせるかどうかのフラグ public void OnBeginDrag(PointerEventData eventData) // ドラッグを始めるときに行う処理 { CardController card = GetComponent<CardController>(); canDrag = true; if (card.model.canUse == false) ///【解説②】「CanUse」に合わせてフラグを変更する { canDrag = false; } if (canDrag == false) ///【解説③】フラグがFalseなら処理を止める { return; } (Parentを変える等の処理 省略) } public void OnDrag(PointerEventData eventData) // ドラッグした時に起こす処理 { if (canDrag == false) ///【解説③】フラグがFalseなら処理を止める { return; } transform.position = eventData.position; } public void OnEndDrag(PointerEventData eventData) // カードを離したときに行う処理 { if (canDrag == false) ///【解説③】フラグがFalseなら処理を止める { return; } (Parentを変える等の処理 省略) } } |
ざっくり説明していったけど分かったかな??
色々コードが出てきて混乱するかもしれないけど、処理は一方向に進んでるだけなので解説を何度か読んでみれば分かるはず!
それでもよくわからなかった場合は、解説を読みながらもう一度ゲームを作り直してみると深く理解出来るはずだよ!
それでも分かりづらい等あればコメントくださいー!!
【バグ修正】バグ探しとその修正
では最後にバグの修正をしていこう!!
まずは今回直すべきバグは何があるかを探してみよう!
今回気になるバグは大きく3つかな??
いつも言ってますが、バグ探しも自分でゲームを作る際の立派な練習になるよ!
バグを見つけたら「こうやったら直せるかも!」って風にその解決方法も考えるとメキメキ力が付くよ!
解説に沿って作るだけでなく、ちょっと立ち止まって考えてみてね!!
では実際にゲームを動かしながらバグを3つ見つけてみよう!!
バグ修正対象3つ
ではでは、今回直すべきバグはこの3つ↓
・手札から場に置いたときに出せてないのに、コストが引かれる
・フィールドからフィールドにドロップした時にもコストが引かれる
・フィールドのカードが動かせない
どれもなかなか致命的なのでしっかり直していこう!!
バグ修正① フィールドに置いた時、出せないカードをドロップした場合もコストが減る
現状では、使用できない手札のカードをフィールドにドロップすると、カードはフィールドに置けないけどマナポイントがコスト分引かれてしまう!
という訳で、まずはこのバグを解消していこう!!
ではまず、こうなってしまう原因を考えると、
今の処理だと「とりあえず全部区別なく、フィールドにカードが置かれたらコスト分マナポイントを減らせ!!」ってなってるのが原因だよね。
ということは、「使用できないカード(「CanUse」フラグがFalseのカード)をフィールドにドロップした時は、処理を動かすな!!!」とすれば解決するので、
現状、DropPlaceクラスの「OnDrop」メソッドが
「カードがフィールドに置かれたら、DropFieldメソッドを実行する」となっているのを、
「CanUseフラグがTrueなら、DropFieldメソッドを実行する」という分岐にしてあげればOK!!
コードで言うとこんな感じ↓
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public void OnDrop(PointerEventData eventData) // ドロップされた時に行う処理 { CardController card = eventData.pointerDrag.GetComponent<CardController>(); if (card != null) // もしカードがあれば、 { if (card.model.canUse == true) // 使用可能なカードなら、 { card.movement.cardParent = this.transform; // カードの親要素を自分(アタッチされてるオブジェクト)にする 今回の書き換え部分 card.DropField(); // カードをフィールドに置いた時の処理を行う } } } |
DropPlaceクラスの「OnDrop」メソッドのオレンジ部分を上記の様に修正したら実行してみよう!
マナポイント以上のコストのカードをフィールドにドロップしても、マナポイントは減らないようになったかな??
ちなみにフィールドに出てるカードのCanUseフラグはFalseなので、②のフィールドにある「フィールドにあるカードをフィールドにドロップしてもマナポイントが減る」ってバグも同時解決するよ!!
③フィールドのカードが動かない
「マナポイント関連のバグは直ったけど、
フィールドのカードが動かなくなってんじゃん!!( ゚Д゚)」
って感じなので、次はそこを直していきましょう!
この原因としては、CanUseフラグがFalseなら動けなくしてるからだよね?
まあこれ自体は正しいんだけど、あくまで手札のカードに対してそうして欲しい!って処理なのに、フィールドのカードまでそうなってしまってるのが原因。
という訳で、「フィールドのカードにはこの処理を行いませんよ~」ってやればOK!
なのでまずは、「フィールドのカードですよ~」って分かるようなフラグを作ろう。
CardModelにオレンジ部分を追記↓
1 2 3 4 |
public bool canUse = false; public bool PlayerCard = false; public bool FieldCard = false; public bool canAttack = false; |
そんでその後は、フィールドにカードを出した時に、そのカードの「FieldCard」フラグをTrueにする処理を追記する。
CardControllerの「DropField」メソッドにオレンジ部分を追記↓
1 2 3 4 5 6 7 |
public void DropField() { GameManager.instance.ReduceManaPoint(model.cost); model.FieldCard = true; // フィールドのカードのフラグを立てる model.canUse = false; view.SetCanUsePanel(model.canUse); } |
そんで最後に、
「手札のカードなら、カードのコストがマナポイントより大きいカードは動かない。」
「フィールドのカードなら、攻撃できないカードは動かせない。」
というように、手札のカードかフィールドのカードかで処理を変えればOK!
CardMovementクラスのコードのオレンジ部分を修正してみよう!!↓
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler { public Transform cardParent; bool canDrag = true; public void OnBeginDrag(PointerEventData eventData) // ドラッグを始めるときに行う処理 { CardController card = GetComponent(); canDrag = true; if (card.model.FieldCard == false) // 手札のカードなら { if (card.model.canUse == false) // マナコストより少ないカードは動かせない { canDrag = false; } } else { if (card.model.canAttack == false) // 攻撃不可能なカードは動かせない { canDrag = false; } } if (canDrag == false) { return; } cardParent = transform.parent; transform.SetParent(cardParent.parent, false); GetComponent().blocksRaycasts = false; // blocksRaycastsをオフにする } (省略) } |
では実行してバグを確かめてみよう!!
フィールドの攻撃可能なカードが動かせるようになったかな??
無事出来ていたら完成!!!
超お疲れ様!!!(*´Д`)
最後に、、
今回も最後まで解説を読んでいただき、ありがとうございました!!
処理がだいぶ複雑になってきたので分かりづらい部分もあるとは思いますが、ベースの完成までもうすぐなんで、もうちょっと頑張りましょー!
記事数で言うとあと2記事ですかね。(敵のモンスターの攻撃と、エフェクト関係の2記事)
なので今月中(2月)には完成できるんじゃないかな~と思っております!(あくまで予定)
そして最後にこの場を借りて、謝罪とお礼を言わせてください。
めっちゃ記事の更新放置してしまってごめんよ!!!
そしてその間もコメントを書いて送ってくださった皆様、
ほんっっっとうにありがとうございました!!
コメントをもらう度に、やる気を貰いました。元気を貰ってました。
そしてまたこうやって記事を更新出来ました。
このカードゲーム作成解説記事は、大学2年の時に「カードゲームが作りたい!!」って思って調べてみたけど、ことごとく意味が分かんなくて挫折した当時の僕を助けられる記事を書けたらいいな~って思いで書き始めたものです。
始めは正直「自分の備忘録程度に書いとこ!」ってくらいだったんですが、当時の僕と同じように「プログラミングなんて全然分かんないけど、カードゲームが作りたい!!」って完全な初心者の人達が「僕にも作れました!ありがとうございます!!」って言ってくれるのが嬉しくてここまで書くことが出来ました。
とはいえまだまだ途中なので、
このままでは当時の僕に「なんだよみじんこ!解説が途中でいつまでもカードゲームが完成しないじゃんよ!!おこだぜ!ぷんぷんだぜ!!」って言われちゃうので、出来るだけ早めに、分かりやすく記事を出せるようにします!!
まあいろいろ書きましたが、要は「コメントしてくれてありがとう!!」ってことです!笑
ひとつひとつのコメントが嬉しいです。
(返信してない人はごめん!これからします!笑)
もちろん「ここがちょっと読みづらい。分かりづらい。」ってコメントもありがたいので、気付いたことがあればコメントくださいな!!
ではでは今回はこれで終わりっ!!!
次は敵のモンスターの攻撃を実装していくよ!!
次の記事↓
前の記事↓
参考にさせて頂いた動画
[Unityゲーム開発講座] シャドバ風!?カードゲームの作り方 #1 UIの実装
ここまで何とかついてこれました。
理解するための工夫が凝らされており、非常に助かっています。
次もお待ちしています、、!
>ゆずるさん
コメントありがとうございます!
おー、お疲れ様です!!
もうちょっとで完成なので最後までお付き合いください(‘ω’)
TCG制作の解説は少ないので本当に助かってます。
コメントありがとうございます!
実際、作りたいひとは多そうなのに解説はほぼないですよね。。
この記事のおかげでゲーム制作を始めたものです。
特定のカードが破壊された時の処理などがしたかったのですが、ゲームマネージャーのcardIDを参照しようとしたら、定義されていませんと出てしまいました。何か方法はあるでしょうか?
んー、カードの破壊時効果を付けたいってことですかね?
(シャドウバースのラストワード的な?(知らなかったら申し訳ない。。))
ちょっと複雑になるんでコメントで返答するのが難しいですね。。
今後、解説する予定ではあるので、そこまで待っていただければ、、、
返信ありがとうございます。効果の実装とかはまだ先になりそうですね。その間にゲームルールや企画の練り直しをして待ってます。
効果実装できたみたいで良かったです!
また何かあればコメント下さい!
たぶんthis.cardID=cardEntity.cardIDで参照できると思いますぞ。 ID
なるほどthisを使うんですね。
少し違う形ですがCardControllerのInitにthis.CardID = CardIDを書き加えたらswith文を使ってIDに応じた効果が出るようにできました!ありがとうございます!
次の記事を死ぬほど首を長くして待ってます!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
>ごりごりらさん
次の記事更新したから死なないで!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
すいません1の②のGamaManagerの「PlayerTurn」メソッドにの所で丸々コピペしたのですがエラーが出てしまいました
どうすれば良いのでしょうか
GameManagerの奴貼っておきます
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
[SerializeField] CardController cardPrefab;
[SerializeField] Transform playerHand, playerField, enemyField;
[SerializeField] Text playerLeaderHPText;
[SerializeField] Text enemyLeaderHPText;
[SerializeField] Text playerManaPointText;
[SerializeField] Text playerDefaultManaPointText;
public int playerManaPoint; // 使用すると減るマナポイント
public int playerDefaultManaPoint; // 毎ターン増えていくベースのマナポイント
bool isPlayerTurn = true; //
List deck = new List() { 1, 2, 1, 2, 1, 2, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 }; //
public static GameManager instance;
public void Awake()
{
if (instance == null)
{
instance = this;
}
}
void Start()
{
StartGame();
}
void StartGame() // 初期値の設定
{
enemyLeaderHP = 5000;
playerLeaderHP = 5000;
ShowLeaderHP();
/// マナの初期値設定 ///
playerManaPoint = 1;
playerDefaultManaPoint = 1;
ShowManaPoint();
// 初期手札を配る
SetStartHand();
// ターンの決定
TurnCalc();
void ShowManaPoint() // マナポイントを表示するメソッド
{
playerManaPointText.text = playerManaPoint.ToString();
playerDefaultManaPointText.text = playerDefaultManaPoint.ToString();
}
}
void CreateCard(int cardID, Transform place)
{
CardController card = Instantiate(cardPrefab, place);
// Playerの手札に生成されたカードはPlayerのカードとする
if (place == playerHand)
{
card.Init(cardID, true);
}
else
{
card.Init(cardID, false);
}
}
void DrowCard(Transform hand) // カードを引く
{
// デッキがないなら引かない
if (deck.Count == 0)
{
return;
}
CardController[] playerHandCardList = playerHand.GetComponentsInChildren();
if (playerHandCardList.Length < 9)
{
// デッキの一番上のカードを抜き取り、手札に加える
int cardID = deck[0];
deck.RemoveAt(0);
CreateCard(cardID, hand);
}
}
void SetStartHand() // 手札を3枚配る
{
for (int i = 0; i < 3; i++)
{
DrowCard(playerHand);
}
}
void TurnCalc() // ターンを管理する
{
if (isPlayerTurn)
{
PlayerTurn();
}
else
{
EnemyTurn();
}
}
public void ChangeTurn() // ターンエンドボタンにつける処理
{
isPlayerTurn = !isPlayerTurn; // ターンを逆にする
TurnCalc(); // ターンを相手に回す
}
void PlayerTurn()
{
Debug.Log("Playerのターン");
CardController[] playerFieldCardList = playerField.GetComponentsInChildren();
SetAttackableFieldCard(playerFieldCardList, true);
/// マナを増やす
playerDefaultManaPoint++;
playerManaPoint = playerDefaultManaPoint;
ShowManaPoint();
DrowCard(playerHand); // 手札を一枚加える
}
void EnemyTurn()
{
Debug.Log(“Enemyのターン”);
CardController[] enemyFieldCardList = enemyField.GetComponentsInChildren();
if (enemyFieldCardList.Length < 5)
{
if (enemyFieldCardList.Length defenceCard.model.power)
{
defenceCard.DestroyCard(defenceCard);
}
// 攻撃された側のパワーが高かった場合、攻撃側のカードを破壊する
if (attackCard.model.power < defenceCard.model.power)
{
attackCard.DestroyCard(attackCard);
}
// パワーが同じだった場合、両方のカードを破壊する
if (attackCard.model.power == defenceCard.model.power)
{
attackCard.DestroyCard(attackCard);
defenceCard.DestroyCard(defenceCard);
}
attackCard.model.canAttack = false;
attackCard.view.SetCanAttackPanel(false);
}
void SetAttackableFieldCard(CardController[] cardList, bool canAttack)
{
foreach (CardController card in cardList)
{
card.model.canAttack = canAttack;
card.view.SetCanAttackPanel(canAttack);
}
}
public int playerLeaderHP;
public int enemyLeaderHP;
public void AttackToLeader(CardController attackCard, bool isPlayerCard)
{
if (attackCard.model.canAttack == false)
{
return;
}
enemyLeaderHP -= attackCard.model.power;
attackCard.model.canAttack = false;
attackCard.view.SetCanAttackPanel(false);
Debug.Log("敵のHPは、" + enemyLeaderHP);
ShowLeaderHP();
}
public void ShowLeaderHP()
{
if (playerLeaderHP <= 0)
{
playerLeaderHP = 0;
}
if (enemyLeaderHP <= 0)
{
enemyLeaderHP = 0;
}
playerLeaderHPText.text = playerLeaderHP.ToString();
enemyLeaderHPText.text = enemyLeaderHP.ToString();
}
}
ちなみにエラーはこう出てました
Assets\scripts\GameManager.cs(130,9): error CS0103: The name ‘ShowManaPoint’ does not exist in the current context
問題はコピペした部分にある事は分かっているのですが
どこを直せば良いか分からずじまいです
なんか解決できてしまいました
void ShowManaPoint()
(省略)
これを一番下に送ったら直りました
お手数おかけしました
>お魚さん
返信遅くなってしまってすみません!
自己解決できたみたいで良かったです、
また何かあれば聞いて下さいー!
お疲れさまです
ダイレクトアタックまでできました
出せるカードに色を付けようでエラーが出てしまいました
アタッチする場所も存在しませんでした チャットGPTに聞いても解決方法が分かりません
Assets\Scripts\GameManager.cs(108,58): error CS0411: The type arguments for method ‘Component.GetComponentsInChildren()’ cannot be inferred from the usage. Try specifying the type arguments explicitly.
原因を教えてください
このエラーは、UnityのC#スクリプトでGetComponentsInChildren()メソッドを使用しようとした際に、コンパイラが型引数Tを自動的に推論できない場合に発生します。具体的な原因はコード全体を見ないと確定的には言えませんが、一般的な原因としては以下のようなものが考えられます。
Tの型が複数の候補があり、どの型を指定すればいいかコンパイラが判断できない場合。
メソッド呼び出しの文脈からは型引数を推論できない場合。例えば、ジェネリックメソッドの呼び出しの結果が別のメソッドの引数に渡されていて、その引数の型からは型引数を推論できない場合。
これを解決するには、GetComponentsInChildren()メソッドの呼び出し時に型引数Tを明示的に指定することが必要です。以下に例を示します。
csharp
Copy code
// エラーが発生する誤った使用例
// CS0411: The type arguments for method ‘Component.GetComponentsInChildren()’ cannot be inferred from the usage. Try specifying the type arguments explicitly.
var components = GetComponentsInChildren();
// 正しい使用例:型引数を明示的に指定する
var components = GetComponentsInChildren(true); // MyComponentの部分を、実際に取得したいコンポーネントの型に置き換える
正しい使用例では、MyComponentの部分を、実際に取得したいコンポーネントの型に置き換えます。また、trueパラメータは、検索対象に非アクティブなゲームオブジェクトを含めるかどうかを示しており、必要に応じて変更してください。
ページの最初からやり直してみたら
ゲームマネージャーの108行目
CardController[] playerHandCardList = playerHand.GetComponentsInChildren();
これがエラーってでました
Error while saving Prefab: ‘Assets/Resources/Card.prefab’. You are trying to save a Prefab that contains the script ‘CardView’, which does not derive from MonoBehaviour. This is not allowed.
Please change the script to derive from MonoBehaviour or remove it from the GameObject ‘Card’.
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
何が原因ですか?
このエラーは、UnityのPrefabに含まれるスクリプト ‘CardView’ が MonoBehaviour から派生していないために発生しています。UnityのPrefabは、MonoBehaviourを継承したスクリプトを使用して、ゲームオブジェクトのプロパティや振る舞いを定義します。
このエラーを解決するには、次のいずれかの方法を選択できます:
‘CardView’ スクリプトを MonoBehaviour から派生させる: ‘CardView’ スクリプトのクラス定義の前に、以下のように MonoBehaviour を継承するように変更します。
csharp
Copy code
public class CardView : MonoBehaviour
{
// 既存のコード
}
Prefabから ‘CardView’ スクリプトを削除する: もしそのスクリプトがPrefabに不要である場合は、Prefabから ‘CardView’ スクリプトを削除します。これにより、Prefabの保存が可能になります。
以上のいずれかの方法を選択して、Prefabの保存ができるようにしてください。
Regenerate response
パート8の最初からやり直しました
まる4の使えるカードを光らそうまで打ち込んで表示されたエラーをチャットGPTに説明してもらいました
このエラーは、Assets\Scripts\GameManager.csの215行目で使用されている’CardView’というクラスに、’SetCanAttackPanel’というメソッドが定義されていないために発生しています。また、’CardView’クラスに対して、’SetCanAttackPanel’というメソッドを呼び出すための拡張メソッドも見つからないため、エラーが発生しています。
このエラーを解決するためには、以下のいくつかの方法があります:
‘CardView’クラスに ‘SetCanAttackPanel’ という名前のメソッドを定義する: ‘CardView’クラスに ‘SetCanAttackPanel’ という名前のメソッドを追加して、呼び出し元のGameManager.csで正しく使用されるようにします。
‘CardView’クラスに ‘SetCanAttackPanel’ という名前のプロパティを定義する: ‘SetCanAttackPanel’ をプロパティとして実装することで、GameManager.csの呼び出しに対応できるようにします。
必要な名前空間の using ディレクティブを追加する: ‘CardView’クラスが定義されている名前空間が、GameManager.csで正しく参照されているか確認し、必要に応じて using ディレクティブを追加します。
以上のいずれかの方法で、’CardView’クラスに ‘SetCanAttackPanel’ メソッドを定義し、GameManager.csから正しく呼び出せるようにすることで、エラーを解決することができます。
すみません また少しやり直しました こちらのエラーがどうしても解消できませんAssets\Scripts\GameManager.cs(109,58): error CS0411: The type arguments for method ‘Component.GetComponentsInChildren()’ cannot be inferred from the usage. Try specifying the type arguments explicitly.
原因は?
このエラーは、UnityのスクリプトのGameManager.csファイルの109行目にあるGetComponentsInChildren()メソッドの型引数が推論できないために発生しています。このエラーの原因として考えられるのは以下のいくつかの可能性があります。
型引数が不足している: GetComponentsInChildren()メソッドは、のようにジェネリックな型引数を必要とします。エラーが発生している109行目には、正しい型引数が指定されていない可能性があります。例えば、GetComponentsInChildren()のように明示的に型引数を指定することで解決できます。
複数のT型が存在する: GetComponentsInChildren()メソッドは、T型のコンポーネントをヒエラルキー内で検索し、複数のコンポーネントが見つかる場合には配列として返します。しかし、この場合にどのT型を返すべきかが推論できないためにエラーが発生しているかもしれません。この場合、型引数を明示的に指定することで解決できます。
T型が定義されていない: GetComponentsInChildren()メソッドは、T型が定義されていない場合にもエラーが発生します。この場合、T型が正しく定義されているか確認してください。
上記のいずれかの原因が考えられますので、エラーメッセージを確認し、適切な型引数を指定するか、T型が正しく定義されているか確認して解決してください。
ちなみに109行目は
CardController[] playerHandCardList = playerHand.GetComponentsInChildren();
です