Unityを使用して作成したシャドウバース風カードゲームの作り方のメモです。トグルボタンをクリックすると記事が表示されます。小さくて見ずらい画像はクリックで拡大できます。
シャドウバースについて
シャドウバースは、Cygamesによって開発された日本のトレーディングカードゲームです。
以下はゲームの基本ルールです。
- リーダーとデッキ:
- プレイヤーはゲームを進めるための「リーダー」と呼ばれるキャラクターを選び、デッキを構築します。
- プレイヤーターン:
- ターンは、自分と相手の2つのフェーズに分かれています。まず、自分のターンがあり、その後相手のターンが続きます。
- コストとマナ:
- カードをプレイするには、コストと呼ばれるリソースが必要です。コストは、手札のカードに表示されている数字です。これに対応するリソースは「マナ」と呼ばれ、ターンごとにリフレッシュされます。
- 進化:
- プレイヤーは自分のターン中に進化ポイントを使ってカードを進化させることができます。進化したカードは能力が向上し、進化前よりも強力になります。
- アタックとディフェンス:
- プレイヤーは進化したカードや進化ポイントを使ったカードで相手のリーダーを攻撃します。相手のリーダーの体力をゼロにすると勝利となります。
- シャドウ:
- フィールド上に破壊されたカードは「シャドウ」として墓地に送られます。一部のカードはシャドウを利用して強力な効果を発動させることができます。
- 勝利条件:
- 相手のリーダーの体力をゼロにするか、相手がデッキを使い切った時点で勝利となります。
UIの作成
カードのベースになるオブジェクトを作成します。
Canvas→Imageを作成しました。名前はCardにしました。

サイズを調整してImageをremoveしました。

カードの背景部分を作成します。
子オブジェクトにImageを作成しました。名前はBackPanelにしました。

サイズを親オブジェクトに合わせました。

カードの画像を設定するオブジェクトを作成します。
Cardの子オブジェクトにImageを作成しました。名前はCharaImageにしました。

Spriteを設定してサイズを調整しました。
画像はこちらのアセットを使用しました。

カードの名前、攻撃力、HP、コストを表示するテキストを作成します。
テキストを4つ作成して画像のように配置しました。

位置の調整が終わったらCardをprefabにしておきます。

手札を表示する場所を作成します。
UI→Panelを作成しました。名前はPlayerHandにしました。

位置を調整してadd componentからHorizontalLayoutGroupをアタッチしました。

実際にカードを配置してHorizontalLayoutGroupを設定しました。


プレイヤーのフィールドを作成します。
PlayerHandをコピーして名前を変更してPlayerFieldを作成しました。

手札と同じようにカードを配置してサイズを調整しました。

敵の手札とフィールドを作成します。
プレイヤーのフィールドが完成したらPlayerFieldとPlayerHandをコピーして敵のフィールドを作成します。

PosYの+-を変更するだけで位置調整は完了します。

ヒーローの背景部分を作成します。
UI→Imageを作成しました。名前はPlayerHeroにしました。

ヒーローの画像部分を作成します。
PlayerHeroの子オブジェクトにImageを作成しました。名前はIconにしました。

IconにSpriteを設定して位置を調整しました。

ヒーローのHPを表示するテキストを作成します。
PlayerHeroの子オブジェクトにTextを作成しました。名前はHPにしました。

テキストの位置、色、サイズを調整しました。

敵のヒーローを作成します。
PlayerHeroをコピーして位置を調整しました。

ターンエンドボタン、制限時間、コストを表示するUI作成しました。


カードのデータについて
scriptableObjectでこのようなカードデータを作成します。

新しくC#スクリプトを作成しました。名前はCardEntityにしました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//カードデータ本体
[CreateAssetMenu(fileName ="CardEntity",menuName ="Create CardEntity")]
public class CardEntity : ScriptableObject
{
public new string name;
public int hp;
public int at;
public int cost;
public Sprite icon;
}
プロジェクトビューの+ボタンをクリックします。

CreateCardEntityという項目ができているのでクリックします。

するとファイルが作成されます。名前をCard1に変更しました。

inspector画面からカードデータを設定します。3種類ほど作成しました。

このデータを読み込ませてカードに反映します。
Resourcesフォルダを作成してその中に新しくCardEntityListフォルダを作成しました。

CardEntityListフォルダにカードデータのファイルを入れておきました。

