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

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

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

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

Q&A

解決済

1回答

1120閲覧

unity imageに線を引くアプリでCommandButtonを押した時はMainy画面に戻る処理をしたい

29507-01

総合スコア46

Unity

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

0グッド

0クリップ

投稿2020/01/12 04:58

前提・実現したいこと

unity imageに線を引くアプリでCommandButtonを押した時はMainy画面に戻りたいなどの処理をしたいのですが,線は引けたのですが,CommandButtonを追加してMaine画面に戻る処理を追加した場合動きがおかしくなります。
ただ GetkeyDownなどの処理は問題ないので,OnButtonClickではなくCommandButonが押された時にGetkeyDownで判定する方法はないでしょうか?

タッチ処理の場合CommandButtonを使えないような気がするのですが?

if (Input.GetKeyDown("u"))この処理は問題なく動きます。

発生している問題・エラーメッセージ

CommandButonの処理が上手くいかない。
イメージ説明
イメージ説明

該当のソースコード

// *** MAINLOOP *** void Update() { if (enableTouch) { TouchPaint(); } else { MousePaint(); } } public void OnButtonClick() { Debug.Log("Click "); } // handle mouse events void MousePaint() { if (Input.GetKeyDown("u")) { DoUndo(); } if (Input.GetMouseButtonDown(0)) { beforeMousePos = GetPosition(); } if (Input.GetMouseButton(0)) { Vector3 v = GetPosition(); LineTo(beforeMousePos, v, lineColor); beforeMousePos = v; UpdateTexture(); textureNeedsUpdate = true; } // left mouse button released if (Input.GetMouseButtonUp(0)) { GrabUndoBufferNow(); } if (Input.GetMouseButton(1)) { DoUndo(); } }

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

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

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

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

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

Bongo

2020/01/14 21:36

私としては、ボタンクリック時の動作はなるべく本来のボタンの流儀に沿って「On Click ()」に実行したいメソッドをセットするのがよさそうに思うのですが、それだとうまくいかないということですね? その際の症状はどのようなものでしょうか(ボタンをクリックすると、ボタンで隠れている背面のImageのお絵かきも作動してしまい、クリック位置に余計な点が描かれてしまう...とか?)。 それともう一つ気になる点として、Painterスクリプトがボタンオブジェクトにもアタッチしてあるようです。これはどのような理由からでしょうか。この状態だとボタンの表面にも線が描かれてしまうかと思いますが、そのような動作をさせたいということでしょうか?
29507-01

2020/01/15 09:06 編集

いつもすみません。 「On Click ()」の処理でundo,又はMain画面に戻りたいのですが Input.GetMouseButtonDown(0)に処理が移って行くようです。 Painterスクリプトがボタンオブジェクトにもアタッチしてあるようです。これはどのような理由からでしょうか。 - - -> 知識不足で意味がありません。あくまで上記undo,又はMain画面に戻りたいのです。 ボタンをクリックすると、ボタンで隠れている背面のImageのお絵かきも作動してしまい、クリック位置に余計な点が描かれてしまい,ボタン処理でundoなどしたいのですが思うように動作しません。
guest

回答1

0

ベストアンサー

一案として、Painterスクリプトに下記のような修正を加えてみてはどうでしょうか?

  • PainterスクリプトにIPointerDownHandlerIPointerUpHandlerインターフェイスを実装する。
  • OnPointerDownは通常はImage上でマウスボタンを押し下げる、あるいはタッチを開始すると動作するが、Imageよりも前面に配置されたボタンをクリックした時には、ボタンによってイベントが遮断され動作しないので都合がいいと思われる。そこで、このタイミングで線引き中であることを示すフラグをtrueにする。
  • 線引きロジックは線引き中フラグがtrueの場合のみ動作させるようにする。OnPointerDownUpdateよりも先行して処理されるので、OnPointerDown内で線引きフラグを立てておけばUpdate内やMousePaint内、TouchPaint内でそれに基づいた分岐ができる。
  • OnPointerUpのタイミングで線引き中フラグをfalseにする。

C#

