AIプログラムとかUnityゲーム開発について

探索や学習などを活用したAI系ゲームを作りたいと思います。

戦車のキャタピラが起伏にそって波打つ仕組み

 AssetStoreにKawaiiTankとして無料の戦車が配布されているのでDownloadして動かしてみました。
 起伏はterrainで作られていますが、戦車を起伏の上で動かすと、戦車は波打ちながら起伏を乗り越えて移動します。
 そのへんの仕組みを見てみました。

"Kawaii" Tank (Tiger-I) 3Dモデル/乗物/車両
https://www.assetstore.unity3d.com/jp/#!/content/54604
 作ってるのは日本の人です。


f:id:yasu9780:20161201013752p:plain

 まず戦車の構造は、MainBodyの下にHingeJointでSuspensionがぶら下がり、さらにHingeJointでRoadWheelがぶら下がっています。
 接地している走行用車輪は外側に6輪・内側に6輪、それぞれ左右にあります。

 HingeJointなので、地面と衝突したRoadWheelのコライダーが上下すると、バネの動きでMainBodyに伝わります。
 複数のバネに下から支えられた神輿のようなものです。

 凄いなと思ったのは、キャタピラがRoadWheelの上下の移動にそって波打ってます。

これはどうやっているのか?

 Track_Lというのがキャタピラ(左)です。
 キャタピラ自体は造形的にただのMeshです。このMeshを歪めめているのがTrack_Deform_CSというスクリプトです。
 このスクリプトを切ると、車輪が上下してもキャタピラは歪まないので車輪がキャタピラを突き抜けます。

 このスクリプトは、キャタピラを構成するmeshの頂点座標列を、車輪の位置によって上下に動かしている。
 別にボーンが入ってるわけではないけど、ポリゴンってのは頂点の集まりだから、頂点自体を動かせば好きに変形できる。
 車輪が6輪入ってるなら、その車輪の近くのmesh頂点だけを車輪に追随させて動かせば、キャタピラが実現できてしまう

f:id:yasu9780:20161201020540p:plain
 緑のボックスが各走行車輪のAnchor。車輪の上下で一緒に上下する。このAnchorの上下の移動によってキャタピラのMeshが歪んでいる。

実際に、Track_Deform_CSを見てみます。

 メッシュの頂点情報はStart()で読みこんでおきます。
 thisMesh.verticesに、キャタピラのmeshの頂点すべてが配列として保存されている。
 車輪は6輪あるので、6輪ごとに、車輪に近い頂点だけを調べて、車輪ごとに関係する頂点として保存しておく。
 anchorArrayは車輪の存在範囲を持っている。
 車輪に近い頂点のみwithinVerticesList.Add ( j ) でリストに追加される。(boneへのmeshの紐づけのようなもの)
 moveVerticesListは、各車輪のwithinVerticesListを保持する多重リスト。

void Start () {
		thisMesh = GetComponent < MeshFilter > ().mesh ;
		initialPosArray = new float [ anchorArray.Length ] ;
		initialVertices = thisMesh.vertices ;
		// Find vertices in the range.
		for ( int i = 0 ; i < anchorArray.Length ; i ++ ) {
			if ( anchorArray [ i ] != null ) {
				initialPosArray [ i ] = anchorArray [ i ].localPosition.x ;
				Vector3 anchorPos = transform.InverseTransformPoint ( anchorArray [ i ].position ) ;
				List < int > withinVerticesList = new List < int > () ;
				for ( int j = 0 ; j < thisMesh.vertices.Length ; j ++ ) {
					float distZ = Mathf.Abs ( anchorPos.z - thisMesh.vertices [ j ].z ) ;
					float distY = Mathf.Abs ( anchorPos.y - thisMesh.vertices [ j ].y ) ;
					if ( distZ <= widthArray [ i ] * 0.5f  && distY <= heightArray [ i ] * 0.5f ) {
						withinVerticesList.Add ( j ) ;
					}
				}
				moveVerticesList.Add ( withinVerticesList ) ;
			} else {
				Debug.LogError ( "Anchor Wheel is not assigned in " + this.name ) ;
				Destroy ( this );
			}
		}
	}

 実行時は、車輪ごとに関係するmeshのリストはすでにあるので、車輪の動きに合わせて頂点を動かして、変形の終わったmesh情報を、meshとして再代入する。
 なお、Y座標のみ頂点は動かしている。

void Update () {
		Vector3 [] tempVertices = new Vector3 [ initialVertices.Length ] ;
		initialVertices.CopyTo ( tempVertices , 0 ) ;
		for ( int i = 0 ; i < anchorArray.Length ; i ++ ) {
			float tempDist = anchorArray [ i ].localPosition.x - initialPosArray [ i ] ;
			for ( int j = 0 ; j < moveVerticesList [ i ].Count ; j ++ ) {
				tempVertices [ moveVerticesList [ i ] [ j ] ].y += tempDist ;
			}
		}
		thisMesh.vertices = tempVertices ;
	}

RigidBodyの設定を見てみたけど
サスペンションは各100Kg。車輪も各100kg。6x2x2で、計2400kg
さらにSprocketWheelが100x2、IdlerWheelが200x2 計600kg
MainBodyは5000kg
合計で、8トン


ちなみにキャタピラの回転はテクスチャをずらして実現している。スクロールさせている。

	void Update () {
		float currentAng = referenceWheel.localEulerAngles.y ;
		float deltaAng = Mathf.DeltaAngle ( currentAng , previousAng ) ;
		offsetX += scrollRate * deltaAng ;
		thisMaterial.SetTextureOffset ( textureName , new Vector2 ( offsetX , 0.0f ) ) ;
		previousAng = currentAng ;
	}