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

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

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

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

Unity

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

Blender

Blenderとは、オープンソースの3DCGソフトウェアです。フリーでありながら、3Dモデル作成、レンダリング、アニメーション、コンポジットなどのハイエンドに匹敵する高い機能を持ち、さらにゲームエンジンも搭載しています。

Q&A

解決済

1回答

3535閲覧

【Unity】オブジェクトを折り紙のように折りたい

退会済みユーザー

退会済みユーザー

総合スコア0

Unity3D

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

Unity

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

Blender

Blenderとは、オープンソースの3DCGソフトウェアです。フリーでありながら、3Dモデル作成、レンダリング、アニメーション、コンポジットなどのハイエンドに匹敵する高い機能を持ち、さらにゲームエンジンも搭載しています。

0グッド

3クリップ

投稿2018/10/12 04:01

Unity 折り紙 等で調べましたが良いのが見つかりませんでした。

紙みたいなオブジェクトに文字を書き込み、
それを四つ折りにしたいと思っています。

四つ折にした紙オブジェクトは、ゴミ箱に投げます。
四つ折りにしたあとは「投げる」もしくは、「もう一度開く」が出来るようにしたいです。

検討している方法

1.UnityでPlaneを作成しアニメーションで四つ折りにし、最後の状態を維持する
四つ折りにする方法がよく分かりません。検索が足りないのかもしれません。
アニメーションで最後の状態を維持するのが良いかと思ったのですが
アニメーション2つを同時には出来ないと見た覚えがあるので
本当にそれで良いのか疑問です。
ただ、20分近く探しても良いのは見つかりませんでした。

2.Blenderで四つ折りの部分までアニメーションを作成する
Blenderでポリゴンを折り紙のように折りたたむ
http://bluebirdofoz.hatenablog.com/entry/2018/07/03/084835
上記が参考になりそうだと思いました。

分からないこと

 アニメーションで「遷移したまま動かない」というのはできるのでしょうか。
ドアで例えるなら、開いたら、開きっぱなしという形に。
あれば参考リンクなど教えていただけますでしょうか。

よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

面白いテーマですね。
アニメーションをループさせない件については、アニメーションクリップの「Loop Time」をオフにするのでどうでしょうか。

Loop Time

さらに、その折り畳むアニメーションステートから次のステート(広げるアニメーション、あるいは投げるアニメーションでしょうか)へ伸びる遷移矢印を選択し、「Has Exit Time」をオフにすれば、「Conditions」に指定した条件が満たされるまで折り畳むステートにとどまるかと思います。
詳しくはアニメーション遷移 - Unity マニュアルをご参照いただくのがよさそうです。

四つ折りに関しては、下図のような構造のメッシュにボーンを仕込み、4つのエリアの回転をコントロールできるようにしてはいかがでしょう。

メッシュ構造

頂点数は「田」の字のような9頂点ではなく、折り目部分に幅を持たせた16頂点としてみました。
折る際に頂点位置が回転すると併せて法線も回転するため、折り目部分が1頂点しかないと折り曲げたときの法線は2つの面の向きの中間になってしまい、面に平らな陰影を付けることが困難になると予想されます。
折り目部分が2頂点になっていればそれぞれの頂点が個別に法線を持つことができるので、陰影の不具合を防ぐことができるでしょう。
また、折り目のエッジがシャープになりすぎるのを避けるため、折り目の2頂点をぴったり同じ位置に重ね合わせることはせず、若干幅を持たせることにしました。

「広げた状態→二つ折り」のZ軸周り回転をboneZ0boneZ1が担当し、「二つ折り→四つ折り」のX軸周り回転をboneXが担当する作りになっています。boneZ0を回す際にはboneZ1を同じだけ逆回転し、黄色の面が水色の面と一緒に回ってしまうのを防いでいます。

Blenderには明るくないため、Unity上でメッシュを作ることにしましたが、Blenderで作っても構わないかと思います。
空のゲームオブジェクトに下記スクリプトをアタッチすることにより、メッシュ生成・ボーン階層構成を行うことにしました。

C#