1// 省略 2 3// イベント関連の機能群を使用できるように、usingディレクティブを追加する 4using UnityEngine.EventSystems; 5 6// IPointerDownHandler、IPointerUpHandlerを実装することを宣言する 7[RequireComponent(typeof(RectTransform), typeof(Image))] 8public class Painter : MonoBehaviour, IPointerDownHandler, IPointerUpHandler 9{ 10 // 省略 11 12 // お絵かき中であることを示すフィールドを追加 13 bool isPainting; 14 15 // 省略 16 17 // このImage上でタッチ開始、またはマウスボタンを押し下げた時にisPaintingをtrueにし... 18 public void OnPointerDown(PointerEventData eventData) 19 { 20 isPainting = true; 21 } 22 23 // タッチが終了、またはマウスボタンが離されたらisPaintingをfalseにする 24 public void OnPointerUp(PointerEventData eventData) 25 { 26 isPainting = false; 27 } 28 29 // MousePaint内でisPaintingを見て適宜分岐する 30 // TouchPaintについても同様に分岐させる 31 // あるいはメソッドの構成を見直して、MousePaintやTouchPaintには線引き関連の 32 // 機能だけを記述することにし、Update内でisPaintingによる分岐を行うという作りなら 33 // 似たような分岐を2ヶ所に分散して記述する必要がなくなっていいかもしれない 34 // handle mouse events 35 void MousePaint() 36 { 37 if (Input.GetKeyDown("u")) 38 { 39 DoUndo(); 40 } 41 42 // 線引き中かどうかに関係なく動作させたいコードはここよりも上に記述する 43 // たとえばキーボード操作によるUndo、Redoなど 44 if (!isPainting) 45 { 46 // 線引き中に動作されると困るコードがあればこの辺に記述する 47 48 return; 49 } 50 // 線引き中のみ動作させたいコードはここよりも下に記述する 51 52 if (Input.GetMouseButtonDown(0)) 53 { 54 beforeMousePos = GetPosition(); 55 } 56 if (Input.GetMouseButton(0)) 57 { 58 Vector3 v = GetPosition(); 59 LineTo(beforeMousepos, v, lineColor); 60 beforeMousePos = v; 61 UpdateTexture(); 62 textureNeedsUpdate = true; 63 } 64 65 // left mouse button released 66 if (Input.GetMouseButtonUp(0)) 67 { 68 GrabUndoBufferNow(); 69 } 70 71 // 右ボタンクリックによるUndoも、この位置にあるとこのImage上で 72 // クリックした時しか動作しなくなると思われる 73 // もしそれだと困るようなら、適宜分岐経路を検討する 74 if (Input.GetMouseButton(1)) 75 { 76 DoUndo(); 77 } 78 } 79 80 // 省略 81}

すみませんがすぐに試せるモバイル環境を持っていないため、実機上だとどうなるか未確認です。意図と異なる動きをするようでしたら、異常の様子を詳しくコメントいただければ可能であれば直してみようと思います。

追記
UNITY LineRendererで線を描く事はできるのですが,canvasの下のimageに描く事は出来ないでしょうか?」の時のPainterと折衷させたバージョン

C#

