
ここまでで、攻撃・防御・回復と、カードを引く・エネルギーを増やすカードがそろいました。今回は、残りの6枚を作ってカードを15種類そろえます。「次の攻撃を強くする」「敵を弱らせる」のように、あとの行動に効いてくる「状態」を持たせると、カードを使う順番を考える楽しさがぐっと増えます。#7で作った効果の仕組み(継承。6-14 クラスを受け継ごう:継承とabstract)に、新しい効果を足していってみたいと思います!
- シリーズの目次:デッキ構築カードゲームの作り方
- 前回:#12 勝ち負けと報酬のカード選択
今回作るもの
| カード | 種類 | コスト | 効果 | 新しく作るもの |
|---|---|---|---|---|
| 分度器ブーメラン | 攻撃 | 1 | 敵全体に5ダメージ | AllDamageEffect |
| クリップでとめる | 防御 | 1 | 防御3を得て、ねらった敵に3ダメージ | (今ある効果を2つ組み合わせる) |
| えんぴつをけずる | 特殊 | 1 | 次の攻撃のダメージ+4 | SharpenEffect、状態「けずった」 |
| 修正テープ | 特殊 | 1 | ねらった敵の次の攻撃を半分にする | WeakenEffect、状態「弱った」 |
| 大そうじ | 特殊 | 0 | 手札を全部捨てて、同じ枚数を引く | ReshuffleHandEffect |
| おやつタイム | 回復 | 1 | HPを10回復。使ったらなくなる | 「使ったらなくなる」札 |
ほかに、次のスクリプトを直します。
| スクリプト | 直すところ |
|---|---|
| CardData | 「敵をねらう」「使ったらなくなる」の印 |
| CardDrag・EnemyTarget・PlayZone | 攻撃のカード以外でも、敵をねらえるように。全体攻撃は全員に枠を出す |
| Fighter | 状態「けずった」「弱った」 |
| HpGauge | 状態のアイコン |
| DamageEffect | けずった分をダメージに足す |
| EnemyView・BattleManager | 弱った敵の予告と攻撃を半分に |
| Deck | 捨てるときに捨て札へ飛ばす、引き直す、使ったらなくなる |
前回の続きから
前回の最後の状態から始めます。カードの絵6枚と、状態のアイコン2つは、素材のzipを入れ直すと入ります。
- DeckBuildingAssets.zip
DeckBuilding/Sprites/Cards/:card_protractor(分度器)・card_clip(クリップ)・card_sharpener(えんぴつけずり)・card_tape(修正テープ)・card_broom(ほうき)・card_snack(クッキー)DeckBuilding/Sprites/Icons/:icon_sharpen(とがった鉛筆の先)・icon_weak(下向きの矢印)- 自分で入れた場合は、Sprite Mode:Singleにして Apply
カードに2つの印を足そう
敵をねらう・使ったらなくなる
今までは「攻撃のカードだけが敵をねらう」形でした。でも、修正テープ(特殊)やクリップ(防御)も、敵をねらって使います。そこで、CardDataに「敵をねらう」「使ったらなくなる」の2つの印を足します。
public List<CardEffect> effects = new List<CardEffect>(); // ★#7 使ったときの効果(上から順に出す)
public bool targetsEnemy; // ★#13 攻撃のカードでなくても、敵をねらって使う(修正テープ・クリップ)
public bool targetsAllEnemies; // ★#13 敵全員がねらい(分度器ブーメラン)。ねらいの枠を全員に出す
public bool exhaust; // ★#13 使ったらなくなる(捨て札に行かない。おやつタイム)
targetsAllEnemiesは、分度器ブーメランで使います(次の「分度器ブーメランとクリップ」の節)
IsAttackを、中身に合った名前に変える
CardDragのIsAttackは「敵をねらうカードか」を調べるのに使っていました。攻撃以外のカードも入るようになるので、名前も中身に合わせてTargetsEnemyに変えます。
// ★#13 敵をねらうカードか(攻撃のカードと、targetsEnemy にチェックしたカード)。前の名前は IsAttack
public bool TargetsEnemy => cardView.Data.cardType == CardType.Attack || cardView.Data.targetsEnemy;
名前を変えたので、使っているところ2か所も直します。
if (card != null && card.TargetsEnemy) // ★#13 IsAttack から名前を変えた
- EnemyTargetの
OnPointerEnterの中
if (card.TargetsEnemy) // ★#13 IsAttack から名前を変えた
{
// 敵をねらうカードは、敵の上で離したときだけ使う
- PlayZoneの
OnDropの中
名前を変えたあと、古い名前(IsAttack)が残っているとコンパイルエラーになります。エラーの行をダブルクリックすると、直すところへ飛べます。名前が中身とずれたまま使い続けると、あとで読んだときに「攻撃のカードだけ?」と勘ちがいのもとになるので、気づいたときに直しておきましょう。
状態を持たせよう
えんぴつをけずる・修正テープのために、Fighterに2つの「状態」を持たせます。どちらも「次の1回だけ」効く状態です。
- けずった:次の攻撃のダメージに足す数(スライムちゃん)
- 弱った:次の攻撃が半分になる(敵)
Fighterに足す
private int block; // 防御:ダメージを先に受けとめてくれる
private int sharpen; // ★#13 次の攻撃に足すダメージ(えんぴつをけずる)
private bool weakened; // ★#13 次の攻撃が半分になる(修正テープ)
public int Block => block; // ★#8 ゲージで使う
public int Sharpen => sharpen; // ★#13
public bool Weakened => weakened; // ★#13
一番下に、状態を足す・使うメソッドを足します。
// ★#13 次の攻撃のダメージを増やす
public void AddSharpen(int amount)
{
sharpen += amount;
Debug.Log($"{fighterName}はえんぴつをけずった 次の攻撃 +{sharpen}");
}
// ★#13 増やした分を返して、0に戻す(攻撃するときに1回だけ使う)
public int UseSharpen()
{
int bonus = sharpen;
sharpen = 0;
return bonus;
}
// ★#13 次の攻撃を半分にする
public void Weaken()
{
weakened = true;
Debug.Log($"{fighterName}は弱った 次の攻撃が半分になる");
}
// ★#13 弱っていたら攻撃を半分にして、弱った状態を消す
public int UseWeaken(int attack)
{
if (!weakened)
{
return attack;
}
weakened = false;
return attack / 2;
}
UseSharpen・UseWeaken:使ったら消える状態なので、「値を返す」と「0に戻す」を1つのメソッドにまとめています- 呼ぶ側が戻し忘れて、ずっと+4のまま…ということが起きません
attack / 2:int同士の割り算なので、小数は切り捨て。9なら4になります
状態のアイコン
HpGaugeに、状態のアイコンを出すところを足します。#8の防御の盾(BlockBadge)と同じで、「あるときだけ出す」形です。
[SerializeField] private TextMeshProUGUI blockText;
[Header("★#13 状態のアイコン(使わないものは空でよい)")]
[SerializeField] private GameObject sharpenBadge; // けずった(次の攻撃 +4 など)
[SerializeField] private TextMeshProUGUI sharpenText;
[SerializeField] private GameObject weakBadge; // 弱った(次の攻撃が半分)
blockText.text = fighter.Block.ToString();
// ★#13 状態は、あるときだけアイコンを出す
if (sharpenBadge != null)
{
sharpenBadge.SetActive(fighter.Sharpen > 0);
sharpenText.text = "+" + fighter.Sharpen;
}
if (weakBadge != null)
{
weakBadge.SetActive(fighter.Weakened);
}
Updateの、防御の盾のところの下に足す- スライムちゃんには「けずった」だけ、敵には「弱った」だけを付けるので、使わない方は空のままにします。空のときは
!= nullで飛ばします
アイコンを置きます。
- PlayerAreaの子にSharpenBadge(UI (Canvas)>Image)
- Pos X:150、Pos Y:20(ゲージの右)、Width・Height:44
- Source Image:icon_sharpen、Preserve Aspectにチェック、Raycast Targetのチェックを外す
- SharpenBadgeの子にSharpenText(Text – TextMeshPro)
- Pos X:46、Width:70、Height:40
- テキスト:+4、Font Size:28、Font Style:B、Color:#D98C1A、Alignment:左
- PlayerAreaのHp Gauge:Sharpen BadgeにSharpenBadge、Sharpen TextにSharpenText
- Prefabs/Enemyを開いて、Enemyの子にWeakBadge(UI (Canvas)>Image)
- Pos X:126、Pos Y:-68(ゲージの右。防御の盾の反対側)、Width・Height:40
- Source Image:icon_weak、Preserve Aspectにチェック、Raycast Targetのチェックを外す
- EnemyのHp Gauge:Weak BadgeにWeakBadge

えんぴつをけずる(SharpenEffect)
- DeckBuilding/Scriptsに「SharpenEffect」スクリプトを作る
using UnityEngine;
// 次に使う攻撃のダメージを増やす(えんぴつをけずる)
[CreateAssetMenu(fileName = "SharpenEffect", menuName = "DeckBuilding/Effect/Sharpen")]
public class SharpenEffect : CardEffect
{
[SerializeField] private int amount = 4;
public override void Apply(Fighter player, Fighter enemy, Deck deck, Energy energy)
{
player.AddSharpen(amount);
}
}
けずった分を、攻撃のダメージに足します。DamageEffectのApplyを直します。
int bonus = player.UseSharpen(); // ★#13 えんぴつをけずった分を足す
for (int i = 0; i < hits; i++)
{
enemy.TakeDamage(amount + bonus);
}
forの前で1回だけUseSharpenを呼ぶ- ハサミ(3ダメージ×2回)なら、7ダメージ×2回になります。1回目だけに足したいときは、
forの中でi == 0のときだけ足す形にしてもOKです
- ハサミ(3ダメージ×2回)なら、7ダメージ×2回になります。1回目だけに足したいときは、
分度器ブーメランとクリップ
分度器ブーメラン(AllDamageEffect)
敵全体にダメージを与える効果です。
- DeckBuilding/Scriptsに「AllDamageEffect」スクリプトを作る
using UnityEngine;
// 敵全体にダメージを与える(分度器ブーメラン)
[CreateAssetMenu(fileName = "AllDamageEffect", menuName = "DeckBuilding/Effect/All Damage")]
public class AllDamageEffect : CardEffect
{
[SerializeField] private int amount = 5;
public override void Apply(Fighter player, Fighter enemy, Deck deck, Energy energy)
{
int bonus = player.UseSharpen(); // えんぴつをけずった分を足す
EnemyParty party = FindFirstObjectByType<EnemyParty>();
foreach (EnemyView view in party.Views)
{
view.Fighter.TakeDamage(amount + bonus);
}
}
}
Applyに渡されるのは、ねらった1体だけです。全体に当てたいので、EnemyPartyを探して、Viewsの全員にダメージを入れます- 攻撃のカードなので、どれか1体の上で離して使います(全員に当たります)
全員をねらっていると分かるようにする
このままだと、分度器ブーメランを持ってブリキロボの上に来たとき、ブリキロボにだけねらいの枠が出て、単体攻撃と見まちがえてしまいます。全体攻撃のカードのときは、枠を敵全員に出して、カードも全員の真ん中へ飛ぶようにします。
CardDragに、全員をねらうカードかを返すプロパティを足します(TargetsEnemyの下)。
public bool TargetsAllEnemies => cardView.Data.targetsAllEnemies; // ★#13 敵全員をねらうカードか
EnemyTargetは、次のように直します。
using UnityEngine;
using UnityEngine.EventSystems;
// 敵をねらうカードを持って上に来たら、ねらっている印の枠を出す
public class EnemyTarget : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[SerializeField] private GameObject targetFrame; // ねらっている印の枠
public string EnemyName => GetComponent<Fighter>().FighterName; // ★#8 名前は Fighter から取る
private void Start()
{
targetFrame.SetActive(false);
}
public void OnPointerEnter(PointerEventData eventData)
{
// 敵をねらうカードをドラッグしているときだけ、枠を出す
if (eventData.pointerDrag == null)
{
return;
}
CardDrag card = eventData.pointerDrag.GetComponent<CardDrag>();
if (card != null && card.TargetsEnemy) // ★#13 IsAttack から名前を変えた
{
if (card.TargetsAllEnemies)
{
SetAllFrames(true); // ★#13 全体攻撃は、敵全員に枠を出す
}
else
{
targetFrame.SetActive(true);
}
}
}
public void OnPointerExit(PointerEventData eventData)
{
SetAllFrames(false); // ★#13 全員の枠を消す(1体だけ出ているときも、これで消える)
}
// ★#13 敵全員の枠を、出す・消す
public static void SetAllFrames(bool show)
{
foreach (EnemyTarget target in FindObjectsByType<EnemyTarget>(FindObjectsSortMode.None))
{
target.targetFrame.SetActive(show);
}
}
// ★#13 敵全員の真ん中の位置(全体攻撃のカードを飛ばす先)
public static Vector3 CenterOfAll()
{
EnemyTarget[] targets = FindObjectsByType<EnemyTarget>(FindObjectsSortMode.None);
Vector3 sum = Vector3.zero;
foreach (EnemyTarget target in targets)
{
sum += target.transform.position;
}
return sum / targets.Length;
}
}
- 全体攻撃のカードなら、
SetAllFrames(true)で全員に枠を出す staticのメソッドは、どの敵からでもEnemyTarget.SetAllFrames(…)の形で呼べます。中でFindObjectsByTypeを使って、シーンにいる敵全員の枠を切りかえますtargetFrameはprivateですが、同じEnemyTargetクラスの中なので、ほかの敵のtargetFrameにもさわれます
CenterOfAll:敵全員の位置を足して、人数で割る(平均)と、真ん中の位置になります
PlayZoneでは、使ったら枠を消して、全体攻撃のカードは真ん中へ飛ばします。
card.MarkUsed();
EnemyTarget.SetAllFrames(false); // ★#13 使ったら、ねらいの枠を消す
OnDropのcard.MarkUsed();の下に足す- カードを離したあとも、マウスが敵の上にあるうちは枠が残っていたので、使ったらすぐに消します
Fighter target = enemy != null ? enemy : player; // 攻撃は敵へ、ほかはスライムちゃんへ
Vector3 flyTo = target.transform.position;
if (data.targetsAllEnemies)
{
flyTo = EnemyTarget.CenterOfAll(); // ★#13 全体攻撃は、敵全員の真ん中へ飛ばす
}
CardFly fly = card.GetComponent<CardFly>();
yield return StartCoroutine(fly.FlyTo(flyTo));
PlayCardの始めの3行を、この形に直す

