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

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

ただいまの
回答率

90.47%

  • C#

    9246questions

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

  • Unity

    5661questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

Unityのスマホタッチ操作に関して(1本目に置いた指を2本目に引っ張られたくない)

解決済

回答 1

投稿 編集

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

jury

score 6

こんにちは。
昨日に引き続きUnityに関して質問させて頂きます。

解決したいこと

現在、以下画像のようにシューティングゲームを作成したいと思っています。
左下のUIが中央の青いオブジェクト(プレイヤー)を動かすジョイスティックで、右下のUIがプレイヤーから弾を発射させるボタンです。
イメージ説明

スティックで移動させながら弾を発射出来るようにしたのですが、発射ボタンを押すとタップ感知が反応してしまい、ジョイスティックが右側に持っていかれてしまいプレイヤーの挙動がおかしなことになってしまいます。

移動や発射自体はそれぞれ問題無くうごくのですが、両立させる方法が中々思いつきません… もし何か良いアイディア等あればご教授頂ければと思います。

編集追記
方法として、タップ処理でタップできる箇所の範囲を制限する。
あるいはドラッグ中の場合のタップを受け付けないといった物を思いついたのですが、
上手く実装できません……

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

/// <summary>
/// ジョイスティック
/// </summary>
public class Joystick : Graphic, IPointerDownHandler, IPointerUpHandler, IEndDragHandler, IDragHandler
{

    //実際に動くスティック部分
    [SerializeField]
    [Header("実際に動くスティック部分(自動設定)")]
    private GameObject _stick = null;
    private const string STICK_NAME = "Stick";

    //スティックが動く範囲の半径
    [SerializeField]
    [Header("スティックが動く範囲の半径")]
    private float _radius = 100;

    //指を離した時にスティックが中心に戻るか
    [SerializeField]
    [Header("指を離した時にスティックが中心に戻るか")]
    private bool _shouldResetPosition = true;

    //現在地(x,y共に値が-1~1の範囲になる)
    [SerializeField]
    [Header("現在地(自動更新)")]
    private Vector2 _position = Vector2.zero;
    public Vector2 Position { get { return _position; } }

    //スティックの位置(Setter)
    private Vector3 _stickPosition
    {
        set
        {
            _stick.transform.localPosition = value;
            _position = new Vector2(
              _stick.transform.localPosition.x / _radius,
              _stick.transform.localPosition.y / _radius
            );
        }
    }

    //=================================================================================
    //初期化
    //=================================================================================

    protected override void Awake()
    {
        base.Awake();
        Init();
    }

    //初期化
    private void Init()
    {
        //スティックを生成する必要があれば生成し、位置を中心に設定
        CreateStickIfneeded();
        _stickPosition = Vector3.zero;

        //スティックのImage取得(なければ追加)、タッチ判定を取られないようにraycastTargetをfalseに
        Image stickImage = _stick.GetComponent<Image>();
        if (stickImage == null)
        {
            stickImage = _stick.AddComponent<Image>();
        }
        stickImage.raycastTarget = false;

        //タッチ判定を受け取れるようにRaycastTargetをTrueに
        raycastTarget = true;

        //タッチ判定をとる範囲は表示されないように透明に
        color = new Color(0, 0, 0, 0);
    }

    //スティックを生成する必要があれば生成
    private void CreateStickIfneeded()
    {
        //スティックが設定されていれば終了
        if (_stick != null)
        {
            return;
        }

        //スティックが子にあるか検索、あれば取得し終了
        if (transform.Find(STICK_NAME) != null)
        {
            _stick = transform.Find(STICK_NAME).gameObject;
            return;
        }

        //スティック生成
        _stick = new GameObject(STICK_NAME);
        _stick.transform.SetParent(gameObject.transform);
        _stick.transform.localRotation = Quaternion.identity;
    }

    //=================================================================================
    //タップ
    //=================================================================================

    //タップ開始時
    public void OnPointerDown(PointerEventData eventData)
    {
        //タップした瞬間にドラッグを開始した事にする
        OnDrag(eventData);
    }

