kazumalab tech log

流行りとリラックマと嵐が大好きです。技術的ログ。

UnityのTerrainで地形を自動生成させてみた!

かずまです。

そろそろ5月、6月も近づいてきました。
6月といえばそう、ゲーム大賞です。

awards.cesa.or.jp

出たい、ただそれだけです。
なので一部ずつ作って行かねば行けませんね。

今日のお題

みんなだいすき、「Terrain」の話題です。
Terrainは結構重いと評判のようです。

さて前にこんな記事を拝見しました。

kan-kikuchi.hatenablog.com
これは単純なブロックで展開しています。
それをごっそりTerrainに置き換えてしまおうということです。

Perlin Noise

Generate 2D Perlin noise.

Perlin noise is a pseudo-random pattern of float values generated across a 2D plane (although the technique does generalise to three or more dimensions, this is not implemented in Unity). The noise does not contain a completely random value at each point but rather consists of "waves" whose values gradually increase and decrease across the pattern. The noise can be used as the basis for texture effects but also for animation, generating terrain heightmaps and many other things.[1]

波の様なものを生成してくれるそう。
しかもただランダムなだけでないみたいですね。
ココらへん勝手にやってくれるあたりC#優しい。

PerlinNoise Terrainでググってみたところ、
...お、日本語の記事が全然ないじゃ~ん!ってことで書いちゃいます。

まぁ、リファレンス読めばできるっしょ!って話みたいなんですが。

TerrainのAPIを読む

正直PerlinNoiseは値を入れてそれに対してシード値を与えて上げるだけなのでサンプルコードをまるまる取ってきてもOKみたいなところがあります。

ここで重要なのはTerrainのAPIをどう使うかです。

重要なクラス

TerrainDataクラス

Terrainを構成している部分になります。
こいつから波形などを生み出しています。

SplatPrototypeクラス

これはTerrainに反映するテクスチャを表すものになります。
インスタンス作って、Texture2Dを渡してあげて、TerrainDateのSplatPrototypeに渡す必要があります。
後に説明。

重要なメソッド

terrainData.SetHeights(int, int, float[,])メソッド

terrainDataのインスタンスメソッドです。
前半2つの引数はどの位置から始めるかというものです。
float[,]が厄介なのですが、これはx,y座標番目の高さを表しています。

ちょっとむずーい。
前回やろうとした時にここで躓いた。

terrainData.SetAlphamaps (int, int, float[,,])メソッド

terrainDataのインスタンスメソッドです。
これも厄介。特にfloat[,,]が。
x,y座標番目になんのTextureが入っているかを表すものです。

このTextureの配列をどう渡すの?かというと、先程説明したSplatPrototypeを使います。
ここにtextureを登録して、terrainDataにセットするとその番号によってTextureが呼ばれます。

さらに紐解くと、terrainDataがSplatPrototypeを受け取るのは複数つまり配列で渡してくれと言っています。
その観点から行くとメソッドを間に挟んであげるといいかもですね。

// TerrainController.cs

public SplatPrototype[] getSplatProttypes (Texture2D[] texs) {
		SplatPrototype[] splayPrototypes = new SplatPrototype[texs.Length];

		for (int i = 0; i < texs.Length; i++) {
			splayPrototypes [i] = new SplatPrototype ();
			splayPrototypes [i].tileSize = Vector2.one;
			splayPrototypes [i].texture = texs [i];
		}

		return splayPrototypes;
	}

もうわからなくなってきましたね。
では次に行きましょう。

その作ったのを受取ます。データサイズは面倒なので一緒にしておきます。

// TerrainController.cs

terrainData.alphamapResolution = 33;
terrainData.heightmapResolution = 33;

terrainData.splatPrototypes = getSplatProttypes (textures);

これであとはSplatPrototypeで高さが決まったらTextureの色を高さに合わせて変えて上げればOKです。
それもメソッドで用意してあげます。

// TerrainController.cs
private int getTextureIndex (float height) {
		if (height < 0.3f)
			return 0;
		if (height >= 0.3f && height < 0.6f)
			return 1;
		if (height >= 0.6)
			return 2;
		return 1;
}

heightのパラメーターの中身はご自由に!
Texture3枚だとこのままで動きますが、5枚とかになる場合はgetTextureIndexも変更してください。
ほんとは可変させたい感。笑

Github

ちなみにgithubにもありますのでそちらもCloneすると使えます。
github.com
それを動かすとこんな感じになります。

動かしてみた

f:id:kazumalab:20170425001838g:plain
おぉー動きました。
Coroutineを使っているのでリアルタイム生成みたいになってます。

Partynote

これから結婚式の季節?!ですので。
party.mwed.jp
めっちゃ投稿簡単なのでぜひ。