カードドローシステムは、カードゲームの核心です。
山札の生成とシャッフルを実装すれば、ランダム性が確保できます。
この記事では、実装方法を詳しく解説します。
✨ この記事でわかること
- 山札生成の実装
- シャッフルアルゴリズムの実装
- 手札ドローの実装
- 墓地処理の実装
- 実装例とコード

カードドローシステムは、山札生成から始めましょう。デッキから山札を作成すれば、ゲームが開始できます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
山札生成の実装

山札生成は、ゲーム開始時にデッキの内容を元に、実際に使用する山札を作成する処理です。
デッキと山札を分けて管理することで、ゲーム進行中にカードを引いたり、捨てたりしても、
元のデッキ構成を保持できます。
ここでは、デッキ情報をコピーして山札を生成し、初期状態でシャッフルを行う基本的な実装方法を紹介します。
山札生成システム
|
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 |
using UnityEngine; using System.Collections.Generic; public class DeckManager : MonoBehaviour { public List<CardData> deck = new List<CardData>(); public List<CardData> drawPile = new List<CardData>(); public List<CardData> hand = new List<CardData>(); public List<CardData> discardPile = new List<CardData>(); public void InitializeDrawPile() { // デッキをコピーして山札を作成 drawPile = new List<CardData>(deck); // シャッフル ShuffleDrawPile(); } public void ShuffleDrawPile() { // Fisher-Yatesシャッフルアルゴリズム for (int i = drawPile.Count - 1; i > 0; i--) { int randomIndex = Random.Range(0, i + 1); CardData temp = drawPile[i]; drawPile[i] = drawPile[randomIndex]; drawPile[randomIndex] = temp; } } } |
InitializeDrawPile()では、デッキのリストをそのまま使わず、新しいリストとしてコピーしています。
これは、ゲーム中に山札の内容が変化しても、元のデッキデータに影響を与えないためです。
また、山札生成と同時にシャッフルを行うことで、ゲーム開始時点からランダム性が確保されます。
シャッフルには、偏りの少ないFisher-Yatesシャッフルを採用しています。

この構成により、山札・手札・墓地を明確に分離した、扱いやすいカード管理システムを実装できます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
シャッフルアルゴリズムの実装

シャッフルアルゴリズムは、カードの順序をランダム化し、ゲームの公平性と再現性のなさを担保する重要な処理です。
実装方法を誤ると、特定のカードが出やすくなるなど、プレイ体験に大きな影響を与えます。
ここでは、カードゲームで最も標準的に使われているFisher-Yatesシャッフルを、再利用しやすい形で実装します。
Fisher-Yatesシャッフル
|
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 |
public class ShuffleSystem : MonoBehaviour { public List<CardData> Shuffle(List<CardData> cards) { List<CardData> shuffled = new List<CardData>(cards); // Fisher-Yatesシャッフル for (int i = shuffled.Count - 1; i > 0; i--) { int randomIndex = Random.Range(0, i + 1); CardData temp = shuffled[i]; shuffled[i] = shuffled[randomIndex]; shuffled[randomIndex] = temp; } return shuffled; } public void ShuffleInPlace(List<CardData> cards) { // 元のリストを直接シャッフル for (int i = cards.Count - 1; i > 0; i--) { int randomIndex = Random.Range(0, i + 1); CardData temp = cards[i]; cards[i] = cards[randomIndex]; cards[randomIndex] = temp; } } } |
Shuffle()は、元のリストを変更せずに、新しいシャッフル済みリストを返すメソッドです。
「元データを保持したい場面」や「安全に処理したい場合」に適しています。
一方、ShuffleInPlace()は、渡されたリスト自体を書き換えるメソッドです。
山札や墓地など、状態として管理しているリストを直接シャッフルしたい場合に向いています。
このように用途に応じてメソッドを分けておくことで、カード管理処理を柔軟かつ安全に再利用できます。

Fisher-Yatesシャッフルは、すべての並び順が等確率になることが数学的に保証されています。
そのため、カードゲームやガチャシステムなど、公平性が求められる場面で広く使われています。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
手札ドローの実装

手札ドローは、山札からカードを引いて手札に加える処理です。
単純にカードを1枚引くだけでなく、山札切れ・手札上限・再シャッフルなど、ゲーム進行上のルールをまとめて管理する必要があります。
ここでは、カードゲームで一般的な挙動を想定した、安全に使えるドローシステムを実装します。
ドローシステム
|
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 |
public class DrawSystem : MonoBehaviour { public DeckManager deckManager; public int maxHandSize = 7; public CardData DrawCard() { // 山札が空なら墓地から再シャッフル if (deckManager.drawPile.Count == 0) { ReshuffleFromDiscard(); } // 山札がまだ空ならドローできない if (deckManager.drawPile.Count == 0) { return null; } // 手札が満杯ならドローできない if (deckManager.hand.Count >= maxHandSize) { return null; } // カードをドロー CardData card = deckManager.drawPile[0]; deckManager.drawPile.RemoveAt(0); deckManager.hand.Add(card); return card; } public void DrawCards(int count) { for (int i = 0; i < count; i++) { DrawCard(); } } void ReshuffleFromDiscard() { // 墓地を山札に戻す deckManager.drawPile.AddRange(deckManager.discardPile); deckManager.discardPile.Clear(); // シャッフル deckManager.ShuffleDrawPile(); } } |
DrawCard()では、カードを引く前に必ずドロー可能かどうかを判定しています。
この順序でチェックすることで、想定外の状態でもエラーを起こさずに処理できます。
- 山札が空の場合は、墓地を戻して再シャッフル
- それでも山札が空なら、ドロー不可として処理終了
- 手札が上限に達している場合も、ドローを中断
カードは山札の先頭から取得し、手札に追加します。
この処理により、山札 → 手札というカードの流れが明確になります。
また、DrawCards()を用意することで、初期手札配布や複数枚ドローなどの処理を簡潔に記述できます。
このようにドロー処理を1か所にまとめておくことで、ルール変更や演出追加にも対応しやすい設計になります。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
墓地処理の実装

墓地処理は、使用したカードを管理します。 実装方法を紹介します。
墓地管理システム
墓地は、使用済みカードを一時的に保管するための領域です。
単なる「捨て場」ではなく、山札切れ時の再利用やゲームルールの制御に直結する重要な要素になります。
ここでは、手札からカードを墓地に送る処理と、墓地を山札に戻す基本的な管理システムを実装します。
|
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 |
public class DiscardSystem : MonoBehaviour { public DeckManager deckManager; public void DiscardCard(CardData card) { // 手札から削除して墓地へ送る if (deckManager.hand.Contains(card)) { deckManager.hand.Remove(card); deckManager.discardPile.Add(card); } } public void DiscardHand() { // 手札をすべて墓地に送る deckManager.discardPile.AddRange(deckManager.hand); deckManager.hand.Clear(); } public void ShuffleDiscardIntoDraw() { // 墓地を山札に戻して再シャッフル deckManager.drawPile.AddRange(deckManager.discardPile); deckManager.discardPile.Clear(); deckManager.ShuffleDrawPile(); } } |
DiscardCard()は、カード使用時や効果解決後に、特定のカードだけを墓地に送る場合に使用します。
DiscardHand()は、ターン終了時やラウンド終了時など、手札を一括でリセットしたい場面で役立ちます。
ShuffleDiscardIntoDraw()は、山札が尽きた際に、墓地を山札へ戻してゲームを継続するための処理です。
この処理を分離しておくことで、再シャッフルのタイミングをルールごとに柔軟に制御できます。
墓地管理を独立したシステムとして実装しておくと、カード効果による回収や除外などの拡張も容易になります。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
確率のブレを抑える工夫

カードゲームでは、理論上は同じ確率でも、短期的には極端な結果が続くことがあります。
これをそのまま採用すると、「全然出ない」「不公平に感じる」といったプレイヤー体験の低下につながる場合があります。
そこで一部のゲームでは、確率そのものを変えずに、結果の偏りだけを緩和する仕組みが採用されています。
いわゆる「天井システム」や「救済処理」です。
ここでは、その一例として、指定回数引いても出なかった場合にのみ保証する仕組みを実装します。
確率調整システム
|
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 |
public class ProbabilityControlSystem : MonoBehaviour { public DeckManager deckManager; public CardData DrawCardWithGuarantee(string cardID, int guaranteeAfter) { // 指定枚数引いても出なかったら確実に出す int cardsDrawn = 0; List<CardData> drawnCards = new List<CardData>(); while (cardsDrawn < guaranteeAfter) { CardData card = deckManager.DrawCard(); if (card == null) { break; } drawnCards.Add(card); cardsDrawn++; if (card.cardID == cardID) { // 見つかったら返す return card; } } // 見つからなかったら山札から探す CardData guaranteedCard = deckManager.drawPile.Find(c => c.cardID == cardID); if (guaranteedCard != null) { deckManager.drawPile.Remove(guaranteedCard); return guaranteedCard; } // 見つからなかったら引いたカードを戻す deckManager.drawPile.AddRange(drawnCards); return null; } } |
この処理では、まず通常のドローを繰り返し、指定した回数以内に目的のカードが出るかを確認します。
途中で目的のカードが出た場合は、その時点で処理を終了します。
規定回数引いても出なかった場合のみ、山札の中から対象カードを検索し、強制的に取得します。
これにより、理論確率を大きく崩さずに救済処理を実現できます。
なお、この仕組みはすべてのカードゲームに適しているわけではありません。
対戦型TCGなど完全なランダム性が求められるゲームでは、使用を避けるべきです。
一方で、ソロプレイ中心のゲームやガチャ要素を含むゲームでは、プレイヤー体験を安定させるための有効な選択肢になります。
確率調整は「ズル」ではなく、どの体験を提供したいかに応じた設計判断として慎重に導入することが重要です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
実装例:完全なカードドローシステム

ここまで紹介してきた各システムを組み合わせることで、実際のゲームでそのまま使えるカードドローシステム一式を構築できます。
この実装例では、山札生成・シャッフル・ドロー・墓地管理といった個別の責務を分離したまま、ゲーム開始時の初期化処理をまとめています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using UnityEngine; public class CompleteCardDrawSystem : MonoBehaviour { [Header("システム")] public DeckManager deckManager; public ShuffleSystem shuffleSystem; public DrawSystem drawSystem; public DiscardSystem discardSystem; public ProbabilityControlSystem probabilityControl; public void StartGame() { // 山札を初期化 deckManager.InitializeDrawPile(); // 初期手札をドロー drawSystem.DrawCards(5); } } |
StartGame()では、ゲーム開始時に必要な処理のみをまとめています。
ここで山札を初期化し、初期手札を配ることで、カードゲームとしてのスタート状態を簡潔に構築できます。
各機能はそれぞれ専用のクラスに分かれているため、カード効果やルールが増えても、特定のシステムだけを拡張すれば対応できます。
例えば、ターン終了時の手札破棄は DiscardSystem に、特殊ドローや確率補正は ProbabilityControlSystem に追加するなど、責務ごとに機能を増やせる構成になっています。
このように、システムを疎結合に設計しておくことで、小規模なカードゲームから本格的なデッキ構築型ゲームまで、柔軟に対応できる土台を作ることができます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
よくある質問(FAQ)

Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
まとめ

カードドローシステムは、山札生成から始めましょう。
デッキから山札を作成し、シャッフルすれば、ゲームが開始できます。
✅ 今日から始める3ステップ
- ステップ1:山札生成システムを実装する(所要2時間)
- ステップ2:シャッフルアルゴリズムを実装する(所要1時間)
- ステップ3:手札ドローシステムを実装する(所要2時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる



コメント