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

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

新規登録して質問してみよう
ただいま回答率
85.48%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Unity

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

Q&A

解決済

1回答

5170閲覧

マルチタッチで互いの干渉・混同が起きないようにしたい

funyao

総合スコア12

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Unity

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

0グッド

0クリップ

投稿2018/10/29 08:30

編集2018/11/02 02:00

実現目標

 iOSのゲームを製作中です。
メインキャラクターPlayerに、
・「タッチAでXZ平面の移動」
・「タッチBで向きを徐々に回転」
(・「タッチCで向きを瞬間的に回転」)
を同時に動作させようとしています。

 現在はタッチAとタッチBを同時実現しようとしています。
そのため、“MoveController”タグのImageと“RotateController”タグのImage、2つをキャンバスの離れた左右に貼り付け、
・“Move~”にタッチした時はタッチAとして移動
・“Rotate~”にタッチした時はタッチBとして回転
を判別させようとしています。

発生している問題

現在、Remote機能でiphone7上でテスト中です。
『移動』『回転』それぞれの動作は実現できているのですが、2つのタッチを同時にした際、
・「タッチAで移動中」にタッチBをすると「移動が停止し、回転も動作しない」
・「タッチBで回転中」にタッチAをすると「回転が停止し、移動も動作しない」
となります。後発のタッチを離すと、先発の動作が再開します。

2つのタッチが区別できずに、後発のタッチが先発の条件を塗り替えてしまっていると理解しているのですが、その通りだとして具体的に解決する方法が考えつきません。
ご教授をどうかお願いします。

問題のスクリプト

 以下は、動作させるオブジェクトにアタッチしているスクリプトです。