prefabsフォルダもResourcesフォルダに入れておきました。

カード側にデータを保持させるスクリプトを作成します。
新しくC#スクリプトを作成しました。名前はCardModelにしました。
using UnityEngine;
//カードのデータ
public class CardModel
{
public string name;
public int hp;
public int at;
public int cost;
public Sprite icon;
public CardModel(int cardID)
{
//リソースフォルダからカードデータを取得してカードデータを作成する
CardEntity cardEntity = Resources.Load<CardEntity>("CardEntityList/Card"+cardID);
name = cardEntity.name;
hp = cardEntity.hp;
at = cardEntity.at;
cost = cardEntity.cost;
icon = cardEntity.icon;
}
}
オブジェクトにアタッチする必要はありません。フォルダの名前が違っているとエラーになります。
カードのデータを操作するスクリプトを作成します。
新しくC#スクリプトを作成しました。名前はCardControllerにしました。
using UnityEngine;
public class CardController : MonoBehaviour
{
//カードデータを表示する
CardView view;
//カードデータに関する事を操作
CardModel model;
private void Awake()
{
view = GetComponent<CardView>();
}
public void Init(int cardID)
{
model = new CardModel(cardID);
view.Show(model);
}
}
CardControllerをカードprefabを開いてアタッチしました。
カードの見た目を変更するスクリプトを作成します。
新しくC#スクリプトを作成しました。名前はCardViewにしました。
using UnityEngine;
using UnityEngine.UI;
public class CardView : MonoBehaviour
{
[SerializeField] Text nameText;
[SerializeField] Text hpText;
[SerializeField] Text atText;
[SerializeField] Text cosText;
[SerializeField] Image iconImage;
public void Show(CardModel cardModel)
{
nameText.text = cardModel.name;
hpText.text = cardModel.hp.ToString();
atText.text = cardModel.at.ToString();
cosText.text = cardModel.cost.ToString();
iconImage.sprite = cardModel.icon;
}
}
CardViewスクリプトをカードprefabを開いてアタッチしてテキストとイメージを設定しました。

基本的な機能の実装
一度カード手札に生成してデータが反映されているかチェックします。
新しくC#スクリプトを作成しました。名前はGameManagerにしました。
using UnityEngine;
public class GameManager : MonoBehaviour
{
//カードのプレファブを入れる
[SerializeField] CardController cardPrefab;
//手札のTransformを入れる
[SerializeField] Transform PlayerHandTransform;
void Start()
{
CreateCard(PlayerHandTransform);
}
void CreateCard(Transform hand)
{
CardController card = Instantiate(cardPrefab, hand, false);
//()にカードIDを入れる
card.Init(1);
}
}
この場合CardEntityでデータを入力したCard1が手札に生成されます。

空のオブジェクトを作成してアタッチしました。
カードのプレファブとPlayerHandをアタッチしました。

Unityを実行するとカード(Card1)が手札に生成されます。

ドラッグ&ドロップでカードをフィールドに出せるようにします。
カードのプレファブを開いてadd componentでCanvasGroupを追加しました。

カードをドラッグ&ドロップで移動させるにはCanvasGroupコンポーネントのBlockRaycastsをスクリプトから操作する必要があります。
新しくC#スクリプトを作成しました。名前CardMovementにしました。
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UIElements;
public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
public Transform defaultParent;
public void OnBeginDrag(PointerEventData eventData)
{
defaultParent = transform.parent;
transform.SetParent(defaultParent.parent, false);
GetComponent<CanvasGroup>().blocksRaycasts = false;
}
public void OnDrag(PointerEventData eventData)
{
//カード引っ張ったときに行う処理
transform.position = eventData.position;
}
public void OnEndDrag(PointerEventData eventData)
{
//カードを離したときに行う処理
transform.SetParent(defaultParent, false);
GetComponent<CanvasGroup>().blocksRaycasts = true;
}
}
作成したスクリプトをカードprefabを開いてアタッチしました。

新しくC#スクリプトを作成しました。名前はDropPlaceにしました。
using UnityEngine;
using UnityEngine.EventSystems;
public class DropPlace : MonoBehaviour, IDropHandler
{
public void OnDrop(PointerEventData eventData)
{
//カードが重なった時に親を変更する
CardMovement card = eventData.pointerDrag.GetComponent<CardMovement>();
if (card != null)
{
card.defaultParent = this.transform;
}
}
}
作成したスクリプトをPlayerHandとPlayerFieldにアタッチしました。