クリップでとめる(効果を2つ並べるだけ)
クリップは「防御3を得て、ねらった敵に3ダメージ」です。これは新しいスクリプトは要りません。#7で作ったBlockEffectとDamageEffectのアセットを、カードのEffectsに2つ並べるだけで出来ます。
#7で効果を「CardEffectを受け継いだ部品」にしておいたので、部品を組み合わせるだけで新しいカードが出来ます。クリップは防御のカードですが、targetsEnemyにチェックを入れて、敵をねらって使えるようにします。
修正テープ(WeakenEffect)
ねらった敵を「弱った」状態にして、予告の数字を半分に書き換えます。
- DeckBuilding/Scriptsに「WeakenEffect」スクリプトを作る
using UnityEngine;
// ねらった敵の次の攻撃を半分にする(修正テープ)
[CreateAssetMenu(fileName = "WeakenEffect", menuName = "DeckBuilding/Effect/Weaken")]
public class WeakenEffect : CardEffect
{
public override void Apply(Fighter player, Fighter enemy, Deck deck, Energy energy)
{
if (enemy == null)
{
return;
}
enemy.Weaken();
// 予告の数字を、半分にした数字に出し直す
EnemyView view = enemy.GetComponent<EnemyView>();
if (view != null)
{
view.ShowIntent();
}
}
}
EnemyViewのShowIntentをpublicにして、弱っていたら半分の数字を出すようにします。
// 次の行動の予告を出す(★#13 修正テープから呼ぶので public に)
public void ShowIntent()
{
EnemyData.EnemyAction action = NextAction;
if (action.attack > 0)
{
intentIcon.sprite = attackIcon;
int attack = fighter.Weakened ? action.attack / 2 : action.attack; // ★#13 弱っていたら半分
intentText.text = attack.ToString();
}
ShowIntentは#8で作った「予告がポンと出る」メソッドなので、呼び直すだけで予告が1.6倍からポンと出て、新しい数字に書き換わります
BattleManagerの敵のターンで、攻撃を半分にします。
int attack = enemy.Fighter.UseWeaken(action.attack); // ★#13 弱っていたら半分に
player.TakeDamage(attack);
player.TakeDamage(action.attack);の1行を、この2行に置きかえる

大そうじ(ReshuffleHandEffect)
手札を全部捨てて、同じ枚数を引き直します。せっかくなので、捨てるカードが捨て札の場所へ飛んでいくようにします。ターン終了で手札を捨てるときも、同じ動きになります。
Deckに、捨て札の場所を足します。
[SerializeField] private TextMeshProUGUI discardPileText; // 捨て札の枚数
[SerializeField] private RectTransform discardPileArea; // ★#13 捨て札の場所(捨てたカードが飛んでいく)
DiscardHandで、カードをすぐに消すかわりに飛ばします。
foreach (CardView view in handViews)
{
handLayout.Remove((RectTransform)view.transform); // ★#5
StartCoroutine(FlyToDiscard(view)); // ★#13 捨て札へ飛ばしてから消す
}
UseCardの上に、2つのメソッドを足します。
// ★#13 カードを捨て札の場所へ飛ばしてから消す
private IEnumerator FlyToDiscard(CardView view)
{
view.GetComponent<CanvasGroup>().blocksRaycasts = false; // 飛んでいる間はつかめないように
yield return StartCoroutine(view.GetComponent<CardFly>().FlyTo(discardPileArea.position));
Destroy(view.gameObject);
}
// ★#13 手札を全部捨てて、同じ枚数を引き直す
public void ReshuffleHand()
{
int count = hand.Count;
DiscardHand();
Draw(count);
}
- 飛ぶ動きは、#10のCardFlyをそのまま使います
- DeckのDiscard Pile Areaに、Hud>DiscardPileを入れる
- DeckBuilding/Scriptsに「ReshuffleHandEffect」スクリプトを作る
using UnityEngine;
// 手札を全部捨てて、同じ枚数を引き直す(大そうじ)
[CreateAssetMenu(fileName = "ReshuffleHandEffect", menuName = "DeckBuilding/Effect/Reshuffle Hand")]
public class ReshuffleHandEffect : CardEffect
{
public override void Apply(Fighter player, Fighter enemy, Deck deck, Energy energy)
{
deck.ReshuffleHand();
}
}

おやつタイム(使ったらなくなる札)
おやつタイムは、使ったら捨て札に行かずに「なくなる」カードです。Deckに「なくなった札」のリストを足します。
private List<CardData> discardPile = new List<CardData>(); // 捨て札
private List<CardData> exhaustPile = new List<CardData>(); // ★#13 なくなった札(もう使えない)
UseCardのdiscardPile.Add(card);を、次のように直します。
hand.RemoveAt(index);
handViews.RemoveAt(index);
if (card.exhaust)
{
exhaustPile.Add(card); // ★#13 使ったらなくなる札は、捨て札に入れない
}
else
{
discardPile.Add(card);
}
- 捨て札に入らないので、山札に戻ることもありません。その戦いの間は、もう出てきません
- 回復量(10)が大きいかわりに1回きり、という調整です

カードを作ろう
最後に、効果のアセットとカードのアセットを作ります。作り方は#7と同じです(Projectウインドで右クリック>Create>DeckBuilding>…)。
効果(Data/Effects)
| ファイル名 | 種類(Create>DeckBuilding>Effect>) | 値 |
|---|---|---|
| AllDamage5 | All Damage | Amount:5 |
| Block3 | Block | Amount:3 |
| Damage3 | Damage | Amount:3、Hits:1 |
| Sharpen4 | Sharpen | Amount:4 |
| Weaken | Weaken | (なし) |
| ReshuffleHand | Reshuffle Hand | (なし) |
| Heal10 | Heal | Amount:10 |
カード(Data/Cards、Create>DeckBuilding>Card Data)
| ファイル名 | Card Name | Cost | Card Type | Description | Artwork | Effects | Targets Enemy | Exhaust |
|---|---|---|---|---|---|---|---|---|
| Protractor | 分度器ブーメラン | 1 | Attack | 敵全体に5ダメージ | card_protractor | AllDamage5 | - | - |
| Clip | クリップでとめる | 1 | Defense | 防御を3得て、ねらった敵に3ダメージ | card_clip | Block3、Damage3 | ✓ | - |
| Sharpener | えんぴつをけずる | 1 | Special | 次の攻撃のダメージ+4 | card_sharpener | Sharpen4 | - | - |
| CorrectionTape | 修正テープ | 1 | Special | ねらった敵の次の攻撃を半分にする | card_tape | Weaken | ✓ | - |
| Cleanup | 大そうじ | 0 | Special | 手札を全部捨てて、同じ枚数を引く | card_broom | ReshuffleHand | - | - |
| Snack | おやつタイム | 1 | Heal | HPを10回復。使ったらなくなる | card_snack | Heal10 | - | ✓ |

- 分度器ブーメランは、表にない Targets All Enemies にもチェックを入れる
- 新しい6枚を、#12で作ったRewardViewのCandidatesにも足しておくと、報酬に出てくるようになります
修正テープとクリップは、Targets Enemyにチェックを入れ忘れると、スライムちゃんの上で使えてしまいます。そのときは敵がnullなので、修正テープは何も起きず、クリップは防御だけ得て、ダメージが入りません。
動かして確認!
確かめやすいように、DeckのStarting Deckを「えんぴつをけずる・分度器ブーメラン・修正テープ・定規でペシッ×2」の5枚にして、再生してみましょう(確かめたら元の3行に戻しましょう)。
- えんぴつをけずる → 分度器ブーメランをブリキロボに:2体ともに9ダメージ(5+4)
- 修正テープをブリキロボに:予告が6→3
- ターン終了:ブリキロボの攻撃は3


#13の動作確認
- えんぴつをけずるで、ゲージの右に鉛筆と「+4」。次の攻撃に+4されて、アイコンが消える
- 分度器ブーメランを持って敵の上に来ると、敵全員に枠が出る。どの敵の上で離しても、カードは全員の真ん中へ飛び、全員にダメージが入る
- クリップは敵の上で使えて、防御3と3ダメージが両方入る
- 修正テープで、ねらった敵の予告が半分にポンと書き換わり、紫の矢印が出る。敵の攻撃が半分になったら矢印が消える
- 大そうじで、手札が捨て札へ飛んでいき、同じ枚数を引き直す。ターン終了のときも、手札が捨て札へ飛んでいく
- おやつタイムを使うと、捨て札の数字が増えない
スライム15種類そろうと、どれを先に使うか、かなり迷いますね・・・



それがデッキ構築の楽しいところ!「けずってからブーメラン」みたいな組み合わせを見つけてね
あとがき
今回は、残りの6枚を作って、15種類のカードがそろいました。#7で効果を部品にしておいたおかげで、クリップのように部品を組み合わせるだけのカードや、1つの効果を足すだけのカードが、すぐに作れましたね。次回は、1戦目→2戦目→3戦目→ボスと戦いをつなげて、報酬で強くしたデッキでボスに挑めるようにしたいと思います!
コメント
コメント一覧 (1件)
[…] 前回:#13 特殊なカードを増やそう […]