1using System; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.EventSystems; 5using UnityEngine.SceneManagement; 6using UnityEngine.UI; 7 8/// <summary> 9/// お絵描き 10/// </summary> 11[RequireComponent(typeof(RectTransform), typeof(Image))] 12public class Painter : MonoBehaviour, IPointerDownHandler, IPointerUpHandler 13{ 14 // ペンの半径...0で1ピクセルの線になる 15 [Range(0, 5)] public int penRadius; 16 17 // 前景色(ペンの色) 18 public Color foregroundColor = Color.red; 19 20 // 背景色 21 // ただし、現状は以前のご質問で製作した「元々セットされているテクスチャを下絵にする」 22 // という方式になっているため、この背景色はスクリプト中で使用されていない 23 [SerializeField] private Color backgroundColor = Color.white; 24 25 private RectTransform rectTransform; 26 private Image image; 27 private Texture2D texture; 28 private Vector2Int textureSize; 29 private Vector2Int previousPixelPosition; 30 private Vector2Int cachedPixelPosition; 31 32 // 線引き中であることを示すフラグ 33 private bool isPainting; 34 35 private readonly Stack<Texture2D> undoStack = new Stack<Texture2D>(); 36 private readonly Stack<Texture2D> redoStack = new Stack<Texture2D>(); 37 38 public bool CanUndo => this.undoStack.Count > 0; 39 public bool CanRedo => this.redoStack.Count > 0; 40 41 // タイトルシーンに移動するメソッド 42 // ボタンのインスペクターの「On Click ()」でこのメソッドを実行するよう設定しておく 43 // 個人的な好みでは、「タイトルシーンに移動する」という機能は「Image上にお絵かきする」という機能と 44 // 直接的な関係があるようには感じられないので、別のスクリプトに役割分担させた方がいい気もしますが... 45 public void OnButtonClick() 46 { 47 SceneManager.LoadScene("Title"); 48 } 49 50 public void OnPointerDown(PointerEventData eventData) 51 { 52 this.isPainting = true; 53 } 54 55 public void OnPointerUp(PointerEventData eventData) 56 { 57 this.isPainting = false; 58 } 59 60 public void Undo() 61 { 62 if (!this.CanUndo) 63 { 64 return; 65 } 66 67 var previousTexture = this.texture; 68 var poppedTexture = this.undoStack.Pop(); 69 this.texture = poppedTexture; 70 this.redoStack.Push(previousTexture); 71 this.UpdateSprite(); 72 Debug.Log($"Undo! UndoStack : {this.undoStack.Count} RedoStack : {this.redoStack.Count}"); 73 } 74 75 public void Redo() 76 { 77 if (!this.CanRedo) 78 { 79 return; 80 } 81 82 var previousTexture = this.texture; 83 var poppedTexture = this.redoStack.Pop(); 84 this.texture = poppedTexture; 85 this.undoStack.Push(previousTexture); 86 this.UpdateSprite(); 87 Debug.Log($"Redo! UndoStack : {this.undoStack.Count} RedoStack : {this.redoStack.Count}"); 88 } 89 90 private void Record() 91 { 92 this.undoStack.Push(Instantiate(this.texture)); 93 foreach (var textureInRedoStack in this.redoStack) 94 { 95 Destroy(textureInRedoStack); 96 } 97 98 this.redoStack.Clear(); 99 Debug.Log($"Record! UndoStack : {this.undoStack.Count} RedoStack : {this.redoStack.Count}"); 100 } 101 102 private void UpdateSprite() 103 { 104 var previousSprite = this.image.sprite; 105 this.image.sprite = Sprite.Create( 106 this.texture, 107 new Rect(0, 0, this.texture.width, this.texture.height), 108 Vector2.zero); 109 Destroy(previousSprite); 110 } 111 112 private Vector2Int GetPixelPosition() 113 { 114 if (!RectTransformUtility.ScreenPointToLocalPointInRectangle( 115 this.rectTransform, 116 Input.mousePosition, 117 null, 118 out var pixelPositionFloat)) 119 { 120 return this.cachedPixelPosition; 121 } 122 123 var sizeDelta = this.rectTransform.sizeDelta; 124 pixelPositionFloat += sizeDelta * this.rectTransform.pivot; 125 var pixelPosition = new Vector2Int( 126 Mathf.Clamp((int)pixelPositionFloat.x, 0, this.textureSize.x - 1), 127 Mathf.Clamp((int)pixelPositionFloat.y, 0, this.textureSize.y - 1)); 128 Debug.Log($"Pixel Position: {pixelPosition}"); 129 this.cachedPixelPosition = pixelPosition; 130 return this.cachedPixelPosition; 131 } 132 133 private bool PositionIsWithinTexture(Vector2Int position) 134 { 135 return (position.x >= 0) && (position.y >= 0) && 136 (position.x < this.textureSize.x) && (position.y < this.textureSize.y); 137 } 138 139 private void PaintAt(Vector2Int position, Color32 color, int radius) 140 { 141 var sqrRadius = radius * radius; 142 for (var i = -radius; i <= radius; i++) 143 { 144 for (var j = -radius; j <= radius; j++) 145 { 146 var p = new Vector2Int(position.x + i, position.y + j); 147 var dot = (i * i) + (j * j); 148 if ((dot <= sqrRadius) && this.PositionIsWithinTexture(p)) 149 { 150 this.texture.SetPixel(p.x, p.y, color); 151 } 152 } 153 } 154 155 this.texture.Apply(); 156 } 157 158 private void LineTo(Vector2Int from, Vector2Int to, Color32 color, int radius) 159 { 160 var penPosition = from; 161 var x = (float)penPosition.x; 162 var y = (float)penPosition.y; 163 var fromTo = to - from; 164 var fromToSign = new Vector2Int((int)Mathf.Sign(fromTo.x), (int)Mathf.Sign(fromTo.y)); 165 var fromToXIsZero = fromToSign.x == 0; 166 var fromToYIsZero = fromToSign.y == 0; 167 fromToSign.x = fromToXIsZero ? 1 : fromToSign.x; 168 fromToSign.y = fromToYIsZero ? 1 : fromToSign.y; 169 var absFromTo = fromToSign * fromTo; 170 var dominantDirection = 0; 171 var delta = Vector2.zero; 172 if (absFromTo.x >= absFromTo.y) 173 { 174 var tangent = fromToXIsZero ? 0 : (float)fromTo.y / fromTo.x; 175 delta.x = fromToSign.x; 176 delta.y = tangent * delta.x; 177 } 178 else 179 { 180 var tangent = fromToYIsZero ? 0 : (float)fromTo.x / fromTo.y; 181 delta.y = fromToSign.y; 182 delta.x = tangent * delta.y; 183 dominantDirection = 1; 184 } 185 186 while (this.PositionIsWithinTexture(penPosition)) 187 { 188 try 189 { 190 this.PaintAt(penPosition, color, radius); 191 x += delta.x; 192 y += delta.y; 193 penPosition.x = (int)x; 194 penPosition.y = (int)y; 195 if (fromToSign[dominantDirection] > 0) 196 { 197 if (penPosition[dominantDirection] >= to[dominantDirection]) 198 { 199 break; 200 } 201 } 202 else 203 { 204 if (penPosition[dominantDirection] <= to[dominantDirection]) 205 { 206 break; 207 } 208 } 209 } 210 catch (Exception e) 211 { 212 Debug.LogException(e); 213 break; 214 } 215 } 216 } 217 218 private void Start() 219 { 220 this.rectTransform = this.transform as RectTransform; 221 this.image = this.GetComponent<Image>(); 222 var sizeDelta = this.rectTransform.sizeDelta; 223 var width = (int)sizeDelta.x; 224 var height = (int)sizeDelta.y; 225 var backgroundPixels = (this.image.mainTexture as Texture2D).GetPixels32(); 226 this.image.sprite = null; 227 this.texture = new Texture2D(width, height, TextureFormat.ARGB32, false) {filterMode = FilterMode.Bilinear}; 228 this.texture.SetPixels32(backgroundPixels); 229 this.texture.Apply(); 230 this.textureSize = new Vector2Int(width, height); 231 this.UpdateSprite(); 232 } 233 234 private void Update() 235 { 236 if (Input.GetKeyDown(KeyCode.U)) 237 { 238 this.Undo(); 239 } 240 241 if (Input.GetKeyDown(KeyCode.R)) 242 { 243 this.Redo(); 244 } 245 246 // 線引き中かどうかに関係なく動作させたいコードはここよりも上に記述する 247 if (!this.isPainting) 248 { 249 // 線引き中に動作されると困るコードがあればこの辺に記述する 250 return; 251 } 252 // 線引き中のみ動作させたいコードはここよりも下に記述する 253 254 if (Input.GetMouseButtonDown(0)) 255 { 256 this.Record(); 257 this.previousPixelPosition = this.GetPixelPosition(); 258 } 259 else if (Input.GetMouseButton(0)) 260 { 261 var currentPixelPosition = this.GetPixelPosition(); 262 this.LineTo(this.previousPixelPosition, currentPixelPosition, this.foregroundColor, this.penRadius); 263 this.previousPixelPosition = currentPixelPosition; 264 } 265 266 // 右クリックによるUndo 267 if (Input.GetMouseButtonDown(1)) 268 { 269 this.Undo(); 270 } 271 } 272}

