質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

88.20%

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 504

Gchan

score 11

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

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

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

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

CardController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CardController : MonoBehaviour
{
    public CardModel model; //カードの情報
    public CardMovement movement; //CardMovement.cs扱えるように

    private void Awake()
    {
        movement = GetComponent<CardMovement>(); //CardMovementクラスの取得
    }
}

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems; 

public class CardMovement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    public Transform defaultParent; 
    public bool isDraggable; //ドラッグ可能かどうか。これを使ってどうにか実装できないか考えていた

    public void OnBeginDrag(PointerEventData eventData)
        {
           defaultParent = transform.parent;                           
           transform.SetParent(defaultParent.parent, false);   
           GetComponent<CanvasGroup>().blocksRaycasts = false;
        }

        public void OnDrag(PointerEventData eventData) 
        {
            Vector3 cardPos = Camera.main.ScreenToWorldPoint(eventData.position);
            cardPos.z = 0;
            transform.position = cardPos;
        }

        public void OnEndDrag(PointerEventData eventData) 
        {
            transform.SetParent(defaultParent, false);
            GetComponent<CanvasGroup>().blocksRaycasts = true; 
        }
    }

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class DropPlace : MonoBehaviour, IDropHandler
{ 
    public enum TYPE //手札のカードかフィールドか ここにEHAND(敵の手札)を追加して実装できないかどうかも考えた
    {
        HAND,
        FIELD,
    }
    public TYPE tips;
    public void OnDrop(PointerEventData eventData)
    {
        if (tips == TYPE.HAND) //手札の範囲内でドラッグした時にカードのコスト(soul)を消費しないようにしている
        {
            return;
        }
        CardController card = eventData.pointerDrag.GetComponent<CardController>(); //ドラッグされてきたカードのデータ
        if (card != null) //もしカードが重なっているなら
        {
            if (card.model.soul > GameManager.instance.playerSoul && GameManager.instance.isPlayerTurn == true) 
            {
                return;
                //GameManager.csにあるbool関数のisPlayerTurnで自分のターンであるかを判定している
                //自分のターンに受け取ったカードのコスト(soul)が、自分の所持しているコストよりも大きいなら
                //コストが不足しているためカードのドロップが出来ないようにしている。
            }
            else if (card.model.soul > GameManager.instance.enemySoul && GameManager.instance.isPlayerTurn == false )
            {
                return;
                //上と逆で相手ターンにコスト不足ならドロップ出来ないようにしている
            }
            card.movement.defaultParent = this.transform;  //ドロップされたときにdefaultParentをドロップした箇所に設定している
            card.model.isFieldCard = true; //ドロップされる場所はFieldなので、ドロップされたCardModel.csにあるbool関数のisFieldCardをtrueにすることでFieldに置かれているフィールド上のカードですよという状態にする
        }
    }
}

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

CardModel.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CardModel
{
    public string name;
    public int soul;
    ...
    ...
    public bool isFieldCard; //ここでisFieldCardを付けています
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    public bool isPlayerTurn;  //プレイヤーのターンかどうかの確認するため
    ...
    ...
    void Start()
    {
        StartGame();
    }
    void StartGame()
    {
        ...
        ...
        isPlayerTurn = true; //最初はプレイヤーのターンなのでtrueにしておく
        TurnCalc(); 
    }
    void TurnCalc() //ターンを切り替える処理
    {
        if (isPlayerTurn == true) 
        {
            PlayerTurn(); //プレイヤーのターン処理をする
        }
        else
        {
            EnemyTurn(); //エネミーのターン処理をする
        }
    }
    public void ChangeTurn() //ターンチェンジ処理、ボタンにアタッチしてクリックで切り替えしている
    {
        isPlayerTurn = !isPlayerTurn; //もしプレイヤーのターンなら否定になるのでエネミーターンに、逆の場合も
        if(isPlayerTurn == true)
        {
            playerSoul = playerMaxSoul; //コスト回復処理みたいなものなので今回の質問には関係ないです(多分)
            ShowSoul();                 //そもそも書いてある内容がここにはこれだけなので一応載せました
        }
        else
        {
            enemySoul = enemyMaxSoul; //上に同じく
            ShowSoul();
        }
        TurnCalc(); //ターンが移ったらターン開始の処理が行われる
    }
    void PlayerTurn() //上下ともTurnCalc()で切り替わる時のターン処理
    {
        Debug.Log("Playerのターン"); //現在は特に何も書くことがないので切り替わってるかだけ確認しています
    }
    void EnemyTurn() 
    {
        Debug.Log("敵のターン"); //上に同じく
    }
...
...
}

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

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Draw : MonoBehaviour
{
    [SerializeField] Transform playerHandTransform = default;    
    [SerializeField] Transform enemyHandTransform = default;  
    [SerializeField] CardController cardPrefab = default;

    List<int> playerDeck = new List<int>() { 1, 3, 2, 2, 1 };     
    List<int> enemyDeck = new List<int>() { 2, 2, 1, 1, 2 };  

    public void PlayerDrow() 
    {
        GiveCardToHand(playerDeck, playerHandTransform);
    }
    public void EnemyDrow() 
    {
        GiveCardToHand(enemyDeck, enemyHandTransform);
    }

    public void GiveCardToHand(List<int> deck, Transform hand)
    {
        if (deck.Count == 0) 
            return;            
        }
        int cardID = deck[0];
        deck.RemoveAt(0); 
        CreateCard(cardID, hand); 
    }
    void CreateCard(int cardID, Transform hand)
    {
        CardController card = Instantiate(cardPrefab, hand, false);
        card.Init(cardID);
    }

}

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

長くなりましたのでもう一度実装したい内容なのですが
**
・自分のターン中(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の区別をつけるようにしてアタッチしてみたりもしたのですが上手くいかず、どうすればいいのか混乱している状態です。
宜しくお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/29 12:55 編集

    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枚引くという作業を行っているため、最初のターンに手札が存在せず、ターンが回ってきてから新たにドローしたカードはカードリスト取得後のカードの為コンポーネントが無効化されずにドラッグできてしまうようです
    ・これの改善の為には、ドローのシステムをターン開始時の処理に組み込むことで自動化する
    ・新たになんらかのカードの効果で自分のターン中に相手がカードをドローした時、再度カードリストの取得を行いコンポーネントを無効化するコードを書く

    でいいんでしょうか

    そもそも無効化するとはこのような方法で正解だったのでしょうか?

    キャンセル

  • 2019/10/29 13:42

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

    キャンセル

  • 2019/10/29 13:51

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

    キャンセル

0

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/29 13:00

    いえ、できません。
    逆にそういう特殊なカードゲームに今の時点でなってしまっているので直そうと思っている次第です。
    説明下手で伝わりにくくて申し訳ありません。

    一応今かなり解決には向かっているのですが、tagのやり方は知らなかったのでそちらも試してみようと思います。有難うございます!

    キャンセル

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

  • ただいまの回答率 88.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る