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

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

詳細はこちら
Unity

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

Q&A

解決済

1回答

2141閲覧

unity imageに線を描く

tamachan2020

総合スコア31

Unity

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

0グッド

1クリップ

投稿2019/12/20 22:02

編集2019/12/21 23:43

前提・実現したいこと

imageに線を描きたいのですが,ネットを参考にしてチャレンジしているのですが
線が途切れて表示されます。
線が繋がるようにする方法を見つけたいのですが参考になるnet情報があれば
教えていただけませんか。
URL
イメージ説明

canvas
image

canvasをconstant pixelサイズに変更

イメージ説明

canvas,imageを以前と同じscale with screen sizeのままですが

サイズをどちらも(canvas,image) 200に合わせる
イメージ説明

該当のソースコード

Unity

1sing UnityEngine; 2using System; 3using System.Linq; 4using UnityEngine.UI; 5using UnityEngine.EventSystems; 6 7/// <summary> 8/// お絵描き 9/// </summary> 10public class Painter : MonoBehaviour 11{ 12 Texture2D texture; 13 Vector3 beforeMousePos; 14 Vector2 localCursor; 15 16 Color bgColor = Color.white; 17 Color lineColor = Color.red; 18 //Color lineColor = Color.black; 19 20 void Start() 21 { 22 var img = GetComponent<Image>(); 23 var rt = GetComponent<RectTransform>(); 24 var width = (int)rt.rect.width; 25 var height = (int)rt.rect.height; 26 texture = new Texture2D(width, height, TextureFormat.ARGB32, false); 27 img.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); 28 29 //背景が透明なTexture2Dを作る 30 //http://d.hatena.ne.jp/shinriyo/20140520/p2 31 Color32[] texColors = Enumerable.Repeat<Color32>(bgColor, width * height).ToArray(); 32 texture.SetPixels32(texColors); 33 texture.Apply(); 34 } 35 36 void Update() 37 { 38 if (Input.GetMouseButtonDown(0)) 39 { 40 beforeMousePos = GetPosition(); 41 } 42 else if (Input.GetMouseButton(0)) 43 { 44 Vector3 v = GetPosition(); 45 LineTo(beforeMousePos, v, lineColor); 46 beforeMousePos = v; 47 texture.Apply(); 48 } 49 } 50 51 /// <summary> 52 /// UIのクリック座標のx、y座標を求める - 新しいguiシステムの画像 - Unity Answers 53 /// https://answers.unity.com/questions/892333/find-xy-cordinates-of-click-on-uiimage-new-gui-sys.html 54 /// </summary> 55 public Vector3 GetPosition() 56 { 57 var dat = new PointerEventData(EventSystem.current); 58 dat.position = Input.mousePosition; 59 60 var rect1 = GetComponent<RectTransform>(); 61 var pos1 = dat.position; 62 if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rect1, pos1, 63 null, out localCursor)) 64 return localCursor; 65 66 int xpos = (int)(localCursor.x); 67 int ypos = (int)(localCursor.y); 68 69 if (xpos < 0) xpos = xpos + (int)rect1.rect.width / 2; 70 else xpos += (int)rect1.rect.width / 2; 71 72 if (ypos > 0) ypos = ypos + (int)rect1.rect.height / 2; 73 else ypos += (int)rect1.rect.height / 2; 74 75 Debug.Log("Correct Cursor Pos: " + xpos + " " + ypos); 76 return new Vector3(xpos, ypos, 0); 77 } 78 79 /// <summary> 80 /// Unityでお絵描きしてみる 81 /// http://tech.gmo-media.jp/post/56101930112/draw-a-picture-with-unity 82 /// </summary> 83 public void LineTo(Vector3 start, Vector3 end, Color color) 84 { 85 float x = start.x, y = start.y; 86 // color of pixels 87 Color[] wcolor = { color }; 88 89 if (Mathf.Abs(start.x - end.x) > Mathf.Abs(start.y - end.y)) 90 { 91 float dy = Math.Abs(end.x - start.x) < float.Epsilon ? 0 : (end.y - start.y) / (end.x - start.x); 92 float dx = start.x < end.x ? 1 : -1; 93 //draw line loop 94 while (x > 0 && x < texture.width && y > 0 && y < texture.height) 95 { 96 try 97 { 98 texture.SetPixels((int)x, (int)y, 1, 1, wcolor); 99 x += dx; 100 y += dx * dy; 101 if (start.x < end.x && x > end.x || 102 start.x > end.x && x < end.x) 103 { 104 break; 105 } 106 } 107 catch (Exception e) 108 { 109 Debug.LogException(e); 110 break; 111 } 112 } 113 } 114 else if (Mathf.Abs(start.x - end.x) < Mathf.Abs(start.y - end.y)) 115 { 116 float dx = Math.Abs(start.y - end.y) < float.Epsilon ? 0 : (end.x - start.x) / (end.y - start.y); 117 float dy = start.y < end.y ? 1 : -1; 118 while (x > 0 && x < texture.width && y > 0 && y < texture.height) 119 { 120 try 121 { 122 texture.SetPixels((int)x, (int)y, 1, 1, wcolor); 123 x += dx * dy; 124 y += dy; 125 if (start.y < end.y && y > end.y || 126 start.y > end.y && y < end.y) 127 { 128 break; 129 } 130 } 131 catch (Exception e) 132 { 133 Debug.LogException(e); 134 break; 135 } 136 } 137 } 138 } 139}

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

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

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

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

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