投稿2020/01/15 09:51

編集2020/01/15 17:05
Bongo

総合スコア10807

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

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

29507-01

2020/01/15 10:50 編集

PCでテストしているのですが Imageのみスクリプトをアタッチしたのですが,MousePaint()へはどこから飛んで行くのでしょうか? 又ボタンのクリック処理をどう入れたら良いかが わかりません。 //この処理には来ます public void OnPointerDown(PointerEventData eventData) { isPainting = true; } public void OnPointerUp(PointerEventData eventData) { isPainting = false; } //スクリプト? public void OnButtonClick() { SceneManager.LoadScene("Title"); } //この処理にこない void MousePaint() { if (Input.GetKeyDown(KeyCode.U)) { this.Undo(); } if (Input.GetKeyDown(KeyCode.R)) { this.Redo(); } if (!isPainting) { // 線引き中に動作されると困るコードがあればこの辺に記述する return; } if (Input.GetMouseButtonDown(0)) { this.Record(); this.previousPixelPosition = this.GetPixelPosition(); }
Bongo

2020/01/15 10:55

ご質問者さんのご提示いただいたコードだと、Update内でenableTouchに基づいて分岐しTouchPaintとMousePaintを切り替えて実行しているご様子だったため、それにならってみました(つまりUpdateには手を付けず、その中で呼ばれているMousePaintの中にisPaintingによる分岐を仕込んだ...というわけです)。 もしかして現在のスクリプトはご提示いただいたスクリプトとはまた違った形なのでしょうか?もし以前のご質問で私の例示しましたPainterに沿った形の方がわかりやすそうでしたら、そのように投稿内容を修正しようと思います。 あるいは質問投稿欄の文字数が許せば、ご質問者さんの現在のPainterスクリプトをご提示いただけましたらそれに沿った形にしてみます。
29507-01

