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

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

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

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

Q&A

解決済

2回答

6825閲覧

unity オブジェクト 衝突 凹ませる

911RSR

総合スコア13

Unity3D

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

0グッド

1クリップ

投稿2020/08/27 04:24

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
Unity 3Dでゲームを作っています。簡単に言うと彫刻ゲームのようなものをつくりたいです。
球状のオブジェクトをマウスで移動させ、左ドラッグしている間、元石が削られるようなものです。
まずは当たり判定したオブジェクトのメッシュ変形をさせるようなものを考えているのですが、なかなか参考になるようなページが見つかりません。
(ブーリアン演算ではなかなかゲーム中リアルタイムに行っているものがなく、またステンシルバッファのようなものを利用したとしても、オブジェクトが削られた後、さらに削っていく、といったプロセスが行えず、やはりメッシュをだんだんと変形させていくしかないかと思っています)。
いちばんイメージに近いものではterrainに洞窟を掘るようなものですが、やはりこちらもゲーム中に操作で穴を掘る、というよりも洞窟のあるシーンを作る、というものでゲーム自体が削るものではありません。

抽象的な質問で申し訳ございませんが、なるべく多くのヒントを頂きたいと思っています。
ご参考になるようなC#スクリプト(例えば削る側/削られる側にアタッチするもの)や、参考になるページやアセットなど、幅広くアドバイス頂きたいです。よろしくお願い致します。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

ソースコード

試したこと

ここに問題に対して試したことを記載してください。

ステンシルバッファ

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答2

0

SkinnedMeshRendererで表現できないかと思い、下記のようなスクリプトを試してみました。

C#