    //タップ終了時(ドラッグ終了時には呼ばれない)
    public void OnPointerUp(PointerEventData eventData)
    {
        //タップした終了した時にドラッグを終了した時と同じ処理をする
        OnEndDrag(eventData);
    }

    //=================================================================================
    //ドラッグ
    //=================================================================================

    //ドラッグ終了時
    public void OnEndDrag(PointerEventData eventData)
    {
        if (_shouldResetPosition)
        {
            //スティックを中心に戻す
            _stickPosition = Vector3.zero;
        }
    }

    //ドラッグ中
    public void OnDrag(PointerEventData eventData)
    {
        //タップ位置を画面内の座標に変換し、スティックを移動
        Vector2 screenPos = Vector2.zero;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(),
          new Vector2(Input.mousePosition.x, Input.mousePosition.y),
          null,
          out screenPos
        );

        _stickPosition = screenPos;

        //移動場所が設定した半径を超えてる場合は制限内に抑える
        float currentRadius = Vector3.Distance(Vector3.zero, _stick.transform.localPosition);
        if (currentRadius > _radius)
        {

            //角度計算
            float radian = Mathf.Atan2(_stick.transform.localPosition.y, _stick.transform.localPosition.x);

            //円上にXとYを設定
            Vector3 limitedPosition = Vector3.zero;
            limitedPosition.x = _radius * Mathf.Cos(radian);
            limitedPosition.y = _radius * Mathf.Sin(radian);

            _stickPosition = limitedPosition;
        }

    }

    //=================================================================================
    //更新
    //=================================================================================

#if UNITY_EDITOR
    //Gizmoを表示する
    private void OnDrawGizmos()
    {
        //スティックが移動できる範囲をScene上に表示
        UnityEditor.Handles.color = Color.green;
        UnityEditor.Handles.DrawWireDisc(transform.position, transform.forward, _radius * 0.5f);
    }
#endif

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

大まかな方針だけになりますが
Input.touchesを使うと複数の指の座標がもとまるのでforで一本ずつ処理すると良いと思います
イメージとしては、Input.touches[指のinxex]の座標がJoyStickの範囲内にあるときはJoystickの位置を更新、ボタンの範囲内にあるときはボタンを更新という感じになると思います。UI座標からスクリーンに変換するところはstackoverflowのやり方で出来ると思います

追記
クラスの継承はMonoBehaviorだけにして
(Joystick:MonoBehavior)
イベント処理の関数は消してください
OnDragを以下のように、

    float _radius2=150;//ジョイスティックの最大半径
   public bool OnDrag(Vector2 touch)
    {
        //タップ位置を画面内の座標に変換し、スティックを移動
        Vector2 screenPos = Vector2.zero;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(),
          touch,
          null,
          out screenPos
        );

        _stickPosition = screenPos;

        //移動場所が設定した半径を超えてる場合は制限内に抑える
        float currentRadius = Vector3.Distance(Vector3.zero, _stick.transform.localPosition);
        if (currentRadius > _radius2)
        {return false;}

        if (currentRadius > _radius)
        {

            //角度計算
            float radian = Mathf.Atan2(_stick.transform.localPosition.y, _stick.transform.localPosition.x);

            //円上にXとYを設定
            Vector3 limitedPosition = Vector3.zero;
            limitedPosition.x = _radius * Mathf.Cos(radian);
            limitedPosition.y = _radius * Mathf.Sin(radian);

            _stickPosition = limitedPosition;
        }
        return true;
    }


Updateを以下のようにしてください