1using System.Collections.Generic; 2using UnityEngine; 3 4[ExecuteInEditMode] 5public class Origami : MonoBehaviour 6{ 7 private const float FoldHalfWidth = 0.05f; 8 private static readonly int[] BackIndices = {0, 1, 4, 5, 4, 1}; 9 private static readonly int[] FrontIndices = {4, 1, 0, 1, 4, 5}; 10 [Range(0.0f, 1.0f)] public float Folding = 0.0f; 11 [Range(0.0f, 1.0f)] public float Completeness = 0.98f; 12 [Range(0.0f, 0.5f)] public float Overwrap = 0.1f; 13 private bool inited; 14 private Transform boneZ0; 15 private Transform boneX; 16 private Transform boneZ1; 17 18 private void Init() 19 { 20 if (this.inited) 21 { 22 return; 23 } 24 this.inited = true; 25 var renderer = this.GetComponent<SkinnedMeshRenderer>(); 26 if (renderer != null) 27 { 28 return; 29 } 30 renderer = this.gameObject.AddComponent<SkinnedMeshRenderer>(); 31 var mesh = new Mesh(); 32 33 // 頂点を作成 34 var vertices = new Vector3[32]; 35 var normals = new Vector3[32]; 36 var tangents = new Vector4[32]; 37 var uv = new Vector2[32]; 38 var boneWeights = new BoneWeight[32]; 39 var colors = new Color[32]; 40 // 前面の頂点 41 var count = 0; 42 for (var z0 = -1; z0 < 1; z0++) 43 { 44 for (var z1 = 0; z1 < 2; z1++) 45 { 46 var sz = z1 - z0 - 1; 47 for (var x0 = -1; x0 < 1; x0++) 48 { 49 var boneIndex = (2 * (z0 + 1)) + (((((-2 * z0) - 1) * ((2 * x0) + 1)) + 1) / 2); 50 for (var x1 = 0; x1 < 2; x1++) 51 { 52 var sx = x1 - x0 - 1; 53 var x = (x0 + x1) - (FoldHalfWidth * sx); 54 var z = (z0 + z1) - (FoldHalfWidth * sz); 55 vertices[count] = new Vector3(x, 0, z); 56 normals[count] = Vector3.up; 57 tangents[count] = new Vector4(1, 0, 0, 1); 58 uv[count] = 0.5f * new Vector2(x + 1, z + 1); 59 boneWeights[count].boneIndex0 = boneIndex; 60 boneWeights[count].weight0 = 1; 61 colors[count] = Color.clear; 62 count++; 63 } 64 } 65 } 66 } 67 // 背面の頂点 68 for (var i = 0; i < count; i++) 69 { 70 var j = i + count; 71 vertices[j] = vertices[i]; 72 normals[j] = Vector3.down; 73 tangents[j] = new Vector4(-1, 0, 0, 1); 74 uv[j] = new Vector2(1.0f - uv[i].x, uv[i].y); 75 boneWeights[j] = boneWeights[i]; 76 colors[j] = Color.white; 77 } 78 79 // 面を作成 80 var triangles = new List<int>(); 81 for (var i = 0; i < 3; i++) 82 { 83 var i4 = i * 4; 84 for (var j = 0; j < 3; j++) 85 { 86 // 前面 87 var o = i4 + j; 88 for (var k = 0; k < 6; k++) 89 { 90 triangles.Add(FrontIndices[k] + o); 91 } 92 // 背面 93 o += 16; 94 for (var k = 0; k < 6; k++) 95 { 96 triangles.Add(BackIndices[k] + o); 97 } 98 } 99 } 100 101 // ボーンを作成 102 var bones = new Transform[4]; 103 var bindposes = new Matrix4x4[4]; 104 for (var i = 0; i < 4; i++) 105 { 106 var bone = new GameObject("Bone" + i).transform; 107 bone.SetParent(i > 0 ? bones[i - 1] : this.transform, false); 108 bones[i] = bone; 109 } 110 bones[3].localPosition = Vector3.forward; 111 for (var i = 0; i < 4; i++) 112 { 113 bindposes[i] = bones[i].worldToLocalMatrix * bones[0].localToWorldMatrix; 114 } 115 116 // メッシュに作成したデータを適用 117 mesh.vertices = vertices; 118 mesh.normals = normals; 119 mesh.tangents = tangents; 120 mesh.uv = uv; 121 mesh.boneWeights = boneWeights; 122 mesh.triangles = triangles.ToArray(); 123 mesh.bindposes = bindposes; 124 mesh.colors = colors; 125 mesh.RecalculateBounds(); 126 127 // レンダラーにメッシュをセット 128 var shader = Shader.Find("Custom/Origami"); 129 if (shader == null) 130 { 131 var quad = GameObject.CreatePrimitive(PrimitiveType.Quad); 132 renderer.sharedMaterial = quad.GetComponent<MeshRenderer>().sharedMaterial; 133 DestroyImmediate(quad); 134 } 135 else 136 { 137 renderer.sharedMaterial = new Material(shader); 138 } 139 renderer.rootBone = bones[0]; 140 renderer.bones = bones; 141 renderer.sharedMesh = mesh; 142 } 143 144 private void OnEnable() 145 { 146 this.Init(); 147 } 148 149 private void Start() 150 { 151 var path = "Bone0/Bone1"; 152 this.boneZ0 = transform.Find(path); 153 path += "/Bone2"; 154 this.boneX = transform.Find(path); 155 path += "/Bone3"; 156 this.boneZ1 = transform.Find(path); 157 } 158 159 private void Update() 160 { 161 // Folding ...四つ折りの進行度。0で広げた状態、1で四つ折り状態。 162 // Completeness...どれだけしっかり折るか。1で完全に折り畳まれますが、ポリゴンが重なって 163 // Zファイティング現象が起こるので、1より少しだけ小さくするといいでしょう。 164 // Overwrap ...「広げた状態→二つ折り」と「二つ折り→四つ折り」の動きの重なり。 165 // 0.5にすると二つの動きが同時に起こりますが、大きなひずみが生じて 166 // ポリゴンが引きのばされてしまいます。 167 var foldingZ = Mathf.SmoothStep(0.0f, 1.0f, Mathf.InverseLerp(0.0f, 0.5f + this.Overwrap, this.Folding)); 168 var foldingX = Mathf.SmoothStep(0.0f, 1.0f, Mathf.InverseLerp(0.5f - this.Overwrap, 1.0f, this.Folding)); 169 var angleZ = foldingZ * this.Completeness * 180.0f; 170 var angleX = foldingX * this.Completeness * 180.0f; 171 this.boneZ0.localEulerAngles = new Vector3(0.0f, 0.0f, angleZ); 172 this.boneZ1.localEulerAngles = new Vector3(0.0f, 0.0f, -angleZ); 173 this.boneX.localEulerAngles = new Vector3(angleX, 0.0f, 0.0f); 174 } 175}

また、マテリアルをどうするかについても検討する必要があるかと思います。表面と裏面に異なるテクスチャを使いたいところですが、今回はメッシュを表用と裏用に二重化し、それぞれを片面レンダリングすることにしました。面は一重にして両面レンダリングする手もあるかと思いますが、影の制御が難しくなりそうでしたので見送りました。
描画中の面が表なのか裏なのかを区別するため、表と裏の頂点に異なる頂点カラーを仕込んでおき、シェーダー内ではそれを参照して表・裏テクスチャを選択することにしました。

ShaderLab

1Shader "Custom/Origami" { 2 Properties { 3 _MainTex ("Front (RGB)", 2D) = "white" {} 4 _BackTex ("Back (RGB)", 2D) = "white" {} 5 _Glossiness ("Smoothness", Range(0,1)) = 0.5 6 _Metallic ("Metallic", Range(0,1)) = 0.0 7 } 8 SubShader { 9 Tags { "RenderType"="Opaque" } 10 11 CGPROGRAM 12 #pragma surface surf Standard addshadow fullforwardshadows 13 #pragma target 3.0 14 struct Input { 15 float2 uv_MainTex; 16 float2 uv_BackTex; 17 float4 color : COLOR; 18 }; 19 half _Glossiness; 20 half _Metallic; 21 sampler2D _MainTex; 22 sampler2D _BackTex; 23 void surf(Input IN, inout SurfaceOutputStandard o) { 24 fixed3 frontColor = tex2D(_MainTex, IN.uv_MainTex).rgb; 25 fixed3 backColor = tex2D(_BackTex, IN.uv_BackTex).rgb; 26 o.Albedo = lerp(frontColor, backColor, IN.color.r); 27 o.Metallic = _Metallic; 28 o.Smoothness = _Glossiness; 29 } 30 ENDCG 31 } 32 FallBack Off 33}

後はアニメーションクリップでOrigamiコンポーネントのFoldingをコントロールすればアニメーションを付けることができますし、コライダーとRigidbodyをアタッチして投げることも可能だと思います。

畳んで放り投げる

折りかけの状態で、折り目から若干光が漏れたように見えてしまっています。これは薄っぺらいオブジェクトが影を落とす際にありがちな不具合で、ある程度は妥協するべきかと思います。
光源の影設定のうち、バイアス関連の値を小さくすることでいくらかは軽減できますが、他のオブジェクトの影が狂いやすくなってしまうかもしれません。
アニメーションを素早くして、折り畳む途中経過をあまり見せないようにしてごまかすのがよさそうです...

投稿2018/10/14 11:17

編集2018/10/14 21:53
Bongo

総合スコア10807

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

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

退会済みユーザー

退会済みユーザー

2018/10/18 23:37

返信遅くなり申し訳ありませんでした!回答頂きありがとうございます! 丁寧に参考画像やGIFアニメまで……!本当に助かります!ありがとうございます! >アニメーションをループさせない件については、アニメーションクリップの「Loop Time」をオフにする ありがとうございます。ループOFFにすればいけそうです。 >詳しくはアニメーション遷移 - Unity マニュアルをご参照いただくのがよさそうです。 アニメーション自体あまりやっていなく、全然理解できていないため 教えていただいたので分からなければ参照させて頂きます! GIFアニメがやりたい事そのままで、ソースコードまで記載頂き本当に助かります! まだ全部理解できていないので、ソースは1文1文読み込み解読したいと思います! ありがとうございました!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問