おもちゃラボ

Unityで遊びを作ってます

【Unity】なわとびの縄のシミュレーション

スーパーマリオオデッセイのニュードンクシティでは、縄跳びのミニゲームが遊べます。こんな感じで物理挙動に従って動く縄をUnityで作る方法をまとめました。

www.youtube.com

今回作った縄跳びアプリケーションはこんな感じになりました。

f:id:nn_hokuson:20180130195611j:plain:w600

この記事では、縄を物理挙動に従ってシミュレーションする方法にフォーカスして説明します。キャラクタをジャンプさせる方法などは次の記事を参考にしてみて下さい。

nn-hokuson.hatenablog.com

目次は次のようになります。

縄をリアルに動かす方法

縄は20点の質点を使って表現しています。質点を使って縄や布を表現する方法自体は、古くからクロスシミュレーションなどでよく使われている手法です。

andrew.wang-hoyer.com

qiita.com

この20点の質点をPhysicsを使って動かすことで、リアルな縄の動きをシミュレーションします。ただ、各質点にPhysicsを設定しただけではすぐにバラバラになってしまいます。

そこで質点がバラバラに落下しないよう、各質点を接続する必要があります。質点同士を接続するにはUnityのHinge Jointを使用します。「ヒンジ」とは蝶番のことで、質点の移動範囲を円形に固定することができます。

f:id:nn_hokuson:20180130192030p:plain:w500

このHinge Jointをすべての質点間に設定することで、物理挙動に従った縄のうごきを実現できます。2Dでチェーンをつくる場合は、次の記事も参考になると思います。

nn-hokuson.hatenablog.com

縄の質点をつくる

まずは縄の質点をつくります。ヒエラルキーウインドウで「Create」→「3D Object」→「Sphere」を選択してください。スケールは0.3程度の大きさに変更しておいてください。

f:id:nn_hokuson:20180129200734j:plain

作成したオブジェクトを選択し、インスペクタから「Add Component」で「Rigidbody」と「Phsyics/Hinge Joint」コンポーネントをアタッチしてください。ロープを回したときに床と衝突するように「Enable Collision」にチェックを入れます。また、床と衝突したときにヒンジが破綻しないように「Enable Preprocessing」にチェックを入れておきましょう。

f:id:nn_hokuson:20180129204602j:plain:w250 f:id:nn_hokuson:20180129200227j:plain:w250

アタッチできたらヒエラルキーウインドウのSphereをプロジェクトウィンドウにドラッグ&ドロップしてPrefab化します。作成したPrefabを再びシーンビューにドラッグして合計20個のSphereがX軸上に一列に並ぶように配置します。

f:id:nn_hokuson:20180129200919j:plain:w400

ヒンジで質点をつなぐ

続いて質点間のヒンジを繋いでいきましょう。まず一番左のSphereを選択して、インスペクタのHinge JointコンポーネントのConnected Body欄に、一つ右側のSphereをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180129201237j:plain

同じ要領で全てのヒンジを順番に接続します。最後に両端のSphereは重力に従って落下しないようKinematicの設定をします。

f:id:nn_hokuson:20180130193717p:plain:w550

両端のSphereを選択して、Physicsコンポーネントの「Use Gravity」のチェックを外してください。また「Is Kinematic」にチェックを入れておいてください。

f:id:nn_hokuson:20180129201348p:plain:w300

これで物理挙動に従ってだらんと垂れ下がる縄ができました。ここでは分かりやすいように床を追加しています。次は縄を回すスクリプトを作成します。

f:id:nn_hokuson:20180129204115g:plain:w400

縄をまわす

縄を回すのは、そんなに難しくありません。縄跳びなら縄を回す人の区割りを両端のSphereにお願いするだけです。

ここでは両端のSphereに回転させる次のスクリプトをアタッチしましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotate : MonoBehaviour {
    float radius = 1.4f;
    float speed = 7;
    float x;
    float angle = 0;

    void Start () 
    {
        x = transform.position.x;
    }
	
    void Update () 
    {
        angle += speed;

        float y = 1.2f * radius * Mathf.Sin (-angle * Mathf.Deg2Rad);
        float z = radius * Mathf.Cos (angle * Mathf.Deg2Rad);
        transform.position = new Vector3(x, y, z);
    }
}

