前回までの記事で、実際にデータを持ったカードを自由に出せるようになりましたね!!
前回の記事を見てない人はそちらを参考↓
今回の記事では、ターン開始時のドローやターンエンドなど、ターンフェーズの実装をしていきます!
今回の記事で実装すること↓
・デッキの作成
・ターンエンドボタンの実装
・敵のカードの召喚
・ターンフェイズ管理
完成形としてはこんな感じ↓
ターンの流れが出来ると、よりゲームらしくなるよね!
前回はちょっと難しかったかも知れないけど、
今回は前回に比べると多少簡単なはず!!
ちょっとずつ理解しながら進めてこうね。
ではいくぞ!
Contents
全体の流れの説明
ではまず今回やる作業の全体の流れを抑えてから、実際の作業に進んで行きましょう!
今回やる事としてはこんな感じ↓
・GameManagerへ追記
・TurnEndボタンへのアタッチ
今回実装する「ボタンを押した時に処理を行うやり方」は今度めっちゃ使うので、この機会に覚えちゃおう!!
GameManagerへの追記
今回のコードは全部「GameManager」に書いていきます!!
追加する内容はザックリこんな感じ↓
・デッキのリストの追加
・ゲームスタート時の手札のドロー(3枚)処理
・TurnEndボタンを押したら、相手のターンにする処理
・自分のターンになったらカードを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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { [SerializeField] CardController cardPrefab; [SerializeField] Transform playerHand, playerField, enemyField; bool isPlayerTurn = true; // List<int> deck = new List<int>() { 1, 2, 3, 1, 1, 2, 2, 3, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 }; // void Start() { StartGame(); } void StartGame() // 初期値の設定 { // 初期手札を配る SetStartHand(); // ターンの決定 TurnCalc(); } void CreateCard(int cardID, Transform place) { CardController card = Instantiate(cardPrefab, place); card.Init(cardID); } void DrawCard(Transform hand) // カードを引く { // デッキがないなら引かない if (deck.Count == 0) { return; } // デッキの一番上のカードを抜き取り、手札に加える int cardID = deck[0]; deck.RemoveAt(0); CreateCard(cardID, hand); } void SetStartHand() // 手札を3枚配る { for (int i = 0; i < 3; i++) { DrawCard(playerHand); } } void TurnCalc() // ターンを管理する { if (isPlayerTurn) { PlayerTurn(); } else { EnemyTurn(); } } public void ChangeTurn() // ターンエンドボタンにつける処理 { isPlayerTurn = !isPlayerTurn; // ターンを逆にする TurnCalc(); // ターンを相手に回す } void PlayerTurn() { Debug.Log("Playerのターン"); DrawCard(playerHand); // 手札を一枚加える } void EnemyTurn() { Debug.Log("Enemyのターン"); CreateCard(1, enemyField); // カードを召喚 ChangeTurn(); // ターンエンドする } } |
ちょっと長かったけど、無事貼れたかな??
エラーが出てないことを確認したら、次に進もう!
TurnEndボタンへのアタッチ
では次に、TurnEndボタンを押した時に、ターンエンドを行う処理を紐付けていくよ!!
ボタンに処理を設定する方法
①「TurnEndButtom」をクリック
②「OnClick」って所の「+」をクリック
③④「None」って書かれてる所に向かって、GameManagerオブジェクトをドラッグ&ドロップ
①「NoFunction」をクリック
②「GameManager」を選択
③「ChangeTurn」をクリック
そしたら、こんな感じになればOK!!!↓↓
これでTurnEndボタンに「ChangeTurn」メソッドの紐づけが完成!!
では実行してみよう!!
ちゃんとターンの流れが出来てる!!
という訳でしっかり実装出来たら、コードの解説をしていきます!
コードの解説
今回は大枠で見ると、7つのメソッドを追記した。ということになるので、
メソッド毎にどんな事をしてるメソッドなのか。って言うのを理解していこう!!
ちなみに出てきたメソッドの概要はこんな感じ↓
【メソッド名】 | 【メソッド処理の概要】 |
---|---|
StartGame | 初期値の設定 |
DrawCard | カードをデッキから一枚引く |
SetStartHand | ゲーム開始時の手札を引く(3枚) |
TurnCalc | ターンの管理 |
ChangeTurn | 敵と自分のターンを逆にする |
PlyerTurn | プレイヤーのターンの動きを管理 |
EnemyTurn | 敵のターンの動きを管理 |
ではそれぞれのメソッドの詳細を説明していくよ!!
「StartGameメソッド」解説
1 2 3 4 5 6 7 8 |
void StartGame() // 初期値の設定 { // 初期手札を配る SetStartHand(); // ターンの決定 TurnCalc(); } |
このメソッドは、
ゲーム実行時に最初に呼ばれるメソッドで、初期値の設定をしてるよ。
次の記事以降で、
・リーダーのHP
・ゲーム開始時のマナポイント
などの設定を、ここのメソッドに追記していくよ。
「DrawCard」メソッド解説
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void DrawCard(Transform hand) // カードを引く { // デッキがないなら引かない if (deck.Count == 0) { return; } // デッキの一番上のカードを抜き取り、手札に加える int cardID = deck[0]; deck.RemoveAt(0); CreateCard(cardID, hand); } |
このメソッドは、デッキの一番上のカードをドローするメソッドだよ。
最後の行で、前回の記事で作ったメソッド「CreateCard」を使ってるけど、これは
CreateCard(出したいカードの番号, カードを出したい場所)
というメソッドだったよね。
ここではそれを使って、今回新しく作った「deck」リストの0番目(プログラミングでは1つ目を0番目という)を手札に出す。という処理をしてるよ。
上のIF文は、
「Deck」の枚数が0枚だったら引かないようにする。
という条件分岐をしてるよ。
※IF文はかなり大事なので、全く分からない人は軽く調べること推奨!!
「SetStartHand」メソッド解説
1 2 3 4 5 6 7 |
void SetStartHand() // 手札を3枚配る { for (int i = 0; i < 3; i++) { DrawCard(playerHand); } } |
このメソッドは、手札を3枚ドローする処理。
FOR文は「指定の回数を繰り替えす」処理なので、
さっき説明した「DrawCard」メソッドを3回繰り返してるだけだね。
※FOR文もかなり大事なので、軽く調べること推奨!
まあ簡単に言うと、「指定した回数分処理を繰り返す」もの。って認識でOK
「ChangeTurn」メソッド解説
1 2 3 4 5 |
public void ChangeTurn() // ターンエンドボタンにつける処理 { isPlayerTurn = !isPlayerTurn; // ターンを逆にする TurnCalc(); // ターンを相手に回す } |
このメソッドは、ターンの管理をしているよ。
今回GameManagerの上の方で追記した内容に、
bool isPlayerTurn = true;
というのがあるんだけど、これは「今どっちのターンなのか」を管理する変数になっているよ。
これを逆(つまりTrueをFalseに、FalseをTrue)にすることで、ターンを順番に回すことが出来るよ。
「TurnCalc」メソッド解説
1 2 3 4 5 6 7 8 9 10 11 |
void TurnCalc() { if (isPlayerTurn) { PlayerTurn(); } else { EnemyTurn(); } } |
このメソッドは、
今どっちのターンかを判断して、
次の対象のターン処理を実行するメソッド。
「ChangeTurn」メソッドでも説明したけど、最初の状態(デフォルト)では、isPlayerTurnはTrueだったよね。
つまり最初はプレイヤーのターン。
でもターンエンドボタンが押されたりすると「ChangeTurn」メソッド動いて、isPlayerTurnが逆になる。つまりFalseになる。
Falseになると、EnemyTurn(敵のターン処理)が始まる。
で、EnemyTurnの最後の処理でまたChangeTurnメソッド動いて、isPlayerTurnが逆になる。今度はTrueになる。
そしてまたPlayerのターンになって、、、、
という様な処理を繰り返しているよ!
※IF文がいまいち分かんない人用に説明しとくと、
IF(~)は、「~だったら、この処理をやるよ。」ってことで、
ELSEは、「~じゃなかったら、この処理をやるよ。」ってこと!
とりあえずこのくらいが分かれば全然OK!!
「PlayerTurn」メソッド解説
1 2 3 4 5 |
void PlayerTurn() { Debug.Log("Playerのターン"); DrawCard(playerHand); // 手札を一枚加える } |
このメソッドは、自分のターンの動きを処理してるよ。
今はまだドローする処理しかないけど、
・ターンの始まりにマナポイントを増やす
・攻撃済みのカードを攻撃出来るようにする
等の処理をここに追記していくよ!
「EnemyTurn」メソッド解説
1 2 3 4 5 6 |
void EnemyTurn() { Debug.Log("Enemyのターン"); CreateCard(1, enemyField); // カードを召喚 ChangeTurn(); // ターンエンドする } |
このメソッドは、敵のターンの動きを処理してるよ。
今の動きとしては、この2つだけ。
・Card1を召喚
・ターンエンドする
もっと敵にカードを出させたいなら、同じように召喚するコードを書けばOK
最終的にはターン経過とかHPに応じて、敵の行動を変えるようにしていく予定だよ。
【プラスα】カード枚数を制御したい
よし、完成したから演習に行こう!!!
って思ったけど、ターンエンドボタンを押しまくってみて??
バグじゃあああ!!!!
ってな訳で、流石にこれはカッコ悪いので修正しましょ!
修正したい点としては2点だね。
- フィールドに何枚でも出せちゃう
- 手札が何枚でも持てる
この2つを修正していこう!!
こんな感じにしていくよ!
- フィールドには5体まで
- 手札は9枚まで
なんとなくでもいいから、
「こうすれば出来そうかも!」
って考えてから進んでみてね。
手札枚数に制限を掛けるには??
まず手札枚数の制限をするには、どの処理を変更すればいいのかを考えてみよう。
方法としては色々考えられるけど、
一番簡単に出来るのは「ドローする時」の処理を変更するのが良さそう。
というわけで、
現状では何枚でもドロー出来てしまう「DrawCard」メソッドを、
「手札枚数が9枚より少ないならドローする」という処理に変えていくよ!!
ではDrawCardメソッドを修正していこう。
下のコードになるようにオレンジ部分を追記、修正してみて!!↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void DrawCard(Transform hand) // カードを引く { // デッキがないなら引かない if (deck.Count == 0) { return; } CardController[] playerHandCardList = playerHand.GetComponentsInChildren<CardController>(); if (playerHandCardList.Length < 9) { // デッキの一番上のカードを抜き取り、手札に加える int cardID = deck[0]; deck.RemoveAt(0); CreateCard(cardID, hand); } } |
では追記した部分の解説をしていくよ!
CardController[] playerHandCardList = playerHand.GetComponentsInChildren();
これは日本語で言うと、
PlayerHandにあるカードを、
playerHandCardListというリストに入れますよ。
って処理だよ。
プログラミング的に言うと、
PlayerHandの子要素を
CardController型のリストに突っ込みますよ。
ってことだね。
その下のIF文は、
if (playerHandCardList.Length < 9){省略}
日本語で言うと、
playerHandCardListの個数が9個無ければ、
その次の処理をやりますよ。
ってこと!!
こんな感じの処理を追加することで、
「手札が9枚の時にはドローはしない」
というコードにすることが出来るよ!!
フィールドの枚数に制限を掛けるには??
今度は敵フィールドに出てくるカードの枚数を制限してみよう!!
自分で考えてやってみるとめっちゃ力になるから、一旦考えてみて!!
さっきのとほぼ一緒だからできるはず!
では答え合わせ↓
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void EnemyTurn() { Debug.Log("Enemyのターン"); CardController[] enemyFieldCardList = enemyField.GetComponentsInChildren<CardController>(); if (enemyFieldCardList.Length < 5) { CreateCard(1, enemyField); } ChangeTurn(); // ターンエンドする } |
こんな感じにすればOK!!
一応解説すると、
今度はenemyFieldのカードリストを取得して、
その個数が5個無いならカードを出す。
ってことだね。
ではゲーム実行して、バグ修正出来たのかを確認してみて!!
出来てたら次に進んでいこう!
【演習】リストとIF文の使い方
では最後に演習やっていきましょう!
今回の課題はこれ↓
【課題】
次の画像の1~5ターン目の動きを再現してみよう!!!
【1ターン目】
【2ターン目】
【3ターン目】
【4ターン目】
【5ターン目】
これをコードで実装してみて!!
ヒントとしては、
・デッキのリストを変更する
・2体目まではCard3を召喚する
・3体目からはCard2を召喚する
ここら辺に注意してやってみよう!!
【演習】答え合わせ
では出来たかな??
答えの解説にいくぞ!!
今回演習で修正が必要なのは、以下の2つだけだね。
- deckリスト
- EnemyTurnスクリプト
それぞれの修正後はこんな感じ!!
【deckリスト】
毎ターンCard1とCard2を交互に引いてるので、デッキとしてはこんな感じ↓
9枚目以降は見えてないので、数字の羅列が9個分あれば正解だよ。
1 |
List<int> deck = new List<int>() { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }; |
【EnemyTurnメソッド】
敵フィールドのカードの数が2より小さいなら、Card3を出す。
そうでないなら、Card2を出す。
ということなので、こんな感じかな↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void EnemyTurn() { Debug.Log("Enemyのターン"); CardController[] enemyFieldCardList = enemyField.GetComponentsInChildren<CardController>(); if (enemyFieldCardList.Length < 5) { if (enemyFieldCardList.Length < 2) { CreateCard(3, enemyField); } else { CreateCard(2, enemyField); } } ChangeTurn(); // ターンエンドする } |
今回の演習は出来たかな??
これが出来てたら、かなり理解しながら進めてると思うよ!!
みんなの出来具合を知りたいので、
コメントに「演習できーた!」とか
「演習難しいです。お陰で悲しみです。」
とか書いてくれると今後の記事に活かしやすいのでお願いします!
※普通に書いてくれればいいよ。笑
【まとめ】ターンフェイズの実装
今回でターンフェイズの実装が出来ましたね!!
ターンフェイズを作るにあたって、
- IF文の使い方
- FOR文について
- ボタンにメソッドを付ける方法
- 要素数を取得する方法
ここら辺も理解出来ると、今後ゲームを作るうえで便利なので少しずつでも覚えていこうね!!
では今回はここで終わり!!
演習が出来たかどうかのコメントよろしくね!
ちなみに次回はマナポイントの実装 攻撃の実装をしていくよ!
ではおわりっ!!
次の記事↓
前の記事↓
参考にさせて頂いた動画
[Unityゲーム開発講座] シャドバ風!?カードゲームの作り方 #1 UIの実装
演習はできましたが
答えとは違いました
deckをプレイヤー側、deck_enemyを敵側として
デッキを2つ作成して演習クリアしました
DrowCardメソッドには、第二引数にnumを追加して、0がプレイヤー、1が敵にして、処理を分けるようにしました
でも、そもそもデッキを分けるのはよくないですね(失敗
>もっけさん
なるほど!確かにその方法でも実装出来ますね
確かに今回のゲームの仕様では敵のデッキは存在しないのでデッキを分ける必要は無いですが、敵もデッキを持ってバトルするカードゲームを作る場合は、もっけさんの作った通りの仕様になると思います!
もし別にカードゲームを作る場合には、そっちの仕様で作ってみても面白いと思いますよ!!
敵も自分もカードをランダムに召喚することってできますかね?
>じゃがすけさん
Randomクラスを使えば出来ますよー!
(って説明で分かります??)
分かります!ありがとうございます!
わかりやすかったです。
謎のコンパイラエラーはきまくってソースコピペしたら動いたのでたぶん{}の位置ミスかも…
あるあるミスですね(笑)
カッコを使うときは書き終わってから閉じるんじゃなくて、
最初から{}をセットで書いておくとミスりづらくなりますよ!(って誰かが言ってた。)
毎度演習問題の制作ありがとうございます!!
良い復習になります。
コメントありがとうございます!
演習問題は今後も付けていきますね!!
沢山の動画や記事を読んできたけどここの記事が一番わかりやすかったです!
さっそく質問したいのですが…
カードの動かし方編のDropPlaceについて、カード1をカード2の↑に重ねると、カード1がカード2の中入ってに消えなくなってしまう現象が起こります。
カードデータの反映編のViewについて、私の場合はimageだけ表示すれば良くて、Show()の中に表示しない行を//つけたけど、結果的になぜかImageの横に「New」というテキストが表示されます。
カードデータの反映編のフィールドの枚数に制限について、Drawの時の制限は出来たが、Dragする時にFieldによって最大枚数を制限したくて、いろいろDropPlaceで試してみたができませんでした。
なにかいい方法を教えていただけないでしょうか。
Yukaさん、コメントありがとうございます!
んー、カードが消える現象謎ですね、、
消えるって言うのは、カード自体はあるけど見えなくなってるんですかね??
もしくはカード自体が無くなっちゃうんですかね??
分かりそうならまたコメントで教えて下さい!
ちょっと今色々忙しくて他のところに答えられませんが、日曜には改めてコメント返せると思うので、また日曜辺りに覗いてみてください〜
ご返信ありがとうございます!
その後色々調べて問題解決できました。
Unity本当に面白いですね!
みじんこさんはカードを裏面切り替えとか、mirrorなどを使ってonline対戦についてもしよろしければ是非教えていただきたいです!
Yukaさん、日曜に返信できなくてすみません!
解決できたのであれば良かったです!
(自己解決出来るとは、さてはYukaさん結構出来る人ですね、、?)
カードの裏面切り替えは、カードの一番前面に裏面の画像を貼り付けて、それをIFで表示、非表示させるのが一番簡単かと思います。
オンライン対戦は私も作ったことないんですよね〜
私も全くプロではなく、あくまで独学でゲームを作っているだけのひとなので、一緒に勉強していきましょう(`・ω・´)ゞ
頑張りましょう!
次の記事もお楽しみしております!
DrawCard()のスペルが全部DrowCard()になっているので修正お願いします!
コメントありがとうございます!
これは英語力がないのがバレたやつですね、、(お恥ずかしい)
またミス等あれば教えて下さいー!
最近、趣味でカードゲームを作りたく、Unityをはじめました。大変参考にさせていただいております。
実装したい処理があるのですが、どの様にコードを書けばいいか分からず…質問したいことがあります。
既に解説頂いている内容であればご容赦ください。
【実装した内容】
・ドローフェイズ、マナフェイズ、メインフェイズ、アタックフェイズと4つのフェイズを実装。
・ドローフェイズに入るとデッキからカードを一枚ドローして、マナフェイズに移行する。
【実装したい内容】
・マナフェイズ時は手札からマナゾーンに手札を一枚置くことが出来る。(この時フィールドや墓地などにカードが置くことが出来ないようにしたい。)
・手札からマナゾーンに一枚置いた場合、メインフェイズに移行する。
以上、二つの処理を実装したいのですが、どの様なコードを書けば実装出来ますでしょうか。
お手数ですが、ご教示の程よろしくお願いいたします。
>からすまさん
なるほど、デュエルマスターズ的な感じですね?
んー、他にも効率的な実装方法があるとは思いますが、パッと思いつく方法だと、
例えばですが、ターンフェイズを管理するフラグを作って、そのフラグによって処理を変えるようにするとかどうでしょう??
現時点ではフィールドにカードをドロップすると、召喚処理(コストを消費して、フィールドに置かれる)が行われますが、
その処理をIFで「ターンフェーズがメインフェーズならカード召喚処理を実行する」という風に分岐させて、
更に「マナフェーズならマナチャージ処理を実行する」のようにフェーズ毎の分岐を追記して、
その処理内でメインフェーズへ移行させれば、からすまさんの実装したい内容はどちらも実装出来るかと思います!
というざっくりとした説明でしたが、どうですかね??
難しくなってきてるけど、なんとかついていけてる!
最後の記事まで頑張ってください!!応援しております( ´∀`)
敵フィールドを5体までに制限するコードを書き換えて、味方フィールドも5体までに制限するコードへの書き換えは現段階でおこなって大丈夫ですか?
大丈夫ですよ!
色々いじっちゃってください!
質問です。
フィールに手札からモンスターを出すときに、一つ一つのマスごとに区切って場所を決めて召喚したいのですが(遊戯王の様に)同じマスに何体も出てきてしまうのは、どのように解決すればいいですかね?
もしよろしければ返信お願いいたします。
(わかりづらい質問でしたらすいません)