手札のカードをフィールドにドラッグ&ドロップすると移動できるようになりました。

ゲームの実装
両プレイヤーに3枚づつカードを配るようにします。
GameManagerスクリプトを開いてコードを追加します。
public class GameManager : MonoBehaviour
{
//カードのプレファブを入れる
[SerializeField] CardController cardPrefab;
//両プレイヤーの手札のTransformを入れる
[SerializeField] Transform PlayerHandTransform,EnemyHandTransform;
void Start()
{
StartGame();
}
private void StartGame()
{
SettingHand();
}
void CreateCard(Transform hand)
{
//手札にカードを生成する
CardController card = Instantiate(cardPrefab, hand, false);
//カードIDを入れる
card.Init(1);
}
void SettingHand()
{
//それぞれの手札に3枚カードを配る
for (int i = 0; i < 3; i++)
{
CreateCard(PlayerHandTransform);
CreateCard(EnemyHandTransform);
}
}
}
GameManagerのinspector画面からEnemyHandを設定しました。

ゲームを開始するとカードが配られるようになります。

ボタンを押してターンを切り替えられるようにします。
GameManagerスクリプトを開いてコードを追加しました。
public class GameManager : MonoBehaviour
{
//プレイヤーのターンかの判定
bool isPlayerTurn;
void Start()
{
StartGame();
isPlayerTurn= true;
TurnCalc();
}
void TurnCalc()
{
//ターン進行の処理をする
if (isPlayerTurn)
{
PlayerTurn();
}
else
{
EnemyTurn();
}
}
void PlayerTurn()
{
//プレイヤーターンの処理
Debug.Log("プレイヤーのターンです");
}
void EnemyTurn()
{
//エネミーターンの処理
Debug.Log("敵のターンです");
//ターンを終了する
ChangeTurn();
}
public void ChangeTurn()
{
//ターンの切り替え処理
isPlayerTurn = !isPlayerTurn;
TurnCalc();
}
}
ターンエンドボタンのinspector画面からChangeTurnを設定しました。

ゲームを実行するとプレイヤーのターンが開始します。ターンエンドボタンをクリックすると敵のターンなります。各ターンではデバックログが表示されます。敵は何もせずターンエンドします。

ターンの切り替え時にカードをドローするようにします。
GameManagerスクリプトのChangeTurnにコードを追加しました。
public void ChangeTurn()
{
//ターンの切り替え処理
isPlayerTurn = !isPlayerTurn;
if (isPlayerTurn)
{
//ドローする
CreateCard(PlayerHandTransform);
}
else
{
//ドローする
CreateCard(EnemyHandTransform);
}
TurnCalc();
}
ゲームを実行してターンが切り替わるときにカードをドローするようになりました。
敵のターンにカードを出せるようにします。
CardMovementに関数を作成しました。
//手札からフィールドにカードの位置を変更する
public void SetCardTransform(Transform parentTransform)
{
defaultParent = parentTransform;
transform.SetParent(defaultParent, false);
}
CardControllerにCardMovement変数を作成しました。
//カードの移動を操作
public CardMovement movement;
private void Awake()
{
movement = GetComponent<CardMovement>();
}
GameManagerのEnemyTurnに処理を書きます
void EnemyTurn()
{
//エネミーターンの処理
Debug.Log("敵のターンです");
//手札のカードリストを取得
CardController[] cardList= EnemyHandTransform.GetComponentsInChildren<CardController>();
//場に出すカードを選択
CardController card= cardList[0];
//カードを移動する
card.movement.SetCardTransform(EnemyFieldTransform);
//ターンを終了する
ChangeTurn();
}
敵のターンになるとカードを出してターンエンドするようになりました。

おすすめのアセット
TCG Engineは、Unityで使えるカードゲーム作成キットです。このキットを使えば、一人用もしくはオンラインで遊べるカードゲームを自分で作ることができます。ログイン機能やユーザーデータの管理、カードの集め方やリーダーボードなど、ゲームに必要なさまざまな機能が揃っています。操作はパソコンやスマホで簡単にでき、自分だけのカードゲームをデザインすることが可能です。プログラミングの知識が必要ですが、コードはシンプルで初心者でも始めやすいです。質問やサポートが必要なら専用のDiscordで助けを求められます。