2020/01/15 11:31 編集

以前の処理の延長線で Input.GetKeyDown(KeyCode.U) この処理をコマンドボタン又はイメージをクリックする事によりUNDO,REDOを実現したいのです。 PaintスクリプトをButtonにアタッチすると線も引かなくなります。 // このImage上でタッチ開始、またはマウスボタンを押し下げた時にisPaintingをtrueにし... public void OnPointerDown(PointerEventData eventData) { isPainting = true; } // タッチが終了、またはマウスボタンが離されたらisPaintingをfalseにする public void OnPointerUp(PointerEventData eventData) { //isPainting = false; } public void OnButtonClick() { SceneManager.LoadScene("Title"); } private void Update() { if (Input.GetKeyDown(KeyCode.U)) { this.Undo(); } if (Input.GetKeyDown(KeyCode.R)) { this.Redo(); } if (!isPainting) { // 線引き中に動作されると困るコードがあればこの辺に記述する return; } if (Input.GetMouseButtonDown(0)) { this.Record(); this.previousPixelPosition = this.GetPixelPosition(); } else if (Input.GetMouseButton(0)) { var currentPixelPosition = this.GetPixelPosition(); this.LineTo(this.previousPixelPosition, currentPixelPosition, this.foregroundColor, this.penRadius); this.previousPixelPosition = currentPixelPosition; } }
Bongo

2020/01/15 17:06

はい、そのコードのように条件分岐させれば問題ないと思いますよ。念のため以前ご提示しましたスクリプトに今回の案を組み込んだものを省略なしで追記しました(文字数制限がきつかったのでコード内のコメントはけっこう削ってしまいました...必要に応じてコメントを加えてみてください)。 なおUndo、Redo、OnButtonClickはpublicなので外部から実行することもでき、それぞれボタンを用意して「On Click ()」にセットしてやればボタンクリックでUndo、Redo、タイトルシーンへの移動ができるかと思います。
29507-01

2020/01/26 01:15 編集

ButtonにPainterのスクリプトをアタッチしてOn Click () にPainter.OnButtonClickをセットしたら線も引かなくなります。...アタッチの仕方が違う?
29507-01

2020/01/26 07:27

this.isPaintingの使い方が違っていました。 有難うございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問