🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ドラッグ&ドロップ

コンピューターのGUIにおいて、バーチャルなものを「つかむ」ことによって選択し、別の場所や他のバーチャルなものの上に動かす行為、またはその行為に対応していることを指す。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

1825閲覧

Unity2Dにおけるドラッグの制限のかけかた

Gchan

総合スコア19

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ドラッグ&ドロップ

コンピューターのGUIにおいて、バーチャルなものを「つかむ」ことによって選択し、別の場所や他のバーチャルなものの上に動かす行為、またはその行為に対応していることを指す。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/10/29 00:33

Unity2Dにおいてカードゲームの制作をしているのですが、自分のターン中に相手の手札のカード領域に対してドラッグできないように、また逆に相手のターン中に自分の手札のカード領域に対してドラッグできないようなスクリプトを書きたいのですがどうにも上手くいきません。

最終的に想定しているカードゲームの簡単な説明ですが、
プレイヤーが一つのフィールドにカードを出していくタイプのものです。

プレイヤーのターン開始
コストを支払ってカードをフィールドに出す
フィールドに出したカードの効果が処理される
処理されたカードは廃棄される(ここはまだ未実装)
ターンエンドで相手のターンに
以降HPが0になるまでこれの繰り返し

該当箇所のcsはほぼ下の3つになります

