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

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

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

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

1回答

4950閲覧

【Unity】テクスチャをオブジェクトがなぞった部分だけ消去する方法。

amisia

総合スコア12

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

0クリップ

投稿2021/09/12 18:46

前提・実現したいこと

以下の画像のようにテクスチャをオブジェクトが当たったところだけ消しゴムで消したように削除したい。
(言葉での説明が少し難しいため、理想としている処理を実現している他ゲームの画像をお借りしました。画像の状況は掃除機でホコリを吸い取ると溜まっているホコリが掃除機のノズルの形になぞられて吸い込まれていくという状況です。)
イメージ説明
現在Unity2019を用いて3Dゲームを作成しているのですが、画像の物と全く同じように掃除機で吸った所のホコリが消えてなくなる処理の作成を試みています。色々と試したのですが実現方法が全く見当がつかないため質問いたします。
また以下に示す"試したこと"での手法でも何らかの方法で判定を取れるのであれば、ご指摘していただけると大変ありがたいです。

試したこと

テクスチャに絵を描く時の応用でベースとなるテクスチャに、オブジェクトでなぞった部分に透明色を塗ることで消えているような表現にしようと試みたが、実際にテクスチャが消えている訳ではないため掃除を終えた時の判定方法などが難しく、この手法では実現不可能だと考えている。

補足情報(FW/ツールのバージョンなど)

Unity 使用ver. 2019.3.9f1

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

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

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

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

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

bboydaisuke

2021/09/13 00:25

背景と汚れを別オブジェクトにした方がよいと思います。「テクスチャを書き変える」ということの内容が私とは考えていることが違うかもしれませんが、Unity では実行中にアセットを編集することはできません。質問者のレベルはわかりませんが、そういうものを作る機能は Unity にはないので自分で作り込まなければなりません。なかなか難しいと思います。
amisia

2021/09/13 20:16

こちらの説明不足でした。もともと汚れと背景は別オブジェクトにしており、Planeに貼り付けた汚れのテクスチャをオブジェクトが触れたところだけ透明色で塗ることで汚れを掃除機で吸い取っているように見せる、というような手法を現在は取っています。しかし透明で塗った所とそうでない所の判定を取る方法が分からないというような感じです。
guest

回答1

0

ベストアンサー

こちらのサイトは参考にされたのでしょうか?

Draw関数の中でbuffer.SetValueの第一引数に黒色を指定していますが、これを以下のように指定すればアルファ値のみを変更できるかと思います。

Color color = new Color(1f, 1f, 1f, 0f); buffer.SetValue(color, x + mainTexture.width * y);

そして全て透明に塗ったかを判定したいということであれば、以下のような関数を用意し、呼び出してあげればできるかと思います

void Check() { var pixels = mDrawTexture.GetPixels(0, 0, mDrawTexture.width, mDrawTexture.height); int count = 0; // ピクセルのアルファ値が0(透明)のピクセル数を格納 foreach (var pixel in pixels) { if(pixel.a == 0f) { count++; } } var pixelTexture = pixels.Length / pixels.Length; // テクスチャのサイズを1とする var pixelAlpha = (float)count / (float)pixels.Length; // 透明に塗られたピクセル数を0から1までの範囲で求める // この時点でテクスチャのピクセルに対して透過にした割合を0から1までの範囲で設定できていますが // UIの表示用に値を0から100までの範囲に広げます // // 単純に全部透過したかを確認したい場合は以下のようにすれば判定できます // if(pixelAlpha >= 1) { 全部塗った時の処理 } var magnification = 100; var numPixelTexture = (pixelTexture * magnification); // テクスチャのサイズを100にする var numPixelAlpha = (pixelAlpha * magnification); // 透過率を0から100までの範囲にする if(numPixelAlpha >= 100f) { Debug.Log("掃除完了"); } string text = $"{numPixelAlpha.ToString("N2")} / {numPixelTexture.ToString("N2")}"; mRegionText.SetText(text); // テキストUIに値を設定 }

まとめると以下のようになります

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Write : MonoBehaviour { Texture2D mDrawTexture; Color[] mBuffer; Texture2D mMainTexture; [SerializeField] float mSize = 20f; // ブラシのサイズ [SerializeField] RegionText mRegionText; // テキストUIに付与しているスクリプトを参照 void Start() { mMainTexture = (Texture2D)GetComponent<Renderer>().material.mainTexture; Color[] pixels = mMainTexture.GetPixels(); mBuffer = new Color[pixels.Length]; pixels.CopyTo(mBuffer, 0); mDrawTexture = new Texture2D(mMainTexture.width, mMainTexture.height, TextureFormat.RGBA32, false); mDrawTexture.filterMode = FilterMode.Point; } public void Draw(Vector2 p) { Color color = new Color(1f, 1f, 1f, 0f); for (int x = 0; x < mDrawTexture.width; x++) { for (int y = 0; y < mDrawTexture.height; y++) { if ((p - new Vector2(x, y)).magnitude < mSize) { mBuffer.SetValue(color, x + mMainTexture.width * y); } } } } void Check() { var pixels = mDrawTexture.GetPixels(0, 0, mDrawTexture.width, mDrawTexture.height); int count = 0; // ピクセルのアルファ値が0(透明)のピクセル数を格納 foreach (var pixel in pixels) { if(pixel.a == 0f) { count++; } } var pixelTexture = pixels.Length / pixels.Length; // テクスチャのサイズを1とする var pixelAlpha = (float)count / (float)pixels.Length; // 透明に塗られたピクセル数を0から1までの範囲で求める // この時点でテクスチャのピクセルに対して透過にした割合を0から1までの範囲で設定できていますが // UIの表示用に値を0から100までの範囲に広げます // // 単純に全部透過したかを確認したい場合は以下のようにすれば判定できます // if(pixelAlpha >= 1) { 全部塗った時の処理 } var magnification = 100; // 倍率 var numPixelTexture = (pixelTexture * magnification); // テクスチャのサイズを100にする var numPixelAlpha = (pixelAlpha * magnification); // 透過率を0から100までの範囲にする if(numPixelAlpha >= 100f) { Debug.Log("掃除完了"); } string text = $"{numPixelAlpha.ToString("N2")} / {numPixelTexture.ToString("N2")}"; mRegionText.SetText(text); // テキストUIに値を設定 } void Update() { if (Input.GetMouseButton(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, 100.0f)) { var vec = new Vector2(hit.textureCoord.x * mMainTexture.width, hit.textureCoord.y * mMainTexture.height); Draw(vec); } mDrawTexture.SetPixels(mBuffer); mDrawTexture.Apply(); GetComponent<Renderer>().material.mainTexture = mDrawTexture; Check(); } } }

イメージ説明

マウスではなくオブジェクト(仮に掃除機とする)に沿って透明化したい場合は
レイの第一引数にその掃除機の座標を渡すようにしてあげれば良いかと思います。

投稿2021/10/04 16:09

Kyukooon

総合スコア13

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

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

amisia

2022/02/12 20:46

お礼を申し上げるのが大変遅くなってしまい申し訳ありません。 Kyukooon様のおかげで理想とする処理が実装できました!!本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問