1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4#if UNITY_EDITOR 5using UnityEditor; 6#endif 7 8public class ClayCuboid : MonoBehaviour 9{ 10 [SerializeField] private Material material; 11 [SerializeField][Range(1.0f, 32.0f)] private float verticesPerUnitHint = 16.0f; 12 [SerializeField][Range(1.0f, 8.0f)] private float controlPointsPerUnitHint = 4.0f; 13 [SerializeField][Range(0.0f, 256.0f)] private float controlPointDrag = 32.0f; 14 [SerializeField][Range(0.0f, 256.0f)] private float density = 16.0f; 15 [SerializeField] private bool useGravity; 16 17 private void Awake() 18 { 19 // 現在のスケールを粘土ブロックの大きさを示すものとして... 20 var size = this.transform.localScale; 21 22 // X、Y、Z方向の頂点数、コントロールポイント数を決める 23 var vertexCounts = Vector3Int.Max(Vector3Int.CeilToInt(size * this.verticesPerUnitHint), Vector3Int.one); 24 var controlPointCounts = Vector3Int.Max(Vector3Int.CeilToInt(size * this.controlPointsPerUnitHint), Vector3Int.one); 25 var controlPointIntervals = new Vector3( 26 size.x / controlPointCounts.x, 27 size.y / controlPointCounts.y, 28 size.z / controlPointCounts.z); 29 30 // コントロールポイントの半径は、さしあたり互いに重ならない最大量とする 31 var controlPointRadius = Mathf.Min( 32 Mathf.Min(controlPointIntervals.x, controlPointIntervals.y), 33 controlPointIntervals.z) * 0.5f; 34 35 // オブジェクト自体のスケールは(1, 1, 1)に戻し... 36 this.transform.localScale = Vector3.one; 37 38 // 粘土ブロックの表面下にコントロールポイントを並べる 39 var controlPoints = new List<Transform>(); 40 var controlPointOffset = (controlPointIntervals - size) * 0.5f; 41 for (var k = 0; k < controlPointCounts.z; k++) 42 { 43 for (var j = 0; j < controlPointCounts.y; j++) 44 { 45 for (var i = 0; i < controlPointCounts.x; i++) 46 { 47 if ((i != 0) && (i != (controlPointCounts.x - 1)) && 48 (j != 0) && (j != (controlPointCounts.y - 1)) && 49 (k != 0) && (k != (controlPointCounts.z - 1))) 50 { 51 continue; 52 } 53 54 var controlPoint = new GameObject($"({i:D2}, {j:D2}, {k:D2})").transform; 55 controlPoints.Add(controlPoint); 56 controlPoint.SetParent(this.transform, false); 57 controlPoint.localPosition = Vector3.Scale(new Vector3(i, j, k), controlPointIntervals) + controlPointOffset; 58 } 59 } 60 } 61 62 // コントロールポイントにSphereCollider、Rigidbodyをアタッチする 63 var controlPointMass = (this.density * size.x * size.y * size.z) / controlPoints.Count; 64 foreach (var controlPoint in controlPoints) 65 { 66 var controlPointCollider = controlPoint.gameObject.AddComponent<SphereCollider>(); 67 controlPointCollider.radius = controlPointRadius; 68 var controlPointRigidbody = controlPoint.gameObject.AddComponent<Rigidbody>(); 69 controlPointRigidbody.drag = this.controlPointDrag; 70 controlPointRigidbody.angularDrag = this.controlPointDrag; 71 controlPointRigidbody.mass = controlPointMass; 72 controlPointRigidbody.useGravity = this.useGravity; 73 } 74 75 // メッシュを作成する 76 var vertices = new List<Vector3>(); 77 var uvs = new List<Vector2>(); 78 var indices = new List<int>(); 79 var halfExtents = size * 0.5f; 80 CreateFace( 81 vertices, uvs, indices, 82 -halfExtents, 83 Vector3.right, Vector3.up, 84 new Vector2(size.x, size.y), 85 new Vector2Int(vertexCounts.x, vertexCounts.y)); 86 CreateFace( 87 vertices, uvs, indices, 88 -halfExtents, 89 Vector3.up, Vector3.forward, 90 new Vector2(size.y, size.z), 91 new Vector2Int(vertexCounts.y, vertexCounts.z)); 92 CreateFace( 93 vertices, uvs, indices, 94 -halfExtents, 95 Vector3.forward, Vector3.right, 96 new Vector2(size.z, size.x), 97 new Vector2Int(vertexCounts.z, vertexCounts.x)); 98 CreateFace( 99 vertices, uvs, indices, 100 halfExtents, 101 -Vector3.right, -Vector3.up, 102 new Vector2(size.x, size.y), 103 new Vector2Int(vertexCounts.x, vertexCounts.y), 104 true); 105 CreateFace( 106 vertices, uvs, indices, 107 halfExtents, 108 -Vector3.up, -Vector3.forward, 109 new Vector2(size.y, size.z), 110 new Vector2Int(vertexCounts.y, vertexCounts.z), 111 true); 112 CreateFace( 113 vertices, uvs, indices, 114 halfExtents, 115 -Vector3.forward, -Vector3.right, 116 new Vector2(size.z, size.x), 117 new Vector2Int(vertexCounts.z, vertexCounts.x), 118 true); 119 var parentToLocal = Matrix4x4.identity; 120 var weights = new BoneWeight1[4]; 121 var mesh = new Mesh 122 { 123 name = "Clay Cuboid", 124 vertices = vertices.ToArray(), 125 uv = uvs.ToArray(), 126 triangles = indices.ToArray(), 127 bindposes = controlPoints.Select( 128 c => 129 { 130 Debug.Assert(Matrix4x4.Inverse3DAffine(Matrix4x4.TRS(c.localPosition, c.localRotation, c.localScale), ref parentToLocal)); 131 return parentToLocal; 132 }).ToArray(), 133 // 頂点に対して最大4個のコントロールポイントが影響する 134 // 影響率はさしあたりコントロールポイントへの距離の2乗に反比例させた 135 boneWeights = vertices.Select( 136 v => 137 { 138 for (var i = 0; i < weights.Length; i++) 139 { 140 weights[i].boneIndex = 0; 141 weights[i].weight = float.PositiveInfinity; 142 } 143 144 var j = 0; 145 foreach (var sqrDistancesAndIndex in controlPoints.Select((c, i) => ((c.localPosition - v).sqrMagnitude, i)).OrderBy(ci => ci.Item1).Take(weights.Length)) 146 { 147 weights[j].boneIndex = sqrDistancesAndIndex.Item2; 148 weights[j].weight = sqrDistancesAndIndex.Item1; 149 j++; 150 } 151 152 var weightSum = 0.0f; 153 for (var i = 0; i < weights.Length; i++) 154 { 155 weights[i].weight = 1.0f / weights[i].weight; 156 weightSum += weights[i].weight; 157 } 158 159 var boneWeight = new BoneWeight(); 160 if (weightSum > 0.0f) 161 { 162 boneWeight.boneIndex0 = weights[0].boneIndex; 163 boneWeight.boneIndex1 = weights[1].boneIndex; 164 boneWeight.boneIndex2 = weights[2].boneIndex; 165 boneWeight.boneIndex3 = weights[3].boneIndex; 166 boneWeight.weight0 = weights[0].weight / weightSum; 167 boneWeight.weight1 = weights[1].weight / weightSum; 168 boneWeight.weight2 = weights[2].weight / weightSum; 169 boneWeight.weight3 = weights[3].weight / weightSum; 170 } 171 172 return boneWeight; 173 }).ToArray() 174 }; 175 mesh.RecalculateNormals(); 176 mesh.RecalculateTangents(); 177 178 // レンダラーを作成しメッシュをセットする 179 var meshRenderer = this.gameObject.AddComponent<SkinnedMeshRenderer>(); 180 meshRenderer.sharedMesh = mesh; 181 meshRenderer.bones = controlPoints.ToArray(); 182 meshRenderer.sharedMaterial = this.material; 183 } 184 185 private static void CreateFace( 186 List<Vector3> vertices, 187 List<Vector2> uvs, 188 List<int> indices, 189 Vector3 origin, 190 Vector3 u, 191 Vector3 v, 192 Vector2 size, 193 Vector2Int divisions, 194 bool invert = false) 195 { 196 var indexOffset = vertices.Count; 197 vertices.AddRange( 198 Enumerable.Range(0, divisions.y + 1).SelectMany( 199 j => Enumerable.Range(0, divisions.x + 1).Select( 200 i => origin + (u * ((i * size.x) / divisions.x)) + (v * ((j * size.y) / divisions.y))))); 201 uvs.AddRange( 202 Enumerable.Range(0, divisions.y + 1).SelectMany( 203 j => Enumerable.Range(0, divisions.x + 1) 204 .Select(i => new Vector2((float)i / divisions.x, (float)j / divisions.y)))); 205 indices.AddRange( 206 Enumerable.Range(0, divisions.y).SelectMany( 207 j => Enumerable.Range(0, divisions.x) 208 .SelectMany( 209 i => 210 { 211 var i00 = i + (j * (divisions.x + 1)); 212 var i01 = i00 + 1; 213 var i10 = i00 + divisions.x + 1; 214 var i11 = i10 + 1; 215 i00 += indexOffset; 216 i01 += indexOffset; 217 i10 += indexOffset; 218 i11 += indexOffset; 219 return invert 220 ? new[] {i00, i01, i10, i11, i10, i01} 221 : new[] {i00, i10, i01, i11, i01, i10}; 222 }))); 223 } 224 225 #if UNITY_EDITOR 226 private void OnDrawGizmos() 227 { 228 if (EditorApplication.isPlaying) 229 { 230 return; 231 } 232 233 // シーン編集中の段階では、シーンビューに直方体を描画して 234 // オブジェクトの大きさや配置を確認しやすくする 235 Gizmos.color = new Color(0.8f, 0.2f, 0.0f); 236 Gizmos.matrix = this.transform.localToWorldMatrix; 237 Gizmos.DrawCube(Vector3.zero, Vector3.one); 238 } 239 240 [MenuItem("GameObject/3D Object/Clay Cuboid")] 241 public static void Create() 242 { 243 var newObject = new GameObject("Clay Cuboid"); 244 Undo.RegisterCreatedObjectUndo(newObject, "Create Clay Cuboid"); 245 var clayCuboid = Undo.AddComponent<ClayCuboid>(newObject); 246 var dummyObject = GameObject.CreatePrimitive(PrimitiveType.Cube); 247 clayCuboid.material = dummyObject.GetComponent<Renderer>().sharedMaterial; 248 DestroyImmediate(dummyObject); 249 } 250 #endif 251}