このスクリプトでは、ゲームオブジェクトをX軸を中心にして回転させているだけです。縄を回す軌跡が楕円になるように、Y軸方向の移動には少しオフセットをのせています。

f:id:nn_hokuson:20180130194241p:plain:w300

スクリプトがつくれたら両端のShpereにドラッグ&ドロップしてアタッチしてください。ロープがちゃんと回転するのが確認できます。

f:id:nn_hokuson:20180129205957g:plain

表示を縄っぽくする

最後に各質点を繋いだ線を引いてなわとびの縄っぽくしましょう。Unityで線を引くにはLine Rendererという(そこそこ)便利な機能が用意されているのでそれを使います。

ヒエラルキーウインドウで「Create」→「Empty Object」を選択し、空のゲームオブジェクトを作成、名前をRopeに変更しておきます。

f:id:nn_hokuson:20180129201512p:plain:w200

続いて、いま作成したRopeをヒエラルキーウインドウで選択し、インスペクタから「Add Component」→「Line Renderere」でコンポーネントをアタッチします。

f:id:nn_hokuson:20180129201542p:plain:w250

最後にLine Rendererを使って各質点の間に線を引くスクリプトを作成します。プロジェクトウィンドウでRopeスクリプトを作成し、次のプログラムを入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rope : MonoBehaviour {

    public GameObject[] vertices = new GameObject[20];
    LineRenderer line;

    void Start () 
    {		
        line = GetComponent<LineRenderer>();
        line.material =  new Material(Shader.Find("Unlit/Color"));
        line.positionCount = vertices.Length;

        foreach (GameObject v in vertices)
        {            
            v.GetComponent<MeshRenderer> ().enabled = false;
        }
    }
	
    void Update () 
    {
        int idx = 0;
        foreach (GameObject v in vertices)
        {
            line.SetPosition(idx, v.transform.position);
            idx++;
        }
    }
}

このスクリプトでは各質点の座標を取得し、LineRendererを使ってその間に線を引いています。LineRendererを使うにはマテリアルを設定する必要があるため、Startメソッドの中でマテリアルと線の太さ、質点の数などを指定しています。

作成したRopeスクリプトをヒエラルキーウインドウのRopeオブジェクトにドラッグ&ドロップしてアタッチします。

最後に、アタッチしたスクリプトのVertices欄に質点オブジェクトの実体を設定します。ヒエラルキーウインドウでRopeを選択し、インスペクタからVertexs欄に各質点オブジェクトをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180129201853j:plain:w550

ゲームを実行すると、質点のSphereは非表示になり、縄が表示されます。

f:id:nn_hokuson:20180129210252g:plain:w400

【Blender】ナイフで切るときにポリゴンの裏側まで切る

Blenderのナイフでポリゴンを普通に切ると手前のポリゴンしか切ることができません。

編集モード時に「Kキー」でナイフモードを起動し、ドラッグ&ドロップで切れ目を入れると・・・下図のようになります。こうみると裏面まで切れていそうですが・・・横から見ると残念、切れてませんよ!

そこで、ナイフで裏面まで透過するように切断する方法を紹介します。

ナイフツールで裏面ポリゴンも切る

ナイフツールを使ってレーザー光線でスパッと切るように切断する(Mayaでいうところのマルチカットツール)には、ナイフモードに入ってから、「Cキー」をクリックします。

するとメニューバーの透過カットがONに変わります。

この状態で、もう一度切ってみて下さい。今度は裏面まで透過カットできました!これでイツデモスッパリバッサリ切れます。

【Unityシェーダ入門】綺麗に半透明のモデルが表示できるシェーダを作る

UnityのStandard Surface Shaderを使うと3Dモデルを半透明で表示することが出来ます。ただ、モデルの形状が複雑な場合には、裏面のポリゴンが見えて汚い表示になってしまうことがあります。

