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

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

詳細はこちら
Unity

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

Q&A

1回答

14426閲覧

unityでgameobjectを部分的に消去したい

RyosukeSHIBATA

総合スコア11

Unity

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

0グッド

1クリップ

投稿2019/09/09 11:24

質問概要

unityにはdestroyというgameobjectを消去する関数があります。
しかし、その方法だとgameobject単位でブロックであればブロックが消えると思います。
自分が実現したいことは、ブロック1があったときにブロック2と重なった部分を
(ブロック1∩ブロック2)に当たる部分のブロック1の範囲を消去したいです。

追記

【Unityシェーダ入門】オブジェクトが重なった部分をくり抜く
のところにあったものだと不自然なくり抜かれ方でイメージと少し違いました。

イメージとしてはメッシュを組みなおして重なっていない部分のみのオブジェクトを生成すればいいのかなと思うのですが、
自分の知識とプログラミングの力では
メッシュの組み方も難しいと感じているのでご教授お願いします。

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

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

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

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

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

guest

回答1

0

見た目がくり抜かれた形になればいいのでしたら、以前C# - オブジェクトが重なった部分のみを切り取る。|teratailでも挙げさせていただいたUnity でスクリーンスペースのブーリアン演算をやってみた - 凹みTipsの手法が使えそうです。レンダリングモードをDeferredにしないといけないという制限はありますが、いい感じにくり抜けるんじゃないかと思います。

ご質問者さんのおっしゃる「メッシュを組みなおして重なっていない部分のみのオブジェクトを生成すれば」というアプローチもおそらく可能でしょう(私は実装した経験はないですが...)。
たとえばちょっと検索してみたところSimple and Robust Boolean Operations for Triangulated SurfacesPDF)なんて論文が出てきました。こういった方法ならメッシュが本当にくり抜かれた形になるので、MeshColliderを使えばくり抜かれた形通りに衝突判定を行うなんてことも可能かもしれません。

追記
unity上で三次元座標の二点を選択して、直方体のブロックを生成したい」を拝見しますに、くり抜く側もくり抜かれる側も軸平行な直方体だという制限を設けてもいいのでしょうかね?
でしたら問題がだいぶ簡略化でき、最初の回答で例示しました論文のような複雑なことをしなくても済むかもしれません。

今さらながらその条件で実験してみました。軸平行にくり抜いていくなら、くり抜かれた結果は直方体の集まりとして表現できるはずだと思い、くり抜く側の上・下・左・右・前・後の6面でくり抜かれる側を切断して小さな直方体に分割するという方針で行きました。

直方体を最大隅・最小隅の2点で表現する構造体を作り...

C#

