前回の記事でモンスターへの攻撃が実装出来ましたね!
やってない人は前回の記事参照↓
(前回の演習の答え合わせは次の第8.5回にて解説してるので、まだ出来てないひとも安心して進めてくださいな!!)
今回は敵リーダーへの攻撃を実装していきます!!
完成形としてはこんな感じ↓
基本的には前回のモンスターの攻撃と同じ様なやり方で実装していくので、前回の復習も含めてやっていこう!
では行くぞ!!
Contents
【全体概要】全体の流れの説明
では最初に今回の流れを説明します。
全体の流れはこんな感じ↓
- 「AtattackedLeader」クラスを作る
- コードの記入
- アタッチをする
- リーダーのHPの表示を反映させる
- 攻撃可能の枠を作る
- アタッチする
では順番に進めていこう!
【実装】リーダーへの攻撃
①「AttackedLeader」クラスの作成
ではまず「AttackedLeader」クラスを新しく作ろう。
こんな感じになればOK↓
②コードの記入
次はリーダーへ攻撃するためのコードを追記していくよ。
AttackedLeaderクラスに丸々コピペしよう↓
(エラーが出るけど気にしないでOK!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class AttackedLeader : MonoBehaviour, IDropHandler { public void OnDrop(PointerEventData eventData) { /// 攻撃 // attackerを選択 マウスポインターに重なったカードをアタッカーにする CardController attackCard = eventData.pointerDrag.GetComponent<CardController>(); GameManager.instance.AttackToLeader(attackCard, true); } } |
GameManagerにも追記していくよ。
一番下の行(この前書いた、SetAttackableFieldCardメソッドの下とか)に全部コピペしよう↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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; Debug.Log("敵のHPは、"+enemyLeaderHP); } |
こっちはStartGameメソッドにオレンジ部分を追記しよう↓
1 2 3 4 5 6 7 8 9 10 11 |
void StartGame() // 初期値の設定 { enemyLeaderHP = 5000; playerLeaderHP = 5000; // 初期手札を配る SetStartHand(); // ターンの決定 TurnCalc(); } |
無事にコードを張り付けられたかな??
エラーが出てなければ次に進もう!
③「AttackedLeader」のアタッチ
では次はアタッチをやっていこう!
①.「EnemyLeader」をクリック
②.「AddComponent」をクリックして「AttackedLeader」を選択。
③.こんな感じに張り付けばOK!
では実行してみよう!!
敵リーダーに攻撃出来るようになった!!
(下のログメッセージにリーダーのHPが表示されるようになってるよね?)
でもHPがゲームの画面に表示されていない!
というわけで、次はHPをゲーム画面に反映させていくよ!
④リーダーのHPを表示に反映させる
ではいくつかコードを追記していこう!
まずはGameManagerクラスに追記していくよ。
一番上の「using~」が並んで書いてある所に、オレンジ部分を追記しよう↓
1 2 3 4 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; |
[SerializeField] Transform ~って色々書いてあるところの下にオレンジの2行を追記↓
1 2 3 4 |
[SerializeField] CardController cardPrefab; [SerializeField] Transform playerHand, playerField, enemyField; [SerializeField] Text playerLeaderHPText; [SerializeField] Text enemyLeaderHPText; |
StartGameメソッドにオレンジ部分を追記↓
1 2 3 4 5 6 7 8 9 10 11 12 |
void StartGame() // 初期値の設定 { enemyLeaderHP = 5000; playerLeaderHP = 5000; ShowLeaderHP(); // 初期手札を配る SetStartHand(); // ターンの決定 TurnCalc(); } |
AttackToLeaderメソッドにオレンジ部分を追記↓
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public void AttackToLeader(CardController attackCard, bool isPlayerCard) { if (attackCard.model.canAttack == false) { return; } enemyLeaderHP -= attackCard.model.power; attackCard.model.canAttack = false; Debug.Log("敵のHPは、"+enemyLeaderHP); ShowLeaderHP(); } |
AttackToLeaderメソッドの下に全部追記↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void ShowLeaderHP() { if (playerLeaderHP <= 0) { playerLeaderHP = 0; } if (enemyLeaderHP <= 0) { enemyLeaderHP = 0; } playerLeaderHPText.text = playerLeaderHP.ToString(); enemyLeaderHPText.text = enemyLeaderHP.ToString(); } |
ちょっと追記する量が多かったけど追記出来たかな??
中身は後で解説するので、エラーが出てないことを確認して次に進もう!
⑤HPTextのアタッチ
では次にアタッチをしていこう!
①.「GameManager」をクリック
②.PlayerLeaderの「HPText」を”PlayerLeaderHPText”にドラッグ&ドロップ
③.EnemyLeaderの「HPText」を”EnemyLeaderHPText”にドラッグ&ドロップ
うまく出来たかな??
では実行してみよう!
こんな感じに
敵と自分のHPが5000になって、
攻撃したらHPが減るようになったかな??
無事ここまでこれたら、最後に攻撃可能カードに付ける枠を作っていくよ!
⑥枠の作成
では攻撃可能なカードに付ける緑の枠の作成をしていくよ!!
画像に沿って順番にやっていこう↓
①.Cardプレハブをクリック
②.OpenPrefabをクリック
③.Sceneビューをクリック(クリックしてもカードが見えない場合はCardをダブルクリックすると見やすい位置に移動してくれるよ)
①.Panelをコピー&ペースト(Ctrl+C→Ctrl+V)
②.Panelをコピーして出来たものを「CanUsePanel」に名前を変える
③.Panelの上にCanUsePanelを持っていく
①.パラメータを変える
②.Colorをクリック
③.いい感じの緑色にする
④.チェックを外しておく
これで攻撃可能なカードに付けるパネルは作成完了!!
⑦パネルを表示する
では今作った緑色のパネルをコードから表示させていくよ!!
まずカードの見た目を操作するので、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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CardView : MonoBehaviour { [SerializeField] Text nameText, powerText, costText; [SerializeField] Image iconImage; [SerializeField] GameObject canAttackPanel; 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); } } |
では次は、GameManagerクラスの追記部分。
AttackToLeaderメソッドに11行目を追記しよう↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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(); } |
SetAttackableFieldCardメソッドに6行目を追記しよう↓
1 2 3 4 5 6 7 8 |
void SetAttackableFieldCard(CardController[] cardList, bool canAttack) { foreach (CardController card in cardList) { card.model.canAttack = canAttack; card.view.SetCanAttackPanel(canAttack); } } |
ではこれでコードの追記は終わり!
⑧パネルのアタッチ
では最後にパネルをアタッチしていこう!
①.Cardプレハブをクリックして、OpenPrefabをクリック
①.「CanUsePanel」を「CanAttackPanel」にドラッグ&ドロップする。
では実際に動かしてみよう!
完成だ!!!
こんな感じに攻撃出来る時に緑の枠が表示されるようになったかな??
ではでは実装できたので、コードの解説をしていくよ。
【解説】コードの詳細について
今回はちょこちょこと色んなクラスに追記したので、
詳しい説明する前に、一旦ざっくりなんの処理を追記したのかをまとめるよ。
表にまとめるとこんな感じ↓
【クラス名】 | 【追記内容】 |
---|---|
AttackedLeader | リーダーの上にカードをドラッグ&ドロップした時の処理 |
GameManager | リーダーに攻撃した時の処理、HP表示の処理 |
CardView | 攻撃可能パネルを表示・非表示にするメソッド |
ではそれぞれのクラスに追記した内容が分かったら、コードそれぞれの解説に入っていこう!
「AttackedLeader」の説明
ではまずAttackedLeaderクラスのコードの説明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class AttackedLeader : MonoBehaviour, IDropHandler { public void OnDrop(PointerEventData eventData) { /// 攻撃 // attackerを選択 マウスポインターに重なったカードをアタッカーにする CardController attackCard = eventData.pointerDrag.GetComponent<CardController>(); GameManager.instance.AttackToLeader(attackCard, true); } } |
ここのコードで大事になるのは、「OnDrop」メソッド。
OnDropメソッドは、ドロップされた時に動くメソッドだったよね。
まあ中身は前回説明したのとほぼ一緒。
※忘れた人はこちらを参照
ざっくり言うとこんな感じ↓
public void OnDrop(PointerEventData eventData)
{
【解説】リーダーにドロップしてきたカードをアタッカーにする
CardController attackCard = eventData.pointerDrag.GetComponent<CardController>();
【解説】AttackToLeaderメソッドを実行する
GameManager.instance.AttackToLeader(attackCard, true);
}
つまり、
リーダーの上にカードがドロップされると、
そのカードをアタッカーとして、
「AttackToLeader」メソッド(リーダーに攻撃するメソッド)を発動する。
って処理をしているよ。
「GameManager」の説明
では次にGameManagerクラスに追記した分の解説をしていくよ!
GameManagerには、いくつか処理を追記したので、処理内容をまとめてから解説をしていきます。
【メソッド名】 | 【メソッドの概要】 |
---|---|
AttackToLeader | リーダーに攻撃した時の処理を行うメソッド |
ShowLeaderHP | リーダーのHPを表示するメソッド |
「AttackToLeader」メソッド
まずは「OnDrop」メソッドで呼ばれた、「AttackToLeader」メソッドの説明からしていくよ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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(); } |
これは簡単に言うと、リーダーに攻撃するメソッド。
上からざっくり説明していくと、
これでまず攻撃してきたカードが攻撃可能なのかを判断する。
攻撃出来ないなら、returnで処理を止める。
if (attackCard.model.canAttack == false)
{
return;
}
次に、敵リーダーのHPをアタッカー(attckCard)のパワーの分減らす。
enemyLeaderHP -= attackCard.model.power;
そして、アタッカーを攻撃不可にして、緑のパネルを消す。
attackCard.model.canAttack = false;
attackCard.view.SetCanAttackPanel(false);
最後に、ログにリーダーのHPを表示して、
ShowLeaderHPメソッド(リーダーのHPを表示するメソッド)を動かす。
Debug.Log(“敵のHPは、”+enemyLeaderHP);
ShowLeaderHP();
という感じの処理!!
「ShowLeaderHP」メソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void ShowLeaderHP() { if (playerLeaderHP <= 0) { playerLeaderHP = 0; } if (enemyLeaderHP <= 0) { enemyLeaderHP = 0; } playerLeaderHPText.text = playerLeaderHP.ToString(); enemyLeaderHPText.text = enemyLeaderHP.ToString(); } |
次は「ShowLeaderHP」メソッドの説明!!
これは、リーダーのHPを表示するメソッドだね。
やってることは以下の2つ↓
- リーダーのHPが0以下の時には0にする
- ゲーム画面に反映する
まずこれで、リーダーのHPが0以下の時には0と表示するようにする↓
if (playerLeaderHP <= 0)
{
playerLeaderHP = 0;
}
if (enemyLeaderHP <= 0)
{
enemyLeaderHP = 0;
}
次に、ゲーム画面に反映!!↓
playerLeaderHPText.text = playerLeaderHP.ToString();
enemyLeaderHPText.text = enemyLeaderHP.ToString();
という感じだね!
「CardView」の説明
CardViewクラスには、
SetCanAttackPanelメソッドしか追記してないのでサラッと。
これは攻撃可能パネル(緑の枠)を出すor消すメソッド。
中身としては↓
public void SetCanAttackPanel(bool flag)
{
【解説】canAttackPanelを消す or 表示する
canAttackPanel.SetActive(flag);
}
こんだけ!!
【演習】バグの解消
では今回も理解の定着のために演習やっていこう!!
今回もバグの解消を演習としてやっていくよ!
今回の修正対象のバグは、
- モンスターに攻撃しても枠が消えない
というバグを解消してもらいます!!
ヒントとしては、
リーダーへ攻撃した時と、モンスターに攻撃した時の処理の違い
に注目してみて!!
まあ理解しながら進めてるなら簡単だと思うよ!
というわけで、また今回も答えはまだ教えません!!
ただちょっと前回のが難しそうだったので、何度は下げました。
だから前回よりは簡単だから頑張って!
みんなの理解度が知りたいので、分かった人はコメントで教えてね!
(最近コメントが増えてきて嬉しいです(‘ω’)笑)
出来た人が出てきたら答え合わせとして、答えを載せる予定です。
ではでは演習頑張って!
次はマナポイントの実装をしていくよ。
(演習の答え合わせを先にやります!!)
おわりっ!!
次の記事↓
前の記事↓
参考にさせて頂いた動画
[Unityゲーム開発講座] シャドバ風!?カードゲームの作り方 #1 UIの実装
今回も面白かったです!
リーダーのカードが意味を持ち始めると
ゲームっぽい華やかさがでてきますね
今回の課題はサクッとできました!
前回は難易度の高さにビビっていたので
個人的にはこれくらいがちょうどよかったです
次回の記事が待ち遠しいです!!
首を長くして待っています
>もっけさん
いつもコメントありがとうございます!
課題の難度がちょうどよくなって良かったです。笑
もっけさんがフィードバックをくれたお陰です、ありがとうございますね!
完成目指して作っていきましょう!!
わかりやすい解説ありがとうございます!
みじんこさんのわかりやすい記事のおかげで、
これまでゼロ知識だった私でもここまで追いつきました!
本を買ってやるよりもわかりやすいのに、無料で本当にいいのか心配にもなりますが。。笑
これまでの復習をしっかりしながら、次の更新も楽しみにしています!
>りんごさん
返信遅くなってすみません!
コメントありがとうございます、ゲームの修正も出来たみたいで良かったです(^^)
たいぶお待たせしましたが続きの記事も書きましたので、気が向いたら続きを作ってみて下さいー!
いつもわかりやすい記事をありがとうございます。⑤のHPTextのアタッチができません。HPTextをPlayer Leader HP Textにドラッグしてもアタッチできず、すぐ下にdefault references will only be applied in edit mode.という注意書きみたいなのが出てきます。何が原因なのか、ご教授いただければ幸いです。
>サイさん
あー、やりがちなやつですね。笑
恐らくスクリプトの「GameManager」にドラッグしようとしてないですか?
アタッチするのは、スクリプトの「GameManager」ではなく、
Hierarchy上のオブジェクトとしての「GameManager」です。
記事の⑤の①にも書いてあるので確認してみて下さい!!
解決できなかったらまた聞いてくださいー!
ありがとうございます!スクリプトのGameManagerにアタッチしようとしていました。Hierarchyのほうにアタッチしたらエラーを解消することができました。
解決出来たみたいで良かったです!
unityは基本的にスクリプトのままでは使えないので、そこを意識すると扱いやすくなるかと思いますー!
お世話になってます。以下のエラーが出てお伺いしたいです。
ssets/Scripts/CardView.cs(21,24): error CS1061: ‘GameObject’ does not contain a definition for ‘setActive’ and no accessible extension method ‘setActive’ accepting a first argument of type ‘GameObject’ could be found (are you missing a using directive or an assembly reference?)
要は「setActive」メソッドが定義されていない、ということだと思うのですが、みじんこさんのスクリプトだとsetActiveは定義しなくても稼働していると考えてよろしいでしょうか?
ちなみにUnityのバージョンは2020.3.18f1 personalです。
SetActiveメソッドはUnityのGameObjectクラスに用意されているので、定義が無くても使用できるはずです。
察するに、SetActiveのSが小文字になっているのが原因ではないでしょうか?
おっしゃる通りでした…
ありがとうございます!
これで2日も悩んでたのがもったいなすぎる…
はじめまして!こちらのブログをきっかけにゲーム制作を始めました、たか氏と申します。毎日数時間、拝見しながらunityと楽しく格闘しております。
突然の質問を失礼いたします。
バトルによるカードのHPの増減も表示に反映させたいのですが、やり方がわからず困っております。ヒントをいただけないでしょうか?
今回の記事と前回の記事の内容を掛け合わせることで、カードそれぞれがHPをもつゲームも作成できるのではないかと思い、自分なりに試行錯誤したのですが、どうしてもうまくいかず悩んでいます。
cardbattleメソッドの中で、「攻撃側、防御側のhpから相手の攻撃力を引く」というところまでは上手くできたのですが、
見た目上のHPのテキストにそれを反映できません。
リーダーのHPと違い、プレハブ上にテキストが存在するため、ゲームオブジェクト内にドラッグアンドドロップができない状態です。
何か良い解決策はありますでしょうか、、、?
もしお返事いただけましたら、誠に幸いでございます。
1週間前ぐらいに同じことで悩んでいたので、僕の解決方法を参考までに
カードデータを表示する処理はCardViewのShow()で、これを戦闘処理をした後に呼び出してあげればいいかと思います。ただそのままでは呼び出せないので、僕はCardContorollerに新しくデータ表示反映用のメソッドを作りそれを呼び出すことにしました。DestroyCardメソッドと同じ感じなのでこちらを参考にしてみるとよいかと。
まだ実装も試してもいませんがバフ効果の反映とかも同じ感じでできるのかなーと思っています。
まだ手探りな僕の解決方法であり、もっと良い方法があるかもしれませんが。。。
ヒントではなく答えになってしまいますが一応僕のコード以下に貼ります。見たくなければスルーで
—CardContorollerクラス——————
public void StateChange(CardController card)
{
view.Show(card.model);
}
——————————————
—GameManagerクラス内CardBattleメソッド–
//攻撃側のパワー分防御側のHPを減らす
defenceCard.model.hp -= attackCard.model.power;
//防御側のパワー分攻撃側のHPを減らす
attackCard.model.hp -= defenceCard.model.power;
//データ反映
attackCard.StateChange(attackCard);
defenceCard.StateChange(defenceCard);
——————————————
なるほど!!!
実は、view上のHPの定義とmodel上のHPの定義を別にして、戦闘で後者を減少せて前者に反映させるという(今思えばまどろっこしい)方法で自己解決できていたのですが、アルスさんの方法の方が圧倒的にシンプルで良いですね。ご返信ありがとうございました!!
>たか氏さん
返信遅くなりすみません!!
でも実装できたみたいで良かったです!
また何かあればコメントください!(返信遅いですが。。笑)
>アルスさん
代わりに回答して頂き、ありがとうございます!
ここ最近忙しかったので、助かりました、、!
書いて頂いた実装方法で完璧だと思います!100点満点中120点です!!
また困っている方がいたら、気が向いたらでいいので助けてください!
(蛇足的なやつ)
基本的にはアルスさんが書いてくれてものでOKですが、自分ならこうするかな?って言うのを追記しておきます。
※ これも絶対的な正解ではないので、他にも「こっちの方がいいんじゃん??」って方がいたらコメント書いてね。
(本サイトは皆様のコメントで成り立っております故。一緒に作り上げていこうぜ!!)
—CardContorollerクラス——————
// カードがダメージを受けた時の処理
public void Damaged(int damage)
{
// 攻撃を受けた側のHPをdamege分減らす
this.model.hp -= damage;
// HPが0以下なら破壊する
if (this.model.hp <= 0) { DestroyCard(this); } // カードのパラメータを再表示 view.Show(this.model); }
—GameManagerクラス——————
// カードバトルの処理はカード自身にやらせる方式
public void CardBattle(CardController attackCard, CardController defenceCard)
{
// 攻撃側と防御側のカードにダメージを与える処理
attackCard.Damaged(defenceCard.model.power);
defenceCard.Damaged(attackCard.model.power);
}
(コメント欄でコード書くと若干見た目が崩れるので、うまく読み替えて!)
まあ基本的に「やりたいことが出来れば、実装方法なんてなんでもいいんだよ!!!」派なので、自分で考えて作ったコードが一番だと思うよ!