Unityでトランプゲームのスピードを作ってみよう!
こんにちは!ジェイです。今回は、Unityを使ってカードゲーム「スピード」を実装する方法を詳しく解説します。
初心者でも理解しやすいように、カードの生成からデッキのシャッフル、プレイヤーの手札管理、中央の山札の管理、勝敗判定まで、ステップバイステップで説明します。
また、AIプレイヤーのターン処理や勝敗表示など、ゲームの完成までの全工程を網羅しています。Unityの基本的な操作やスクリプトの書き方を学びながら、実際にゲームを作成する体験ができるので、ぜひ挑戦してみてください。
Unityでトランプゲームのスピードを作ってみよう!
まず、Unityプロジェクトを新規作成します。プロジェクト名は「SpeedCardGame」にしましょう。
アセットの準備
以下のアセットが必要です。
- カードのスプライト(表と裏)
- オーディオクリップ(カードを出す音など)
Unityエディタでの手順
プロジェクトのセットアップ
- Unityを起動し、新しいプロジェクトを作成します。プロジェクト名を「SpeedCardGame」にします。
シーンの準備
- メインシーンを開き、必要なUI要素を配置します。例えば、TextMeshProを使用して勝敗表示用のテキストを配置します。
- Hierarchyビューで右クリックし、
UI > Canvas
を作成します。 - Canvasを選択し、その中に
UI > TextMeshPro - Text
オブジェクトを追加します。名前をresultText
に変更し、非表示に設定します(Inspectorビューでチェックを外す)。 - 同様に、Player1とPlayer2の残りカード枚数を表示するためのTextMeshPro – Textオブジェクトを追加します。名前をそれぞれ
player1CardNumText
とplayer2CardNumText
に変更します。
カードのプレハブを作成
- プロジェクトビューで右クリックし、
Create > Folder
を選択してPrefabs
フォルダを作成します。 - Hierarchyビューで右クリックし、
2D Object > Sprite
を選択してカードオブジェクトを作成します。 - このオブジェクトに
SpriteRenderer
コンポーネントを追加し、カードのスプライトを設定します。さらに、AudioSource
コンポーネントを追加します。 - カードオブジェクトをプロジェクトビューの
Prefabs
フォルダにドラッグし、プレハブとして保存します。
スクリプトの配置
- プロジェクトビューで右クリックし、
Create > Folder
を選択してScripts
フォルダを作成します。 - 以下のスクリプト(Card.cs、Deck.cs、GameManager.cs)を作成し、それぞれ
Scripts
フォルダに保存します。 Deck.cs
をデッキオブジェクトに、Card.cs
をカードプレハブに、GameManager.cs
を空のGameObject(名前をGameManager
にする)にアタッチします。
Card.cs
using UnityEngine;
public class Card : MonoBehaviour
{
public SpriteRenderer spriteRenderer;
public Sprite cardFace;
public Sprite cardBack;
public AudioSource audioSource;
public string suit;
public int rank;
public int DeckNumber = -1;
public int PlayerIndex = -1;
void Start()
{
// SpriteRendererが設定されていることを確認
if (spriteRenderer == null)
{
spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
{
Debug.LogError("SpriteRenderer component is missing on the Card prefab.");
}
}
}
public void Initialize(string cardSuit, int cardRank)
{
suit = cardSuit;
rank = cardRank;
cardFace = Resources.Load<Sprite>($"Cards/{suit}_{rank}");
if (cardFace == null)
{
Debug.LogError($"Failed to load sprite for {suit}_{rank} from path Cards/{suit}_{rank}");
}
audioSource = GetComponent<AudioSource>();
}
public void ShowCardFace()
{
if (cardFace != null)
{
spriteRenderer.sprite = cardFace;
}
else
{
Debug.LogError("Card face sprite is not set.");
}
}
public void ShowCardBack()
{
if (cardBack != null)
{
spriteRenderer.sprite = cardBack;
}
else
{
Debug.LogError("Card back sprite is not set.");
}
}
public void PlaySound()
{
if (audioSource != null)
{
audioSource.Play();
}
}
void OnMouseDown()
{
if (GameManager.Instance != null)
{
GameManager.Instance.OnCardClicked(this);
}
else
{
Debug.LogError("GameManager instance is null.");
}
}
}
Deck.cs
using System.Collections.Generic;
using UnityEngine;
public class Deck : MonoBehaviour
{
public GameObject cardPrefab;
public List<Card> player1Cards = new List<Card>();
public List<Card> player2Cards = new List<Card>();
public Transform player1Deck;
public Transform player2Deck;
public void CreateDeck()
{
string[] suits = { "Hearts", "Diamonds", "Clubs", "Spades" };
int[] ranks = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
foreach (var suit in suits)
{
foreach (var rank in ranks)
{
if (suit == "Clubs" || suit == "Spades")
{
GameObject cardObject = Instantiate(cardPrefab, player1Deck.position, Quaternion.identity);
cardObject.name = suit + "_" + rank;
Card newCard = cardObject.GetComponent<Card>();
newCard.Initialize(suit, rank);
newCard.ShowCardBack();
cardObject.transform.SetParent(player1Deck);
player1Cards.Add(newCard);
}
else if (suit == "Hearts" || suit == "Diamonds")
{
GameObject cardObject = Instantiate(cardPrefab, player2Deck.position, Quaternion.identity);
cardObject.name = suit + "_" + rank;
Card newCard = cardObject.GetComponent<Card>();
newCard.Initialize(suit, rank);
newCard.ShowCardBack();
cardObject.transform.SetParent(player2Deck);
player2Cards.Add(newCard);
}
else
{
Debug.Log("no cards");
}
}
}
}
public void ShuffleDeck()
{
for (int i = 0; i < player1Cards.Count; i++)
{
Card temp = player1Cards[i];
int randomIndex = Random.Range(i, player1Cards.Count);
player1Cards[i] = player1Cards[randomIndex];
player1Cards[randomIndex] = temp;
}
for (int i = 0; i < player2Cards.Count; i++)
{
Card temp = player2Cards[i];
int randomIndex = Random.Range(i, player2Cards.Count);
player2Cards[i] = player2Cards[randomIndex];
player2Cards[randomIndex] = temp;
}
}
}
GameManager.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine.UI; // TextMeshProUGUI用の名前空間を追加
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public Deck deck;
public List<Card> player1Hand = new List<Card>();
public List<Card> player2Hand = new List<Card>();
public Transform player1HandTrans;
public Transform player2HandTrans;
public Transform centerPile1;
public Transform centerPile2;
public TextMeshProUGUI resultText; // TextMeshProUGUIを使用するように変更
public TextMeshProUGUI player1CardNumText; // Player1の残りカード枚数を表示
public TextMeshProUGUI player2CardNumText; // Player2の残りカード枚数を表示
private Card centerCard1;
private Card centerCard2;
private int sortingOrder = 0;
private Coroutine player2TurnCoroutine; // コルーチンを保持するためのフィールド
public float cardDistance = 1.5f;
public Button restartButton;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
void Start()
{
InitializeGame();
}
public void InitializeGame()
{
restartButton.gameObject.SetActive(false);
deck.player1Cards.Clear();
deck.player2Cards.Clear();
player1Hand.Clear();
player2Hand.Clear();
deck.CreateDeck();
deck.ShuffleDeck();
DealCards();
resultText.gameObject.SetActive(false); // 勝敗テキストを非表示にする
player1CardNumText.text = "Player:" + (deck.player1Cards.Count + player1Hand.Count).ToString();
player2CardNumText.text = "CPU:" + (deck.player2Cards.Count + player2Hand.Count).ToString();
player2TurnCoroutine = StartCoroutine(Player2Turn());
}
void DealCards()
{
int initialHandSize = 4; // プレイヤーの初期手札の枚数
// プレイヤー1の手札を配置
for (int i = 0; i < initialHandSize; i++)
{
if (deck.player1Cards.Count > 0)
{
Card card1 = deck.player1Cards[0];
card1.gameObject.transform.SetParent(player1HandTrans);
card1.DeckNumber = player1Hand.Count;
card1.PlayerIndex = 1;
player1Hand.Add(card1);
card1.transform.position = player1HandTrans.position + new Vector3(i * cardDistance, 0, 0);
card1.ShowCardFace();
deck.player1Cards.RemoveAt(0);
}
if (deck.player2Cards.Count > 0)
{
Card card2 = deck.player2Cards[0];
card2.gameObject.transform.SetParent(player2HandTrans);
card2.DeckNumber = player2Hand.Count;
card2.PlayerIndex = 2;
player2Hand.Add(card2);
card2.transform.position = player2HandTrans.position + new Vector3(i * cardDistance, 0, 0);
Destroy(card2.GetComponent<Collider2D>());
card2.ShowCardFace();
deck.player2Cards.RemoveAt(0);
}
}
// センターピルの最初のカードを配置
if (deck.player1Cards.Count > 0)
{
centerCard1 = deck.player1Cards[0];
centerCard1.gameObject.transform.SetParent(centerPile1);
centerCard1.transform.position = centerPile1.position;
Destroy(centerCard1.GetComponent<Collider2D>());
centerCard1.ShowCardFace();
centerCard1.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
deck.player1Cards.RemoveAt(0);
}
if (deck.player2Cards.Count > 0)
{
centerCard2 = deck.player2Cards[0];
centerCard2.gameObject.transform.SetParent(centerPile2);
centerCard2.transform.position = centerPile2.position;
Destroy(centerCard2.GetComponent<Collider2D>());
centerCard2.ShowCardFace();
centerCard2.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
deck.player2Cards.RemoveAt(0);
}
}
public void OnCardClicked(Card clickedCard)
{
if (clickedCard.PlayerIndex != 1) return; // 表示以外ならリターン
if (IsValidMove(clickedCard, centerCard1))
{
MoveCardToCenterPile(clickedCard, centerPile1, ref centerCard1, player1Hand);
}
else if (IsValidMove(clickedCard, centerCard2))
{
MoveCardToCenterPile(clickedCard, centerPile2, ref centerCard2, player1Hand);
}
else
{
CheckIfBothPlayersCannotMove();
}
}
private bool IsValidMove(Card clickedCard, Card centerCard)
{
return clickedCard.rank + 1 == centerCard.rank ||
clickedCard.rank - 1 == centerCard.rank ||
(clickedCard.rank == 1 && centerCard.rank == 13) ||
(clickedCard.rank == 13 && centerCard.rank == 1);
}
private void MoveCardToCenterPile(Card clickedCard, Transform centerPile, ref Card centerCard, List<Card> playerHand)
{
// 中央の山札のカードのソートレイヤーを変更
clickedCard.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
// クリックされたカードを中央の山札の位置に移動
clickedCard.transform.SetParent(centerPile);
clickedCard.transform.position = centerPile.position;
// 中央の山札のカードをクリックされたカードに置き換え
centerCard = clickedCard;
Destroy(centerCard.GetComponent<Collider2D>());
// 必要に応じてカードの表示を更新
centerCard.ShowCardFace();
centerCard.PlaySound();
// クリックされたカードの位置を取得
int clickedCardIndex = playerHand.IndexOf(clickedCard);
// 新しいカードをデッキから手札に移動
if (playerHand == player1Hand)
{
if (deck.player1Cards.Count > 0)
{
Card newCard = deck.player1Cards[0];
newCard.Initialize(newCard.suit, newCard.rank); // スプライトを正しく設定
newCard.gameObject.transform.SetParent(player1HandTrans);
newCard.transform.position = player1HandTrans.position + new Vector3(clickedCardIndex * cardDistance, 0, 0);
newCard.ShowCardFace();
playerHand[clickedCardIndex] = newCard;
newCard.DeckNumber = clickedCardIndex;
newCard.PlayerIndex = 1;
deck.player1Cards.RemoveAt(0);
}
else
{
// デッキが空の場合、手札からカードを削除
playerHand.RemoveAt(clickedCardIndex);
}
}
else if (playerHand == player2Hand)
{
if (deck.player2Cards.Count > 0)
{
Card newCard = deck.player2Cards[0];
newCard.Initialize(newCard.suit, newCard.rank); // スプライトを正しく設定
newCard.gameObject.transform.SetParent(player2HandTrans);
newCard.transform.position = player2HandTrans.position + new Vector3(clickedCardIndex * cardDistance, 0, 0);
newCard.ShowCardFace();
playerHand[clickedCardIndex] = newCard;
newCard.DeckNumber = clickedCardIndex;
newCard.PlayerIndex = 2;
deck.player2Cards.RemoveAt(0);
}
else
{
// デッキが空の場合、手札からカードを削除
playerHand.RemoveAt(clickedCardIndex);
}
}
player1CardNumText.text = "Player:" + (deck.player1Cards.Count + player1Hand.Count).ToString();
player2CardNumText.text = "CPU:" + (deck.player2Cards.Count + player2Hand.Count).ToString();
CheckForWinner();
}
private void CheckIfBothPlayersCannotMove()
{
bool player1CannotMove = !CanPlayerMove(player1Hand);
bool player2CannotMove = !CanPlayerMove(player2Hand);
if (player1CannotMove && player2CannotMove)
{
bool player1win = false;
bool player2win = false;
if (deck.player1Cards.Count > 0)
{
DrawCardsToCenterPilesPlayer1();
}
else
{
if (player1Hand.Count > 0)
{
DrawCardsFromHandsToCenterPilesPlayer1();
}
else
{
// Player1勝利
player1win = true;
}
}
if (deck.player2Cards.Count > 0)
{
DrawCardsToCenterPilesPlayer2();
}
else
{
if (player2Hand.Count > 0)
{
DrawCardsFromHandsToCenterPilesPlayer2();
}
else
{
// Player2勝利
player2win = true;
}
}
if(player1win)
{
if(!player2win)
{
Debug.Log("PlayerWin");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Player Win!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
else
{
Debug.Log("Draw");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Draw!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
}
else if(player2win)
{
if (!player1win)
{
Debug.Log("CPUWin");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "CPU Win!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
else
{
Debug.Log("Draw");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Draw!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
}
else
{
StartCoroutine(ShowSpeedText("Seed")); // "Speed" テキストを表示
}
}
}
private bool CanPlayerMove(List<Card> playerHand)
{
foreach (Card card in playerHand)
{
if (IsValidMove(card, centerCard1) || IsValidMove(card, centerCard2))
{
// プレイヤーはまだカードを出すことができる
return true;
}
}
return false;
}
private void DrawCardsToCenterPilesPlayer1()
{
// プレイヤー1のデッキからカードを引いてセンターピル1に置く
if (deck.player1Cards.Count > 0)
{
// 既存の中央の山札のカードのソートレイヤーを変更
foreach (Transform child in centerPile1)
{
child.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
}
Card newCenterCard1 = deck.player1Cards[0];
newCenterCard1.Initialize(newCenterCard1.suit, newCenterCard1.rank);
newCenterCard1.gameObject.transform.SetParent(centerPile1);
newCenterCard1.transform.position = centerPile1.position;
newCenterCard1.ShowCardFace();
newCenterCard1.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
centerCard1 = newCenterCard1;
deck.player1Cards.RemoveAt(0);
}
}
private void DrawCardsToCenterPilesPlayer2()
{
// プレイヤー2のデッキからカードを引いてセンターピル2に置く
if (deck.player2Cards.Count > 0)
{
// 既存の中央の山札のカードのソートレイヤーを変更
foreach (Transform child in centerPile2)
{
child.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
}
Card newCenterCard2 = deck.player2Cards[0];
newCenterCard2.Initialize(newCenterCard2.suit, newCenterCard2.rank);
newCenterCard2.gameObject.transform.SetParent(centerPile2);
newCenterCard2.transform.position = centerPile2.position;
newCenterCard2.ShowCardFace();
newCenterCard2.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
centerCard2 = newCenterCard2;
deck.player2Cards.RemoveAt(0);
}
}
private void DrawCardsFromHandsToCenterPilesPlayer1()
{
// プレイヤー1の手札からカードを引いてセンターピル1に置く
if (player1Hand.Count > 0)
{
// 既存の中央の山札のカードのソートレイヤーを変更
foreach (Transform child in centerPile1)
{
child.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
}
Card newCenterCard1 = player1Hand[0];
newCenterCard1.Initialize(newCenterCard1.suit, newCenterCard1.rank);
newCenterCard1.gameObject.transform.SetParent(centerPile1);
newCenterCard1.transform.position = centerPile1.position;
newCenterCard1.ShowCardFace();
newCenterCard1.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
centerCard1 = newCenterCard1;
player1Hand.RemoveAt(0);
}
}
private void DrawCardsFromHandsToCenterPilesPlayer2()
{
// プレイヤー2の手札からカードを引いてセンターピル2に置く
if (player2Hand.Count > 0)
{
// 既存の中央の山札のカードのソートレイヤーを変更
foreach (Transform child in centerPile2)
{
child.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
}
Card newCenterCard2 = player2Hand[0];
newCenterCard2.Initialize(newCenterCard2.suit, newCenterCard2.rank);
newCenterCard2.gameObject.transform.SetParent(centerPile2);
newCenterCard2.transform.position = centerPile2.position;
newCenterCard2.ShowCardFace();
newCenterCard2.GetComponent<SpriteRenderer>().sortingOrder = sortingOrder++;
centerCard2 = newCenterCard2;
player2Hand.RemoveAt(0);
}
}
private void CheckForWinner()
{
if (player1Hand.Count == 0)
{
if(player2Hand.Count > 0)
{
Debug.Log("Player Win!");
foreach(var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Player Win!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
else
{
Debug.Log("Draw!");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Draw!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
}
else if (player2Hand.Count == 0)
{
if (player1Hand.Count > 0)
{
Debug.Log("CPU Win!");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "CPU Win!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
else
{
Debug.Log("Draw!");
foreach (var hand in player1Hand)
{
Destroy(hand.GetComponent<BoxCollider2D>());
}
resultText.text = "Draw!";
resultText.gameObject.SetActive(true);
restartButton.gameObject.SetActive(true);
if (player2TurnCoroutine != null) StopCoroutine(player2TurnCoroutine); // コルーチンを停止
}
}
}
private IEnumerator Player2Turn()
{
while (true)
{
yield return new WaitForSeconds(Random.Range(1, 4));
Card cardToPlay1 = null, cardToPlay2 = null;
foreach (Card card in player2Hand)
{
if (card.PlayerIndex == -1) continue;
if (IsValidMove(card, centerCard1))
{
cardToPlay1 = card;
break;
}
}
foreach (Card card in player2Hand)
{
if (card.PlayerIndex == -1) continue;
if (IsValidMove(card, centerCard2))
{
cardToPlay2 = card;
break;
}
}
if (cardToPlay1 != null)
{
MoveCardToCenterPile(cardToPlay1, centerPile1, ref centerCard1, player2Hand);
}
else if (cardToPlay2 != null)
{
MoveCardToCenterPile(cardToPlay2, centerPile2, ref centerCard2, player2Hand);
}
else
{
CheckIfBothPlayersCannotMove();
}
}
}
private IEnumerator ShowSpeedText(string _text)
{
resultText.text = _text;
resultText.gameObject.SetActive(true);
yield return new WaitForSeconds(2);
resultText.gameObject.SetActive(false);
}
}
オブジェクトのリンク
- GameManagerオブジェクトのInspectorで、以下をアタッチして設定します。
Deck
: デッキオブジェクトをドラッグアンドドロップplayer1HandTrans
: Player1の手札のTransformをドラッグアンドドロップplayer2HandTrans
: Player2の手札のTransformをドラッグアンドドロップcenterPile1
: 中央の山札1のTransformをドラッグアンドドロップcenterPile2
: 中央の山札2のTransformをドラッグアンドドロップresultText
: ResultTextオブジェクトをドラッグアンドドロップplayer1CardNumText
: Player1CardNumTextオブジェクトをドラッグアンドドロップplayer2CardNumText
: Player2CardNumTextオブジェクトをドラッグアンドドロップ
ボタンの設定
UI > Button
をCanvas内に追加し、名前をrestartButton
に変更します。- Buttonオブジェクトを選択し、Inspectorで
OnClick
イベントを追加し、GameManager
オブジェクトをドラッグアンドドロップします。 No Function
をクリックし、ドロップダウンメニューからGameManager > InitializeGame
を選択します。
ゲームの実行
- Unityエディタでゲームを実行し、カードゲーム「スピード」をプレイします。プレイヤーがカードをクリックすると、そのカードが中央の山札に移動し、AIプレイヤーが自動的にターンを進行します。
スクリプトの説明
Card.cs
Card.csはカードの情報を保持し、カードの表示や音声の再生を行うスクリプトです。このスクリプトでは、カードのスーツやランクを設定し、カードの表と裏の画像を表示する方法を提供します。また、カードがクリックされたときにGameManagerに通知する機能も備えています。
Deck.cs
Deck.csはデッキの生成とシャッフルを行うスクリプトです。このスクリプトでは、カードデッキを生成し、各プレイヤーにカードを割り当て、デッキをシャッフルします。
GameManager.cs
GameManager.csはゲーム全体を管理するスクリプトです。このスクリプトでは、ゲームの初期化、カードの配布、プレイヤーの手札管理、中央の山札管理、ターンの進行、勝敗判定、AIプレイヤーの動作などを制御します。また、プレイヤーの手札の数を更新し、勝敗を表示する機能も含まれています。
おわりに
このプロジェクトでは、Unityを使ってカードゲーム「スピード」を実装する方法を学びました。カードの生成、デッキのシャッフル、プレイヤーの手札管理、中央の山札の管理、勝敗判定、AIプレイヤーのターン処理など、ゲームの基本的な要素を網羅しています。この記事を参考にして、ぜひ自分のゲームを作ってみてください。