そこで、隠面は表示せず、手前側の面のみ半透明で表示するシェーダを作ってみました。左が綺麗な半透明シェーダを使ったドラゴン、右がStandard Surface Shaderを使って半透明にしたドラゴンです。

f:id:nn_hokuson:20180123193622j:plain

Standard Surface Shaderではドラゴン裏側の羽まで見えてしまっていますね。

f:id:nn_hokuson:20180123194352j:plain:w300

これを解決する方法を紹介していきます。目次は次のとおりです。

半透明でモデルをきれいに表示する方法

綺麗に表示する方法を考える前に、なぜモデルを半透明で表示すると汚く表示されるのかを考えてみましょう。

Unityのサイトによると・・・

Usually semitransparent shaders do not write into the depth buffer. However, this can create draw order problems, especially with complex non-convex meshes. If you want to fade in & out meshes like that, then using a shader that fills in the depth buffer before rendering transparency might be useful.

Unity - Manual: ShaderLab: Culling & Depth Testing

どうやらコレが原因のようです。半透明モデルを描画する際にデプスバッファ(Zバッファ)への書き込みを行わないため、後から裏面を描画すると上書きされてしまうのですね。

これを解決するため、先にデプスバッファにモデルのZ値(デプス値)のみを書き込みます。この時モデルは描画しません。

f:id:nn_hokuson:20180123202047j:plain:w600

これでモデルを表示する場所のZ値だけ正しく更新されます。続けて、そのZ値を参照しながらモデルを描画します。デプス値は正しく設定されているので、Zテストにより裏面は描画されないことになります。

f:id:nn_hokuson:20180123201728j:plain:w600

分かってしまえば、超簡単ですね!では早速シェーダを作ってみましょう。

半透明シェーダプログラム作る

プロジェクトウインドウで右クリックし、Create→Shader→Standard Surface Shaderを選択してください。名前はSemiTransparent.shaderにしました。

f:id:nn_hokuson:20180123194944p:plain:w100

いま作ったシェーダファイルを右クリックし、Create→Materialを選択してください。これでSemiTransparent用のマテリアルが作成できました。

f:id:nn_hokuson:20180123195132p:plain:w220

今回はSurface Shaderを使ってモデルを綺麗に半透明表示するシェーダを作ってみます。SemiTransparent.shaderを開いて次のプログラムを入力してください。

Shader "SemiTransparent" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
	}

	SubShader {
		Tags { "RenderType"="Transparent" "Queue"="Transparent"}
		LOD 200

		Pass{
  		  ZWrite ON
  		  ColorMask 0
		}

		CGPROGRAM
		#pragma surface surf Standard fullforwardshadows alpha:fade
		#pragma target 3.0

		sampler2D _MainTex;

		struct Input {
			float2 uv_MainTex;
		};

		fixed4 _Color;

		void surf (Input IN, inout SurfaceOutputStandard o) 
		{
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Metallic = 0;
			o.Smoothness = 0;
			o.Alpha = c.a;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

このシェーダでは2パスでモデルを描画しています。1パス目では「ColorMask 0」を指定することでモデルは描画せずZバッファ(デプスバッファ)にのみ書き込んでいます。

2パス目で通常通りモデルを描画しています。先にZバッファに値を書き込んでいるため、裏面のモデルはZテストに失敗し描画されません。

実行結果

シェーダが作成できたら、マテリアルをモデルにアタッチしてみましょう。プロジェクトウィンドウからマテリアルをドラッグ&ドロップします。

モデルのインスペクタからマテリアルのColorのアルファ値を操作することで、モデルの透明度を変化させることができます。

f:id:nn_hokuson:20180123195539p:plain:w350  f:id:nn_hokuson:20180123200120p:plain:w200

きれいに半透明のモデルが表示されたら成功です!

f:id:nn_hokuson:20180123195938g:plain:w450

GPU Gems 3 日本語版

GPU Gems 3 日本語版

Amazon