public void Update(PointerEventData eventData) {
    bool isInsideBoundary;
    Touch[] touches = Input.touches;
    for (int i = 0; i < Input.touchCount; i++) {
        Touch touch=touches[i];
        if(touch.phase==TouchPhase.Began||touch.ohase==TouchPhase.Moved){
             isInsideBoundary=OnDrag(touch.position);
              if(isInsideBoundary)break;
       }
    }
    if(!isInsideBoundary){
        if (_shouldResetPosition)
        {
            //スティックを中心に戻す
            _stickPosition = Vector3.zero;
        }
    }
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/16 15:29

    返事が遅くなってしまい申し訳ございません…
    ご回答ありがとうございます!
    それっぽく作ってみたのですが、中々思うようにいきません…

    public void Update(PointerEventData eventData) {
    Touch[] myTouches = Input.touches;
    for (int i = 0; i < Input.touchCount; i++) {
    //ここに処理内容
    if (Input.touchCount > 0) {
    myTouches[i] = Input.GetTouch(0);
    //Debug.Log(i);
    if (myTouches[i].fingerId == 0) {
    Rect rect = new Rect(x, y, w, h);
    bool contain = rect.Contains(myTouches[i].position);
    if (contain) {
    OnDrag(eventData);
    }
    }
    }
    }
    }

    想定ではこのrect範囲内で制御できるかと思っていたのですが、実行してみると
    Script error (Joystick): Update() can not take parameters.
    と出てしまいます。

    引き続き、解決方法などございましたらお願い致します。

    キャンセル

  • 2019/01/16 18:50

    public void Update(PointerEventData eventData) {

    public void Update() {
    にすればそのエラーはなくなりますが
    座標をジョイスティック側に与えるようにしないといけないですね
    時間があれば書いてみようと思います

    キャンセル

  • 2019/01/18 10:36

    コードの記載までありがとうございます!
    お答えいただいて、エラーのある個所は修正してみたのですが、
    以下の2か所のエラーがどうしても取り除けません…

    ・OnDrag(touch.position)でvoidをboolに暗示的には変換できません。

    ```C#
    public void Update(PointerEventData eventData){
    bool isInsideBoundary1 = true;
    Touch[] touches = Input.touches;
    for (int i = 0; i < Input.touchCount; i++){
    Touch touch = touches[i];
    if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved){
    isInsideBoundary1 = OnDrag(touch.position);
    if (isInsideBoundary1) break;
    }
    }
    if (!isInsideBoundary){
    if (_shouldResetPosition1){
    //スティックを中心に戻す
    _stickPosition1 = Vector3.zero;
    }
    }
    }
    ```
    ・return false;
    public void OnDrag(Vector2)はvoid型を返すため、キーワードreturnのあとにobject式を指定することはできません。
    ```C#
    public void OnDrag(Vector2 touch){
    //タップ位置を画面内の座標に変換し、スティックを移動
    Vector2 screenPos = Vector2.zero;
    //Debug.Log(Input.mousePosition);
    RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(),
    new Vector2(Input.mousePosition.x, Input.mousePosition.y),
    null,
    out screenPos
    );

    _stickPosition1 = screenPos;

    //移動場所が設定した半径を超えてる場合は制限内に抑える
    float currentRadius = Vector3.Distance(Vector3.zero, _stick1.transform.localPosition);

    if (currentRadius > _radius3){
    return false;
    }

    if (currentRadius > _radius1){
    //角度計算
    float radian = Mathf.Atan2(_stick1.transform.localPosition.y, _stick1.transform.localPosition.x);

    //円上にXとYを設定
    Vector3 limitedPosition = Vector3.zero;
    limitedPosition.x = _radius1 * Mathf.Cos(radian);
    limitedPosition.y = _radius1 * Mathf.Sin(radian);

    _stickPosition1 = limitedPosition;
    }
    }

    private T GetComponent<T>()
    {
    throw new NotImplementedException();
    }
    ```

    以上です。
    どうかお答え頂ければと思いますm(_ _)m

    キャンセル

  • 2019/01/18 18:06

    public void OnDrag

    public bool OnDragにしてください

    キャンセル

  • 2019/01/22 11:07

    確認が遅くなってしまいました…
    お答えいただきありがとうございます!
    実装してみたところ、上手く作動しました!

    キャンセル

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

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

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

  • C#

    9246questions

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

  • Unity

    5661questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。