はじめに
平面上に線を滑らかに描く方法を調べました。
VR空間でもぬるぬる動いている例を参考にしました(↓すごい)
【参考リンク】:ホワイトボードで線がヌルヌル描ける仕組み
描く実装
"描く部分"のコードは下記リンクからまるまる拝借しました。
【参考リンク】:Unityでテクスチャにお絵描きしよう
デモ
線がかすれてしまって不格好です。
しっかりと線の間を補間してくれました。
コード
下記全文です。
usingUnityEngine;//使い方↓//https://nn-hokuson.hatenablog.com/entry/2016/12/08/200133publicclassSmoothPaint:MonoBehaviour{Texture2DdrawTexture;Color[]buffer;privateVector2_prevPosition;voidStart(){Texture2DmainTexture=(Texture2D)GetComponent<Renderer>().material.mainTexture;Color[]pixels=mainTexture.GetPixels();buffer=newColor[pixels.Length];pixels.CopyTo(buffer,0);drawTexture=newTexture2D(mainTexture.width,mainTexture.height,TextureFormat.RGBA32,false);drawTexture.filterMode=FilterMode.Point;}publicvoidDraw(Vector2p){for(intx=0;x<256;x++){for(inty=0;y<256;y++){if((p-newVector2(x,y)).magnitude<5){buffer.SetValue(Color.black,x+256*y);}}}}voidUpdate(){if(Input.GetMouseButton(0)){//前回値がまだないなら現在の値を前回値として扱うif(_prevPosition==Vector2.zero){_prevPosition=Input.mousePosition;}//線形補間に使う入力の終点座標Vector2endPosition=Input.mousePosition;//1フレームの線の距離floatlineLength=Vector2.Distance(_prevPosition,endPosition);//線の長さに応じて変わる補間値 CeilToIntは小数点以下を切り上げintlerpCountAdjustNum=5;intlerpCount=Mathf.CeilToInt(lineLength/lerpCountAdjustNum);for(inti=1;i<=lerpCount;i++){//Lerpの割合値を "現在の回数/合計回数" で出すfloatlerpWeight=(float)i/lerpCount;//前回の入力座標、現在の入力座標、割合を渡して補間する座標を算出Vector3lerpPosition=Vector2.Lerp(_prevPosition,Input.mousePosition,lerpWeight);Rayray=Camera.main.ScreenPointToRay(lerpPosition);RaycastHithit;if(Physics.Raycast(ray,outhit,100.0f)){Draw(hit.textureCoord*256);}drawTexture.SetPixels(buffer);drawTexture.Apply();GetComponent<Renderer>().material.mainTexture=drawTexture;}//前回の入力座標を記録_prevPosition=Input.mousePosition;}else{//前回の入力座標をリセット_prevPosition=Vector2.zero;}}}ロジックとしては下記です
①前フレームのマウスの入力座標を保持
②次フレームにて①と現在の入力座標の距離を算出
③距離に応じて補間値(補間回数)を算出
④補間値を利用しLerpで補間
線が繋がる
実装していてそこそこ時間を使ってしまったのが、
線が繋がってしまう という現象でした。
下記GIFのように新しく描き始めた箇所と
前回の終了地点が繋がってしまっていました。
これは描き終えた際の値を前回値として保持したまま
新しい入力箇所での補間を行っていることが原因でした。
ですので、下記箇所で入力が無い状態になった際に前回値のリセットを行っています。
さらに、入力値のリセットにより前回値が存在しないフレーム、
すなわち描き始めのフレームにおいては補間を行う必要が無いので
前回の値=現在の値として扱うようにしています。
if(Input.GetMouseButton(0)){//前回値がまだないなら現在の値を前回値として扱うif(_prevPosition==Vector2.zero){_prevPosition=Input.mousePosition;}}else{//前回の入力座標をリセット_prevPosition=Vector2.zero;}これにより描き始めた箇所に線を新しく描画することができました。
おわりに
線の太さ(大きさ)も考慮した補間値を算出すれば
もっと最適な補間ができるみたいです。
そのうち書けたら書きます。