問題があるのがどの部分か判断できず、全記述を掲載させていただきます。当たり前ですが、エラー警告は発生していません。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.EventSystems; 5 6public class PlayerContTouchUI : MonoBehaviour { 7 8 Vector3 Movestart; 9 Vector3 Move; 10 float deltaX; 11 float deltaZ; 12 13 public float MoveMagni = 0.1f; 14 public float amountOfRotation = 1f; 15 16 Vector3 Rostart; 17 Vector3 Ro; 18 float RodeltaX; 19 20 void Start () {} 21 22 void Update () { 23 if (Input.touchCount > 0) 24 { 25 Touch[] myTouches = Input.touches; 26 for (int i = 0; i < Input.touchCount; i++) 27 { 28 Touch t = Input.GetTouch(i); 29 if (OnMoveTouchDown() == true) //“Move~”にタッチしているか 30 { 31 if (t.phase == TouchPhase.Began) 32 { 33 Movestart = myTouches[i].position; 34 } 35 36 if (t.phase == TouchPhase.Moved) 37 { 38 Move = myTouches[i].position; 39 deltaX = (Move - Movestart).x; 40 deltaZ = (Move - Movestart).y; 41 } 42 //移動動作 43 this.transform.Translate 44 (deltaX * Time.deltaTime * MoveMagni, 0f, 45 deltaZ * Time.deltaTime * MoveMagni, 46 Space.World); 47 //移動動作ここまで 48 } 49 50 if (OnRotateTouchDown() == true) //“Rotate~”にタッチしているか 51 { 52 if (t.phase == TouchPhase.Began) 53 { 54 Rostart = myTouches[i].position; 55 } 56 57 if (t.phase == TouchPhase.Moved) 58 { 59 Ro = myTouches[i].position; 60 RodeltaX = (Rostart - Ro).x; 61 } 62 //回転動作 63 transform.Rotate(Vector3.up, RodeltaX * Time.deltaTime * -amountOfRotation); 64 } 65 } 66 } 67 } 68 69 //“Move~”にタッチしているか判定 70 bool OnMoveTouchDown() 71 { 72 if (0 < Input.touchCount) 73 { 74 for (int i = 0; i < Input.touchCount; i++) 75 { 76 Touch t = Input.GetTouch(i); 77 if (t.phase == TouchPhase.Began || t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary) 78 { 79 PointerEventData pointer = new PointerEventData(EventSystem.current); 80 pointer.position = Input.mousePosition; 81 List<RaycastResult> result = new List<RaycastResult>(); 82 EventSystem.current.RaycastAll(pointer, result); 83 84 foreach (RaycastResult raycastResult in result) 85 { 86 if (raycastResult.gameObject.tag == "MoveController") 87 { 88 return true; 89 } 90 } 91 } 92 } 93 } 94 return false; 95 } 96 97 //“Rotate~”にタッチしているか判定 上記OnMoveTouchDown()とほぼ同内容 98 bool OnRotateTouchDown() 99 { 100 if (0 < Input.touchCount) 101 { 102 for (int i = 0; i < Input.touchCount; i++) 103 { 104 Touch t = Input.GetTouch(i); 105 if (t.phase == TouchPhase.Began || t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary) 106 { 107 PointerEventData pointer = new PointerEventData(EventSystem.current); 108 pointer.position = Input.mousePosition; 109 List<RaycastResult> result = new List<RaycastResult>(); 110 EventSystem.current.RaycastAll(pointer, result); 111 112 foreach (RaycastResult raycastResult in result) 113 { 114 if (raycastResult.gameObject.tag == "RotateController") 115 { 116 return true; 117 } 118 } 119 } 120 } 121 } 122 return false; 123 } 124}

その他に試したこと

タッチBeganとEndedでONOFFするboolを管理すれば、「後発のタッチで先発の動作が停止」することはないだろうと試しました。動作の停止はなくなりましたが、結局は条件内でタッチが混同して、移動と回転がぶっ飛ぶことになりました。
今後もタッチによる他動作が加わる余地を考えると、タッチの判別ができる記述と知識が必要だろうと思い、質問を投稿した次第です。

追記

「根本的におめーの学習が足りてないだろ」というご指摘は避けられませんので、「ここを見ろ」「あれを読め」というだけのご回答でもありがたいです。

※同一の質問で二重投稿してしまい、こちらを残し、一方を削除依頼しております。

追試スクリプト記述(1)

この記述の場合、タッチの混同が発生しました。
事例:画面左(-x)のMoveコントローラーで移動動作中、画面右(+x)のRotateコントローラーにタッチする
→対象Objが左(-x)に大きく吹っ飛ぶ+新規のデバックログMovestartを確認
→条件が一致しないはずの、後発のRotateタッチが、MoveタッチBeganとして判定されている。

C#

1//前略 2 3void Update () { 4 if (Input.touchCount > 0) 5 { 6 for (int i = 0; i < Input.touchCount; i++) 7 { 8 Touch t = Input.GetTouch(i); 9 if (OnMoveTouchDown() == true) 10 { 11 if (t.phase == TouchPhase.Began) 12 { 13 //Movestart = myTouches[i].position; //御回答の指摘と直接関係しないが下に変更 14 Movestart = t.position; 15             Debug.Log("Movestart" + Movestart); //タッチ混同を確認 16 } 17 //中略 18 } 19 20 if (OnRotateTouchDown() == true) 21 { 22         //略 23 } 24 } 25 } 26 } 27 28 bool OnMoveTouchDown() 29 { 30 if (0 < Input.touchCount) 31 { 32 for (int i = 0; i < Input.touchCount; i++) 33 { 34 Touch t = Input.GetTouch(i); 35 if (t.phase == TouchPhase.Began || t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary) 36 { 37 PointerEventData pointer = new PointerEventData(EventSystem.current); 38 //pointer.position = Input.mousePosition; //★ご指摘の箇所★ 39 pointer.position = t.position; //上記述から変更 40 //以下、略 41 } 42 43 bool OnRotateTouchDown() 44 { 45 //OnMoveTouchDown()と同様の変更のため、略 46 } 47}

追試スクリプト記述(2)

タッチ判定の条件付けに原因があると思え、別の方式を試行。
nullがちゃんと扱えず、エラー。

C#

1//前略 2void Update() 3 { 4 //“Move~”にタッチしているか 5 Touch? t = OnMoveTouchDown(); //null許容のためにTouch?に変更 6 if (t != null) //Touch?のため、ここは通過 7 { 8 Debug.Log(t); //Touchログが出るが… 9 if (t.phase == TouchPhase.Began) //'phase'がTouch?に含まれていない.エラー 10 { 11 Movestart = t.position;  //'position'同様 12 } 13 //中略 14 } 15 16 //“Rotate~”にタッチしているか 17 Touch? r = OnRotateTouchDown(); 18 if (r != null) 19 { 20 //中略 21 } 22 } 23 24Touch? OnMoveTouchDown() 25 { 26 if (0 < Input.touchCount) 27 { 28 for (int i = 0; i < Input.touchCount; i++) 29 { 30   //中略 31 if (raycastResult.gameObject.tag == "MoveController") 32 { 33 return t; 34 }}}}} 35 return null; 36 } 37 38 Touch? OnRotateTouchDown() 39 { 40 //略 41 }

解決後の記述(一部、略)

C#

1//いろいろ略 2using UnityEngine.EventSystems; 3 4 void Update() 5 { 6 if (Input.touchCount > 0) 7 { 8 for (int i = 0; i < Input.touchCount; i++) 9 { 10 Touch t = Input.GetTouch(i); 11 if (OnMoveTouchDown(t) == true) //チェック中のTouchを引数で渡す 12 { 13 if (t.phase == TouchPhase.Began) 14 { 15 Movestart = t.position; 16 } 17 if (t.phase == TouchPhase.Moved) 18 { 19 Move = t.position; 20 deltaX = (Move - Movestart).x; 21 deltaZ = (Move - Movestart).y; 22 } 23 //略 24 } 25 26 if (OnRotateTouchDown(t) == true) //チェック中のTouchを引数で渡す 27 { 28 //略 29 } 30 } 31 } 32 } 33 34 bool OnMoveTouchDown(Touch t) 35 { //当初for文を入れていたが、Update内で済ませているので不要 36 if (t.phase == TouchPhase.Began || t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary) 37 { 38 PointerEventData pointer = new PointerEventData(EventSystem.current); 39 pointer.position = t.position; 40 List<RaycastResult> result = new List<RaycastResult>(); 41 EventSystem.current.RaycastAll(pointer, result); 42 43 foreach (RaycastResult raycastResult in result) 44 { 45 if (raycastResult.gameObject.tag == "MoveController") 46 { 47 return true; 48 } 49 } 50 } 51 return false; 52 } 53 54 bool OnRotateTouchDown(Touch t) 55 { 56 //略 57 } 58}

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

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

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

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

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

guest

回答1

0

ベストアンサー

どちらもタッチ位置の取得が
pointer.position = Input.mousePosition;
なので、(Unityの仕様上どっちか分かりませんが)先か後、どっちかのタッチのみが衝突判定に使われているものと思います。

とりあえず以下にすれば干渉は起こらないんじゃないかと。
pointer.position = t.position;

おまけですが、OnMoveTouchDownとOnRotateTouchDownの返り値をTouch型にする(該当のオブジェクトに当たっていなかったらnull返す)とUpdate内のfor文が無くなってちょっとスマートかと。

C#

1void Update () { 2 //“Move~”にタッチしているか 3 Touch t = OnMoveTouchDown(); 4 if (t != null) 5 { 6 if (t.phase == TouchPhase.Began) 7 { 8 Movestart = t.position; //myTouches[i]の代わりにtを使う 9 } 10 //中略 11 } 12 13 //“Rotate~”にタッチしているか 14 Touch t = OnRotateTouchDown(); 15 if (t != null) 16 { 17 if (t.phase == TouchPhase.Began) 18 { 19 Rostart = t.position; //myTouches[i]の代わりにtを使う 20 } 21 //中略 22 } 23}

(2018/10/30追記)
すいません、大分思い違いしてました。
pointer.position = t.position;にするだけだとやっぱり干渉します。

例えばTouch[0]がMove側にタッチ、Touch[1]がRotate側にタッチしているとしたら、現状の処理は以下のようになります。

Update:for文1回目 i=0 OnMoveTouchDown:for文1回目→[0]の判定開始、trueを返す Update:Touch[0]を元に移動処理実行 OnRotateTouchDown:for文1回目→[0]の判定開始、条件を満たさない OnRotateTouchDown:for文2回目→[1]の判定開始、trueを返す Update:Touch[0]を元に回転処理実行 Update:for文2回目 i=1 OnMoveTouchDown:for文1回目→[0]の判定開始、trueを返す Update:Touch[1]を元に移動処理実行 OnRotateTouchDown:for文1回目→[0]の判定開始、条件を満たさない OnRotateTouchDown:for文2回目→[1]の判定開始、trueを返す Update:Touch[1]を元に回転処理実行

つまり「Updateのforループ1回につき、全タッチの確認をしてしまっている」ので条件が狂う訳です。

そしてTouchってNull許容してないんですね、未確認で申し訳無いです。
よくよく考えればUpdateで対象のTouchは1つずつ取得してるんですから、そいつをそのまま衝突判定に使えばいいだけでした。
という訳で修正版が以下です。

C#

1void Update () { 2 if (Input.touchCount > 0) 3 { 4 for (int i = 0; i < Input.touchCount; i++) 5 { 6 Touch t = Input.GetTouch(i); 7 if (OnMoveTouchDown(t) == true) //チェック中のTouchを引数で渡す 8 { 9 if (t.phase == TouchPhase.Began) 10 { 11 Movestart = t.position; 12             Debug.Log("Movestart" + Movestart); //タッチ混同を確認 13 } 14 //中略 15 } 16 17 if (OnRotateTouchDown(t) == true) //チェック中のTouchを引数で渡す 18 { 19 if (t.phase == TouchPhase.Began) 20 { 21 Rostart = t.position; //同様の変更 22 } 23         //中略 24 } 25 } 26 } 27} 28 29bool OnMoveTouchDown(Touch t) { 30 if (t.phase == TouchPhase.Began || t.phase == TouchPhase.Moved || t.phase == TouchPhase.Stationary) 31 { 32 PointerEventData pointer = new PointerEventData(EventSystem.current); 33 pointer.position = t.position; //上記述から変更 34 List<RaycastResult> result = new List<RaycastResult>(); 35 EventSystem.current.RaycastAll(pointer, result); 36 37 foreach (RaycastResult raycastResult in result) 38 { 39 if (raycastResult.gameObject.tag == "MoveController") 40 { 41 return true; 42 } 43 } 44 } 45 return false; 46} 47 48//OnRotateTouchDownは省略

投稿2018/10/29 10:44

編集2018/10/30 13:47
sakura_hana

総合スコア11427

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

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

funyao

2018/10/30 10:04

sakura_hanaさんには以前も助けていただきました。またもありがとうございます。 ご回答の案を試してみましたが、うまくいきませんでした。 まず本文追記編集の(1)にて 【pointer.position = t.position;】 の変更を試してみましたが、それまではタッチが干渉して停止していた動作が、タッチが混同して先発の座標が後発の座標に塗り替えられるような動作になってしまいました。 これはやはりタッチの条件付けに原因があると感じ、おまけとして挙げられた「null確認法」に条件付けの活路があるのではと、追試(2)を試しました。 が、自分の知識が根本的に足りないのか…。 Touch型はreturnnullを受け付けてくれず、Touch?はと試してもこの記述において必要な情報を含まなくなってしまいます。 何か、自分からご回答への理解がズレていれば、またご指摘をお願いします。
sakura_hana

2018/10/30 13:48

すみません、回答に追記しました。
funyao

2018/11/02 01:34

返信が遅れ、申し訳ありません。 いろいろと他を試しつつ、ご指摘の修正を加えて実装し、まさに意図通りの挙動になりました。 自分でも追加の勉強をして、自分がfor文の何を間違えていたかようやく理解できたようです。これも御回答のおかげです。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問