メニューからオブジェクトを作成し...

図1

適当に伸縮させて配置すると下図のように見えます。

図2

実行時には、下図のようにブロック表面に沿ってSphereColliderを持ったオブジェクトが配置されます。

図3

SkinnedMeshRendererの皮をかぶせた状態でこれらオブジェクトが物理的相互作用によって動けば、メッシュがひずんで見えるんじゃないかと考えました。

図4

まあそれなりの見た目になったんじゃないかと思いますが、このやり方では上図のように物体を貫通して穴を開けようとしても、トポロジー的に異なる形状にはなれないため表面が引き伸ばされるだけとなってしまいます。
また、粘土細工のように精密な変形をさせるにはもっと大量のコライダーを配置する必要がありそうで、重すぎて実用に耐えないかもしれません。スカルプトモデリングソフトのようなことをさせたいのでしたら、おそらく根本的に方針を変えなければならないような気がします。

この方式だと大ざっぱな変形しか対応困難でしょうが、Unityの物理シミュレーションシステムをそのまま使っているので、シーン上の物体と相互作用させてそれっぽく見せかけるのには向いていると思います。たとえばDragをもっと小さくし、Use Gravityをオンにすると落下するようになり、地面と衝突して潰れてひしゃげます。

図5

今回の例では個々のコントロールポイントが自由に動けますが、たとえば互いにジョイントで連結してある程度の復元力を持たせれば、ところてんの塊のようなプルンプルンした物体を作ることもできそうな気がします。

投稿2020/08/29 07:30

Bongo

総合スコア10811

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

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

911RSR

2020/09/03 03:29

大変気合の入ったご回答、動画までつけて頂き、詳細なスクリプトまで本当にありがとうございます! mesh deformationに近いですね。最終的な力技はこちらい行きつくかもしれません。 もう少し試してみます!ありがとうございます。
guest

0

自己解決

凹みtipsのブーリアン演算を実装し解決しました!ご教示ありがとうございました。

投稿2020/10/14 01:27

911RSR

総合スコア13

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問