1using System; 2using System.Collections.Generic; 3using UnityEngine; 4 5public struct Box 6{ 7 public readonly Vector3 Max; 8 public readonly Vector3 Min; 9 public readonly Vector3 Center; 10 public readonly Vector3 Size; 11 public readonly bool HasVolume; 12 13 // 対角隅2点を指定してBoxを作る 14 public Box(Vector3 cornerA, Vector3 cornerB) 15 { 16 this.Max = Vector3.Max(cornerA, cornerB); 17 this.Min = Vector3.Min(cornerA, cornerB); 18 this.Center = (this.Max + this.Min) * 0.5f; 19 this.Size = this.Max - this.Min; 20 this.HasVolume = (this.Size.x * this.Size.y * this.Size.z) > 0.0f; 21 } 22 23 // Cubeを伸縮させたオブジェクトをもとにBoxを作る 24 public Box(Transform cube) 25 { 26 if (cube == null) 27 { 28 this = new Box(); 29 return; 30 } 31 32 var position = cube.position; 33 var halfExtents = cube.localScale * 0.5f; 34 this = new Box(position + halfExtents, position - halfExtents); 35 } 36 37 // BoxをもとにCubeを伸縮させたオブジェクトを作る 38 public Transform ToCube(Transform parent = null, Material sharedMaterial = null) 39 { 40 if (!this.HasVolume) 41 { 42 return null; 43 } 44 45 var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); 46 var cubeTransform = cube.transform; 47 cubeTransform.position = this.Center; 48 cubeTransform.localScale = this.Size; 49 cubeTransform.SetParent(parent); 50 if (sharedMaterial != null) 51 { 52 cubeTransform.GetComponent<Renderer>().sharedMaterial = sharedMaterial; 53 } 54 55 return cubeTransform; 56 } 57 58 // Boxを特定の軸で切断し2つのBoxに分ける 59 // 軸番号はXが0、Yが1、Zが2 60 public (Box upper, Box lower) Cut(float at, int axis) 61 { 62 if (!this.HasVolume) 63 { 64 return (new Box(), new Box()); 65 } 66 67 var sourceMax = this.Max[axis]; 68 var sourceMin = this.Min[axis]; 69 if (at <= sourceMin) 70 { 71 return (this, new Box()); 72 } 73 74 if (at >= sourceMax) 75 { 76 return (new Box(), this); 77 } 78 79 var lowerMin = this.Min; 80 var lowerMax = this.Max; 81 lowerMax[axis] = at; 82 var upperMin = this.Min; 83 var upperMax = this.Max; 84 upperMin[axis] = at; 85 return (new Box(upperMin, upperMax), new Box(lowerMin, lowerMax)); 86 } 87 88 // Boxを別のBoxでくり抜き、できあがった断片をnewBoxesに詰める 89 public void Carve(Box carver, List<Box> newBoxes) 90 { 91 if (newBoxes == null) 92 { 93 throw new InvalidOperationException($"{nameof(newBoxes)} must not be null."); 94 } 95 96 if (!this.HasVolume) 97 { 98 throw new InvalidOperationException($"carvee must have volume."); 99 } 100 101 newBoxes.Clear(); 102 var residue = this; 103 104 // X、Y、Z軸についてそれぞれ... 105 for (var i = 0; i < 3; i++) 106 { 107 // 最小側の面で切断 108 var (upper, lower) = residue.Cut(carver.Min[i], i); 109 if (lower.HasVolume) 110 { 111 newBoxes.Add(lower); 112 } 113 114 if (!upper.HasVolume) 115 { 116 break; 117 } 118 119 residue = upper; 120 121 // 最大側の面で切断 122 (upper, lower) = residue.Cut(carver.Max[i], i); 123 if (upper.HasVolume) 124 { 125 newBoxes.Add(upper); 126 } 127 128 if (!lower.HasVolume) 129 { 130 break; 131 } 132 133 residue = lower; 134 } 135 } 136}

くり抜かれる側には下記のようなスクリプトをアタッチして...

C#

1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4 5public class Carvee : MonoBehaviour 6{ 7 public void Carve(Transform carverTransform) 8 { 9 if (carverTransform == null) 10 { 11 return; 12 } 13 14 var carver = new Box(carverTransform); 15 if (!carver.HasVolume) 16 { 17 return; 18 } 19 20 // くり抜かれる側自体は空オブジェクトになっており、子オブジェクトとしてCubeオブジェクトを持っている 21 // Carveを実行すると子オブジェクトをそれぞれくり抜いて、断片を再び自身の子とする 22 var newBoxes = new List<Box>(); 23 var carveeTransforms = this.transform.Cast<Transform>().ToArray(); 24 foreach (var carveeTransform in carveeTransforms) 25 { 26 var carvee = new Box(carveeTransform); 27 if (!carvee.HasVolume) 28 { 29 continue; 30 } 31 32 carvee.Carve(carver, newBoxes); 33 var carveeMaterial = carveeTransform.GetComponent<Renderer>().sharedMaterial; 34 foreach (var newBox in newBoxes) 35 { 36 newBox.ToCube(this.transform, carveeMaterial); 37 } 38 39 Destroy(carveeTransform.gameObject); 40 } 41 } 42}

その他オブジェクト操作用のスクリプト(コードは省略)を付けて実行したところ、下図のような感じになりました。

図

投稿2019/09/09 13:14

編集2019/09/19 20:32
Bongo

総合スコア10811

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問