guest

回答1

0

ベストアンサー

おそらく斜め45°または-45°の線を描こうとすると頻繁に途切れが発生するかと思います。
LineToメソッドの構造は...

C#

1public void LineTo(Vector3 start, Vector3 end, Color color) 2{ 3 // 省略 4 5 if (Mathf.Abs(start.x - end.x) > Mathf.Abs(start.y - end.y)) 6 { 7 // 省略 8 } 9 else if (Mathf.Abs(start.x - end.x) < Mathf.Abs(start.y - end.y)) 10 { 11 // 省略 12 } 13}

という風に描こうとしている線が水平と垂直のどちらに近いかで場合分けしておりますが、条件をよく見ていただきますと、ぴったり45°または-45°の時にはどちらの場合にも当てはまらないことが見てとれるかと思います。

一方コメントにある参考元のサイト(Unityでお絵描きしてみる | GMOメディア エンジニアブログ)をご覧いただきますと...

C#

1public void lineTo(Vector3 start, Vector3 end, Color color) { 2 // 省略 3 4 if (Mathf.Abs(start.x-end.x) >= Mathf.Abs(start.y-end.y)) { 5 // 省略 6 } else if (Mathf.Abs(start.x-end.x) < Mathf.Abs(start.y-end.y)) { 7 // 省略 8 } 9}

という風に、ぴったり45°または-45°の時には「水平に近い」と判定されるようになっていて、場合漏れがないようにしているようです(というよりもelse節に条件はいらないんじゃないですかね?)。

何カ所か条件を変更して、下記のようにしてみるとどうでしょうか。

C#

1public void LineTo(Vector3 start, Vector3 end, Color color) 2{ 3 float x = start.x, y = start.y; 4 // color of pixels 5 Color[] wcolor = { color }; 6 7 if (Mathf.Abs(start.x - end.x) >= Mathf.Abs(start.y - end.y)) // 条件を変更 8 { 9 float dy = Math.Abs(end.x - start.x) < float.Epsilon ? 0 : (end.y - start.y) / (end.x - start.x); 10 float dx = start.x < end.x ? 1 : -1; 11 //draw line loop 12 while (x >= 0 && x < this.texture.width && y >= 0 && y < this.texture.height) // 条件を変更 13 { 14 try 15 { 16 this.texture.SetPixels((int)x, (int)y, 1, 1, wcolor); 17 x += dx; 18 y += dx * dy; 19 if (start.x <= end.x && x >= end.x || 20 start.x >= end.x && x <= end.x) // 条件を変更 21 { 22 break; 23 } 24 } 25 catch (Exception e) 26 { 27 Debug.LogException(e); 28 break; 29 } 30 } 31 } 32 else // 条件を削除 33 { 34 float dx = Math.Abs(start.y - end.y) < float.Epsilon ? 0 : (end.x - start.x) / (end.y - start.y); 35 float dy = start.y < end.y ? 1 : -1; 36 while (x >= 0 && x < this.texture.width && y >= 0 && y < this.texture.height) // 条件を変更 37 { 38 try 39 { 40 this.texture.SetPixels((int)x, (int)y, 1, 1, wcolor); 41 x += dx * dy; 42 y += dy; 43 if (start.y <= end.y && y >= end.y || 44 start.y >= end.y && y <= end.y) // 条件を変更 45 { 46 break; 47 } 48 } 49 catch (Exception e) 50 { 51 Debug.LogException(e); 52 break; 53 } 54 } 55 } 56}

投稿2019/12/21 01:05

Bongo

総合スコア10811

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

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

tamachan2020

2019/12/21 03:53

LineToの個所を入れ替えたのですが,縦,横関係なく線が途切れるのです。
Bongo

2019/12/21 08:33 編集

すみません、まだその現象を再現させられず、原因を突き止められていない状況です。たしかにご参考にされた「UI.Image にペイントする。線を手書きする。」のスクリーンショットのような斜め線の途切れとは違う途切れ方をしているようですね... なるべく条件を同じにしたいので念のためおうかがいしますが、CanvasやImageのインスペクター上の設定は初期状態のままいじっていないでしょうか(線を描いた映像がなんだか縮小された時のようにかすれて見えるのが気になるのです...Canvas Scalerあたりを操作したでしょうか?もし変更したのであれば、CanvasとImageのインスペクターのスクリーンショットもご提示いただけるとヒントになるかもしれません)。その他、プロジェクトを新規作成したときの設定からいじった部分が思い当たればお知らせいただけるでしょうか。
tamachan2020

2019/12/21 10:18

Canvasは、 Screen Space – Overlay Imageのピボットは、(0.5、0.5) ImageのGameObjectにアタッチする の設定だけで他には何もしていません。
Bongo

2019/12/21 22:52

情報提供ありがとうございます。おかげで私の環境でも同様の途切れを発生させることができました。どうやらCanvas Scalerの影響みたいですね。 Canvas ScalerのUI Scale ModeがScale With Screen Sizeになっており、Reference Resolutionは800×600になっています。この状態ですとCanvas上の座標系は画面サイズ800×600を想定して固定された状態となり、最後に最終的な表示先にフィットするようにCanvas全体が拡縮されるかと思います。 この条件で、まずCanvas上に配置されたImageのサイズは300×300に設定されておりますので、Painterスクリプトが生成するお絵かき用テクスチャのサイズも300ピクセル×300ピクセルになるはずです。そして次にお絵かき後のスクリーンショットを見ますと、Gameビューのサイズはタブの位置から想像しますにだいぶ小さくて800×600には満たないように見えます。ですのでImageは縮小して表示された状態にあり、ご提示のとおりかすれた状態になってしまったのでしょう。 試しにCanvas ScalerのUI Scale ModeをConstant Pixel Sizeに変え、その下に表示されるScale Factorの設定項目を1にしてみるとどのように表示されるでしょうか?この設定ですと表示先によらずスケールが等倍になるはずですので、Gameビュー上のImageの見え方も300ピクセル×300ピクセルになるかと思います。この状態で線を描いてみておかしな途切れが起こらないようならスクリプトの動作に問題はなく、かすれてしまったのは縮小のせいだと言えるんじゃないでしょうか。 画像処理ソフトによるきれいな縮小とは違って、このCanvas Scalerによる縮小は3Dグラフィックスシステムの機能を使ったスピード重視の処理になっており、縮小の品質をこれ以上高くしてかすれを解消させることはおそらく困難だろうと思います。 しょうがないものとして妥協していただけるならいいのですが、何とかしたいということでしたら、どのようなお絵かき機能を作りたいか詳しくご説明いただければ(お絵かきテクスチャのサイズは300×300でないと困るとか、テクスチャサイズはどうでもいいので縮小かすれ・拡大ボケのないその時の画面サイズに合わせたドットバイドットの線を描きたいとか...)何かアドバイスできることがあるかもしれません。ですがたぶんやっかいになると思います。線を1ピクセルではなくもっと太くして縮小に耐えられるようにする...とかでしたら変更点は比較的少なく済むかもしれませんが、場合によってはお絵かきの仕組みを現在のやり方とは大きく違う形に作り直す必要があるかもしれません。
tamachan2020

2019/12/21 23:53

指摘頂いた内容と以前のやり方でcanvasのサイズを変えたもの二通りをテストしたら同じ結果ですが線は繋がっているように思います。実機でテストしてみようと思います。サイズにこだわるのはこのサイズでパズルを作りそのパズルに絵を描き保存できたら良いなと思って作っています。 canvasのスケーラーとImageのサイズが関係しているなど今まで少しも思いませんでした。初心者なので当たり前か。 線の幅を変えるとか,色の設定をするにはどこか参考のURLがあれば教えていただけませんか? 質問の丸投げみたいで恐縮です。 でも解らないような気もしますが。
tamachan2020

2019/12/22 00:29

線に丸みをつける方法としておもちゃラボの "Unityでテクスチャにお絵描きしよう" を参考に考えようと思うのですが,Undo,Redoの仕方の考え方を教えて頂けたら幸いです。質問ばかりですみません。
Bongo

2019/12/22 04:11

線を太くするのについてはおっしゃるように他サイトのやり方をうまく折衷してみるのがいいでしょうね。 Undo、Redoについては、さしあたり思いつく手としては線の引き始め(マウスボタンを押し下げたとき)のタイミングでテクスチャを複製してリストか何かに入れておく...とかでしょうかね。 線を引くたびに引く直前の状態をどんどん覚えていって、Undo時にはリストから過去のテクスチャを取り出していき、取り出したテクスチャはRedoに備えて別のリストに入れておき、線を新しく引いたらRedoリストを空にしてやるというのはどうでしょう。 線を引くのをD、UndoをU、RedoをRとすると... D 現在のテクスチャ:(T1) Undoリスト:[T1] Redoリスト:[] D 現在のテクスチャ:(T2) Undoリスト:[T1, T2] Redoリスト:[] D 現在のテクスチャ:(T3) Undoリスト:[T1, T2, T3] Redoリスト:[] D 現在のテクスチャ:(T4) Undoリスト:[T1, T2, T3, T4] Redoリスト:[] U 現在のテクスチャ:(T3) Undoリスト:[T1, T2, T3] Redoリスト:[T4] U 現在のテクスチャ:(T2) Undoリスト:[T1, T2] Redoリスト:[T4, T3] R 現在のテクスチャ:(T3) Undoリスト:[T1, T2, T3] Redoリスト:[T4] U 現在のテクスチャ:(T2) Undoリスト:[T1, T2] Redoリスト:[T4, T3] D 現在のテクスチャ:(T5) Undoリスト:[T1, T2, T5] Redoリスト:[] といったイメージで実現できないでしょうかね? ゲーム上でのUndo、Redoについては、Unityに限って検索しようとしてもエディタ用スクリプトの話ばかりが引っかかっていまいちかもしれません。Unityをキーワードに含めずに検索してみて、それらを参考にUnityのやり方に沿って取り入れていくのがいいんじゃないでしょうか。 ちょっと検索してみたところ、「Undoの実装の考え方」(https://qiita.com/Azleep/items/861ee64867d7b016a966 )なんて記事がありました。今回のようなお絵かきでは「「変化」に着目するアプローチ」だと変化が複雑で相性が悪いかもしれません。テクスチャを何枚も保存しておかないといけないという弱点はありますが、上で申し上げたような「「状態」に着目するアプローチ」の方が適しているような気がします。
tamachan2020

2019/12/22 06:20

texture.SetPixels((int)x, (int)y, 1, 1, wcolor); この所のteztureを保存する事だと思うのですが,textureは何が保存されているのですか?画面全体? 今回描いた線になるのですか? 教えて頂けますか。
tamachan2020

2019/12/22 07:05

解らないことが多いですが自分で考えてみます。 有難うございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問