質問概要
unityにはdestroyというgameobjectを消去する関数があります。
しかし、その方法だとgameobject単位でブロックであればブロックが消えると思います。
自分が実現したいことは、ブロック1があったときにブロック2と重なった部分を
(ブロック1∩ブロック2)に当たる部分のブロック1の範囲を消去したいです。
追記
【Unityシェーダ入門】オブジェクトが重なった部分をくり抜く
のところにあったものだと不自然なくり抜かれ方でイメージと少し違いました。
イメージとしてはメッシュを組みなおして重なっていない部分のみのオブジェクトを生成すればいいのかなと思うのですが、
自分の知識とプログラミングの力では
メッシュの組み方も難しいと感じているのでご教授お願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答1件
0
見た目がくり抜かれた形になればいいのでしたら、以前C# - オブジェクトが重なった部分のみを切り取る。|teratailでも挙げさせていただいたUnity でスクリーンスペースのブーリアン演算をやってみた - 凹みTipsの手法が使えそうです。レンダリングモードをDeferredにしないといけないという制限はありますが、いい感じにくり抜けるんじゃないかと思います。
ご質問者さんのおっしゃる「メッシュを組みなおして重なっていない部分のみのオブジェクトを生成すれば」というアプローチもおそらく可能でしょう(私は実装した経験はないですが...)。
たとえばちょっと検索してみたところSimple and Robust Boolean Operations for Triangulated Surfaces(PDF)なんて論文が出てきました。こういった方法ならメッシュが本当にくり抜かれた形になるので、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総合スコア10809
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。