CardController.cs

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class CardController : MonoBehaviour 6{ 7 public CardModel model; //カードの情報 8 public CardMovement movement; //CardMovement.cs扱えるように 9 10 private void Awake() 11 { 12 movement = GetComponent<CardMovement>(); //CardMovementクラスの取得 13 } 14}

CardMovement.cs///カードのドラッグに関する処理のスクリプト

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.EventSystems; 5 6public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler 7{ 8 public Transform defaultParent; 9 public bool isDraggable; //ドラッグ可能かどうか。これを使ってどうにか実装できないか考えていた 10 11 public void OnBeginDrag(PointerEventData eventData) 12 { 13 defaultParent = transform.parent; 14 transform.SetParent(defaultParent.parent, false); 15 GetComponent<CanvasGroup>().blocksRaycasts = false; 16 } 17 18 public void OnDrag(PointerEventData eventData) 19 { 20 Vector3 cardPos = Camera.main.ScreenToWorldPoint(eventData.position); 21 cardPos.z = 0; 22 transform.position = cardPos; 23 } 24 25 public void OnEndDrag(PointerEventData eventData) 26 { 27 transform.SetParent(defaultParent, false); 28 GetComponent<CanvasGroup>().blocksRaycasts = true; 29 } 30 } 31

DropPlace.cs///カードのドロップに関する処理のスクリプト

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.EventSystems; 5 6public class DropPlace : MonoBehaviour, IDropHandler 7{ 8 public enum TYPE //手札のカードかフィールドか ここにEHAND(敵の手札)を追加して実装できないかどうかも考えた 9 { 10 HAND, 11 FIELD, 12 } 13 public TYPE tips; 14 public void OnDrop(PointerEventData eventData) 15 { 16 if (tips == TYPE.HAND) //手札の範囲内でドラッグした時にカードのコスト(soul)を消費しないようにしている 17 { 18 return; 19 } 20 CardController card = eventData.pointerDrag.GetComponent<CardController>(); //ドラッグされてきたカードのデータ 21 if (card != null) //もしカードが重なっているなら 22 { 23 if (card.model.soul > GameManager.instance.playerSoul && GameManager.instance.isPlayerTurn == true) 24 { 25 return; 26 //GameManager.csにあるbool関数のisPlayerTurnで自分のターンであるかを判定している 27 //自分のターンに受け取ったカードのコスト(soul)が、自分の所持しているコストよりも大きいなら 28 //コストが不足しているためカードのドロップが出来ないようにしている。 29 } 30 else if (card.model.soul > GameManager.instance.enemySoul && GameManager.instance.isPlayerTurn == false ) 31 { 32 return; 33 //上と逆で相手ターンにコスト不足ならドロップ出来ないようにしている 34 } 35 card.movement.defaultParent = this.transform; //ドロップされたときにdefaultParentをドロップした箇所に設定している 36 card.model.isFieldCard = true; //ドロップされる場所はFieldなので、ドロップされたCardModel.csにあるbool関数のisFieldCardをtrueにすることでFieldに置かれているフィールド上のカードですよという状態にする 37 } 38 } 39} 40

かなり長文になるので必要部分だけを切り取ってCardModel.csとGameManager.csの方も掲載します

CardModel.cs

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class CardModel 6{ 7 public string name; 8 public int soul; 9 ... 10 ... 11 public bool isFieldCard; //ここでisFieldCardを付けています 12}

C#

1using System; 2using System.Collections; 3using System.Collections.Generic; 4using UnityEngine; 5using UnityEngine.UI; 6 7public class GameManager : MonoBehaviour 8{ 9 public bool isPlayerTurn; //プレイヤーのターンかどうかの確認するため 10 ... 11 ... 12 void Start() 13 { 14 StartGame(); 15 } 16 void StartGame() 17 { 18 ... 19 ... 20 isPlayerTurn = true; //最初はプレイヤーのターンなのでtrueにしておく 21 TurnCalc(); 22 } 23 void TurnCalc() //ターンを切り替える処理 24 { 25 if (isPlayerTurn == true) 26 { 27 PlayerTurn(); //プレイヤーのターン処理をする 28 } 29 else 30 { 31 EnemyTurn(); //エネミーのターン処理をする 32 } 33 } 34 public void ChangeTurn() //ターンチェンジ処理、ボタンにアタッチしてクリックで切り替えしている 35 { 36 isPlayerTurn = !isPlayerTurn; //もしプレイヤーのターンなら否定になるのでエネミーターンに、逆の場合も 37 if(isPlayerTurn == true) 38 { 39 playerSoul = playerMaxSoul; //コスト回復処理みたいなものなので今回の質問には関係ないです(多分) 40 ShowSoul(); //そもそも書いてある内容がここにはこれだけなので一応載せました 41 } 42 else 43 { 44 enemySoul = enemyMaxSoul; //上に同じく 45 ShowSoul(); 46 } 47 TurnCalc(); //ターンが移ったらターン開始の処理が行われる 48 } 49 void PlayerTurn() //上下ともTurnCalc()で切り替わる時のターン処理 50 { 51 Debug.Log("Playerのターン"); //現在は特に何も書くことがないので切り替わってるかだけ確認しています 52 } 53 void EnemyTurn() 54 { 55 Debug.Log("敵のターン"); //上に同じく 56 } 57... 58... 59} 60

これでおそらく必要部分のスクリプトは掲載出来ているかと思います。
またカードの手札領域やフィールドですが、UIとしてそれぞれの領域にパネルを配置しています。
ゲーム画面は流石に載せられないのですが、ドロー関連のスクリプトを置いておきます

これは殆ど質問内容には関係ありませんが[SerializeField]でplayerHandTransformにはプレイヤーの手札となるパネルの領域をヒエラルキーからインスペクターにドロップして設定して~といった形で作っています
Draw.cs

c#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Draw : MonoBehaviour 6{ 7 [SerializeField] Transform playerHandTransform = default; 8 [SerializeField] Transform enemyHandTransform = default; 9 [SerializeField] CardController cardPrefab = default; 10 11 List<int> playerDeck = new List<int>() { 1, 3, 2, 2, 1 }; 12 List<int> enemyDeck = new List<int>() { 2, 2, 1, 1, 2 }; 13 14 public void PlayerDrow() 15 { 16 GiveCardToHand(playerDeck, playerHandTransform); 17 } 18 public void EnemyDrow() 19 { 20 GiveCardToHand(enemyDeck, enemyHandTransform); 21 } 22 23 public void GiveCardToHand(List<int> deck, Transform hand) 24 { 25 if (deck.Count == 0) 26 return; 27 } 28 int cardID = deck[0]; 29 deck.RemoveAt(0); 30 CreateCard(cardID, hand); 31 } 32 void CreateCard(int cardID, Transform hand) 33 { 34 CardController card = Instantiate(cardPrefab, hand, false); 35 card.Init(cardID); 36 } 37 38} 39

現在コメントアウトで説明している機能に関しては全て実装できていて、ドラッグの制限をかける機能だけがどうやっても実装できません。

長くなりましたのでもう一度実装したい内容なのですが
**
・自分のターン中(isPlayerTurn==true)の場合は相手の手札をドラッグできないように
・相手のターン中はその逆に
・フィールドもドロップできるだけにしておかないと相手のターン中にフィールドに出したカードを自分がドラッグ出来てしまうバグが起きるかもしれないのでその可能性?についてもお願いしたいです。(↑で相手の手札に加えてフィールドもドラッグできないようにすれば解決するかも?)
**

試したこと

DropPlace.cs内のenumのTYPEにEHAND(敵の手札)を追加
CardController.csに public DropPlace drop;
Awake()内に
drop = GetComponent<DropPlace>();で movementのようにクラスを取得させ
CardMovement.csのOnBeginDrop()内で

CardController card = GetComponent<CardController>();
if (GameManager.instance.isPlayerTurn == true) //もしプレイヤーのターンなら
{
if (card.drop.tips == DropPlace.TYPE.EHAND) //動かそうとするカードのdrop.tipsのTYPEがEHAND(敵の手札の場合)
{
isDraggable = false; //ドラッグを不可能にする
}
}

や、そもそも別のドラッグに関連するスクリプトを書いて、その中でenumでHANDの区別をつけるようにしてアタッチしてみたりもしたのですが上手くいかず、どうすればいいのか混乱している状態です。
宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

・自分のターン中(isPlayerTurn==true)の場合は相手の手札をドラッグできないように
・相手のターン中はその逆に

上記の条件がありますが「プレイヤーは敵のターン中、相手の手札をドラッグ出来る特殊なカードゲーム」なのでしょうか?
タップや長押しでカードパラメータを見る機能があるかもしれませんが普通のカードゲームは一律に相手のカードはドラッグ出来ない筈です。

特殊なカードゲームでないならばtagを用い自分の手札にPlayerCard、相手の手札にEnemyCardを設定してドラッグの処理が始まる段階で条件式を使って処理を中断させれば良いと思います。
若しくは一度試しているようですがCardModelクラスに自札・敵札を判別する変数を追加してそれでおこなって下さい。

投稿2019/10/29 03:53

編集2019/10/29 09:37
Hawn

総合スコア1222

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Gchan

2019/10/29 04:00

いえ、できません。 逆にそういう特殊なカードゲームに今の時点でなってしまっているので直そうと思っている次第です。 説明下手で伝わりにくくて申し訳ありません。 一応今かなり解決には向かっているのですが、tagのやり方は知らなかったのでそちらも試してみようと思います。有難うございます!
guest

0

ベストアンサー

まず、ドラッグできないを定義しましょう
現実には指やマウスはドラッグできない領域に移動できてしまうので
そうなったときにどう表現するのかです。

・カードがドラッグできる領域に留まる
・ドラッグのキャンセルとして機能する

そして、その状態からドラッグできる領域に戻ったときの挙動も考えます
・キャンセルされているので何もおきない
・再度カードが表示されてドラッグ状態になる

・ドラッグが始まったらドラッグ表示用のPrefabを生成する
・ドラッグ中は指(マウス)の位置に合わせて位置を移動する
・ドラッグできない領域に移動したら
→SetActive(false)で非表示にする
→ドラッグ状態を中止する
→マスクを利用してカードの一部は見えなくなるが
位置の更新だけはそのまま継続する

という感じになるかと思います

投稿2019/10/29 01:54

izmktr

総合スコア2856

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Gchan

2019/10/29 02:30

自分でも混乱していてよくわからなくなってしまっているのですが、 自分の手札領域でドラッグを開始したカードはフィールドにドロップしない限りどこまでドラッグしてどこにドロップしたとしても自分の手札に戻ってくるようにdefaultParentを設定しているのでそこは問題ないです 要するに今問題になってるのが、自分のターンに相手の手札領域にあるカードもドラッグが可能になっている状態なんですね、つまり自分のターンに相手の手札のカードをドラッグして場にだして自分のカードとして使えてしまう状態になってるんです なので、ドラッグを開始するポイント?ですかね 相手の手札にあるカードからドラッグを開始しようとしたときにそもそもドラッグが不可能という状態にしたいのです 相手の手札のカードリストを取得して、自分のターン中は相手手札のカードリストからドラッグしようとしたときにOnBeginDrag()の部分で今は自分のターンで、あなたがドラッグしようとしてるのは相手の手札だからドラッグできませんよ という風にしたいんですが、ご回答通りのやり方で実装できるでしょうか? 自分の理解力不足が原因なのは分かっているのですが、回答を読んだ所、手札でドラッグした自分のカードを相手の手札の方までドラッグできないようにして、相手の手札の方まで自分のカードをドラッグしてドロップした場合自分の手札に戻ってくる?感じの処理に読めてしまって…… 言葉足らずで申し訳ありません……
izmktr

2019/10/29 02:53

画面のものがすべてドラッグできるわけではないですよね 例えば背景とか装飾物とか それとカードみたいなドラッグできるものを分けているものはなんですか 自分のターンに相手の手札がドラッグできるのがおかしいのなら、 そのドラッグできるものを無効化する方法はありませんか?
Gchan

2019/10/29 03:58 編集

void PlayerTurn() { CardController[] phandList = pHandTransform.GetComponentsInChildren<CardController>(); int size = phandList.Length; for (int i = 0; i < size; i++) { phandList[i].GetComponent<CardMovement>().enabled = true; } CardController[] ehandList = eHandTransform.GetComponentsInChildren<CardController>(); int esize = ehandList.Length; for(int i =0; i < esize; i++) { ehandList[i].GetComponent<CardMovement>().enabled = false; } Debug.Log("Playerのターン"); } void EnemyTurn() //エネミーのターンにやる処理 { CardController[] phandList = pHandTransform.GetComponentsInChildren<CardController>(); int size = phandList.Length; for (int i = 0; i < size; i++) { phandList[i].GetComponent<CardMovement>().enabled = false; } CardController[] ehandList = eHandTransform.GetComponentsInChildren<CardController>(); int esize = ehandList.Length; for (int i = 0; i < esize; i++) { ehandList[i].GetComponent<CardMovement>().enabled = true; } 無効化するとのことだったので、GameManagerのほうに[SerializeFied]でエネミーの手札領域と自分の手札領域を取得させ そのごターン開始時の処理として相手の手札のカードリストを取得後カードの移動を担っているCardMovementのコンポーネントを無効化、逆にターンが回ってきたときに使えないと意味が無いので、自分の手札のカードリストを取得後コンポーネントを有効化 これを相手ターンには逆の処理をしてあげる というコードを書いたところ見事に成功しました しかし今のプログラムではドローのスクリプトをデッキのイメージにボタンを設置し、そのボタンを操作側がクリックすることによって1枚1枚引くという作業を行っているため、最初のターンに手札が存在せず、ターンが回ってきてから新たにドローしたカードはカードリスト取得後のカードの為コンポーネントが無効化されずにドラッグできてしまうようです ・これの改善の為には、ドローのシステムをターン開始時の処理に組み込むことで自動化する ・新たになんらかのカードの効果で自分のターン中に相手がカードをドローした時、再度カードリストの取得を行いコンポーネントを無効化するコードを書く でいいんでしょうか そもそも無効化するとはこのような方法で正解だったのでしょうか?
izmktr

2019/10/29 04:42

処理の都合上、無効化のタイミングが作れないのなら逆にするのもありです CardMovementのenabledを関数にして、GameManagerに自分のカードはドラッグできる状況なのか確認する、という感じですね
Gchan

2019/10/29 04:51

わかりました!! 大変参考になりました、有難う御座いました!!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問